LucidDoc Design
The design of a GUI application involves the identification of objects within a system based on the things required of the system, things which might potentially be required of the system in the future, and the communication possibilities between proposed objects. The last, more specifically, involves deciding how and which GUI components communicate with each other, and how they communicate with the system of data which lies behind them.
The system of data, in LucidDoc, is composed of a set of functions
and their attributes (including resources). This is persistent
information, clearly distinct from the GUI components (it might even be
used by a separate user interface), so it makes sense to establish a fundamental
division in the code between the model (data) and the view (GUI).
This is fairly standard in Java GUI applications. It is described in the
Gang of Four's Design Patterns like this:
MVC [Model-View-Controller] decouples views and models by establishing a subscribe/notify protocol between them. A view must ensure that its appearance reflects the state of the model. Whenever the model's data changes, the model notifies views that depend on it. In response, each view gets an opportunity to update itself.This is the approach followed by LucidDoc. The overall model is essentially a collection of functions and some information about which ones should be available for display: it is simply called the Function Collection Model. Each function also contains resources, and each resource contains, in addition to descriptive attributes, data relevant to something the user is looking for. So the model is constantly changing, and it must notify GUI components like the Information Area when new information comes in, or when attributes' values change, e.g. if the user makes a function active. To do this the model keeps a list of listeners, including the Information Area, and when something changes, it generates an event and invokes a method on all the listeners (which they all have since they must implement a listener interface) with the event as the parameter. In Java an event is just a little object which contains a reference back to the object which created it (source), and possibly some additional information (such as the new data of a resource). This system of communication mimics the delegation event model, which has been used in Java since 1.1, and dominates the Swing packages.
Typically in Java, for one object to communicate with another, it must
have a reference to it. So it would seem that for the view to get information
from the model it must have a reference to the model, and for the model
to update the view, it must have a reference to the view. In LucidDoc the
view does indeed gain a reference to the model in this way:
gui.setModel(functionCollectionModel);
The top-level GUI class can then select parts of the model and send
them to the components which need them. The model, however, does not have
a reference to the GUI, but only to those portions of the view (listeners)
which have chosen to add themselves (register themselves/subscribe) to
the open list it keeps. This system of referencing preserves the proper
dependency relationship between model and view: the view is dependent on
the model, but the model does not need to know anything about the view,
is independent of it, and so can serve different views. (Dependencies can
be established between other objects besides views and models in the same
way; the idea in its general form is known as the Observer pattern.)
The idea of functions illustrates the Command design pattern. This pattern requires that the actions taken on user events should be encapsulated in their own objects, rather than tied to specific GUI objects where the events are triggered. So the user could click on a function either from a menu or a toolbar, and the same code would get called (though to be strictly accurate, functions are typically only executed automatically, on selecting words in an edit window). In Java, the Action interface facilitates the Command pattern, allowing things like a name and image to be identified with a command without tying it down to a particular GUI component. The LucidDoc Function class implements Action indirectly, and adds a lot more attributes. In addition, special actions like "Save" and "Paste" (which are not Functions but are commands and inherit from a class called GUIAction) each have a class defined for them.
The user may change the Function Collection Model in various dialogs, so it needs to be saved at the end of each session and restored at the beginning of each session. To this, Java's serialization mechanism is employed: it simply writes takes a target object (the main model class) and writes it out as a stream to a file, including its whole tree of sub-objects which are discovered by Java reflection. The code looks a bit like this:
FileOutputStream fout = new FileOutputStream(FUNCTION_DATA_FILE);
ObjectOutputStream out = new ObjectOutputStream(fout);
out.writeObject(functionCollectionModel);
out.close();
When necessary the data is deserialized in a similar manner, with
an ObjectInputStream. This method of saving and restoring objects can be
applied to most standard Java classes and to any class you create yourself
which you mark as Serializable. This is not the Gang of Four's Memento
pattern, because it violates encapsulation, but it is an instance of
the broader
Snapshot pattern described in Mark Grand's Patterns
in Java. When serializing a model, one must be careful not to include
irrelevant objects which it somewhere references — e.g. the GUI listeners
which are held in lists within functions in the model. The way this is
avoided in LucidDoc is by declaring such listener lists transient (i.e.
not to be serialized).
Description of packages and important classes
com.oranda.luciddoc.actions
Includes a class for each ordinary action on the toolbar like Open,
Save, Cut, etc. These subclass GUIAction which subclasses AbstractAction.
Each action accesses the GUI singleton by a static call getInstance().
(This is not ideal because the GUI is open to everything.)
com.oranda.luciddoc.function
The model classes include:
FunctionCollectionModel.java: contains a
Hashtable of known functions and a Vector of displayed functions, and various
getters and setters.
Function.java: attributes of a function,
including a list of resources
Resource.java: attributes of a resource (typically
a Web form) and code to get data from it (stored in a ResourceState object).
FunctionGroupExecutor: starts a group of
threads, one for each function, which search all the associated resources
for a particular word.
com.oranda.luciddoc.gui
The view classes include:
GUI.java: the main frame for the application.
MainArea.java: the main area of the application,
including EditArea and InformationArea.
EditArea.java: the MDI area for editing and
viewing.
InformationArea.java: contains a "MultiSplitPane"
for viewing results of user selections in the EditArea.
com.oranda.luciddoc.util
Code for reading and writing files, and for printing,
some string functions, etc..