Last updated August 31, 2009 06:55, by 5er_levart
Feedicon  

XProperty, ExpressionProperty, Property$, ...

The idea I had the other day about property factories wouldn't let me sleep until I implemented it. The difference between the idea and implementation is: I dropped the special Props interface from implementation and rather based all bean property factory types on an abstract org.jdesktop.beansbinding.Property subclass, which means that it is not possible to use multiple inheritance for property factory types but that is not a real drawback in practice as I found out so far. I also added a couple of other useful things and now that they have been used successfully in a project I do at my work, I decided to pack them up and share them with the community to get some feedback.

When you clone the incubator mercurial repository at:

    https://kenai.com/hg/betterbeansbinding~incubator-src

you will find a subfolder called XProperty. It's a standalone maven project, dependent only on betterbeansbinding-core and javassist (and junit for tests).

XProperty

The name XProperty stands for "eXtended Property" and it actually is an abstract extension to org.jdesktop.beansbinding.Property class. It defines additional utility API usefull when using it's concrete subclasses. All property implementations in this project are subclasses of XProperty.

The main addition to this public API is the following method:

 
    public PropertyValue<S, V> of(S source);
 

it returns an object called PropertyValue representing the property and source object pair. This is something that is missing from beansbinding API. Some call this a "bound property" (which is misleading since word "bound" has different meaning here), others rather call this a "tethered property", but I simply called it a "property value".

PropertyValue

PropertyValue is an interface with methods that delegate to underlying property's methods and methods used when binding two properties together:

 
 public interface PropertyValue<SS, SV>
 {
    //
    // obtaining underlying source and property
 
    SS getSource();
    Property<SS, SV> getProperty();
 
    //
    // delegating to underlying property for underlying source
 
    Class<? extends SV> getWriteType();
    SV getValue();
    void setValue(SV value);
    boolean isReadable();
    boolean isWriteable();
    void addPropertyStateListener(PropertyStateListener listener);
    void removePropertyStateListener(PropertyStateListener listener);
    PropertyStateListener[] getPropertyStateListeners();
 
    //
    // binding
 
    <TS, TV> AutoBinding<SS, SV, TS, TV> readWriteBind(PropertyValue<TS, TV> propertyValue);
    <TS, TV> AutoBinding<SS, SV, TS, TV> readBind(PropertyValue<TS, TV> propertyValue);
    <TS, TV> AutoBinding<SS, SV, TS, TV> readOnceBind(PropertyValue<TS, TV> propertyValue);
    <TS, TV> AutoBinding<SS, SV, TS, TV> bind(AutoBinding.UpdateStrategy strategy, PropertyValue<TS, TV> propertyValue);
 ...
 

ExpressionProperty

A useful concrete XProperty subclass is ExpressionProperty (well, it's actually abstract, but it has lots of static factory methods that return different anonymous implementations). It is used to produce property implementations out of other properties - to build expressions out of properties. This is meant to replace partially what ELProperty offers, but with no DSLs in strings - just pure java.

For illustration, here's the set of expression methods you can use so far:

 
    public static <S> XProperty<S, Boolean> not(Property<S, Boolean> property)
    public static <S> XProperty<S, Boolean> and(Property<S, Boolean>... properties)
    public static <S> XProperty<S, Boolean> or(Property<S, Boolean>... properties)
 
    public static <S> XProperty<S, Boolean> isNull(Property<S, ?> property)
    public static <S> XProperty<S, Boolean> isNotNull(Property<S, ?> property)
 
    //
    // comparisons
 
    // property <=> value
 
    public static <S, V extends Comparable<V>> XProperty<S, Boolean> gt(Property<S, V> property, V value)
    public static <S, V extends Comparable<V>> XProperty<S, Boolean> ge(Property<S, V> property, V value)
    public static <S, V extends Comparable<V>> XProperty<S, Boolean> lt(Property<S, V> property, V value)
    public static <S, V extends Comparable<V>> XProperty<S, Boolean> le(Property<S, V> property, V value)
    public static <S, V extends Comparable<V>> XProperty<S, Boolean> eq(Property<S, V> property, V value)
 
    // property1 <=> property2
 
    public static <S, V extends Comparable<V>> XProperty<S, Boolean> gt(Property<S, V> property1, Property<S, V> property2)
    public static <S, V extends Comparable<V>> XProperty<S, Boolean> ge(Property<S, V> property1, Property<S, V> property2)
    public static <S, V extends Comparable<V>> XProperty<S, Boolean> lt(Property<S, V> property1, Property<S, V> property2)
    public static <S, V extends Comparable<V>> XProperty<S, Boolean> le(Property<S, V> property1, Property<S, V> property2)
    public static <S, V extends Comparable<V>> XProperty<S, Boolean> eq(Property<S, V> property1, Property<S, V> property2)
   
    //
    // null safe equality
  
    public static <S> XProperty<S, Boolean> equal(Property<S, ?> property, final Object value)
    public static <S> XProperty<S, Boolean> equal(Property<S, ?> property1, Property<S, ?> property2)
    public static <S> XProperty<S, Boolean> notEqual(Property<S, ?> property, final Object value)
    public static <S> XProperty<S, Boolean> notEqual(Property<S, ?> property1, Property<S, ?> property2)
 
    //
    // string expressions
 
    public static <S> XProperty<S, String> join(Property<S, ? extends CharSequence>... properties)
    public static <S> XProperty<S, String> join(final CharSequence delimiter, Property<S, ? extends CharSequence>... properties)
    public static <S> XProperty<S, String> format(final String format, Property<S, ?>... properties)
    public static <S> XProperty<S, String> format(final Locale locale, final String format, Property<S, ?>... properties)
 
    //
    // collections
 
    public static <S, V extends Collection<?>> XProperty<S, Boolean> isEmpty(Property<S, V> property)
    public static <S, V extends Collection<?>> XProperty<S, Boolean> isNotEmpty(Property<S, V> property)
    public static <S, V extends Collection<?>> XProperty<S, Integer> size(Property<S, V> property)
    public static <S, E, V extends Collection<E>> XProperty<S, E> firstElement(Property<S, V> property)
    public static <S, E, V extends Collection<E>> XProperty<S, E> singleElement(Property<S, V> property)
 
    //
    // tupples
 
    public static <S> XProperty<S, Object[]> tupple(final Property<S, ?> ... properties)
  

...and if that is not enough you can always create a subclass of ExpressionProperty and implement a single method that calculates the value of "expression" from the values of parameters.

Property$

And here we are at the final implementation of XProperty that started it all, the Property$. It has an unusual name ending with character $. That's to distinguish it from normal Property but also to visually associate it with the convention I used for other related things I will describe here. It is in essence just a delegating property implementation that can wrap any existing beansbinding property implementation like BeanProperty or ObjectProperty. Its main purpose is that it can be used as a base class to create abstract subclasses that act as property factories for sub-properties of a bean property that they represent.

The easyest way to describe this is with an example (from a unit test in the XProperty project):

 
 public class Address extends AbstractBean
 {
    public static final $<Address, Address> $ = Property$Factory.create($.class);
 
    public static abstract class $<S, V extends Address> extends Property$<S, V>
    {
        public abstract Property$<S, String> street$();
        public abstract Property$<S, Integer> houseNumber$();
        public abstract Property$<S, String> city$();
        public abstract Property$<S, String> country$();
    }
 
    private String street;
    private int houseNumber;
    private String city;
    private String country;
 
    public String getCity()
    {
        return city;
    }
 
    public void setCity(String city)
    {
        firePropertyChange($.city$().getPropertyPath(), this.city, this.city = city);
    }
 
    public String getCountry()
    {
        return country;
    }
 
    public void setCountry(String country)
    {
        firePropertyChange($.country$().getPropertyPath(), this.country, this.country = country);
    }
 
    public int getHouseNumber()
    {
        return houseNumber;
    }
 
    public void setHouseNumber(int houseNumber)
    {
        firePropertyChange($.houseNumber$().getPropertyPath(), this.houseNumber, this.houseNumber = houseNumber);
    }
 
    public String getStreet()
    {
        return street;
    }
 
    public void setStreet(String street)
    {
        firePropertyChange($.street$().getPropertyPath(), this.street, this.street = street);
    }
 }
 

Above is a standard JavaBean with "bound" properties that fire PropertyChangeEvents when you call it's setters. What differs it from a normal JavaBean is the addition of an abstract static inner class called $ that is a subclass of Property$ and declares some methods that resemble the names of properties of this JavaBean (methods end with character $ to distinguish them from other methods provided by Property$) and act as factories of sub-properties of the property represented by this class.

But that's an abstract class. Where's the implementation? It is built automatically at runtime. I use javassist for this purpose. It's all hidden in a Property$Factory class used to provide an implementation instance that is assigned to a public static final field of the above Address class called $ (can you spot the pattern?).

With all this you can write in your code:

 
    Address.$.street$();
 

instead of:

 
    BeanProperty.create("street");
 

and it's even more type-safe since the generic parameters of the Property type are not infered from the context but are fixed to correct types.

Using this in conjunction with PropertyValue and ExpressionProperty one can write such things (also from a unit test):

 
        format(
            "%1$s %2$d, %3$s, %4$s",
            Employee.$.address$().street$(),
            Employee.$.address$().houseNumber$(),
            Employee.$.address$().city$(),
            Employee.$.address$().country$()
        ).of(employee).readBind(
            Employee.$.descriptiveAddress$()
                .of(employee)
        );
 

Ckeck out the code and try it for yourself.

Following is the original text of the idea that this code is based on but with changes described above...

An Idea for Property factories

Currently creating properties with JSR-295 API is possible by using BeanProperty or ELProperty factory methods which involves specifying either property paths or EL expressions in the form of strings. This is of course undesirable since strings are not "understood" by various IDEs out there when re-factoring. It also makes code cluttered with boilerplate.

The complete solution would involve Java Language support for properties which is a far-reaching goal. In the meanwhile community could explore what Java properties should actually represent from the programming/API viewpoint and then perhaps some day Java Language could incorporate support for pretty syntax.

Here's an idea of BeanProperty factories that let you create BeanProperty instances and chain them together without specifying a single String.

Take for example the following two standard Java Beans with bound JavaBean properties:

Person:

  
    public class Person extends AbstractBean
    {
        private String name;
        private int age;
        private Address address;
    
        public Address getAddress()
        {
            return address;
        }
    
        public void setAddress(Address address)
        {
            firePropertyChange("address", this.address, this.address = address);
        }
    
        public int getAge()
        {
            return age;
        }
    
        public void setAge(int age)
        {
            firePropertyChange("age", this.age, this.age = age);
        }
    
        public String getName()
        {
            return name;
        }
    
        public void setName(String name)
        {
            firePropertyChange("name", this.name, this.name = name);
        }
    }
 

and Address:

 
    public class Address extends AbstractBean
    {
        private String street;
        private int houseNumber;
        private String city;
        private String country;
    
        public String getCity()
        {
            return city;
        }
 
        public void setCity(String city)
        {
            firePropertyChange("city", this.city, this.city = city);
        }
 
        public String getCountry()
        {
            return country;
        }
 
        public void setCountry(String country)
        {
            firePropertyChange("country", this.country, this.country = country);
        }
 
        public int getHouseNumber()
        {
            return houseNumber;
        }
 
        public void setHouseNumber(int houseNumber)
        {
            firePropertyChange("houseNumber", this.houseNumber, this.houseNumber = houseNumber);
        }
 
        public String getStreet()
        {
            return street;
        }
 
        public void setStreet(String street)
        {
            firePropertyChange("street", this.street, this.street = street);
        }
    }
 

AbstractBean here is just a helper superclass that encapsulates delegation to PropertyChangeSupport methods.

Then let's create a special base interface:

 
    public interface Props<S, B>
    {
        Property<S, B> this$();
    }
 

We can now add the following code to the standard Java Beans described above:

1st for Address:

 
    public class Address extends AbstractBean
    {
        public static final P$<Address, Address> P$ = PropsFactory.props(P$.class);
  
        public interface P$<S, B extends Address> extends Props<S, B>
        {
            Property<S, String> street();
            Property<S, Integer> houseNumber();
            Property<S, String> city();
            Property<S, String> country();
        }
 
        private String street;
        private int houseNumber;
        private String city;
        private String country;
        ...etc
 

then for Person:

 
    public class Person extends AbstractBean
    {
        public static final P$<Person, Person> P$ = PropsFactory.props(P$.class);
 
        public interface P$<S, B extends Person> extends Props<S, B>
        {
            Property<S, String> name();
            Property<S, Integer> age();
            Address.P$<S, Address> address();
        }
 
        private String name;
        private int age;
        private Address address;
        ...etc
 

Let's not go into the details of PropsFactory class for now. First let us see what benefit do we have with this.

Instead of writing:

 
    BeanProperty.<Person, String>create(BeanProperty.<Person, Address>create("address"), "street"); // or
    BeanProperty.<Person, String>create("address.street"); // or
    ELProperty.<Person, String>create("${address.street}");
 

We can write:

 
    Person.P$.address().street();
 

Inheritance

The main idea here is to maintain (in the future perhaps the compiler or even runtime could do this automatically) a parallel hierarchy of interfaces modelled to match the hierarchy of Java Bean classes (or even interfaces). The extension of the above Person would then look like:

 
    public class Employee extends Person
    {
        public static final P$<Employee, Employee> P$ = PropsFactory.props(P$.class);
    
        public interface P$<S, B extends Employee> extends Person.P$<S, B>
        {
            Property<S, String> employeeNumber();
        }
    
        private String employeeNumber;
        ...etc
 

So now you can write:

 
    Person.P$.address().city(); // or
    Employee.P$.address().city(); // or
    Employee.P$.employeeNumber();
 

but you can not compile this:

 
    Person.P$.employeeNumber();
 

The fact that P$ is a public static final field defined in a Java Bean class is just a convenience so that the syntax for accessing Property objects is as short as possible. In case of auto-generated interfaces it could be put and accessed inside each such interface. Since Property objects are (in this situation) stateless and reusable, the number of instances that must be created is actually the number of different property paths traversed during the program lifetime. The root of property path is anchored on the static final field and is created when the particular class is initialized (in our situation Java Bean class). Chained properties are created (and cached on base Props instance) lazily as requested.

The duality between Property<S, V> and Props<S, V>

Special Props<S, V> interface that each inner P$ interface must extend is a container for this$() Property. Idealy it could be the Property<S, V> type itself, but unfortunately org.jdesktop.beansbinding.Property is an abstract class - not an interface. The requirement for Props to be an interface is in supporting the same sheme of Property factories to be used on Java Bean style of interfaces (interfaces with setter/getter methods). For example if we have the following two interfaces:

 
    public interface Named
    {
        P$<Named, Named> P$ = PropsFactory.props(P$.class);
 
        interface P$<S, B extends Named> extends Props<S, B>
        {
            Property<S, String> name();
        }
 
        String getName();
        void setName(String name);
    }
 
    public interface Coded
    {
        P$<Coded, Coded> P$ = PropsFactory.props(P$.class);
 
        interface P$<S, B extends Coded> extends Props<S, B>
        {
            Property<S, String> code();
        }
 
        String getCode();
        void setCode(String code);
    }
 

Then a Java Bean implementing both interfaces would look like:

 
    public class City implements Named, Coded
    {
        P$<City, City> P$ = PropsFactory.props(P$.class);
 
        interface P$<S, B extends City> extends Named.P$<S, B>, Coded.P$<S, B>
        {
        }
 
        // etc...
 

Since we model parallel hierarchy of Java Bean types and property factory types we must support multiple inheritance for property factory types.

So the question is: "Why is org.jdesktop.beansbinding.Property not an interface?"

In my opinion it could an should be an interface.


Minor comments


One thing that you might want to consider is to call the static member 'properties' (I know that P$ is easy to write - but to mimic the behavior of MyObject.class with MyObject.properties.aProperty() would make this approach a bit closer to what would likely occur if they ever added property support to the language - MyObject.properties.aProperty).


Fabrizio Giudici: I believe that the additional code could be easily generated by an annotation processor. I believe there is already some project based on the annotation processor for adding PropertyChangeSupport to a simple bean with getters / setters; it could be further enhanced for our stuff.


Peter Levart: My objection to those tools is that if program code directly references generated code, you have a chicken/egg problem and if IDE does not see the generated code you have another problem. My approach described above uses minimal manual additions to existing code (interfaces and constant fields) which is all that program code references, so they are fully self-sufficient and re-factor-able. The generated stuff is hidden behind interfaces so it can be generated Just In Time during program execution. This also eliminates some binary compatibility problems.----


Fabrizio Giudici: this info got from the JavaPosse mailing list could be interesting:

Discussions about the future direction of java have been popular lately, so I thought I'd add another option to the mix:

Project Lombok modifies your development environment to enable extra java language features. Right now lombok can inject itself into both javac and eclipse and offers both Automatic Resource Management and generating getters, setters, equals, hashCode, and toString, and a couple of other features. A screencast takes you through how it works and even shows you how to install it, and clocks in at less than 4 minutes. It's open source (MIT license).

Full Disclosure: It's my project (with some friends).

What do you think? Download and video at http://projectlombok.org/

Reinier Zwitserloot


  • Mysql
  • Glassfish
  • Jruby
  • Rails
  • Nblogo
Terms of Use; Privacy Policy;
© 2013, Oracle Corporation and/or its affiliates
(revision 20131025.e7cbc9d)
 
 
Close
loading
Please Confirm
Close