Become a programming Picasso with JHotDraw
Use the highly customizable GUI framework to simplify draw application development
Software developers want to build software applications quickly and still focus on quality. One way to reduce development time and improve software quality is to use a framework. Frameworks are designed for reuse; they offer prefabricated components as building blocks and design patterns as blueprints for the architecture.
Many Java programmers frequently use a framework — maybe even without realizing it. JFC Swing can be viewed as a rather simple framework for creating almost all general-purpose graphical user interfaces (GUIs). Although you can use it for many applications, JFC Swing lacks a clear structure for a GUI-based application. A more specific framework in that respect is JHotDraw (see Resources for a link), which targets applications for drawing technical and structured graphics — such as network layouts and Pert diagrams — and offers much better support to develop editors for those purposes. JHotDraw demonstrates frameworks’ power and usefulness within their application domain. Before I examine JHotDraw in detail, I’ll discuss frameworks and the concepts they embody.
Theory of frameworks and design patterns
By using frameworks, developers usually reuse not only code, but also the design and architecture of a prototype application. Consequently, frameworks must be tailored to meet the needs of the problem domain. Unlike a class library, frameworks provide not only components, but also a structure for integrating those components, a predefined interoperation of components, and often a basic skeleton of an application. The skeleton is not passive like a class library, but has its own execution path from which user-defined component code is called, resulting in an inversion of control.
Developers often face problems in software design that are recurring and typical of a certain situation. You can capture a proven solution to those problems in a design pattern. A design pattern describes a problem, its context, and a reusable solution. It also assigns the problem a meaningful name, which can be used to communicate the solution to other developers.
Frameworks often rely on design patterns to help achieve a flexible general-purpose application design. The patterns introduce indirections and abstractions, which let you plug in your own classes and components.
A framework helps create an application that is developed in a timely manner and customized to the user’s requirements, and still benefits from the framework’s maturity with regard to robustness and stability. However, this result comes at a price — the cost of learning and understanding a framework’s interactions and even limitations. Most frameworks are rather complex pieces of software at high levels of abstraction. Understanding a framework can be difficult, and debugging framework code is sometimes cumbersome. Frameworks offer some customization facilities, but they can impose some restrictions and may require special programming techniques, especially if you want to perform functionality slightly out of the framework’s defined scope.
Before you use a framework, it is important to understand these things: its strengths and weaknesses, what target applications it addresses, its components and structure, the development process, and its fundamental design patterns and programming techniques.
Description of JHotDraw
In contrast to JFC Swing, JHotDraw defines a basic skeleton for a GUI-based editor with tools in a tool palette, different views, user-defined graphical figures, and support for saving, loading, and printing drawings. The framework can be customized using inheritance and combining components.
Besides the main drawing window, JHotDraw offers little support for different kinds of windows, such as text editors. With some knowledge of JHotDraw’s structure, you can extend the framework to include missing functionality. If you run the examples included, you can see what a typical application developed with JHotDraw looks like. (See Resources for information on where to download the package.) For example, JavaDraw
is a standard drawing application that provides a good overview of what is possible with JHotDraw. You can start JavaDraw
by typing:
java CH.ifa.draw.samples.javadraw.JavaDrawApp
in the directory where you unzipped JHotDraw. In addition, CH.ifa.draw.samples.pert.PertApplication
demonstrates some of JHotDraw’s customization possibilities.
JHotDraw is interesting from a software engineering point of view as well. Originally developed in Smalltalk by Kent Beck and Ward Cunningham, JHotDraw was one of the first software development projects explicitly designed for reuse and labeled a framework. It was also documented very early in terms of design patterns (see Resources for a link to “Documenting Frameworks Using Patterns”), and was therefore very influential to the design pattern community. Erich Gamma and Thomas Eggenschwiler developed the original version of JHotDraw. (See Resources for a link.)
This article describes a new version of JHotDraw (v. 5.2) in which the original AWT components have been replaced by their JFC Swing counterparts. It also supports new JFC Swing features like windows with several internal frames, split panes, scrollbars, toolbars, and pop-up menus. Therefore, JHotDraw — as an application-specific GUI framework — is based on the general-purpose GUI facilities that the JFC Swing framework offers, but adds its own features and functionality.
Package organization
All JHotDraw classes and interfaces are organized in packages according to their functionality. The package CH.ifa.draw.framework
contains mostly interface definitions of core component requirements — their responsibility, functionality, and interoperation. You can find a standard implementation of these interfaces in CH.ifa.draw.standard
. You can locate additional functionality in CH.ifa.draw.figures
and CH.ifa.draw.contrib
. A skeleton of an application or applet is defined in CH.ifa.draw.application
or CH.ifa.draw.applet
respectively.
Structure of JHotDraw
A more detailed look at the packages — in particular, the core framework package — reveals JHotDraw’s structure and shows what role each of its components plays. (See Figure 2 below.)
Any application that uses JHotDraw has a window dedicated for drawing. This DrawWindow
is the editor window and is a subclass of javax.swing.JFrame
. It contains one or more internal frames, each associated with a drawing view. The DrawingView
, a subclass of javax.swing.JPanel
, is an area that can display a Drawing
and accepts user input. Changes in the Drawing
are propagated to the DrawingView
that is responsible for updating any graphics. The Drawing
consists of Figure
s, which in turn can be containers for other Figure
s. Each Figure
has Handle
s, which define access points and determine how to interact with the Figure
(for example, how to connect the Figure
with another Figure
). In a DrawingView
, you can select several figures and manipulate them. The DrawWindow
itself usually has one active Tool
from the tool palette, which operates on the Drawing
associated with the current DrawingView
.
Typical development process using JHotDraw
The following is a list of recurring tasks involved with developing an application with JHotDraw. The tasks focus on integrating JHotDraw into your application and working together with your object model from the “Problem description for a sample application” section of this article.
- Create your own graphical figures and symbols for your application. More often than not, it is necessary to define your own graphical figures. Fortunately, some predefined figures like
AbstractFigure
,CompositeFigure
, andAttributeFigure
are already available. You can refine their behavior by subclassing and overriding some methods, such asdraw()
, to customize the graphical representation in the diagram. Typically, the graphical figures should correspond and somehow relate to the objects used in your application. - Develop your own tools to create figures and manipulate them according to application requirements. Again, JHotDraw offers some starting points: for instance,
CreationTool
,ConnectionTool
,SelectionTool
, andTextTool
. Subclassing those tools and overriding methods likemouseUp()
andmouseDown()
allows you to specify your own application interaction and perform the tasks your application needs — such as manipulating the object defined by your application. - Create the actual GUI and integrate it into your application. Unsurprisingly, JHotDraw already includes a basic application skeleton: either a basic
DrawApplication
, aMDI_DrawApplication
with support for several internal frames, or aDrawApplet
. You can define your own menus by refiningcreateMenus()
,createFileMenu()
, and so on, and plug in new tools by overriding thecreateTools()
method in a subclass. A complete GUI is created when you instantiate your application at runtime and call theopen()
method. - Compile the applications using javac. It is important to incorporate all packages needed by JHotDraw into the classpath when calling either javac or java.
Some of these tasks involve applying certain design patterns, which I’ll discuss later in more detail.
Problem description for a sample application
Before using a framework, it is important to know its target application domain (if there is one) and how the framework addresses problems found within that domain.
The development of a simple class diagram editor called JModeller will serve as a sample application in this article. JModeller helps you design class diagrams and document software architecture. It supports classes with association (has-a
), aggregation (consists-of
), dependency (uses-a
), and inheritance (is-a
) relationships between them. Beyond these fundamental design constructs, the editor has no advanced features and can only save, load, and print the object models that are designed with it.
The object model for managing classes in the class diagram editor is rather simple. The main object in this model, JModellerClass
, represents a class, which consists of a class name, attributes, and methods. If the association, dependency, and inheritance relationships become more complex, you can include a dedicated class in the model for keeping track of their properties and behavior. That is not necessary at the moment, so classes store the information about their relationships themselves. Still, there are specialized graphical figures responsible for drawing a class, an association, a dependency, or an inheritance line. You should not confuse the object model for building a class editor with the class editor’s capability to design new object models in a class diagram. This particular object model just stores the information needed by many class diagrams, as well as some graphical information.
Obviously, the kind of application that is suitable to build with JHotDraw fits this requirement description exactly.
Using JHotDraw’s design patterns
Now I will show you how to develop the class diagram editor using JHotDraw’s design patterns.
The Model-View-Controller paradigm
First, keep in mind that JHotDraw is based on the Model-View-Controller (MVC) paradigm (see Resources for a link), which separates application logic from user interface dependencies. The view is usually responsible for displaying information in a user interface; the controller handles the user interaction and maps it onto the application functionality. The model underlies the view and the controller and consists of the application’s logic and data. The view is notified of changes in the data.
You should adhere to MVC when using JHotDraw to build applications; usually, you must provide your own object model and add figures to graphically represent the model’s underlying objects. JHotDraw becomes a view, and partly a controller. As a controller, it offers tool components for managing user interaction and manipulating graphical objects. In turn, changes to a graphical object should be reflected onto the object model. You can also develop tools that contain application logic and deal with the object model directly.
The Composite design pattern
As I mentioned before, the object model for storing information about classes in a class diagram is straightforward. You just need a single JModellerClass
, which you can find in JModellerClass.java. The graphical figure for representing a class in a class diagram is more interesting. It has multiple requirements that are not covered by the basic figures. Therefore, a new GraphicalCompositeFigure
is created; the figure incorporates the advantages of several existing figures, most notably one TextFigure
for the class name and several TextFigure
s for attributes and methods. JHotDraw already provides a CompositeFigure
, which integrates several figures into a single one.
As the figure name suggests, the related design pattern is called Composite. It is described, as are other JHotDraw design patterns, in Design Patterns by Erich Gamma, et al. The idea behind Composite is that a container consists of several components of the same basic type, but is treated like a single component. All behavior in the form of method calls is delegated from the container to its parts. Typically, a client component is unaware that it uses a composition of elements instead of a single component. This encapsulation technique enables the building of a composite, like CompositeFigure
, with a hierarchical structure of components in which all contained components act and react as one unit. Interestingly, a StandardDrawing
is a subclass of CompositeFigure
, which means a Drawing
consists of other figures but is, itself, a figure.
A good example for how a Composite delegates behavior to its components can be found in the draw()
method of CH.ifa.draw.standard.CompositeFigure
:
/**
* Draws all the contained figures
* @see Figure#draw
*/
public void draw(Graphics g) {
FigureEnumeration k = figures();
while (k.hasMoreElements())
k.nextFigure().draw(g);
}
Ordinary CompositeFigure
s have no visual presentation on their own, but simply display their child figures. Nevertheless, a visual representation of a class must calculate its overall visual presentation and add its own graphical container display. A GraphicalCompositeFigure
is a CompositeFigure
and also delegates its visual representation to a dedicated graphics figure. If an AttributeFigure
(or subclass) is used as a graphics figure, then the GraphicalCompositeFigure
can also store attributes like font information and colors.
public class GraphicalCompositeFigure extends CompositeFigure {
....
/**
* Return the display area. This method is delegated to the encapsulated presentation figure.
*/
public Rectangle displayBox() {
return getPresentationFigure().displayBox();
}
/**
* Draw the figure. This method is delegated to the encapsulated presentation figure.
*/
public void draw(Graphics g) {
getPresentationFigure().draw(g);
super.draw(g);
}
....
}
Now, all you must do is create a ClassFigure
that inherits all behavior needed from GraphicalCompositeFigure
and contains a TextFigure
for the class name, and a figure for each attribute and method. A CH.ifa.draw.figures.RectangleFigure
is used in the constructor as graphical representation to draw a box around the whole container figure.
public class ClassFigure extends GraphicalCompositeFigure {
private JModellerClass myClass;
private GraphicalCompositeFigure myAttributesFigure;
private GraphicalCompositeFigure myMethodsFigure;
...
public ClassFigure() {
this(new RectangleFigure());
}
public ClassFigure(Figure newPresentationFigure) {
super(newPresentationFigure);
}
/**
* Hook method called to initialize a ClassFigure.
* It is called from the superclass' constructor and the clone() method.
*/
protected void initialize() {
// start with an empty Composite
removeAll();
// create a new Model object associated with this View figure
setModellerClass(new JModellerClass());
// create a TextFigure responsible for the class name
setClassNameFigure(new TextFigure() {
public void setText(String newText) {
super.setText(newText);
getModellerClass().setName(newText);
update();
}
});
// add the TextFigure to a Composite
GraphicalCompositeFigure nameFigure = new GraphicalCompositeFigure(new SeparatorFigure());
nameFigure.add(getClassNameFigure());
...
add(nameFigure);
// create a figure responsible for maintaining attributes
setAttributesFigure(new GraphicalCompositeFigure(new SeparatorFigure()));
...
add(getAttributesFigure());
// create a figure responsible for maintaining methods
setMethodsFigure(new GraphicalCompositeFigure(new SeparatorFigure()));
...
add(getMethodsFigure());
setAttribute(Figure.POPUP_MENU, createPopupMenu());
super.initialize();
}
...
}
The Strategy design pattern
The graphical figure for representing a ClassFigure
is only responsible for drawing the figure; it does not know how the ClassFigure
should be laid out or how its subcomponents should be graphically arranged. In fact, the graphical representation is independent of the layout algorithm. Consequently, the layout algorithm is separated from the ClassFigure
and encapsulated in an external component, which takes over control and has wide-range access to its host component. If a ClassFigure
has to be laid out, it delegates the task to a CH.ifa.draw.contrib.FigureLayoutStrategy
, which contains the logic of walking through all child elements of the ClassFigure
and arranges those elements. This mechanism is similar to java.awt.LayoutManager
for arranging the content of a java.awt.Window
, and the separation of algorithm from context has been described in a design pattern known as Strategy. Strategy gives you the flexibility to dynamically switch behavior at runtime and increase algorithm reusability.
The State pattern
In JHotDraw you have several tools in a tool palette, which you can use to select, manipulate, or create a figure. Sometimes you have to give yourself another option by defining your own tool. As we have seen, a ClassFigure
contains several other TextFigure
s for the class name, attributes, and methods. Unfortunately, in this case a CH.ifa.draw.standard.SelectionTool
activates the selected container, but none of the TextFigure
s contained in it. You want to double-click on a ClassFigure
and then be able to edit any of its contained TextFigure
s. A CH.ifa.draw.contrib.CustomSelectionTool
fulfills that purpose and also deals with pop-up menus on figures. The selection tool is derived from it and overrides the handleMouseDoubleClick
and handleMouseClick
methods. If a double click occurs now, that tool activates a CH.ifa.draw.figures.TextTool
, which is responsible for text editing.
public class DelegationSelectionTool extends CustomSelectionTool {
private TextTool myTextTool;
public DelegationSelectionTool(DrawingView view) {
super(view);
setTextTool(new TextTool(view, new TextFigure()));
}
protected void handleMouseDoubleClick(MouseEvent e, int x, int y) {
Figure figure = drawing().findFigureInside(e.getX(), e.getY());
if ((figure != null) && (figure instanceof TextFigure)) {
getTextTool().activate();
getTextTool().mouseDown(e, x, y);
}
}
protected void handleMouseClick(MouseEvent e, int x, int y) {
deactivate();
}
public void deactivate() {
super.deactivate();
if (getTextTool().isActivated()) {
getTextTool().deactivate();
}
}
...
}
The selected tool operates in the drawing canvas owned by the drawing view. Therefore, the drawing view always has one active tool; it changes its behavior and user interaction when the tool changes. In other words, the tool is a state within the context of the drawing view. Ideally, the drawing views should be independent from the active tool, so the tools are interchangeable and complicated if/switch statements are not necessary to distinguish between them. With the State pattern, you separate the state from its context by making states their own objects with defined interfaces that the context can use. Thus, introducing the DelegationSelectionTool
is just another state for the drawing view as the context. The drawing view accepts user input as usual, but delegates it to the tool. The tool knows what to do with the user input and performs its task accordingly.
The State pattern is similar to the Strategy pattern in that they both delegate behavior to specialized objects, but they have different purposes. Strategy decouples an object from an algorithm and makes the algorithm reusable; State separates and extracts internal behavior and makes it easily extensible and interchangeable.
The Template method
Classes must be displayed in a diagram, and so must the relationships among those classes. An AssociationLineConnection
figure represents a linear association between two classes, and can be changed into a directional association or aggregation. An InheritanceLineConnection
figure represents an inheritance relationship with an arrow line starting at the subclass and pointing to the superclass. A CH.ifa.draw.figures.LineConnection
provides connectEnd()
and disconnectEnd()
as Template methods.
A Template method is another design pattern and defines a command sequence, which you should always execute. Within that command sequence, other methods are called at certain times. Subclasses can then hook in additional application-specific instructions without changing the overall behavior. Therefore, AssociationLineConnection
and InheritanceLineConnection
need only refine the provided hook methods — handleConnect()
and handleDisconnect()
— to establish a relationship between two classes. The following code demonstrates this principle in InheritanceLineConnection.java:
public class InheritanceLineConnection extends LineConnection {
...
/**
* Hook method to plug in application behavior into
* a template method. This method is called when a
* connection between two objects has been established.
*/
protected void handleConnect(Figure start, Figure end) {
super.handleConnect(start, end);
JModellerClass startClass = ((ClassFigure)start).getModellerClass();
JModellerClass endClass = ((ClassFigure)end).getModellerClass();
startClass.addSuperclass(endClass);
}
...
}
The Template method must perform a sequence of steps before a connection can be established. It tests whether the two figures can be connected by calling canConnect
, then sets the start and end figures, and finally calls the hook method handleConnect
.
Another example of a Template method can be found in CH.ifa.draw.standard.AttributeFigure
, where the draw()
method defines a drawing routine and calls drawBackground()
and drawFrame()
, which have been overridden in subclasses such as CH.ifa.draw.figures.RectangleFigure
.
The Factory method
You can think of a Factory method — another design pattern from Design Patterns — as a special case of hook method during a construction process. It lets you create customized components. This kind of method is used extensively in JHotDraw, especially when creating user interface components like menus and tools. Many Factory methods can be found in CH.ifa.draw.application.DrawApplication
and have names like createTools()
and createMenus()
, which in turn call createFileMenu()
, createEditMenu()
, and so forth. Depending on the granularity of your customization, you can place changes in the appropriate create method. To plug in your own tools for creating classes and drawing associations and inheritance relations between them, override the createTools()
method in your main application class, JModellerApplication.java:
public class JModellerApplication extends MDI_DrawApplication {
...
public JModellerApplication() {
super("JModeller - Class Diagram Editor");
}
/**
* Create the tools for the toolbar. The tools are
* a selection tool, a tool to create a new class and
* two tools to create association and inheritance
* relationships between classes.
*
* @param palette toolbar to which the tools should be added
*/
protected void createTools(JToolBar palette) {
super.createTools(palette);
Tool tool = new ConnectedTextTool(view(), new TextFigure());
palette.add(createToolButton(IMAGES+"ATEXT", "Label", tool));
tool = new CreationTool(view(), new ClassFigure());
palette.add(createToolButton(DIAGRAM_IMAGES+"CLASS", "New Class", tool));
tool = new ConnectionTool(view(), new AssociationLineConnection());
palette.add(createToolButton(IMAGES+"LINE", "Association Tool", tool));
tool = new ConnectionTool(view(), new DependencyLineConnection());
palette.add(createToolButton(DIAGRAM_IMAGES+"DEPENDENCY", "Dependency Tool", tool));
tool = new ConnectionTool(view(), new InheritanceLineConnection());
palette.add(createToolButton(DIAGRAM_IMAGES+"INHERITANCE", "Inheritance Tool", tool));
}
...
}
You must also override createSelectionTool()
to create your own DelegationSelectionTool
:
protected Tool createSelectionTool() {
return new DelegationSelectionTool(view());
}
The Prototype design pattern
You might have noticed in createTools()
that each tool is initialized with an instance of the figure it is meant to create. Every creation tool in JHotDraw uses the original figure instance to create duplicate instances. This concept is known as the Prototype design pattern. The basic clone()
mechanism is defined in CH.ifa.draw.standard.AbstractFigure
, where an instance copy is created using Java’s serialization.
Serialization is an easy way to write a whole object tree and read it afterwards. A deep copy of the original figure is also created, and contains duplicates of all referenced objects. Thus, the original and copy figures do not share any object references. For example, a ClassFigure
does not serialize associated context menus. Those must be initialized during deserialization:
private void readObject(ObjectInputStream s)
// call superclass' private readObject() indirectly
s.defaultReadObject();
// Create popup menu after it has been read
setAttribute(Figure.POPUP_MENU, createPopupMenu());
}
Moreover, reading and writing drawings with their contained graphical figures can also simply use the serialization mechanism. JHotDraw also has its own mechanism for storing objects to disk and restoring them. Each figure implements the CH.ifa.draw.util.Storable
interface and provides its own implementation of how to write itself to disk and reread from it. The information written contains the class name and all attribute values as strings. Obviously, the order in which attribute values are written and read is important. With the class name, JHotDraw creates a new Drawing
instance using Java’s reflection mechanism. In this case, the default constructor with an empty parameter list is called.
The main() routine
Finally, the class diagram editor must be instantiated and the editor window opened. This is done in the main()
method of JModellerApplication
, which is derived from the skeleton application with support for several internal frames:
/**
* Start the application by creating an instance and open
* the editor window.
*/
public static void main(String[] args) {
JModellerApplication window = new JModellerApplication();
window.open();
}
Using JModeller
Using JModeller
should be rather straightforward. There are only a few obstacles that require a short description. First, each ClassFigure
has a context menu that lets you add attributes and methods. By double-clicking on the class name, an attribute, or a method, you can edit the text. An empty attribute or method name deletes an attribute or method, while an empty class name deletes the whole class. The AssociationLineConnection
also has a context menu, which changes the association into an aggregation and switches between bidirectional and unidirectional associations. You can attach text comments to any class relationship with the help of the ConnectedTextTool
. You can insert additional handle points into any class relationship line by clicking on the connection line with the appropriate connection tool selected. Finally, make sure that jhotdraw.jar
is included in your classpath when you compile or run JModeller
. For example, under Windows you can compile JModeller
with the following command line:
javac -classpath "%CLASSPATH%;<JHotDraw-Directory>/jhotdraw.jar;." *.java
You can start it with:
java -classpath "%CLASSPATH%;<JHotDraw-Directory>/jhotdraw.jar;." JModellerApplication
Conclusion
You must understand a framework to use it correctly. Design patterns are a way of achieving a highly reusable software architecture, and are also very suitable for documenting that architecture in a language intelligible to other developers. JHotDraw is a good example of a framework that is composed of fundamental design patterns, such as Composite, State, Template method, Factory method, and Strategy. Knowing the basic concepts behind those patterns, and their applications within JHotDraw, helps you decide how to customize and adapt the framework to meet your application’s requirements. Although learning a framework requires extra effort and may initially slow you down, a framework like JHotDraw often reduces development time and improves the quality of your software. Of course, JModeller is just a basic example of JHotDraw’s capabilities, but I hope it will serve as a starting point for your own hands-on experience.
JHotDraw has evolved into an open-source project. From this project, you can download the latest releases and contribute to the project as well: