Working with Graph Hierarchies on a yFiles Java Server

The yFiles FLEX server library provides support for hierarchically organized graphs. To get a brief review over the concepts of grouping and folding see the corresponding chapter describing the client library.

Grouping

Analogous to the client the hierarchy of nodes is managed by the interface INodeHierarchy. This interface provides convenient utility methods for querying and manipulating a LayoutGraph's hierarchical structure:

Node getParent(Node child)
Description Get a node's parent.
void setParent(Node child, Node parent)
Description Set a node's parent.
void setParent(NodeList children, Node parent)
Description Add a list of nodes to a parent
setGroupNode( Node node, boolean isGroupNode )
Description Set whether the node should be considered a group node
boolean isGroupNode(Node node)
Description Returns whether the node is considered a group node
NodeList getChildren(Node parent)
Description Get the children of a group node

The INodeHierarchy of a graph can be obtained using StyledLayoutGraph's method getNodeHierarchy(). Note that GraphRoundtripSupport's method createRoundtripGraph() always returns a StyledLayoutGraph.

Example 7.7, “Creating a Grouped Graph on the Server” demonstrates how the grouped graph structure shown in Figure 7.1, “Example grouped graph” can be created on the server using a LayoutGraph instance with the yFiles FLEX server API.

Figure 7.1. Example grouped graph

The hierarchic graph structure created in Example 7.7, “Creating a Grouped Graph on the Server”, 
after a hierarchic layout has been applied.
The hierarchic graph structure created in Example 7.7, “Creating a Grouped Graph on the Server”, after a hierarchic layout has been applied.

Example 7.7. Creating a Grouped Graph on the Server

/**
 * Create a simple graph hierarchy containing 
 * nested group nodes
 */
private void createGroupNodes(StyledLayoutGraph graph) {

    // createGroupNode assigns a distinct style
    // and label model to nodes that will be used as group nodes.
    Node outerGroup = createGroupNode(graph, "Outer Group");
    Node innerGroup = createGroupNode(graph, "Inner Group");

    // createLeafNode assigns a distinct style
    // and label model to nodes that will be used as leaf nodes.
    Node leafNode1 = createLeafNode(graph,"Leaf Node");
    Node leafNode2 = createLeafNode(graph,"Leaf Node");
    Node leafNode3 = createLeafNode(graph,"Leaf Node");
    Node leafNode4 = createLeafNode(graph,"Leaf Node");

    // So far, we have a flat graph - no hierarchy 
    // has been defined yet. 
    // Now, we define the hierarchy.

    INodeHierarchy nodeHierarchy = graph.getNodeHierarchy();

    nodeHierarchy.setParent(leafNode1, outerGroup);
    nodeHierarchy.setParent(innerGroup, outerGroup);
    nodeHierarchy.setParent(leafNode2, innerGroup);
    nodeHierarchy.setParent(leafNode3, innerGroup);
    nodeHierarchy.setParent(leafNode4, outerGroup);
    
    // Add some edges.
    graph.createEdge(leafNode2, leafNode1);
    graph.createEdge(leafNode3, leafNode2);
    graph.createEdge(leafNode3, leafNode4);
}

/**
 * Create a node that will be used as leaf node, 
 * using a BevelNodeStyle and a centered label.
 */
private Node createLeafNode(StyledLayoutGraph graph, String labelText) {

    Node node = graph.createNode();
    graph.setSize(node, 60, 40);

    BevelNodeStyle style = new BevelNodeStyle();
    style.setColor(new Color(255, 140, 0));
    graph.setStyle(node, style);

    if (null != labelText) {
      InteriorLabelModel labelModel = new InteriorLabelModel();
      ILabelModelParameter parameter = 
          new InteriorLabelModel.ModelParameter(labelModel,
                        InteriorLabelModel.POSITION_CENTER);
      graph.addLabel(node, new Label(labelText, parameter));
    }

    return node;
}

/**
 * Create a node that will be used as a group node, 
 * using a PanelNodeStyle and
 * a label that is stretched to the node's width.
 */
private Node createGroupNode( StyledLayoutGraph graph, String labelText ) {

    Node groupNode = graph.createNode();

    InteriorLabelModel labelModel = new InteriorLabelModel();
    ILabelModelParameter parameter = 
            new InteriorLabelModel.ModelParameter(labelModel,
                                InteriorLabelModel.POSITION_NORTH);
    Label label = new Label(labelText, parameter);
    graph.addLabel(groupNode, label);

    PanelNodeStyle style = new PanelNodeStyle();
    style.setColor(new Color(128, 128, 128));
    style.setLabelInsetsColor(new Color(192, 192, 192));

    graph.setStyle(groupNode, style);

    // Define some insets for the group node,
    // that will be considered by layout algorithms.
    DataProvider insetsProvider = 
                graph.getDataProvider(GroupingKeys.GROUP_NODE_INSETS_DPKEY);
    if (null != insetsProvider && insetsProvider instanceof DataMap) {
      ((DataMap) insetsProvider).set(groupNode, new Insets(25, 5, 5, 5));
    }

    return groupNode;
}

Folding

Folding, i.e. expanding and collapsing group nodes, is an important means in breaking large graphs into smaller units. yFiles FLEX supports folding at the client as well as at the server side.

Folding is supported by FoldedLayoutGraph, which is a subclass of StyledLayoutGraph. GraphRoundtripSupport's method createRoundtripGraph() returns a FoldedLayoutGraph if folding is enabled. Folding support is not enabled by default but has to be enabled by setting setFoldingEnabled() to true.

Generally, there are two ways to send a folded graph to the client:

  • Sending the folder nodes as folders together with their nested graphs (the folder contents). This allows to open and close the folders on the client side without contacting the server again. On the other hand, transferring the nested graphs can increase the size of the transferred GraphML significantly, which affects roundtrip time and network traffic.

    This mode can be enabled by setting setLocalViewMode() on GraphRoundtripSupport to false (the default).

    Note that folding has to be enabled on the client.

  • Sending the closed folder nodes as "normal" nodes. With this option opening a folder requires a new roundtrip to the server. On the other hand, the transferred GraphML will be kept small which results in shorter roundtrip times and less network traffic.

    This mode can be enabled by setting setLocalViewMode() on GraphRoundtripSupport to true.

Folding is managed by the FoldedLayoutGraph which provides convenient utility methods for expanding and collapsing groups:

void collapse(Node node)
Description collapse (close) a group node, hiding its children from the current view
void expand(Node node)
Description expand (open) a group node, revealing its children
void isExpanded(Node node)
Description whether the given node is expanded
void setLocalRoot(Node node)
Description "enter" the given group node, i.e. let the folded graph reflect only the contents of the group

A group node can be opened or closed using the methods expand(Node node) and collapse(Node node), respectively. Closing the "innerGroup" node from Example 7.7, “Creating a Grouped Graph on the Server” is shown in Example 7.8, “Closing a group node”.

Example 7.8. Closing a group node

// modify the method createGroupNodes in the
// "create a grouped graph" example to apply folding:
private void createGroupNodes(FoldedLayoutGraph graph) {
    ...
  // insert the new line at the end
  graph.collapse(innerGroup);
}

Figure 7.2. Closed group node

The server-side folded graph also supports the concept of a virtual "local root" through the setLocalRoot(Node node) method of FoldedlayoutGraph. When a graph layout is calculated for the folded graph, the layout will be applied to the contents of the group node that is set as the local root.

When reading a graph from a HttpServletRequest GraphRoundtripSupport will set the local root automatically if folding is enabled and the request is sent with a "localRoot" parameter. The local root will be set to the node identified by the id given with that parameter.

Different View States for Open and Closed Groups

As discussed in the section called “Folding-related State Across Views”, closed folder nodes and inter-edges (edges between hidden contents of a closed folder and nodes outside the folder) can have different visual representations. Class NodeViewState keeps the visual information such as labels, style, and layout for a dummy node. Class EdgeViewState keeps the visual information such as labels, styles, bends, and source and target ports for a dummy edge. FoldedLayoutGraph's methods to access the view states are listed below.

NodeViewState getNodeViewState(DummyNodeId nodeId)
Description // get the view state of a collapsed group node
EdgeViewState getEdgeViewState(DummyEdgeId edgeId)
Description // get the view state of a dummy edge

If a folder node should have a different label, style and size than in its open state, a view state has to be assigned as shown in Example 7.9, “Setting a view state”. Note that collapsing a group node will not automatically create view states for the collapsed state of the group node and for the inter edges resutling from the collapse operation. However, when a view state of a collapsed node or interedge is queried from the FoldedLayoutGraph using getEdgeViewState(DummyEdgeId edgeId) / getNodeViewState(DummyNodeId nodeId), and no view state instance has been assigned to the collapsed node or interedge yet, a new view state instance will be created and returned. The default implementations of createNodeViewState(DummyNodeId nodeId) and createEdgeViewState(DummyEdgeId edgeId) will copy the visual properties of the corresponding master node or master edge to the view state instance.

Example 7.9. Setting a view state

// get the nodeViewState for the innerGroup
NodeViewState nodeViewState = graph.getNodeViewState(new DummyNodeId(innerGroup));
// add a different label
nodeViewState.getLabels().add(new Label("Collapsed node", InteriorLabelModel.center));
// set a different style
nodeViewState.setStyle(new BevelNodeStyle());
// make it smaller
nodeViewState.setLayout(new YRectangle(0, 0, 60, 40));

Figure 7.3. Folder node with different view state

The closed group node with the different view state created in Example 7.9, “Setting a view state”.
The closed group node with the different view state created in Example 7.9, “Setting a view state”.

Automatic Layout

When applying an automatic layout the layouter does not know about the folding state by default. FoldedLayoutGraph offers a convenience method to configure the graph and the layouter and apply the layout: doLayout(Layouter layouter), see also Example 7.10, “Applying a layout on a folded graph”. Its overload doLayout(Layouter layouter, Node root, boolean recursive, byte dummyEdgeMode) allows further configurations. See the API documentation for details.

Example 7.10. Applying a layout on a folded graph

// graph is of type com.yworks.yfiles.server.graphml.folding.FoldedLayoutGraph
// layouter is of type y.layout.Layouter
graph.doLayout(layouter);

Tutorial Demo Code

The following servlets which can be found in subpackages of com.yworks.yfiles.server.graphml.demo demonstrate aspects of working with Hierarchies on a yFiles Java Server:

  • servlets.RandomGraphServlet creates random graphs which are styled and grouped using the capabilities of StyledLayoutGraph. The client side GroupingDemo uses this servlet.
  • incrementalHierarchicGroup.IncrementalHierarchicGroupRoundtrip demonstrates how to use FoldedLayoutGraph's capabilities to collapse and expand group nodes. This demo uses the "local view mode" to minimize the size of the transferred GraphML. The client side IncrementalHierarchicGroupDemo uses this servlet.
  • servlets.FoldingServlet demonstrates how to create a folded graph using FoldedLayoutGraph. It also demonstrates setting different view states to nodes and edges and applying different layout modes. This demo sends the entire graph (i.e. the contents of closed folders, too) to the client, enabling opening and closing group nodes at the server side. The client side FoldingDemo uses this servlet.