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 undo it.
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.
Normally you won't need to directly implement the Command interface. You can instead extend the AbstractCommand class.