4
\$\begingroup\$

Goal: typesafe PropertyChanged events, minimum effort implementation for PropertyChanged events, runtime reflection of properties.

Am I reinventing the wheel or have I created something worthwile? Would you use it in your next project?

public interface StaticProperty {
  public boolean canGet();                     /** assert canGet()] */
  public boolean canSet();
  public boolean isvalid();                    /** return rules().areAllMet() */
  public boolean supportsPersistence();        /** assert instanceof Serializable && new T() */
  public boolean canReturnNull();              /** assert canReturnNull && this.property != null */
  public boolean canSetToNull();               /** assert canSetToNull() && value != null */
  public boolean firesEvent();

  public abstract class AbstractProperty
    implements
      StaticProperty {

    protected boolean canGet = false;          /** All false by default: explicitly set true in base */
    protected boolean canGetNull = false;
    protected boolean canSet = false;
    protected boolean canSetNull = false;
    protected boolean isValid = false;
    protected boolean firesEvent = false;
    protected boolean supportsPersistence;

    protected AbstractProperty() {}
    @Override public boolean canSet() {
      return canSet;
    }
    @Override public boolean canSetToNull() {
      return canSetNull;
    }
    @Override public boolean canReturnNull() {
      return canGetNull;
    }
    @Override public boolean firesEvent() {
      return firesEvent;
    }
    @Override public boolean isvalid() {
      return isValid;
    }
    @Override public boolean canGet() {
      return canGet;
    }
    @Override public boolean supportsPersistence() {
      return supportsPersistence;
    }
  }
}

public class Props {
  /**
   * Any class having either getters, setters, or both
   * With this, you can do:
   *  
   *  for (Property property : someObject.properties()) {
   *    query.select(property.name())
   *         .from(someObject.name());
   *  }
   *    
   * */
  public interface HasProperties {
    public PropertyTypeList properties();
  }

  public interface IsChild<H extends HasSetters> {
    public void doSet(H hasProperty);            /** Dispatch setting to the property itself */
  }

  /**
   * A readonly property  
   * */
  public interface HasGetters extends HasProperties {
    public IsChild getProp(StaticProperty type);
  }

  public interface FakeProperty extends HasGetters {

  }

  /** Write only property */
  public interface HasSetters extends HasProperties {
    public void setProp(StaticProperty type, IsChild value);
  }

  /** Because each property needs a dispatch method set(HasProperty parent), simple value types
   * must be wrapped. */
  public interface Value {
    public interface StringValue extends Value {
      public String value();
    }

    public interface IntValue extends Value {
      public int value();
    }

    public interface DoubleValue extends Value {
      public double value();
    }

    public interface DateValue extends Value {
      public java.util.Date value();
    }

    public interface MultiValue extends Value {
      public interface ValueList extends ImmutableList<Value> {}

      public ValueList value();
    }
  }

  public interface Handler<P extends IsChild> {
    public void changed(P newProp, P oldProp);

    public interface Handle {
      public void remove();
    }
  }

  public interface NotifiesPropertyChange {
    // Type-safe property handling
    public void addHandler(StaticProperty type, Handler handler);

    // Register handling of any property change
    public void addHandler(Handler handler);
  }

  /** Keeps track of property values, fires events, dispatches to property */
  public static interface PropertyList
    extends
      ImmutableList<StaticProperty> {

    public <H extends HasSetters, P extends IsChild> void set(StaticProperty type, P value, H hasProp);
    public IsChild get(StaticProperty type);
    public <H extends HasSetters, P extends IsChild> void setWithoutDispatch(StaticProperty type, P value, H hasProp);
    public PropertyTypeList types();
    public void addHandler(StaticProperty type, Handler handler);
    public void addHandler(Handler handler);

    public static class PropertyTypeList
      extends
        ImmutableListImpl<StaticProperty> {

      public PropertyTypeList(List<StaticProperty> properties) {
        super(properties);
      }
      public PropertyTypeList(StaticProperty... properties) {
        super(properties);
      }
      @Override public String toString() {
        return "PropertyTypeList [items=" + items + "]";
      }
    }

    public static class Impl
      extends
        AbstractImmutableList<StaticProperty>
      implements
        PropertyList {

      private final static boolean doDispatch = true;
      private final static boolean ignoreDispatch = false;

      private List<StaticProperty> types = new ArrayList<StaticProperty>();
      private Map<StaticProperty, IsChild> properties = new HashMap<StaticProperty, IsChild>();
      private Map<Class, List<Handler>> handlers = new HashMap<Class, List<Handler>>();
      private List<Handler> geenricHandlers = new ArrayList<Handler>();

      /** Construct from a list of property types */
      public Impl(PropertyTypeList items2) {
        for (StaticProperty p : items2) {
          properties.put(p, null);
          types.add(p);
        }
      }
      /** Returns all the registered property types */
      public PropertyTypeList types() {
        return new PropertyTypeList(types);
      }
      @Override protected List<StaticProperty> createList() {
        return new ArrayList<StaticProperty>();
      }
      /** Asserts given property is present and returns it */
      public IsChild get(StaticProperty type) {
        assert properties.containsKey(type);
        return properties.get(type);
      }
      /** Asserts given type is present and sets the property, dispatching to the property */
      @Override public <H extends HasSetters, P extends IsChild>
          void set(StaticProperty type, P value, H hasProp) {
        assert properties.containsKey(type.getClass());
        doSet(type, value, hasProp, doDispatch);
      }
      /** Sets target property _without_ dispatching to the property */
      @Override public <H extends HasSetters, P extends IsChild>
          void setWithoutDispatch(StaticProperty type, P value, H hasProp) {
        doSet(type, value, hasProp, ignoreDispatch);
      }
      /** Sets the new value, dispatches to property and fires event */
      private <H extends HasSetters, P extends IsChild<H>>
          void doSet(StaticProperty type, P value, H hasProp, boolean setParent) {
        assert value != null && type.canSetToNull();
        properties.put(type, value);
        if (setParent)
          value.doSet(hasProp);
        if (type.firesEvent())
          onChanged(type, value, null);
      }
      /** Adds handler for target property */
      @Override public void addHandler(StaticProperty type, Handler handler) {
        if (!handlers.containsKey(type.getClass()))
          handlers.put(type.getClass(), new ArrayList<Handler>());
        handlers.get(type.getClass()).add(handler);
      }
      /** Adds handler for any property change event */
      public void addHandler(Handler handler) {
        geenricHandlers.add(handler);
      }
      /** Fires event for target property and generic event */
      public <P extends IsChild> void onChanged(StaticProperty type, P p, P old) {
        List<Handler> handlersForType =  handlers.get(type.getClass());
        if (handlersForType != null)
          for (Handler handler : handlersForType)
            handler.changed(p, old);
        for (Handler hanlder : geenricHandlers)
          hanlder.changed(p, old);
      }
    }

  }


}




  public interface Woei
          extends
          HasName, SetsName,
          HasDescription, SetsDescription,
          NotifiesPropertyChange {

    public static WoeiName nameP = new WoeiName();
    public static WoeiDescription descriptionP = new WoeiDescription();

    /** Extend properties and set rule values after calling the super constructor */
    public static class WoeiName extends AbstractProperty {
      public WoeiName() {
        canSetNull = false;
        firesEvent = true;
        isValid = true;
        canGetNull = true;
      }
    }

    public static class WoeiDescription extends AbstractProperty {
      public WoeiDescription() {
        canSetNull = false;
        firesEvent = true;
        isValid = true;
        canGetNull = true;
      }
    }

    public static class WoeiImpl
            implements Woei {
      private static PropertyTypeList propertyTypes = new PropertyTypeList(nameP, descriptionP);
      private PropertyList properties = new PropertyList.Impl(propertyTypes);
      private Name name;
      private Description description;

      @Override public IsChild getProp(StaticProperty type) {
        return properties.get(type);
      }
      @Override public PropertyTypeList properties() {
        return properties.types();
      }
      @Override public Name name() {
        return name;
      }
      @Override public void setName(Name name) {
        this.name = name;
        properties.setWithoutDispatch(nameP, name, this);
      }
      @Override public void setProp(StaticProperty type, IsChild value) {
        properties.set(type, value, this);
      }
      @Override public Description description() {
        return description;
      }
      @Override public void setDescription(Description description) {
        this.description = description;
        properties.setWithoutDispatch(descriptionP, description, this);
      }
      @Override public void addHandler(StaticProperty type, Handler handler) {
        properties.addHandler(type, handler);
      }
      @Override public void addHandler(Handler handler) {
        properties.addHandler(handler);
      }
    }
  }
\$\endgroup\$
  • \$\begingroup\$ I don't quite understand why you have both canReturnNull and canSetNull. Why not a single permitsNull or so? \$\endgroup\$ – Landei Oct 21 '11 at 9:34
  • 1
    \$\begingroup\$ I'm not sure either. I guess I expect usecases where canGetNull is possible, but not canSetNull. \$\endgroup\$ – Ruudjah Oct 25 '11 at 19:02
5
\$\begingroup\$

I honestly think that your code suffers from over-engineering.

As you can see in your Woei interface and WoeiImpl class, there is a lot of code that is required to use what you are building here. So to be blunt, I have to answer Would you use it in your next project? with NO.


Regarding this code:

for (Property property : someObject.properties()) {
    query.select(property.name())
       .from(someObject.name());
}

If you want to create a SQL query to search for properties like you are doing here, I recommend using Hibernate (which simplifies this a lot -- once you actually get the whole system up and running correctly...)


If I want to execute an event whenever a property has changed, I override the corresponding set-method in my code to do what I want to be done whenever the property is changing.

There are way too many interfaces that has to be implemented/extended for me to want to use your code. I don't think it is worth all the trouble of your code just to make it a bit more typesafe, I would rather use the built-in Java Reflection API.

\$\endgroup\$
  • \$\begingroup\$ Only took 2 years before he got an answer! \$\endgroup\$ – Max Nov 26 '13 at 13:51
  • \$\begingroup\$ @user1021726 That's because we're on a mission now. \$\endgroup\$ – Simon Forsberg Nov 26 '13 at 13:59
  • \$\begingroup\$ Oops didn't know! \$\endgroup\$ – Max Nov 26 '13 at 14:30
  • \$\begingroup\$ @user1021726 To help us on our mission please vote on questions and answers you find good. There are very few voters around here \$\endgroup\$ – Simon Forsberg Nov 26 '13 at 14:37

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.