Introduction to CADEMIA Development

1. Motivation
2. Command language
3. Command subsystem
4. Model subsystem
5. View subsystem
6. Packaging plugins
7. Further help

1. Motivation

CADEMIA is a platform that allows the integration of engineering applications. Since CADEMIA is an Open Source project developers could have the idea of adding their applications before compile time into the CADEMIA source code. Such an approach would, however, reduce the idea of an engineering platform to that of a source code template only. This document describes how the CADEMIA platform can be dynamically extended at runtime. Basically, there are two recommended ways: via a macro written in the CADEMIA command language or via a Plugin written in Java.

The figure below shows the subsystems of the CADEMIA engineering platform. The Model subsystem represents the structured object sets of a specific engineering application. The user's input is handled by the Command subsystem. The model is visually represented by the View subsystem that is embedded into the Graphical User Interface (GUI).

The CADEMIA system architecture

2. Command language

The fundamental user interface of CADEMIA is plain text that is structured according to the rules of a command language. The user's input is represented as a sequence of commands. A command is described by a name and a sequence of parameters. As a prerequisite the command name must be mapped to the respective command class . The grammar of the command language is given below, for simplicity reasons the definition of the symbols is omitted:

start   -> cmdList
cmdList -> (cmd ";")*
cmd     -> <TEXT> argList
argList -> (arg)*
arg     -> <TEXT>|<LITERAL>

A command argument consists of a simple text or a string literal with double quotes as bracket delimiters. When a command is prompting for arguments the user can enter one of the symbols described in table below:

SyntaxSemantics
#menu(m) item m of current menu
#window(w) window w
#digitize(x, y, w) digitize at (x, y) in window w
#cancel() cancel command
#interrupt(l) interrupt command by command list l
<TEXT>|<LITERAL> text or string literal

Macros must be formatted on the basis of this command language. Macros are to be stored in the file system, preferably with the extension cademia_macro. At runtime, they are dynamically loaded and executed via the CADEMIA command play.

For mouse strokes also the rules of the command language apply. It is possible to refer to the coordinates of the currently issued mouse stroke.

SyntaxSemantics
#strokestart<N> Start point of stroke <N>, e.g. #strokestart0 for the first part stroke
#strokeent<N> End point of stroke <N>, e.g. #strokeend2 for the 3rd part stroke
#strokewin<N> Window name where stroke <N> was entered, e.g. #strokewin1 for the 2nd part stroke

Command language extensions have their limitations, both in complexity and in execution speed. For extensions of a certain complexity in the next section extensions in Java should be considered.

3. Command subsystem

In engineering practice it is important that the effect of operations applied to the model instance can be reversed. CADEMIA relies on the basic assumption is that the engineering process can be described by a sequence of commands. In CADEMIA each command is represented as an object whose class has to implement the Cmd interface:

interface Cmd {
  void doCmd(Object context) throws CmdAbortedException;
  void undoCmd(Object context);
  void redoCmd(Object context);
  boolean changesState();
  boolean isUndoable();
}

The doCmd() method is responsible for the initial execution of the command. It is invoked exactly once in the lifetime of the object and has the user interaction code. The object set is transformed from state m to m'.

The Cmd interface

The undoCmd() method is responsible for undoing the changes applied to the object set by the doCmd() method. No user interaction code must be contained in this method. The model instance is transformed from state m' to state m. It should be noted that the object set must be exactly in state m' when undoCmd() is invoked.

The redoCmd() method redoes the command without any user interaction. Analogous to the doCmd() method the object set is transformed from state m to m'. Again, the model instance must be exactly in state m when redoCmd() is invoked.

The changesState() method indicates whether the object set was changed as a result of the doCmd() method. If this method returns false then the command object should not be added to the sequence of executed commands. An example is an aborted command or a command that translates objects by a null vector.

The isUndoable() method should return false in the rare case where a command cannot be reversed. An example for an irreversible command is the loading of a document, since normally it cannot be guaranteed that the state of the document hasn't changed at the time of a later redoCmd().

The commands are managed by an instance of class CmdMgr (see figure above). The command manager stores the sequence of command objects in a list and has a cursor that identifies the actual command object.

User actionObject and invoked method Command sequence
Do command a a.doCmd() (a)
Do command b b.doCmd() (a, b)
Do command c c.doCmd() (a, b, c)
Undo c.undoCmd() (a, b, c)
Undo b.undoCmd() (a, b, c)
Redo b.redoCmd() (a, b, c)
Do command d d.doCmd() (a, b, d)

This is exemplified in the table above where for a given user action in column 1 the appropriate command object and method are indicated in column 2. Column 3 contains the sequence of command objects with the actual command printed in bold face. It should be noted that the doCmd() method results in a change of the list structure and the actual element whereas the undoCmd() and redoCmd() methods only change the actual element.

4. Model subsystem

A strictly separated model is the basis for a definition of application objects, for name spaces of uniquely identifiable entities, for component sets, selection sets, coordinate spaces and for hierarchical ordering schemes by layers. It was a strict design goal throughout the CADEMIA project to reuse existing classes of the Java Platform, Standard Edition. This approach does not only accelerate the development process. It is even more important that the well-established and proven design patterns of the Java platform can be adapted to the own needs.

4.1 Components

The model of an engineering platform should be extensible according to the needs of the users. Extensibility, however, requires the specification of certain services to be offered by the components. The difference between an Object and a Component is the higher level of abstraction of the latter.

The model cannot be strictly separated from the other subsystems. For instance, the view subsystem should reflect changes of the model subsystem. On the other hand the model components should not be dependent of the view subsystem. Thus, a generic notification mechanism is of particular importance.

interface ObservableObject {
  static interface Listener {
    void wasChanged(ObservableObject o);
    void wasCloned(ObservableObject src,
      ObservableObject dest);
  }
  void addListener(Listener l);
  void removeListener(Listener l);
}

Objects implementing the ObservableObject interface must notify their observers when they are changed or cloned. The set of listeners is controlled by the addListener() and removeListener() methods. Components have to implement the Component interface shown in the next figure. The Serializable and Cloneable interfaces are well known from the Java platform.

The Component interface

Components contain lists of attributed shapes and texts. The border of a shape consists of an arbitrary number of 3rd order Bezier curves. Attributes describe items like the boundary and the interior of a shape or the font, the height and the subscripting even of a arbitrary subranges of a text.

Components are modified via controls. This is illustrated in figure 6 for a 3rd order Bezier curve. Components have a set of features with clear semantics for the user. For instance, the features of the curve shown in figure 6 could be the Length and the Color.

3rd order BeziƩr curve

4.2 Component Sets

The modeling of engineering problems requires specific sets and relations that are normally not contained in standard packages.

interface ObservableSet extends Set {
  static interface Listener {
    void wasAdded(ObservableObject o);
    void wasChanged(ObservableObject o);
    void wasRemoved(ObservableObject o);
    void wasCloned(ObservableObject src,
      ObservableObject dest);
  }
  void addListener(Listener l);
  void removeListener(Listener l);
}

An instance of ObservableSet contains ObservableObjects. ObservableSet.Listener objects are added by the addListener() method. Via the Listener methods the client is informed about changes of the set (wasAdded() and wasRemoved()) and events caused by the contained objects (wasChanged() and wasCloned()).

The ObservableSet interface

The implementation of an ObservableSet is shown in the figure above. ObservableSetImpl implements the ObservableSet interface that extends the Set interface. A private Set implemented by a HashSet contains the ObservableObjects. This Set is privately used by ObservableSetImpl for its implementation and cannot be seen from outside.

Changes of the set are easily observed because the only way of adding and removing objects is via the ObservableSet or its base interface Set. To observe object events the ObservableSetImpl class adds an ObservableObject.Listener to each ObservableObject contained in the set.

5. View subsystem

The basic design of system for handling computer graphics and geometry is modeled in accordance with the Java 2D API. This applies for the graphical objects shape and text that can be rendered to the raster devices screen, printer and image.

A completely modular graphical user interface (GUI) is defined for all CAD functionality with synchronization of input by keyboard, mouse, menus and icons. The next figure illustrates the concept of an InputDevice that allows arbitrary input devices to be plugged to the user interface.

The InputDevice interface

Since input handling is strictly text based the communication between user interface and input device is straightforward.

interface InputDevice {
  static interface Listener {
    void gotInput(String str);
    void wasChanged(ObservableObject o);
  }
  void addInputListener(Listener l);
  void removeInputListener(Listener l);
  void setEnabled(boolean enabled);
  boolean isEnabled();
}

The synchronization of the windowing system and the model is shown in the figure below. The GUI contains an arbitrary number of GeometryPanels. Each GeometryPanel has a projection that maps model to view coordinates. Each model component is visualized by a ViewController that in turn is composed of an arbitrary number of Views. The synchronization of model and view is based upon the ObservableSet already introduced.

The View-Model synchronization

6. Packaging plugins

Java extensions are packaged as zip files. Such an extension is denoted as a CADEMIA-Plugin. It is recommended to use the file extension cademia_plugin instead of the extension zip. A CADEMIA-Plugin must have a directory /META-INF/ that contains the files plugin.cademia_macro and plugin.ini.

The file plugin.cademia_macro is a CADEMIA macro that is executed after the plugin has been loaded. The macro should execute commands to dynamically add the commands to the command table. This is done via the putcmd/ command.

The plugin.ini file defines the Plugin's properties Name, Version and Info. The Plugin is stored under this name. The Plugin-name must be unique. It is not possible to have two Plugins with the same name loaded.

The Java classpath is extended by the CADEMIA-Plugin.

See the Development Tutorial on how to develop a CADEMIA-Plugin.

7. Further help

The CADEMIA Source System is available free of charge. Subsequently it is documented where solutions to specific problems are to be found.