By April 3, 2012 7 Comments Read More →

This… This!

This post has 329 words. Reading it will take approximately 2 minutes.

GD Star Rating
loading...

This is why I often want to kill when dealing with XES…  The API is designed for implementation, not for use.

So, what’s wrong with the above?

First, the fact that I need a class EventWrapper.  The reason is that XES does not define equals and hashCode correctly.  Each XEvent is assigned a GUID (globally unique ID), which is used for equality check.  This means that no two events are equal to one another.  While there is some sense to two events in the same log not being equal to one another, XES does have an answer (actually two) for that, namely an instance number, indicating which pairs of start and complete events belong together and is required to be unique to (at least) a trace, and a separate ID attribute type, which could perfectly well implement the GUID if really (really, really, really) needed.  Both hashCode and equals can be implemented efficiently using attributes using caching and an incremental hash function and using the cached hash code in the comparison.

Second, the attributes are almost but not exactly maps from keys to values.  This means that even though each of the specific types of attributes have a getValue method, the super-interface does not.  Granted it was probably designed before auto-boxing, but it is still possible to do manually.  It does not even have to cost time nor space (using overloading and unboxing).  I have already previously argued against attributed being something special, but that seems to be a popular functionality (though the usage is very limited at the moment), but this is just ridiculous.  Not only does my code rely on sub-class behavior, it also breaks the moment somebody decides to implement a new kind of attribute.

I could of course fix this fix this using reflection, but that would be the equivalent of fixing a broken finger with a tank.  By driving over it repeatedly.  Overkill and introducing a new load of problems.

Posted in: Uncategorized

7 Comments on "This… This!"

Trackback | Comments RSS Feed

  1. Jan Martijn says:
    GD Star Rating
    loading...

    You forgot one! XAttributeID 🙂

  2. Danny says:
    GD Star Rating
    loading...

    Yeah, I encountered these issues last year too. I managed to fix enough of this to be able to compare XAttribute instances by key and value without having to type-cast them manually.

    On the other hand, you are reaching for the actual type of the attribute. My reasoning was that normally you would only need to access the actual type and value if you already know what type of data you are accessing. (I.e. you know that an attribute named ‘foo’ will carry a value of type ‘bar’.)

    I expected the XVisitor interface to be a Visitor-design pattern implementation introduced in order to tackle exactly this problem, however this was not the case. I’m curious whether there’s a nice solution for this kind of use case. I know I did my best not to create these kinds of constructs, because new XAttribute-types may be created at any time. (Dynamic typing would be really useful here as it would obviate the need for these kind of type-checking-based constructions :-D)

    • Michael says:
      GD Star Rating
      loading...

      You can actually quite easily make this right using covariance; e.g.:

      interface XAttribute {
      Object getValue();
      }

      interface XAttributeInteger extends XAttribute {
      Integer getValue();
      }

      You can add a getIntValue() returning the primitive type int to avoid conversion if the value is stored internally as the primitive type. You can extend this to setters as well using generics

      interface XAttribute<T> {
      T getValue();
      void setValue(T v);
      }

      interface XAttributeInteger extends XAttribute<integer> {
      Integer getValue();
      void setValue(Integer v);
      int getIntValue();
      void setIntValue(int v);
      }

      This could also be done elegantly in a visitor, suppose we have:

      interface XVisitor {
      void attribute(XAttribute a);
      void attributeInteger(XAttributeInteger ai);
      }

      Then we would have:

      abstract class XAttributeImpl implements XAttribute {
      void visitFrom(XVisitor visitor) {
      visitor.attribute(this);
      }
      }

      class XAttributeIntegerImpl extends XAttributeImpl implements XAttributeInteger {
      int value;
      int getIntValue() { return value; }
      void setIntValue(int value) { this.value = value; }
      void Integer getValue() { return getIntValue(); } // Autoboxing = :-)
      void void setValue(Integer value) { setValue((int) value); } // Autounboxing also = :-)
      void visitFrom(XVisitor visitor) {
      super.visitFrom(visitor);
      visitor.integerAttribute(this);
      }
      }

      This gives you the best of both worlds: type-safe and efficient specific operations, type-safe generic operations (except for setValue if you set the value to the wrong type), and the ability to handle all integer attributes as one and also to handle all attributes, current and future, as one.

      • Danny says:
        GD Star Rating
        loading...

        Hmmm… is the first solution you explain that much different from the original? I mean, you still don’t know the exact type of attribute, hence an Object will get returned. You will still have to do type checking to find out what type it is, before you can cast it to the appropriate value. This is similar to when you know that you have a XAttributeDiscrete instance, you can simply call getValue().

        There is however a definite advantage in that you don’t have to cast in order to get the value instance. In that sense, I guess it would be a nice addition to have a ‘getValue’ method that returns an Object in the XAttribute interface.

        ‘getIntValue’ would be a nice addition, but not really necessary, since with autoboxing you can simply assign it to the corresponding primitive type. (If my reasoning is correct.) Oh right, if internally stored as a primitive. I guess the same holds, for example, for attributes that are a composition of values. (To avoid having to return some sort of a collection or “Pair”-object that combines the values into one.)

        Yeah, like I said, the Visitor pattern is a valid solution for this kind of problem. And, although it very elegantly applies the properties of polymorphishm and it basically solves most (all?) of the problems, I don’t think it’s a very “nice” (as in easy, compact, little complexity, etc.) solution.

        Also, you’re cheating 😉 The XVisitor interface is the (general) interface. It shouldn’t have an ‘attributeInteger’ method. You need an ‘attribute’ method with a specific (sub)type of implementation class which can be resolved by the JIT at runtime using late binding. So, actually we’re reversing dependencies and making the assumption that the XES model interfaces don’t change that much over time. Otherwise the XAttribute implementation would have to be extended again. (Or something like that, it’s getting late, but I think I’m on the right track ;-P)

        • Michael says:
          GD Star Rating
          loading...

          Yeah, unless you know you are processing an integer, you can only get an object with the generic characteristics. If you need the more specific type, you need to test for it, but doing like this allows you to do something like

          if (attribute.getValue() instanceof Number) {
          Number n = (Number) attribute.getValue();
          ...

          allowing you to handle integer and double attributes as one, as well as handling future hypothetical ComplexNumbers.

          The getIntValue is there purely for efficiency – there is never a reason not to give as much information as is available.

          The visitor pattern is less for avoiding the type check (you would just mask it as a null-check like

          Integer value = null;
          log.visitFrom(new XVisitorAdaptor() {
          public attributeInteger(XAttributeInteger ia) {
          value = ia.getValue();
          }
          });
          if (value != null) { ...

          It is for efficient and type-safe traversal (and has relations to the function fold used in functional languages, which is sort-of replicated in Googles Map-Reduce framework). The visitor is very close to the hierarchy and is allowed to know the internals. There is no “abstract” visitor. Typically one would keep source-code compatibility by allowing specifying a XVisitorAdaptor, providing dummy implementations of all methods required by the interface (via-à-vis, Java’s MouseListener and MouseAdapter).

          What you are talking about cannot be expressed in a statically typed language. You can look to dynamically typed languages, but they provide a whole new bag of hurt.

          • Michael says:
            GD Star Rating
            loading...

            Forgot to add: in my code in the post, what I’m trying to do is just to get all attributes of any type in order to be able to state that two events are the same if they have the same attributes. Using an abstract getValue method, this could be done as:

            for (Entry<String, XAttribute> e : event.getAttributes().entrySet()) {
            attributes.put(e.getKey(), e.getValue().getValue())
            }

            and would work with any current and future extensions. Similarly, it would be useful for operational support to be able to handle arbitrary “numerical” values and do standard processing using:

            Node node = ...;
            for (Entry<String, XAttribute> e : event.getAttributes().entrySet()) {
            if (e.getValue().getValue() instanceof Number) {
            Number n = (Number) e.getValue().getValue();
            node.assignValue(e.getKey(), n);
            node.computeStatistics(e.getKEy(), n);
            ...

            All other values could then be assumed to be “weird” and handled separately. This of course sort-of breaks doen as Dates aren’t Numbers.

Post a Comment