Once you have an object model you need to interact with it, and the most common way of interacting with an application is:
This interaction can be mediated using services, and we can identify two distinct set of services: Browsers and Commands.
JavATE ApplicATE offers both interfaces and default implementations for this services.
Browsers let you navigate through the web of your object model.
The simplest way of doing this is using a list. You can scroll the list, filter its content and select one (or more) element. So we have ListBrowsers.
But there are other ways. If your objects are naturally organized in a hierarchy, you can browse them using a TreeBrowser. If your objects have a master date you can use a CalendarBrowser. And you can invent others.
Pay attention to not confuse the browser (the service) with the user interface. There can be different views for the same way of browsing your objects. A ListBrowser can be viewed as a table where each row represents an object and each column is an object attribute, but we can view it as a grid with an icon representing a single object in each cell.
Most of the time you will interact with your object model using a ListBrowser.
ListBrowser is an interface that has methods that allow you to:
The default implementation of this interface is ListBrowserImpl. This class accept a repository as the source of the objects to be browsed. So, given:
ListBrowser brw = new ListBrowserImpl(new MyObjectRepository());
you can retrieve all the objects in the repository with
brw.getList();
If you are interested in a subset of the repository only, you can filter the objects using a Filter, so after:
MyObjectFilter f = new MyObjectFilter(); brw.setFilter(f);
a call to getList() will retrieve only the objects that satisfy the filter condition.
ListBrowserImpl will listen to PropertyChange events sent by the filter and react so to change its content properly. If we do
f.setMyProperty(value);
a successive call to getList() will probably retrieve a different subset.
To order the objects we can use the two overloaded setObject() methods. One version takes two parameters: the name of the property on which you want to perform the ordering and a boolean that indicate if you want ascending or descending order.
The second version takes only the property name. If you call it two times with the same property name the order will be inverted.
To know if the content of the browser has changed, independently of the way it happened (setting a filter or one of the setOrder methods, etc.) you can listen to the PropertyChanged events of the "list" property in a way like this:
brw.addPropertyChangeListener(new PropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent evt) { if ("list".equals(evt.getPropertyName())) { List<?> newContent = browser.getList(); // ... do something with the retrieved content } } });
An object browser is a wrapper around an object of the model that allows to browse its attributes.
At first an object browser could seem useless. You get an object from the model and you can retrieve its attributes using its methods. Why we need a wrapper?
The answer is that the object browser has features that a plain model object can't have:
Let's start from the first one. Every time a command is done there's the possibility that an object changed its internal state. If you have a view on an object there are two possibilities:
ApplicATE use this second approach, and the object browser act as the listener of the event: every time an object browser receive an event from a command signaling that something happened, it retrieves a fresh copy of its content from the repository and fires a PropertyChangeEvent event. The name of the property in the fired event is "hold" because through this property you can retrieve the full object you are browsing on. The "view" object, finally, can listen to this event to update the screen.
If your objects are organized in a tree-like structure (as in the Composite pattern) you can use a TreeBrowser to navigate among them.
You will typically pass the root of the tree to the browser, the you will be able to retrieve direct children using the getChildrenOf() method while a generic node can be retrieved using the getTargetOf() method. The latter takes a TreePath object as a parameter and return the node targeted by this path.
Let see some examples using the DefaultTreeBrowser implementation of the TreeBrowser interface.
Suppose you have a set of entities organized in tree-structure like the following:
public class MyNode extends EntityImpl { private MyNode parent; private List<MyNode> children; ........ public MyNode getParent() { return parent; } ........ public List<MyNode> getChildren() { return children; } }
When we instantiate the DefaultTreeBrowser we need to give it a repository to access the entities, the id of the root entity, and the name of the properties used to access the parent and the children of each node:
TreeBrowser<Long,MyNode> brw = new DefaultTreeBrowser<Long,MyNode>( RepositoryRegistry.instance().getRepository(MyNode.class), 1L, "children", "parent" );
Now we can retrieve all the children of the root with:
MyNode root = brw.getRoot(); List<MyNode> rootChildren = brw.getChildrenOf(root);
Using getPathOf() we can retrieve the path of the root entity and the use it to navigate down the tree. In the following example we will retrieve the path of the second child of the third child of the root and the use this path to retrieve this element.
TreePath path = brw.getPathOf(root); path = path.childrenPath(2).childrenPath(1); MyNode target = brw.getTargetOf(path);
You can select a node using both the path or the node itself
brw.select(path); // the same as brw.select(target);
and then you can retrieve the selected node or the selected path
MyNode selectedNode = brw.getSelectedObject(); TreePath selectedPath = brw.getSelectedPath();
A command is a way to encapsulate an operation in an object. You can create a command object, set the operation parameters passing them to the constructor or using properties accessor methods, and then execute the operation calling a particular method on the command object.
This approach has various advantages:
In JavATE a command is an instance of the Command interface. This interface has a doCommand() method that is used to execute the command and a cancelCommand() method that is used to cancel its execution.
For example, imagine you need a command that call a simple method on an Entity. This is the base code for doing it:
public class MyMethodCommand implements Command { private Long targetId; public void setTargetId(Long id) { this.targetId = id; } public Long getTargetId() { return targetId; } public void doCommand() { MyEntity target = MyEntity.repository().get(targetId); target.myMethod(); } public void cancelCommand() { } ............... }
Usually the cancelCommand method is empty but you can override it to perform some cleanup code.
The other two methods of the Command interface deal with the command events. Every time a command is executed a CommandEvent is fired and you can listen to these events adding a CommandListener implementation to the command using one of the two addCommandListener methods.
So the complete code for our command now is:
public class MyMethodCommand implements Command { private CommandEventSupport cmdEvtSupport = new CommandEventSupport(); private Long targetId; public void setTargetId(Long id) { this.targetId = id; } public Long getTargetId() { return targetId; } public void doCommand() { MyEntity target = MyEntity.repository().get(targetId); target.myMethod(); cmdEvtSupport.fireCommandEvent(new CommandEvent(this, CommandResult.SUCCESSFUL)); } public void cancelCommand() { cmdEvtSupport.fireCommandEvent(new CommandEvent(this, CommandResult.CANCELLED)); } public void addCommandListener(CommandListener listener) { cmdEvtSupport.addListener(listener); } public void addCommandListener(CommandListener listener, CommandResult... results) { cmdEvtSupport.addListener(listener, results); } }
Normally you won't need to directly implement the Command interface. You can instead extend the AbstractCommand class and save some boilerplate code.
The AbstractCommand class is a base implementation of the Command interface with some other common facilities that typically needs to be used in a command.
Both the addCommandListener methods are implemented and the default implementation of doCommand and cancelCommand fire a CommandEvent with appropriate result so, if you override them you simply need to call super.doCommand or super.cancelCommand() at the end of the method to fire the correct method to your command listeners.
Using AbstractCommand the example of the previous paragraph become:
public class MyMethodCommand extends AbstractCommand { private Long targetId; public void setTargetId(Long id) { this.targetId = id; } public Long getTargetId() { return targetId; } public void doCommand() { MyEntity target = MyEntity.repository().get(targetId); target.myMethod(); super.doCommand(); } }
Suppose that the method to be called takes a String parameter. We can add a String property to our command to support it:
public class MyMethodCommand extends AbstractCommand { private Long targetId; private String param; public void setTargetId(Long id) { this.targetId = id; } public Long getTargetId() { return targetId; } public void setParam(String param) { this.param = param; } public String getParam() { return param; } public void doCommand() { MyEntity target = MyEntity.repository().get(targetId); target.myMethod(param); super.doCommand(); } }
If your client need to bind to this property the AbstractCommand class already implements the PropertyChangeEmitter interface for you and has a protected firePropertyChange() method so you only need to change your setParam method in this way:
public void setParam(String param) { String old = getParam(); this.param = param; firePropertyChange("param", old, param); }
AbstractCommand implements the ValuesLister interface too. This means that you can retrieve the possible values that are admitted for a property using the getPropertyValues() method. For example:
Command cmd = new MyMethodCommand(); Collection<?> possibleValues = cmd.getPropertyValues("param");
The algorithm used to retrieve the list of values is the following:
Normally a property writability is controlled by the existence of its setter method. But we can do more. There are cases where we need to dynamically control the writability of a property. In this cases we can develop a isPropertyNameWritable() method (where PropertyName must be substituted with the name of the property) that returns a boolean. If this method returns true the property is writable, otherwise the property is not writable.
We can check the writability of a property using PropertyWritabilityRetrieverImpl. Construct an instance of this class passing the command to the constructor and then use the isPropertyWritable() method. For example, a GUI could check if a property is writable to render the corresponding field as read-only.
Let's try it in a little example. Suppose you have a TakeOrderCommand command class and the user is not supposed to change the quantity that can be ordered on the first day of month because in that period you know you have stock shortage. You can do the following:
public class TakeOrderCommand { .......... public void setQuantity(int qty) { ....... } public boolean isQuantityWritable() { return Calendar.getInstance().get(Calendar.DAY_OF_MONTH) != 1; } .......... }
and then, to check writability:
PropertyWritabilityRetriever wr = new PropertyWritabilityRetrieverImpl(); boolean result = wr.isPropertyWritable("quantity");
In a similar way you can control the property availability, i.e. if the property makes sense in the actual context of the command. For example, imagine to develop a command that prints a report. The "duplex" property is available only if a printer is selected that can print on both faces of a sheet. A GUI could check properties availability to render the corresponding field as visible or not.
To control property availability you can develop the corresponding isPropertyNameAvailable() method, and to check it we can use the isPropertyAvailable() method of PropertyAvailabilityRetrieverImpl.
Domain objects maintain business state and have methods to modify this state. Frequently this state can be modified directly without the need of a sophisticated method. Imagine, for example, the "description" property of an Entity or a simple association between two entities.
Depending on your application needs there should be cases where you need to edit this state in a lot of ways. And these ways only slightly depend by the domain object itself.
For example a user can be able to associate an entity with only a subset of another entity or he could want his preferred way to chose among all the associable entities.
An Editor let you decouple an object from the way it can be modified.
The simplest editor is the bean editor: an editor that let you edit the properties of a bean.
There is an interface called BeanEditor for this but it has only methods for setting and retrieving the bean that is being edited.
More interesting is the AbstractBeanEditor class that implements BeanEditor adding validation and property change support. When the editing bean is set:
Typically you will extend AbstractBeanEditor to create your custom editors.
If you only need an out-of-the-box implementation for an editor that is able to edit all the properties of an object you can use BeanEditorImpl.
BeanEditorImpl is an implementation of the DynaBean specification (from the Apache Commons BeanUtils project) that replicates all the properties of the bean adding: