JGraph Extension

Purpose

Many business applications need to display graphs. This contribution shows how the free graph library JGraph can be integrated into ULC applications.The integration is done entirely on the client side. This approach has the advantages that all functionalities of JGraph are available on client side and that only a small and light weighted communication between client and server is necessary.Since the JGraph library is only 136 KB big, the download is done rapidly.

Screenshots


jgraphextension_1 jgraphextension_2

Features

Features of the extension

  • Implements the graph model event handling of JGraph: Whenever the graph changes, the UlcGraphModelEvent stores information about these changes.
  • Implements the graph selection event handling of JGraph: Whenever the selection of the graph changes, the UlcGraphSelectionEvent stores information about the change in the current selection.
  • Supports full Drag & Drop functionality: all graph elements can easily be moved and resized, edges can be interactively connected to cells.
  • Like JGraph, the extension uses the GraphConstants class to set the properties of the graph elements. So far, not all of the property settings are supported. But the most important properties such as colour, border, bounds, dash pattern, arrow and label are implemented.
  • The API strongly depends on the JGraph API. It also offers many interfaces such as IGraphModel, IGraphSelectionModel, IGraphCell, IEdge and IPort. Therefore one is free to create his own models to implement these interfaces (be aware that in such a case, one is self responsible for the client-server communication). Usually, the default models and graph elements are sufficient.
  • Contrary to JGraph, this contribution is implemented much more type-safe. Instead of parameters of type Object, the contribution methods demand parameters of type IGraphCell, which is the highest type in the class hierarchy of the graph elements.

Features of the sample application

  • Click "Insert Cell" or "Insert Edge" to insert cells and edges either with default or random properties (activate or deactivate the "Random Values" checkbox).
  • Connect an edge to a cell by using the Drag & Drop mechanism: Click the start or the end of an edge and drag it to the middle of a cell (the cell gets highlighted when the connection is about to be successful). It is also possible to disconnect an edge from a cell: Click the start or the end of the edge and unlock the connection from the cell by dragging the edge to another cell or just somewhere on the graph.
  • Select a cell or an edge and click "Remove" to remove it from the graph. When you remove a cell, all edges connecting to that cell are also removed.
  • Click "Zoom In" and "Zoom Out" in order to zoom on the graph. Click "Reset Zoom" to set the zooming factor to the default value.

How to use

A sample usage of the JGraph Extension is provided in com.canoo.ulc.community.jgraphextension.application.ULCGraphSample.ULCGraph is an ULCComponent containing all required information to create a JGraph object within its client counterpart UIGraph.The first step is to instantiate a graph model. Then, create an ULCGraph object that is based on that model. In addition, a HashMap object is needed in order to store all the attributes of the graph and its elements.
	IGraphModel model = new DefaultGraphModel();
	ULCGraph graph = new ULCGraph(model);
	Map attributes = new HashMap();
Now, create some graph cells and edges. Important to mention here is the fact that all attributes are not stored under the object itself, but in an ULCAttributeMap object that is finally saved in the HashMap object built above. This is exactly the same way JGraph does it. Perform additional operations using the static methods of the GraphConstants class to change the properties of the graph elements.
	IGraphCell hello = new ULCGraphCell("Hello");
	ULCAttributeMap helloAttribute = new ULCAttributeMap();
	GraphConstants.setBounds(helloAttribute, new ULCRectangle(50, 50, 40, 20));
	GraphConstants.setBackground(helloAttribute, Color.yellow);
	attributes.put(hello, helloAttribute);IEdge edge = new ULCEdge("Hello-World-Edge");
	ULCAttributeMap edgeAttribute = new ULCAttributeMap();
	GraphConstants.setLineEnd(edgeAttribute, GraphConstants.ARROW_CIRCLE);
	GraphConstants.setEndFill(edgeAttribute, true);
	attributes.put(edge, edgeAttribute);
Never forget to add at least one port to a cell. Otherwise you are not able to connect edges to the cell.
	IPort helloPort = new ULCPort();
	hello.addPort(helloPort);
Now, link the edge to the cells. This happens by instantiating an ULCConnectionSet object with the three parameters edge, source port and target port of the edge.
ULCConnectionSet connectionSet = new ULCConnectionSet(edge, helloPort, worldPort);
Eventually, insert the above-created elements into the graph model, which enforces the graph to repaint itself.
	IGraphCell[] cells = {edge, hello, world};
	model.insert(cells, attributes, connectionSet);
There are two listeners that can be added to the graph model. One is the IGraphModelListener, which is always notified after the graph has changed, and the other is the IGraphSelectionListener, which is informed when the current selection has changed. Both listeners can be added to the graph model as follows:
	model.addGraphModelListener(new IGraphModelListener() {
		public void graphChanged(UlcGraphModelEvent event) {
			System.out.println(event.getInsertedCells());
		}
	});

	graph.getSelectionModel().addGraphSelectionListener(new IGraphSelectionListener() {
		public void valueChanged(UlcGraphSelectionEvent event) {
			System.out.println(event.getRoots());
		}
	});

How it is implemented

The core components are ULCGraph and UIGraph, as well as ULCGraphModelAdapter and UIGraphModelAdapter. ULCGraph provides a small API to set a couple of graph properties. It also holds a graph model and a graph selection model. You can add an IGraphModelListener to receive notifications when the graph has changed and an IGraphSelectionListener to receive notifications when the selection of the graph has changed. The ULCGraphModelAdapter is responsible for the communication between the IGraphModel object on server side and JGraph's GraphModel on client side. It holds a weak reference to the graph model and it is at the same an IGraphModelListener that always synchronizes the graph's current state to the client when the graph is changed by code execution on the server side. Its counterpart UIGraphModelAdapter listens to the changes coming from interactions with the user and sends those changes to the server. Moreover, it handles all feedback on the client side. This has the advantage that no server roundtrip is necessary until the user has finished moving or dragging the elements, which eventually triggers the change event. Important information: At the present state, UIGraphModelAdapter delegates all interface method invocations to JGraph's DefaultGraphModel. The reason for this delegation is due to the improper implementation of the MVC pattern in JGraph (DefaultGraphModel.insert() does not only change the model but also updates the views). This prevents a flexible implementation of the GraphModel interface (access to inner classes of JGraph needed). In order to allow a flexible implementation of e.g. GraphModel.acceptsSource() or GraphModel.acceptsTarget(), UIGraphModelAdapter needs to be implemented as a decorator instead of using delegation only. GraphConstants is used to set the properties of the graph elements. This class does not communicate directly with the client, but instead invokes the different put() methods of ULCAttributeMap, which then enforces a server push.

Resources