The management of a graph hierarchy is a multi-faceted undertaking that is handled by a small number of classes. Adding support for grouping to a "flat" graph can easily be achieved with little effort. Setting up the model-view relationship for additional folding support using a grouped graph as the model as well.
Class DefaultGraph, the default IGraph implementation used in yFiles FLEX, allows to conveniently enable grouping support by means of a property. The same effect can also be achieved by directly creating the GroupedGraph instance that ultimately handles the graph hierarchy.
Folding support is enabled using the FoldingManager class and creating managed views.
Class DefaultGraph provides convenient high-level support in order to enable grouping support for the graph structure. Instead of explicitly creating a GroupedGraph instance for a graph (as described in the section called “Class GroupedGraph”), the groupingSupported property allows to easily enable grouping support for a graph. Example 3.1, “Enabling grouping support with a DefaultGraph instance” presents how grouping support is enabled for a DefaultGraph instance.
Example 3.1. Enabling grouping support with a DefaultGraph instance
// 'graph' is of type com.yworks.graph.model.DefaultGraph. graph.groupingSupported = true;
Tutorial demo application GroupingDemo presents an extensive example of an application that handles hierarchically organized graphs.
Class GroupedGraph is an implementation of interface IGroupedGraph. It maintains the model that is behind a graph hierarchy and also manages changes relevant to this model. GroupedGraph uses an implementation of interface INodeHierarchy that holds the actual hierarchy of nodes. This INodeHierarchy implementation binds the type parameter to model item type INode.
GroupedGraph's responsibilities include, e.g., creating group nodes and grouping and ungrouping of nodes. Also, re-parenting, i.e., setting another parent node for a node is handled by GroupedGraph, too. The necessary low-level changes that correspond to the provided functionality are then invoked on the INodeHierarchy implementation.
Adding grouping support to a graph is achieved by creating a GroupedGraph instance for it. Example 3.2, “Adding grouping support to a graph” demonstrates how to add a GroupedGraph instance to a yet "flat" graph, i.e., one that lacks support for grouping.
Example 3.2. Adding grouping support to a graph
// 'flatGraph' is of type com.yworks.graph.model.IGraph. // Create grouping support for the given IGraph. var grouped:GroupedGraph = new GroupedGraph(flatGraph);
Basically, this has the same effect as the line in Example 3.1, “Enabling grouping support with a DefaultGraph instance”.
API Excerpt 3.1, “Structure-related methods” lists the methods defined in interface IGroupedGraph that allow to create and modify the hierarchical organization of a grouped graph. Group nodes can be created both explicitly as well as implicitly when grouping a set of nodes. Un-grouping of nodes is achieved by means of the general re-parenting functionality.
API Excerpt 3.1. Structure-related methods
// Re-parenting, i.e., changing the parent node of a given INode. setParent(node:INode, parent:INode):void // Grouping nodes. groupNodes(children:Iterable, parent:INode = null):INode // Creating group nodes. createGroupNode(parent:INode = null, bounds:IRectangle = null, style:INodeStyle = null):INode
Conceptually, grouped nodes are one level below their containing group node in the graph hierarchy. Technically, however, they are in the same graph as their enclosing group node. Example 3.3, “Grouping nodes” shows how nodes are grouped in a common group node which is created implicitly.
Example 3.3. Grouping nodes
// 'grouped' is of type com.yworks.graph.model.IGroupedGraph. // Create a new top-level group node that has two children. var groupNode:INode = grouped.groupNodes(new YList(new Array(someNode, someOtherNode)));
Class GroupedGraph also provides support for the notion of default styles as laid out in interface IGraph. Property defaultGroupNodeStyle can be used to set the default style for group nodes. Style sharing semantics are controlled using property shareDefaultGroupNodeStyleInstance.
Node styles are described in the section called “Visual Representation of Graph Elements”.
When a GroupedGraph is created, the look-up behavior of the given IGraph instance is modified. More precisely, the types listed in Table 3.1, “Additional types supported with look-up operations” are additionally supported with look-up operations initiated on the IGraph instance using the lookup method.
Table 3.1. Additional types supported with look-up operations
Requested Type | Description |
---|---|
IGroupedGraph | The returned IGroupedGraph implementation can be used to manipulate the hierarchical organization of the graph. |
GroupedGraph | The returned GroupedGraph can be used to manipulate the hierarchical organization of the graph as well as to control group node style-related settings. |
INodeHierarchy | The returned INodeHierarchy implementation presents a view of the actual hierarchical structure of the nodes. (See below for the description of interface INodeHierarchy.) |
Note that by default the IGroupedGraph instance and the GroupedGraph instance that are returned when requesting the respective type are in fact the GroupedGraph instance that has been created to add grouping support to the graph. Example 3.4, “Retrieving the GroupedGraph instance that is associated with a graph” shows how to get the IGroupedGraph instance from the graph's look-up.
Example 3.4. Retrieving the GroupedGraph instance that is associated with a graph
// 'graph' is of type com.yworks.graph.model.IGraph. // Retrieve the IGroupedGraph implementation that adds grouping support to the // graph. var grouped:IGroupedGraph = graph.lookup(IGroupedGraph) as IGroupedGraph; if (grouped != null) { // Do something with the IGroupedGraph. }
In order to revert any changes that were introduced by creating grouping support for a graph, a GroupedGraph instance needs to be properly disposed of. To this end, the method in API Excerpt 3.2, “Clean-up method for GroupedGraph” is provided.
When grouping support has been enabled using the GroupingSupported property of DefaultGraph, disposing of the GroupedGraph instance will be handled automatically.
Interface INodeHierarchy defines the basis for a data structure that holds a hierarchical organization of nodes. It models a tree-like hierarchy that allows to observe structural changes.
In conjunction with a GroupedGraph, an implementation of INodeHierarchy is used to model the actual hierarchy of nodes for the IGraph that has grouping support enabled. This INodeHierarchy instance handles the low-level changes to the hierachy of nodes.
API Excerpt 3.3, “Methods defined in INodeHierarchy” lists the methods from interface INodeHierarchy that allow to query and modify a hierarchical structure of nodes.
On the INodeHierarchy instance, which is used together with the GroupedGraph object that adds grouping support to a graph, no methods should be invoked that modify a hierarchy. Instead, all modifications should be carried out using the GroupedGraph instance.
API Excerpt 3.3. Methods defined in INodeHierarchy
contains(item:INode):Boolean addChild(parent:INode, child:INode):void remove(child:INode):void getChildCount(parent:INode):int getChildren(item:INode):Iterable isLeaf(item:INode):Boolean setLeaf(item:INode, leaf:Boolean):void getParent(child:INode):INode setParent(child:INode, parent:INode):void
The root property returns the value for the root of the hierarchy. For a hierarchy that is used by a GroupedGraph, this value is normally null.
Static class Hierarchies provides utility methods that allow to conveniently query information about a hierarchy held by an INodeHierarchy implementation. API Excerpt 3.4, “Utility methods in Hierarchies” lists some of the methods.
API Excerpt 3.4. Utility methods in Hierarchies
static elements(hierarchy:INodeHierarchy):Iterable static isDescendant(hierarchy:INodeHierarchy, node:INode, parent:INode):Boolean static getDescendants(hierarchy:INodeHierarchy, root:INode):Iterable static getNearestCommonAncestor(hierarchy:INodeHierarchy, items:Iterator):INode
Class FoldingManager allows to conveniently add folding support to a grouped graph. It provides and handles the so-called managed views that enable folding operations in a graph hierarchy. Among other things, this includes:
In addition, FoldingManager also synchronizes data that is bound to model items using the ITagOwner interface. Specifically, this enables using either original or representative graph elements in order to access the same data.
The code in Example 3.5, “Creating a managed view” presents how to use FoldingManager to create a managed view.
Example 3.5. Creating a managed view
// 'gc' is of type com.yworks.ui.GraphCanvasComponent var fm:FoldingManager = new FoldingManager(); // Creating a managed view. var fg:IFoldedGraph = fm.createManagedView(); // Showing the folding-enabled graph in a GraphControl. gc.graph = fg.graph;
There can be an arbitrary number of managed views for a master graph, and managed views can present different parts of a graph hierarchy. For example, one managed view may present the entire hierarchy of nodes, while another only presents the contents of a given group node.
FoldingManager's createManagedView() method enables creating graph structures for managed views that show a subset of the master graph only. Also supported is a way to specify the initial collapse/expand state of representatives for group nodes from the master graph.
API Excerpt 3.5. Methods for creating managed views
// The predicate allows to specify the initial collapse/expand state of group // node representatives. function createManagedView( root:INode=null, expandedPredicate:Function=null ):IFoldedGraph
The initial collapse/expand state of group nodes representatives can also be set using the setInitiallyExpanded() method.
For each dummy graph element in a managed view, FoldingManager keeps a separate folding-related state, which it synchronizes across all its managed views. When dummy elements are first created, so-called converters are queried for the dummy element's initial state. Dummy node converters and dummy edge converters can be set using the dummyNodeConverter and dummyEdgeConverter property of class FoldingManager, respectively.
By default, FoldingManager uses classes DefaultDummyNodeConverter and DefaultDummyEdgeConverter. The default behavior for dummy node creation is to use all state from the original group node. The default behavior for dummy edge creation is to use all state from the original edge, and to create exactly one representing dummy edge.
Dummy element converters can be widely customized. In the section called “Converters and Callbacks” all converter-related aspects are discussed.
The FoldingManager's dummy element converters are queried for each transition where graph elements in a managed view become dummy elements. However, if there is already folding-related state held for the dummy elements, then this state will be used.
FoldingManager modifies the look-up behavior of the IGraph instance in a managed view. More precisely, the types listed in Table 3.2, “Additional types supported with look-up operations” are additionally supported with look-up operations initiated on a managed view's IGraph instance.
Table 3.2. Additional types supported with look-up operations
Requested Type | Description |
---|---|
IFoldedGraph | The returned IFoldedGraph implementation can be used to execute folding operations in the graph. Also, ît provides access to the FoldingManager that is responsible for the graph's managed view. |
IGroupedGraph | The returned IGroupedGraph implementation can be used to manipulate the hierarchical organization of the graph. |
Note that the IFoldedGraph implementation available through the look-up operation is the same as the one returned by the CreateManagedView call that created the managed view.
Also, the IMapperRegistry that is available both via IGraph's look-up as well as through the mapperRegistry property provides convenient direct access to any IMapper implementations from the master graph using their respective tag. If another IMapper is registered in a managed view using the tag from a master graph's IMapper, no direct access is possible. Instead, the same tag refers to different IMapper instances for the managed view's graph and the master graph.
Setting up folding support using FoldingManager is illustrated in detail in the tutorial demo application FolderDemo.
Interface IFoldedGraph defines the contract for working with the folding-enabled graph in a managed view, and also for specifying the subset of a graph hierarchy to build the view's graph from.
The actual IFoldedGraph implementation that is associated with the graph in a specific managed view is returned when creating that view using one of FoldingManager's CreateManagedView methods, but it can also be queried from that graph's look-up, if necessary.
Example 3.6. Getting the IFoldedGraph implementation from the folding-enabled graph's look-up
// 'gc' is of type com.yworks.ui.GraphCanvasComponent. // Retrieve the IFoldedGraph implementation that enables folding operations. var fg:IFoldedGraph = gc.graph.lookup(IFoldedGraph) as IFoldedGraph; if (fg != null) { // Do something with the IFoldedGraph implementation. }
Working with a folding-enabled graph mainly means collapsing and expanding group nodes, which can be done using the methods listed in API Excerpt 3.6, “Collapsing and expanding group nodes”.
API Excerpt 3.6. Collapsing and expanding group nodes
function collapse( groupNode:INode ):void function expand( groupNode:INode ):void
IFoldedGraph also provides several methods that allow to query, for example, whether a given group node in the managed view is currently expanded or not, or what the collapse/expand state for a group node that is currently not visible in the managed view would be. API Excerpt 3.7, “Querying state aspects of elements in a managed view” lists the methods from IFoldedGraph that allow to query state aspects of graph elements in a managed view.
API Excerpt 3.7. Querying state aspects of elements in a managed view
// Collapsed/expanded state for group nodes in a managed view. function isInitiallyExpanded(masterGroupNode:INode):Boolean function isExpanded( groupNode:INode ):Boolean // Dummy state for graph elements in a managed view. function isDummy( item:IModelItem ):Boolean
Provided by IFoldedGraph are further methods that allow to get original graph elements for given graph elements from a managed view, or vice versa, to get the representative graph elements for given graph elements from the master graph. API Excerpt 3.8, “Mapping between graph elements in the model (master graph) and a managed view” lists these methods.
API Excerpt 3.8. Mapping between graph elements in the model (master graph) and a managed view
// Getting original graph elements (if any) for graph elements in the managed // view. function getMaster( item:IModelItem ):IModelItem function getMasterNode( node:INode ):INode function getMasterEdge( edge:IEdge ):IEdge function getMasterLabel( label:ILabel ):ILabel function getMasterPort( port:IPort):IPort function getMasterBend( bend:IBend ):IBend function getMasterEdges( dummyEdge:IEdge ):Iterable // Getting representing graph elements (if any) for original graph elements. function getRepresentative( modelItem:IModelItem ):IModelItem function getRepresentativeNode( node:INode ):INode function getRepresentativeEdge( edge:IEdge ):IEdge function getRepresentativeLabel( label:ILabel ):ILabel function getRepresentativePort( port:IPort ):IPort function getRepresentativeBend( bend:IBend ):IBend
It is important to understand that there is not necessarily a one-to-one mapping between graph elements from the master graph and graph elements in a managed view. For example, for an edge in a managed view that connects to a collapsed group node, there can be several original edges in the master graph.
Figure 3.7, “Edge mappings according to the dummy edge policy” illustrates two possible situations that result when a group node in a managed view is collapsed. Edges in the master graph (left) that connect to nodes inside that group node can be retained in a managed view (middle) or can be merged into a single representative edge (right). A third alternative, where all edges are cleared, is also possible.
Figure 3.7. Edge mappings according to the dummy edge policy
Edges in master graph | 1:1 mapping in managed view | Merging policy |
Interface IFoldedGraph provides support for a managed view to present arbitrary parts of the master graph's hierarchy of nodes. The localRoot property can be conveniently used to specify a group node from the master graph which is taken as the root for the subset of the graph hierarchy that should be presented. Effectively, this means that a managed view always presents either a specific group node's contents or the root of the hierarchy of nodes.
In case that the group node that is given as the root is removed from the master graph, the managed view's invalid property becomes true and the view can no longer be edited. By default, however, the autoSwitchToAncestor property makes sure that the local root is automatically changed such that an ancestor of the original root is used as the view's new root.
Usage of interface IFoldedGraph can be observed in tutorial demo application FoldingDemo.
FoldingManager uses so-called converters to handle the transition for graph elements in a managed view that become dummy elements. Converters determine what state (style, geometry, label text, etc.) should be used for a dummy graph element, if there is not already folding-related state held for them by FoldingManager.
Whenever a group node gets collapsed, the IDummyNodeConverter implementation that is set with FoldingManager via the dummyNodeConverter property is queried to determine the actual state that should be used for the dummy node.
Table 3.3, “Predefined dummy node converter implementations” lists the predefined dummy node converter implementations present in the com.yworks.graph.model namespace.
Table 3.3. Predefined dummy node converter implementations
Type Name | Description |
---|---|
DefaultDummyNodeConverter | By default, creates/updates dummy nodes using all state from the original group node. |
DefaultDummyNodeConverter provides properties to conveniently specify state aspects for a new dummy node. In addition, there is also a variety of callbacks available that easily allow to customize creating and updating dummy nodes.
Even more control can be achieved by directly implementing the IDummyNodeConverter interface. Most importantly with the interface's methods is that all modifications of the dummy node's state need to be carried out using the IChangeDummyNodeAppearanceCallback implementation.
API Excerpt 3.9. IDummyNodeConverter interface methods
// Customizing creation of collapsed group nodes. function createDummyNodeAppearance( callback:IChangeDummyNodeAppearanceCallback, foldedGraph:IFoldedGraph, dummyNode:INode, masterNode:INode ):void // Customizing update of collapsed group nodes. function changeDummyNodeAppearance( callback:IChangeDummyNodeAppearanceCallback, foldedGraph:IFoldedGraph, dummyNode:INode, masterNode:INode ):void
Whenever a group node gets collapsed, and there would connect dummy edges to the collapsed group node, the IDummyEdgeConverter implementation that is set with FoldingManager via the dummyEdgeConverter property is queried to determine which dummy edge should be created and what state should be used for that dummy edge.
Table 3.4, “Predefined dummy edge converter implementations” lists the predefined dummy edge converter implementations present in the com.yworks.graph.model namespace.
Table 3.4. Predefined dummy edge converter implementations
Type Name | Description |
---|---|
DefaultDummyEdgeConverter | Creates a corresponding dummy edge for each edge in the master graph that connects to a node whose representative is hidden in a managed view. |
MergingDummyEdgeConverter | Creates at most one dummy edge (two dummy edges, if edge direction is not ignored) between each pair of source node and target node. Conceptionally, this implementations merges dummy edges that connect the same source node and target node in a managed view into one dummy edge (two dummy edges). |
ExcludingDummyEdgeConverter | Creates no dummy edges at all. |
Both DefaultDummyEdgeConverter and MergingDummyEdgeConverter provide properties to conveniently specify state aspects for a new dummy edge that is created using either policy. In addition, there is also a variety of callbacks available that easily allow to customize creating and updating dummy edges.
Even more control can be achieved by directly subclassing AbstractDummyEdgeConverter. Most importantly with that class's single abstract method is that the dummy edge creation policy needs to be specified using the provided IAddDummyEdgeCallback implementation. Also, any modifications of the dummy edge's state need to be carried out using that callback, too.
Copyright ©2007-2015, yWorks GmbH. All rights reserved. |