wiki:PRO_LIB_CORE_TUTORIAL
Last modified 11 years ago Last modified on 07/15/09 19:26:26

Properties Library Tutorial

The ProLib is a library written in Java which is intended to serve as somewhat an intermediate layer between client Sophie2 code and Java code. It has concepts and functionality which help solve a list of problems in an elegant (and hopefully efficient) manner. Its purpose is to ease the developer, not burden him, and in many cases leads to improved readability, more compact code which is also less error-prone if used correctly. Also, the problems which the ProLib solves are done (mostly) invisible to the developer so in the general case he shouldn't worry about how the ProLib works internally but just follow a set of rules and conventions for proper usage.

The ProLib is now in a relatively stable state, though there are still new ideas to implement and integrate inside it as well as some performance optimizations.

Basically, the structure looks like this:

source:branches/private/gogov/PRO_LIB_CORE_TUTORIAL_R1/sophie2-platform/modules/org.sophie2.core/src/test/resources/Layers.png

So, most Sophie2 client code is intended to use the ProLib in the cases when the ProLib solves problems and is helpful and use normal Java code in other cases.

As you see above, the ProLib doesn't depend on Sophie in any sense, so it's universal and any application can use it.

ProLib basics

Here we'll provide a simple comparison between ProLib code and normal Java code as well as an example.

ProObject & Properties vs Java Object & Java fields

In Java, there are Objects and each Object has methods and fields.

In the ProLib, there are ProObjects and each ProObject has normal Java methods and Properties instead of fields. The different concept here are that Properties are something like smart Java fields.

  • You can create a ProObject by implementing the ProObject interface or by using the default implementation BaseProObject
  • You can declare Properties inside the ProObject with Java methods which return a Prop<T> (later you'll get what is this), where T is a concrete class like Integer. The method executes some logic and returns the actual Property.
    • There are different kinds of Properties, so inside these methods you can provide logic which returns the desired kind of Property.

Example

Let's look at the following example:

// Basic rectangle class which is ProObject.
class Rectangle extends BaseProObject {
        // Integer property for the width of the rectangle. Analogical to an Integer Java field.
        RwProp<Integer> width() {
                return getBean().makeValueProp("width", Integer.class);
        }

        // Integer property for the height of the rectangle. Analogical to an Integer Java field.
        RwProp<Integer> height() {
                return getBean().makeValueProp("height", Integer.class);
        }

        // Smart property which returns the area of the rectangle.
        Prop<Integer> area() {
                class area extends AutoProperty<Integer> {
                        @Override
                        protected Integer compute() {
                                Integer res = width().get() * height().get();
                                return res;
                        }
                }

                return getBean().makeProp(area.class);
        }
}

// this is something like pseudo ProLib code which demonstrates basic ProLib usage
public void demoCode() {
    // create a new Rectangle
    Rectangle rectangle = new Rectangle();		

    // set the width to 6
    rectangle.width().set(6);

    // set the height to 8
    rectangle.height().set(8);

    // it is true then that the area is equal to 6*8 = 48
    assertEquals(48, (int)rectangle.area().get());
}

So, you can see the analogy with Java code, though the difference is with the area() Property. This is an AutoProperty: a property which is automatically computed. In the compute() method you specify the logic of the computation, and then this Property gets automatically computed. Even more, when the width and height change, this Property immediately gets recomputed and stay up-to-date which is very useful.

Return statements might seem a bit awkward, though later in the tutorial you'll learn how to use and understand them.

Problems Solved

So, ProLib is intended to be used as somewhat intermediate layer between client code and normal Java code, thus some concepts are implemented in the ProLib and thus all client code which uses ProLib can benefit from these features.

The ProLib gives solution to the following problems:

  • Dependencies, initialization order and updating problems:
    Let's say we've got lots of objects which we're using in our application. These objects depends on each other in a sense that:
    • object A holds a reference to object B;
    • object C must be initialized before object D because D needs data from object C to initialize itself;
    • object E changes and object F depends on E, so F needs to get informed when E changes, in order to update itself.

The ProLib helps solve this problem by introducing somewhat a declarative way to state such dependencies and then handles initialization order and update notifications in an invisible and convenient manner for the client code developer.

  • Undo/Redo:
    Serving as an intermediate layer helps the ProLib to keep histories of changes which occur to the ProObjects and properties involved in our application and thus the ProLib provides (at least soon will provide) a mechanism to automatically undo and redo these changes.
  • Save/Load:
    The ProLib has the means for automatic persistence of objects which use it, as well as custom persistence options which let the developer choose how he want his object to be persisted which helps implementing saving and loading as well as transmission over a network by serializing, for instance, much simpler for the developer.
  • Optimization:
    Being an intermediate layer allows the ProLib to serve as a central point for performance optimizations of the application and profiling and searching for bottlenecks.
  • Safety:
    The ProLib helps (in the future in will help even more) to detect errors in the client code such as cyclic dependencies between objects (in the cases when such dependencies are unacceptable), missing logic, etc.

It's fundamental to know that there are not many defensive mechanisms implemented in the ProLib against improper usage yet, so the developer can easily write stupid and not working ProLib code, though if he does it in the way ProLib is intended to be used, it will save him lots of time and headache. So, at least for now, a list of rules and recommendations on proper ProLib usage should be followed, which will be described later.

ProObjects and Properties

Now, we'll describe the ProLib fundamental classes and interfaces, what is their purpose and how are they intended to be used.

ProObjects

source:branches/private/gogov/PRO_LIB_CORE_TUTORIAL_R1/sophie2-platform/modules/org.sophie2.core/src/test/resources/ProObjects.png

  • ProObject is the base interface for all objects which want to use Properties.
  • BaseProObject is the default implementation of ProObject. You should use it when possible to ease your life.
  • Each ProObject is associated one-to-one with its own ProBean. A ProBean is basically the manager of the ProObject - it takes care for creating and remembering the Properties of the ProObject, initalizing them and other administrative work.
  • Each ProObject can declare one or many Properties, like each Java object can declare one or many fields and methods.

Properties

source:branches/private/gogov/PRO_LIB_CORE_TUTORIAL_R1/sophie2-platform/modules/org.sophie2.core/src/test/resources/Property Hierarchy.png

Interfaces and classes

Above the gray line are basic interfaces which Properties implement. Below the gray line are classes which implement these interfaces.

Fundamental (GREEN)

These are the fundamental interfaces and classes for all Properties.

  • Prop<T>:
    This is the most basic interface which all Properties implement.
    • Each Property holds an object, and with this interface you can use get() to get this object (which is actually the value of the Property).
    • You can also get the owner of the Property (which is a ProObject) by using the owner() method.
  • RwProp<T>:
    Extends the Prop<T> interface.
    • The only thing this interface adds is the ability to set the value of the Properrty with the set() method. Thus Prop<T> provides read-only access and RwProp<T> provides read-write access to the value of the Property.
  • ListProp<T>:
    While Prop<T>s hold reference to a single value, ListProp<T>s holds a reference to a ProList<T> which is the base interface for lists in the ProLib.
    • You get the number of values this Property holds by the size() method;
    • You can also get the value at a valid position in the ProList<T> by the get(int index) method.
  • RwListProp<T>:
    This interface extends ListProp<T> and in analogy with RwProp<T>, provides read-write access to the ProList<T> which is the RwListProp<T> holds.
    • You can add elements at the end of the underlying ProList<T>, remove a given element from it, or change a given element at a valid position by using the add(T element), set(int index, T element) and remove(int index) methods respectively.
  • Property<T>:
    This is the base abstract implementation of the Prop<T> interface.
    • It allows you to set an initial value to the Property by the init() method.
      You should extend this class if you want to provide new Property kinds in the ProLib, like MapProperty for instance (however this is a very difficult task and one is not recommended to do so, unless they know what they are doing).
  • ObjectProperty<T>:
    This class extends Property<T> and provides the base usable implementation of Prop<T> used for dealing with single object values.
    • It implements the get() logic. You should use this class when you need single Properties which you only need to initialize and get.
  • ListProperty<T>:
    This is the base abstract implementation of the ListProp<T> interface.
    • It provides basic get(), get(int index) and size() implementation. You should extend this class if you want to provide new ListProperty kinds in the ProLib, like SkipListProperty (which internally uses skiplists) for instance (again remember that this is not so trivial and one is not recommended to do so, unless they know what they are doing).

Single Properties (BLUE)

These are classes which extend ObjectProperty<T> and thus inherit its capabilities, though they implement specific behavior for specific purposes.

  • ValueProperty<T>:
    This Property is the equvalent to a Java field of type T which allows getting and setting a value. The different thing is that any Property A which depends on a ValueProperty B gets notified when B changes and thus A can update accordingly. Sample usage was already shown in the rectangle example above:
    ...
        RwProp<Integer> width() {
            return getBean().makeValueProp("width", Integer.class);
        }
    ...
    
    So, this is actually a Java method which returns a RwProp of Integer. The previously awkward return statement is now more clear: it instructs the ProBean of the ProObject to create a new ValueProperty with the name width which has a value of type Integer. Other Properties are created in a similar fashion as is described later.
    Thus, when you want to set or get the value of this Property you just use the set() and get() methods like in the demoCode() method above. You can also initialize the value properties like in the example below, though you should note that the init value must be constant.
    ...
        RwProp<Integer> width() {
            return getBean().makeValueProp("width", Integer.class, 5);
        }
    ...
    
  • FinalProperty<T>:
    This Property is somewhat analogical to the final fields in Java. You can set the value of a FinalProperty only once, though not it's not mandatory to do it in the constructor of the ProObject.
    Such Properties are intended to be used in cases where some user action leads to set the value of a given Property exactly once. This is different than const AutoProperties as described below.
  • AutoProperty<T>:
    This Property is one of the most commonly used and convenient Properties the ProLib offers.
    Basically this is a Property which has a value which is computed directly from the values of some other Properties. In a way, the developer sets the logic of the computation of the AutoProperty's value in a declarative manner and the actual computation of the value is done automatically. Even more, when some of the Properties on which the AutoProperty depends change their value, the AutoProperty automatically recomputes its value which is very convenient. This saves the developer the hassle of manually implementing some Observer like functionality in all objects a certain Java field X would logically depend and then manually notifying X when they've changed so X can then be manually forced to recompute.

So, like inside the Rectangle example, the area() AutoProperty is implemented like this:

    Prop<Integer> area() {
        class area extends AutoProperty<Integer> {
            @Override
            protected Integer compute() {
                Integer res = width().get() * height().get();
                    return res;
                }
            }
        }

        return getBean().makeProp(area.class);
    }

In order to create an AutoProperty, by convention you declare a method structure like the structure of the area() method which declares a local class which extends the AutoProperty class. The type argument of the local class should be identical to the type argument of the Prop which the area() method returns - after all an AutoProperty is a Property which computes a given value following some logic and that value has a type which in our case is Integer.
So you override the compute() method to provide the AutoProperty's value computation logic inside. You can provide an arbitrary complex logic inside. In this example whenever the width() and height() Properties change their value, the AutoProperty is smart enough to call the compute() method again and update its value.
Something very important: compute() will be recomputed only in the cases when some of the Properties used inside it change their value!! If compute() depends on some normal Java field, there's no way for the ProLib to know when this field has changed its value and then notify the AutoProperty to change its value!! There's no magic here!''' Expecting the ProLib to detect that means hooking to JVM's system events and maybe bytecode instrumentation which obviously we don't want to use!

  • Actually, AutoProperties have a doSet() method, which can be overriden to provide setting manually a value of a given AutoProperty. This can be used in very rare cases when you have an AutoProperty X which depends on other Properties but also there's an AutoProperty Y which depends on X. So let's say that we need Y to have a valid value but at this stage of execution of our program we don't have all the Properties which X depends on initialized. That's when we can manually set the value of X, so Y gets property computed, and later, when all the Properties X depends on initialize, then X gets recomputed and Y respectively.
    This should be used very rarely, so if you think you need this, consult first with someone else.
  • There's a special case of AutoProperties which are const AutoProperties. Such AutoProperties depend on some other Properties, but their value should be computed only once. For instance you can have this case:
        public Prop<Integer> X() { ... }
        public Prop<Char> Y() { ... }
        public Prop<String> Z() { ... }
    
        @Const
        @Override
        public Prop<JLabel> swingComponent() {
    	class swingComponent extends AutoProperty<JLabel> {
    	    @Override
    	    protected JLabel compute() {
    		assert getLastValue() == null;
    
    		JLabel res = new JLabel();
    		res.setBackground(Color.green);
                    res.setText("text is " + X().get() + ", " + Y().get() + ", " + Z().get());
    	
       	        return res;
    	    }
    	}
    
        return getBean().makeProp(swingComponent.class);
    }
    
    In this case the AutoProperty swingComponent() depends on X(), Y(), Z(), provide some relatively complex initializing logic in the compute() method and you want to make sure this component is created only once. Thus:
    • You'll be happy if you don't want to worry about when X(), Y() and Z() get initialized and this way the ProLib can do it for you,
    • You can capsulate the initialization logic in the compute() method so your ProObject constructor doesn't get bloated with it instead,
    • By convention you insert assert getLastValue() == null; assertion which makes sure that the Property is still not initialized by the time the compute() method is called (which means that it's value is still null). So it's not just a convention, it's a must, otherwise you just don't check whether this AutoProperty is constant at all.
    • Also by convention, you annotate such AutoProperties with @Const.
  • Although I think the above example is much more sophisticated, I also add this one as proposed by one of the integrators along with his comments:
    The @Const AutoProperties rarely depend on anything, they are "bottom" properties that can calculate themselves by themselves only once.
    The greatest example for such an AutoProperty is:
    
    	@Const
    	public Prop<Link> link() {
    		class link extends AutoProperty<Link> {	
    
    			@Override
    			protected Link compute() {
    				assert getLastValue() == null;
    				return new FrameLink(getChangeManager());
    			}
    		}
    		return getBean().makeProp(link.class);
    	}
    

Imprortant note: I'll outline again the difference between @Const AutoProperties and FinalProperties: AutoProperties know how to compute themselves whereas Final and ValueProperties do not (they get their values from some user action or an external source in general).

  • ResourceProperty<T>:
    Important note: ResourceProperties just share partially their name with Resources. They are totally different concepts so don't mix them. This Property is something like advanced AutoProperty. It wraps some nonProLib entity like a Swing Component, for instance, and manages creation, destruction and updating of this entity, so it's something like an AutoProperty-like bridge between nonProLib entities and the ProLib and makes life easier. You create a ResourceProperty<T> in a similar fashion to creating AutoProperties<T> though there are more methods to override:
    • There's a create() method which is responsible for the initial creation of the managed entity. You should provide custom (maybe quite complex) creation logic here.
    • There's a destroy() method which is responsible for the destruction of the managed entity. Currently it doesn't work very well, though it is intended to provide destruction logic like closing streams, freeing memory etc.
    • There is a setup() method which is very similar to the compute() method of AutoProperties - each time something the ResourceProperty X depends on changes, X's setup() method gets invoked so X can update accordingly.
  • Here's an example:
        ...
    
        @Override
        public Prop<JButton> swingComponent() {
    	class swingComponent extends ResourceProperty<JButton> {
        	    @Override
    	    protected JButton create() {
    		JButton res = new JButton();
    		res.addActionListener(new ActionListener() {
    		    @Override
    		    public void actionPerformed(final ActionEvent e) {
    		        userClick();
    		    }
    	        });
    	    
                    return res;
    	    }
    
    	    @Override
    	    protected void setup(JButton res) {
    		Icon icon = icon().get().toIcon();
    		res.setIcon(icon);
    		Dimension sz = size().get().toDimension();
    		res.setPreferredSize(sz);
    		res.setSize(sz);
    		res.setToolTipText(toolTip().get());
    	    }
    
    	    @Override
    	    protected void destroy(JButton res) {
    		// nothing
    	    }
    
    	}
    
    	return getBean().makeProp(swingComponent.class);
        }
    
        ...
    
    • Further more, the setup() method can be "split" in pieces by the @Setup annotation. In the ResourceProperty descendant class you provide, swingComponent in our case, you can provide a multitude of methods which are annotated with @Setup and together they form the logic for updating the ResourceProperty. This is useful for performance reasons, because let's say you've got a very heavy setup() method which consists of logically independent parts P1 and P2, and P1 depends on some Property X while P2 doesn't depend on X so when X changes, P2 doesn't need to be executed again. Thus, providing two new setup methods which correspond to P1 and P2 leads to performance optimization (and capsulation of independent code in different methods) because the ProLib is smart enough to invoke only the needed setup methods. You should note though that the order of invoking the @Setup methods is undefined and you shouldn't depend on this because it can change. For example the above setup() method can be split like this:
      ...
      	    @Setup
      	    protected void setupIcon(JButton res) {
      		Icon icon = icon().get().toIcon();
      		res.setIcon(icon);
                  }
      
                  @Setup
      	    protected void setupSize(JButton res) {
      		Dimension sz = size().get().toDimension();
      		res.setPreferredSize(sz);
      		res.setSize(sz);
                  }
      
      	    @Setup
                  protected void setupToolTipText(JButton res) {
      		res.setToolTipText(toolTip().get());
      	    }
      ...
      

Multi Properties (RED)

The following Properties are all the current multi Properties, so they both implement RwProp<T> and extend ListProperty<T>. Important remark: Actually ListProperty<T> implements RwProp<ProList<T>>. ProList<T> is the base List interface intended to be used in the ProLib so what this means is that ListProperties actually hold one single value which is a List, and not multiple values. This List is actually a ProList and as the ListProperty is initialized a ProList instance is created and it doesn't change over time compared to the value instance of normal Properties which can change over time. From a client point of view you don't need to know internal specifics of Pros and ProLists, though if you want to use ListProperties effectively, you should read the Pros and ProLists section below.

  • ValueListProperty<T> This is the most basic ListProperty. It's analogical to ValueProperty in the sense you can set and get the values.
    • This Property implements add, remove and set behavior by using the add(T element), remove(int index) and set(int index, T value) methods.
    • It also has a getInitContents() method which you can override to specify a List<T> of initial content for the ListProperty when it's initialized. Here's an example:
      ...
          public RwListProp<String> items() {
      	class Items extends ValueListProperty<String> {
      	    @Override
      	    protected List<String> getInitContents() {
      		return Arrays.asList("dog", "cat", null, "parrot");
      	    }
      	}
      	
              return getBean().makeListProp(Items.class);
          }
      ...
      

  • AutoListProperty<T> This Property is analogical to AutoProperty, though it works with an underlying list of values. Basically, in a similar fashion to AutoProperty, you override one method where you specify the logic of computing the List of values and that List gets updated when any of the Properties it depends on change.
    • You should override the constructList() method to return a ProList<T> and inside you specify the computing logic. Actually, there are a multitude of ProList<T> implementations which you can use and one of them is the ComposingProList<T> which is used in the example below. In the Pros and ProLists section below, you'll get the details. In the example below there is an AutoListProperty names() which depends on the Properties janitor(), president() and alcoholic() and basically returns a List of all these Properties' values. Whenever any of these changes, names() gets recomputed. Here's the example:
      ...
          RwProp<String> janitor() { ... }
          RwProp<String> president() { ... }
          RwProp<String> alcoholic() { ... }
      
          public ListProp<String> names() {
      	class names extends AutoListProperty<String> {
      	    @Override
      	    protected ProList<String> constructList() {
      		return new ComposingProList<String>() {
      	            @Override
      		    protected List<String> computeData() {
      		        return Arrays.asList(janitor().get(), president().get(), alcoholic().get());
      		    }
      		};
      	    }
      	}
      		
              return getBean().makeProp(names.class);
          }
      ...
      

Here is a more detailed UML diagram with the most important methods of the implementation part: source:branches/private/peko/sophie2-refactoring/doc/uml-design-diagrams/ProLibImpl.png

Pros and ProLists

Note: For a more basic usage of the ProLib, you don't need to read this. If you do want to understand and use ListProperties effectively, then read...

source:branches/private/gogov/PRO_LIB_CORE_TUTORIAL_R1/sophie2-platform/modules/org.sophie2.core/src/test/resources/Pros And ProLists.png

Interfaces(PURPLE)

So, these are the most fundamental interfaces in the ProLib.

  • Pro:
    This is THE most fundamental interface in the ProLib though if you don't refactor the ProLib, you'll most probably never need to know it exists.
    Basically a Pro is something which implements an Observer-like behavior so ProListeners can be attached and detached to/from it. So again, there's no magic in the ProLib, just some smart concepts, and actually detecting dependencies, automatically solving initialization order problems and getting automatic updates is implemented with the help of this Observer-like behavior laid down at the very foundations of the ProLib.
  • Prop<T>:
    Prop<T> was already mentioned, though here we'll add that Prop<T> actually extends Pro so it's also trackable for changes which means that all Properties can be tracked when they and respectively their value changes so that's how the magic happens.
  • List<T>:
    This is simply the standard Java interface List<T>.

  • ProList<T>:
    ProList<T> extends Pro and List<T> so it's basically a List which can be tracked for changes. Now you should understand why ListProp<T> extends Prop<ProList<T>>.

Implementations(YELLOW)

  • BaseProList<T>:
    This is the abstract base implementation of the ProList<T> interface. From a client point of view you use it as a normal Java List<T>, though internally notifications for changes fly to interested parties and the ProLib is able to execute correctly.

All of the following extend BaseProList<T> to provide specific behaviour.

  • EmptyProList<T>:
    This is just an empty ProList<T> and you cannot modify it in any way. Trying to do so will result in an UnsupprotedOperation Exception. This class is a useful shorthand for cases when you need and empty ProList, analogical to Collections.emptyList(), for instance.
  • ValueProList<T>:
    This is the simplest modifiable ProList<T>. The major difference with BaseProList<T> is that ValueProList<T> allows read-write access to its values.
  • ComposingProList<T>:
    This is the most powerful, yet one of the simpler ProList<T>. It is abstract, so you should provide your own descendant class and override a method if you want to use it.
    • It has a computeData method, which similarly like the compute() method of the AutoProperty, defines the creation logic for its content. Now we can actually say the truth that AutoProperties, ComposingProList<T> and others actually monitor which Pros they depend on and update when any of them changes and notifies its listeners that it's changed.
    • So if you take a look at the AutoListProperty example, you'll see that in the constructList() method of names() actually a new ComposingProList<T> is created and it's computeData() method is overridden to provide the custom creation logic. Very convenient. Important remark: When a Pro that a given ComposingProList<T> X depends on changes, the whole list gets constructed from scratch which is very inefficient, so this ProList should be used with caution, otherwise performance can be easily degraded. In lots of other cases another ProList<T> implementation should be used to improve performance.

  • TrackingProList<T>:
    This is a ProList<T> which is something like an alias for another ProList<T>.
    • It's got a computeSource() method which you should override when extending TrackingProList<T>. Inside this method you can specify a custom logic what ProList you should return. Analogical to the compute() method of AutoProperty, when the ProList X this TrackingProList Y depends on changes, then Y gets updated.
      • One application of this is that you can simply return another ProList<T> inside this method which kindof makes the TrackingProList<T> an alias of the returned ProList<T>. Here's an example from the FakeModuleRegistry:
            public ListProp<SophieModule> activeModules() {
        	class activeModules extends AutoListProperty<SophieModule> {
        	    @Override
        	    protected ProList<SophieModule> constructList() {
        		return new TrackingProList<SophieModule>() {
        		    @Override
        		    protected ProList<SophieModule> computeSource() {
        			return registeredModules().get();
        		    }
        		};
        	    }
        	}
        
        	return getBean().makeProp(activeModules.class);
            }
        
      • Another application is to provide more custom logic inside the computeSource() method, like this:
            new TrackingProList<String>() {
        	@Override
        	protected ProList<String> computeSource() {
        	    try {
                        ... some logic
                        return a ProList;
        	    } catch (Exception e) {
        		return EmptyProList.get(String.class);
        	    }
        	}
            }
        
        Here, if the retrieval logic succeeds, some ProList is returned, though if it fails, an EmptyProList is returned which can be useful in certain cases.

Filtering and transformations

Actually, the ProLib borrows two declarative concepts from functional programming, namely map and filter.

  • FilteringProList<T>:
    So I lied to you earlier - the ProList<T> interface has some more useful methods. One of them is ProList<T> filter(ProListFilter<T> filter).
    When this method is applied to any BaseProList<T>, it creates a FilteringProList<T>. This ProList<T> is basically a filtered view of another ProList<T>. The ProListFilter<T> is a visitor object which, given a T element, decides whether it should be filtered or not. You can create such filters by extending ProListFilter<T> and overriding its accepts() method to provide your custom logic.
  • TransformingProList<S, T>: (S for source, T for target)
    Another useful method of the ProList<T> interface is <T> ProList<T> transform(ProListTransformer<S, T> transformer) (it's analogical to map in functional languages).
    When this method is applied to any BaseProList<T>, it creates a TransformingProList<T>. This ProList<T> is basically a transformed view of another ProList<S>. The ProListTransformer<S, T> is a visitor object which, given a S element, decides how to transform it to a T object. You can create such transformers by extending ProListTransformer<S, T> and overriding its translate() method to provide your custom logic.

Now, here's an example which uses these ProLists as well as other of the ProLib things. It's from the EmbeddedBooksPalette. If you understand it, then you will not have problems using it.

    @Override
    public ListProp<BookItem> items() {
	class items extends AutoListProperty<BookItem> {
	    @Override
	    protected ProList<BookItem> constructList() {
		return new TrackingProList<ResourceRef>() {
		    @SuppressWarnings("synthetic-access")
		    @Override
		    protected ProList<ResourceRef> computeSource() {
			if (openBooksPalette().get().firstSelectedItem() != null
                            && openBooksPalette().get().firstSelectedItem().get() != null) {
		 	    Book parentBook = ((OpenBooksPalette.BookItem)
				openBooksPalette().get().firstSelectedItem()
				.get()).book().get();
			    return parentBook.children().get();
			} else {
			   return EmptyProList.get(ResourceRef.class);
			}
		    }
		}.filter(new ProListFilter<ResourceRef>() {
		    @Override
		    public boolean accepts(ResourceRef item) {
			return item.get(Resource.class) instanceof Book;
		    }
		}).transform(new ProListTransformer<ResourceRef, BookItem>() {
		    @Override
		    public BookItem translate(ResourceRef source) {
			BookItem res = new BookItem();
			res.book().set(source.get(Book.class));
			return res;
		    }
		});
	    }
	}
	
        return getBean().makeProp(items.class);
    }

@Own & ParentProperties

So, you've noticed I missed describing the pink ParentProperty in the Properties section. Good for you, it shows that you read carefully.

source:branches/private/gogov/PRO_LIB_CORE_TUTORIAL_R1/sophie2-platform/modules/org.sophie2.core/src/test/resources/Parent Property.png

  • ParentProperty:
    In many cases you've got an object which you want to have another object as a parent. So there are ProObjects and ParentProperty<T extends ProObject>. Note that T extends ProObject so each parent of a ProObject must be a ProObject.
    • With the ProLib, parents are set automatically via the @Own annotation.
      class Person implements ProObject {
      ...
          @Override
          public Prop<Meddle> parent() {
      	return getBean().makeParentProp(Meddle.class);
          }
      ...
      }
      
      class Meddle implements ProObject {
      ...
          @Own
          Prop<Person> jesus() { ... }
      ...
      }
      
      So jesus() is a Property which returns a Person object. But let's say that we want to "bind" this jesus Property only to one Meddle, so other Meddles couldn't get the same Person object, which jesus() holds, to be "bound" to them. So, each Person much declare a ParentProperty<Meddle> like in the example. In the Meddle object, the jesus() property is annotated with @Own. So the ProLib takes care for two things:
      • Sets the parent() Property of the Person hold by the jesus() Property, to the concrete Meddle instance.
      • And also makes sure that other Meddles cannot "own" the same jesus().
      Another thing to mention it is not mandatory to manually declare ParentProperties in @Owned ProObjects if you don't want to use the parent link but guarantee that the ProObjects is @Owned only by one parent. Currently the ProLib doesn't work like that though it will be fixed very soon because this should be true.
      Also note that though ProLib automatically sets the ParentProperties with correct values, it happens at a later stage, after the creation of the ProObject instance, so if you use the ParentProperty inside the ProObject's constructor, for instance, PArentProperty will still be null. And this is normal! You should have this in mind and write code which takes into consideration both cases and acts adequately.

@Shared & ProUtil.clone()

There's a useful util class called ProUtil. It contains various methods which ease ProLib usage.

  • The ProUtil.clone() method is responsible for cloning a ProObject into a new ProObject which is needed in certain cases. So, standard questions arise like whether to do deep copying or not. For instance:
    class Sheep implements ProObject {
    ...
        @Shared
        Prop<BankAccount> account() { ... }
    ...
    }
    
    When you clone this Sheep, the question what to do with all the Sheep's Properties arise. In the case of @Shared, when the Sheep dolly1 is cloned, the new Sheep dolly2's account() will hold the same BankAccount which dolly1 has.
    In this context we should mention that the ProLib also makes sure that if account was @Own instead of @Shared, then dolly2 won't have the same BankAccount.

@Immutable

This is an annotation which is used to mark a given class as immutable - once it's created, it doesn't change.
In the next section you'll see why this is needed by the ProLib.
Note: The ProLib doesn't ensure that object annotated as @Immutable change because this is equal (again) to doing bytecode instrumentation. It is just a convention which has to be followed.

ProObjects, @Immutables etc.

In OOP there are two kinds of objects - mutable and immutable. In the ProLib mutable objects are intended to be only ProObjects and immutable objects should either be annotated with @Immutable or expand the list of known immutables in the ProLib which currently is limited to standard JDK immutables like Integer, Double etc. Why is this important:

source:branches/private/gogov/PRO_LIB_CORE_TUTORIAL_R1/sophie2-platform/modules/org.sophie2.core/src/test/resources/ProObjects And Immutables.png

This is an example how the dependency graph between ProObjects should look.

  • It shouldn't contain cycles, otherwise we can never initialize this graph.
  • An Immutable shouldn't depend on anything.
  • ProObjects could depend on other ProObjects and Immutables.
  • The leafs of this acyclic graph should be Immutables.

If a ProObject A depends by a Prop<X> P on some object X which doesn't depend on anything, then there are two cases:

  • X is immutable. Then when someone changes P, it would be through P's interface so the ProLib can detect this change and A can get notified that X has changed.
  • X is mutable and ProObject. X can change in two ways:
    • A Property Q of X has changed. Then P would detect when X's Q changes and A would get notified about it.
    • The same as the above but Q is a normal Java field. Well, nothing conventional can be done to detect when Q changes, so we're screwed, so don't use normal Java fields in ProObjects, unless you're sure what you're doing.
  • X is mutable but is not a ProObject. In this case obviously nothing can be automatically done by the ProLib to detect when X changes.

This means that you should follow the rules above in order to produce non-flawed ProLib code. In general you can use ProObject, Immutables and the rest is "junk".

ProLists, findOne(), findAll(), ProUtil.getKey() and ListEntry

Let's have this case:

...
class Party implements ProObject {
    public ListProp<Person> people() { ... }

    public ListProp<Person> invitedPeople() {
	class invitedPeople extends AutoListProperty<Person> {
	    @Override
	    protected ProList<Person> constructList() {
		return new ComposingProList<Person>() {
		    @Override
		    protected ProList<Person> computeData() {
		        ArrayList<Person> res = new ArrayList<Person>();
                        for (Person person : people().get()) {
                            if (person.getName() == "Meddle") {
                                res.add(person);
 			    }
		        };
		    }
		}
            }
        }

        return getBean().makeProp(invitedPeople.class);
    }
...

So, in this example you want to get the List of all invited people to your party, invite only people whose name is "Meddle" and update this list when a potential candidate to come to the party is added. This is kinda slow though, because each time a new Person is added to People, I don't want to reconstruct the whole invitedPeople list.

ProLists are smart though so they act not only as lists but as maps. In the ProLib there is the concept of keys when using ProList, depending on what kind of items are inside:

  • Immutables -- the key of an Immutable is the Immutable itself,
  • ListEntries -- there is a ListEntry class which is basically a pair of a key and a value. The key of a ListEntry is its key of course.
  • others -- key is always null.

You can get the key of a given object by using the ProUitl.getKey() method.

ProLists check if they're working with ListEntry descendants, so you can subclass ListEntry to improve the above example like this:

...
class Party implements ProObject {
    static class PersonEntry extends ListEntry {
        PersonEntry(String name, ... ) { ... }

        Object getKey() { return name; }
        Object getValue() { ... }
    }

    public ListProp<PersonEntry> people() { ... }

    public ListProp<PersonEntry> invitedPeople() {
	class invitedPeople extends AutoListProperty<PersonEntry> {
	    @Override
	    protected ProList<PersonEntry> constructList() {
		return new ComposingProList<PersonEntry>() {
		    @Override
		    protected ProList<PersonEntry> computeData() {
                        return people().findAll("Meddle");
		    }
		}
            }
        }

        return getBean().makeProp(invitedPeople.class);
    }
...

Here the E findAll(Object key) method of ProList<E> is used which basically returns all elements in the list which have the specified key.

The difference here is that ProLib is smart enough to recompute invitedPeople() only when another PersonEntry with key "Meddle" is entered, such is removed or modified. Otherwise it doesn't make sense to recompute because the list hasn't changed if for instance you've added a PersonEntry with key "Christ".

E findOne(Object key) is the same but it expects to find at most one element with the specified key and returns it. If there are more than one, an Exception is thrown.

Bad Code Examples

Resource Property is a better choice

        public Prop<JComponent> swingComponent() {
		class swingComponent extends AutoProperty<JComponent> {
			@Override
			protected JComponent compute() {
				JComponent res = new JPanel();
				res.setLayout(new BoxLayout(res, BoxLayout.Y_AXIS));
				res.setPreferredSize(new Dimension(100, 100));
				res.add(headComponent().get());
				res.add(scrollPane().get());
				res.setVisible(true);
				res.validate();
        
				return res;
			}
		}
		return getBean().makeReadProp(swingComponent.class);
	}

Trying to initialize a resource or auto property manually:

  • suppose one has the following:
    	public Prop<JButton> button() {
    		class button extends ResourceProperty<JButton> {
    
    			@Override
    			protected JButton create() {
    				JButton res = new JButton();
    				return res;
    			}
    
    			@Override
    			protected void destroy(JButton res) {
    				//nothing.
    			}
    
    			@Override
    			protected void setup(JButton res) {
    				res.setToolTipText(toolTip().get());
    			}
    		}
    		return getBean().makeProp(button.class);
    	}
    	
    	public RwProp<String> toolTip() {
    		return getBean().makeValueProp("toolTip", String.class);
    	}
    

  • People tend to the the following in order to initialize the prorperty:
    button().get(); //does nothing!
    
  • This actually will do nothing. The property is automatically created and initialized when the value property containing the tool-tip is initialized. Same applied to auto properties! You do not need to "get()" the property in order for it to be "computed". This is automatically provided by the pro lib.

Using a non-ProObject or non-@Immutable object as a value of a property:

        class BadObject { //does not extend ProObject and is not annotated with @Immutable
		
	}
	
	public RwProp<BadObject> badObject() {
		return getBean().makeValueProp("badObject", BadObject.class);
	}

  • This should never happen. Every object that is a value of a property or is a value in a list property should either be a ProObject or an Immutable. Having objects like this one will never force an AutoProperty update if this AutoProperty depends on the "badObject()'s" property. Same applied to other properties depending on "badObject()'s" property. If a non-ProObject or non-@Immutable is really needed, use the resource property, where it is created and different things are set to it.

One "child" owned by two separate parents

  • Suppose we have:
            class Child extends BaseProObject {
    		//empty.
    	}
    	
    	class Parent extends BaseProObject {
    		
    		@Own
    		public RwProp<Child> child() {
    			return getBean().makeValueProp("child", Child.class);
    		}	
    		
    	}
    

  • The following should will not work and will throw an AlreadyOwnedException. Despite not working, it worth mentioning. Same applied to have any kind of value or list property that has the @Own annotation.
        Child child = new Child();
        Parent parent1 = new Parent();
        Parent parent2 = new Parent();
        parent1.child().set(child);
        parent2.child().set(child); //throws an exception!!!
    

Accessing a parent property that has not been initialized (usually set to null)

  • Suppose one has the following code:
           class Child extends BaseProObject {
    
    		public Prop<Parent> parent() {
    			return getBean().makeParentProp(Parent.class);
    		}
    		
    	}
    	
    	class Parent extends BaseProObject {
    		
    		@Own
    		public RwProp<Child> child() {
    			return getBean().makeValueProp("child", Child.class);
    		}	
    		
    	}
    
    

  • If one tries to do the thing below a NotAvailableException will be thrown.
                    Parent parent = new Parent();
    		Child child = new Child();
    		child.parent().get(); //null at this point.
    		parent.child().get(); //throws NotAvailableException
    
    In order to use the above relation see...
                    Parent parent = new Parent();
    		Child child = new Child();
    		parent.child().set(child);
    		child.parent().get(); // child.parent().get() == parent
    

Depending on a non-final property inside a "create()" method of a ResourceProperty

  • Using an non-final property inside a the create method will eventually work if that property is not null or is already initialized. Changing that property however will not affect the value of the resource property since its create method is invoked only once...
            public RwProp<Icon> icon() {
    		return getBean().makeValueProp("icon", Icon.class);
    	}
    	
    	public RwProp<String> toolTip() {
    		return getBean().makeValueProp("toolTip", String.class);
    	}
    	
    	public Prop<JButton> button() {
    		class button extends ResourceProperty<JButton> {
    
    			@Override
    			protected JButton create() {
    				JButton res = new JButton();
    				res.setIcon(icon().get());
    				return res;
    			}
    
    			@Override
    			protected void destroy(JButton res) {
    				//nothing.
    			}
    
    			@Override
    			protected void setup(JButton res) {
    				res.setToolTipText(toolTip().get());
    			}
    		}
    		return getBean().makeProp(button.class);
    	}
    
  • In this case the icon will be set (if initialized by the time the create method is invoked). Besides, the tool-tip will be set, no matter what because the setUp method will be invoked after the initialization of the toolTip()'s property. If we change the tool-tip the setUp method will be re-setUp-ed. However if we change the icon property, the icon of the button will not be set to the new one. Be careful about such scenarios, because it will probably remain silent.

Debugging ProLib

Some advice:

  • Print. Debugging with a debugger is uglier that usual when using the ProLib. Although integrators claim you should use the Loggers, use them only if you will commit what you print. Otherwise use System.out because you'll get oriented much faster.
  • Run the tests before commit. Don't break tests.
  • Use the Inspector - this is a GUI tool that allows browsing the Properties in the application while it's running. Here you can read more about it: PRO_LIB_INSPECTOR_R0

Good Practices

  • Use ProObject and Immutables when using the ProLib. The rest is "junk". Although if you know what you're doing, you can find useful things in the "junk".
  • Convert mutable classes to ProObjects.
  • Keep the ProLib and with high quality, because everything else will depend on it.
  • Be careful not to create cyclic dependencies (like a = b + 1, b = a + 1). No library can solve them.

More sophisticated scenarios

  • Suppose you have several non-ProObject, non-Immutable objects that you need to keep in sync. Suppose also that they cannot update their "things" automatically using the pro lib, because they may be part of the JDK. See the following example that is taken directly from the source code along with its javadoc(ignore the logic of the setUp method for the time being):
       /**
	 * Manages the layout of the contained elements. We can not point the object
	 * it actually manages, so its value type is {@link Void}. It layouts buttons
	 * according to the bounds of the menu. Buttons are horizontally
	 * oriented and are aligned as if they are all at the beginning of the menu.
	 * 
	 * The setup methods are actually related to the object, but we do not have
	 * object level setuppers.
	 * 
	 * @return property.
	 */
	protected Prop<Void> locationSync() {
		class locationSync extends ResourceProperty<Void> {

			@Override
			protected Void create() {
				return null;
			}

			@Override
			protected void destroy(Void resource) {
				// do nothing?

			}

			@Override
			protected void setup(Void resource) {

				ImmPoint lastHud = bounds().get()
						.getPoint(Position.BOTTOM_LEFT);

				ImmPoint lastHalo = bounds().get().getPoint(
						Position.BOTTOM_LEFT);

				// halo buttons
				for (HaloButton b : buttons().get()) {
					if (b.visible().get()) {
						ImmRect haloBounds = new ImmRect(ImmPoint.ZERO, b
								.size().get()).align(Position.BOTTOM_LEFT,
								lastHalo);
						b.swingComponent().get().setLocation(
								haloBounds.getLocation().toPoint());
						lastHalo = haloBounds.getPoint(Position.BOTTOM_RIGHT);
						lastHud = syncHud(b, lastHud);
					}
				}
			}

		}
		return getBean().makeProp(locationSync.class);
	}

  • We have several buttons that need to be positioned at specified locations inside a rectangle. In order to do this we should set the location of every button, one by one upon updating the rectangle. The rectangle itself is kept in the bounds() property. The line "b.swingComponent().get().setLocation(haloBounds.getLocation().toPoint());" gets the JButton out of the HaloButton and sets its location. Since the JButton is not a ProObject but is depending on the bounds() property, we should manually set its location. It we had only one button, its location would have been set in a the resource property it is created. However we have several buttons and only one rectangle. This is just an example of using solely the setUp method of a resource property. Since this method depends on the bounds()'s property everything in it is reinvoked upon changing the bounds.

The ProLib and Eclipse

  • Writing properties is a very tedious task, since it requires much code writing. However eclipse provides a mechanism for creating templates for pieces of source code that repeat to some extent. In order to take the benefit of this, templates are created for eclipse, that generate the most frequently used properties. In order to import the templates into eclipse follow the next steps:
    • Open "Window" (main menu bar in eclipse) => "Preferences".
    • Go to "Java" => "Editor" => "Templates" (these are located on the left side of the Preferences window).
    • Click on the "Import" button located on the right side of the same window.
      • Templates are located in "/sophie2-platform/dev-tools/eclipse_templates/properties_templates/" that is relative to the root of the repository.
    • The eclipse properties templates are divided into two groups - value properties, list properties. This mean that both 'list-properties-templates.xml and value-properties-templates.xml should be imported from the directory mentioned above.

TODO

  • init final property with constant which isn't an argument of the constructor
  • finalproperty.init(new stuff())
  • Internals
    • Registry
    • ReadTracks
    • AutoTracker
    • Aspects
    • BeanData
    • Status
    • BasePro
    • BaseProList
    • ...