Graph Model

The graph model is represented by the Graph interface as well as other interfaces that represent finer-grained graph-related components.

Interface GraphObject

This interface represents the aspects common to all Graph elements, including a Node, Edge and Subgraph.

GraphObjects have an identifiable nature. Every GraphObject has both an identifier unique to its context as well as a global identifier that is unique within a hierarchy of Graph objects.

In the section GraphObject IDs both kinds of identifiers are explained in detail.
Methods to access both of these identifiers are available.

  • long getID()

    Returns the local identifier (unique within a single GraphObjectContainer.

  • long getUniqueID()

    Returns the global identifier (unique within the complete GraphObjectContainer hierarchy)

It is possible to associate a name and a description with a GraphObject. These can be used for example to easily assign a type and a descriptive text to the GraphObject.

  • String getDescription()

    Returns the description string of this GraphObject

  • void setDescription(String)

    Sets the description of this GraphObject to the given string

The standard attributes of a GraphObject may be extended with custom attributes. The custom attributes added to the GraphObject are grouped into the category custom and returned by a call to getAttributes. The methods provided by a GraphObject to handle custom attributes are listed below:

  • void addCustomAttribute(Attribute)

    Adds the Attribute given by attribute to the custom attributes of the GraphObject. If a custom attribute with the same key as the attribute that is given was already added to this GraphObject then the adding of the new custom attribute fails and the instance remains unchanged.

  • AttributeSet getCustomAttributes()

    Returns an AttributeSet that contains all custom attributes currently assigned to the GraphObject or null if no custom attribute is assigned to the GraphObject. Notice: The Attributes within the returned AttributeSet are copies of the custom attributes currently added to the GraphObject and no references. So that if you want to change them you have to call setCustomAttributes afterwards.

  • void setCustomAttributes(AttributeSet)

    Sets the Attributes within the AttributeSet given by customAttributes to the custom attributes currently added to the GraphObject. This is done by a merge of the given Attributes and the custom attributes currently added. The merge is done by the following rules:

    • The values of Attributes that are part of the custom attributes currently added to the GraphObject and part of the given AttributeSet will be assigned to the custom attribute.

    • Attributes within the given AttributeSet that are not part of the custom attributes currently added to the GraphObject will be added.

    • Attributes that are part of the custom attributes currently added to the GraphObject but not part of the given AttributeSet will NOT be removed.

  • void removeCustomAttribute(String)

    Removes the custom Attribute with the name given by attributename from the GraphObjects custom attributes.

Interface Node

The interface Node extends GraphObject, allowing nodes to be identified and labeled. Adding and removing nodes to and from a Graph is done by invoking methods on a Graph instance. This is not a part of the Node responsibility. The following is a list of important methods provided by this interface:

  • List getAdjacentEdges()

    Returns a list containing all adjacent edges of this node. The list reflects the current state and is not updated after its creation.

  • List getAdjacentNodes()

    Returns a list containing all adjacent nodes.

  • int getDegree()

    Returns the number of edges leaving this node added to the number of edges entering this node. Incoming edges do not contribute to the degree in the case of directed edges.

  • List getInboundEdges()

    Returns a list containing the incoming edges of this node. The number of edges in this list is equal to the value returned by method getIndegree().

  • List getInboundNodes()

    Returns a list containing the nodes connected via incoming edges. The number of nodes in this list is equal to the value returned by getIndegree(). All nodes in this operator are also indirectly available by invoking the getInboundEdges method and then invoking getTarget() on the obtained edges.

  • int getIndegree()

    Returns the number of edges entering this node. You must not invoke this method on an undirected graph.

  • List getOutboundEdges()

    Returns a list containing the outgoing edges of this node. The number of edges in this list is equal to the value returned by method getOutdegree().

  • List getOutboundNodes()

    Returns a list containing the nodes connected via outgoing edges. The number of nodes in this list is equal to the value returned by getOutdegree(). All nodes in this operator are also indirectly available by invoking the getOutboundEdges method and then invoking getSource() on the obtained edges.

  • int getOutdegree()

    Returns the number of edges leaving this node. You must not invoke this method on an undirected graph.

  • Port getPortByID(long)

    Returns the Port with the given id. If the Node has no Port with the given id null is returned.

  • List getPorts()

    Returns a list of the ports of this node. The returned list is read-only and may not be modified.

  • Edge isConnectedFromNode(Node)

    Determines whether this node is connected from the given node. This means this node is the target of the edge and the given node is the source of the edge.

  • Edge isConnectedToNode(Node)

    Determines whether this node is connected to the given node. This means this node is the source of the edge and the given node is the target of the edge.

Note

Methods to attach NodeInfo objects to a Node instance will be explained in section Info Objects.

Caution

This interface contains some methods that are intended for Tensegrity Graph Framework internal use only. It is strongly suggested that you do not invoke them from client code. From one release to another, some of these methods may be removed or they may be moved from one interface to another. Moreover, it is fairly likely that method signatures will change. Methods of this kind usually start with the prefix “internal”. For historical reasons there might be some exceptions to this rule. Methods of the later kind are marked in the JavaDoc and mentioned in this manual. For interface Node, the methods intended for internal use are the following:

  • void setPorts(List)

  • void setID(long)

  • void internalResetPorts()

Interface Edge

Like Node, interface Edge extends the GraphObject interface. Our particular implementation consists of convenience methods allowing you to retrieve the information stored in the Edge instance. Adding and removing edges to and from a Graph is done by invoking methods on a Graph instance and is not a functionality provided by this interface. Some important methods are:

  • Node getOpposite(Node)

    Returns the node of the Edge that is not the given Node. This method is only meaningful in the context of undirected graphs.

  • Node getSource()

    Gets the Node at the source end of this Edge.

  • Node getTarget()

    Gets the Node at the target end of this Edge.

  • EdgeInfo getEdgeInfo()

    Returns the EdgeInfo associated with this Edge.

Note

Methods to attach EdgeInfo objects to an Edge instance will be explained in section Info Objects.

Interface Port

The Port interface defines the characteristics of a single connection point to a Node from which an Edge instance is attached. In other words, an Edge connects to a Node via one of the many Port instances owned and managed by a Node, and not to the Node directly.

A Port uses a so-called PortDenotation, a named meta-level object which defines and returns the rules for incoming and outgoing connections, including their cardinalities.

Ports have identifiers which can be returned to clients who request them.

The important methods of this interface are:

  • PortDenotation getDenotation()

    Returns the denotation object associated with this Port.

  • Port getWrappedPort()

    Returns the reference to the wrapped port or null if no port is wrapped. If null is returned, then this indicates that this instance is a leaf-port. If a value different from null is returned, it indicates that this port wraps the returned port instance.

PortDenotation

A PortDenotation is a meta-level class which encapusates the rules about incoming and outgoing connections to a Port. While a Port maintains an identifier unique to its Node owner, a PortDenotation maintains and returns a name which describes its connection role.

The important methods of this interface are:

  • int getInputCardinality()

    Returns the input cardinality of the port. 0 means unlimited.

  • String getName()

    Returns the name for the PortDenotation. This name is also taken as name for the Port the PortDenotation is assigned to.

  • int getOutputCardinality()

    Returns the output cardinality of the port. 0 means unlimited.

  • boolean isInputPort()

    Returns true if the port can be used as an input port.

  • boolean isOutputPort()

    Returns true if the port can be used as an output port.

Interface GraphObjectContainer

This interface defines the responsibilities for containers of objects of type GraphObject. By providing this interface, different component types are able to play this role.

Orphaned nodes and edges are of no use in a graph model. Although such objects may be allocated on their own, they are usually added to a particular Graph instance, which internally assigns them a unique identifier within the scope of the Graph itself. Moreover, when an Edge is added to a Graph, additional checks are performed. For example, it is not permitted to add an Edge that refers to a Node not already contained by the Graph.

These are the more important methods defined in the interface:

  • void addEdge(Edge)

    Adds the given edge to the GraphObjectContainer. The edge must not be in the GraphObjectContainer already and its source and target pointers must point to nodes in the GraphObjectContainer.

  • void addNode(Node)

    Adds the given node to the GraphObjectContainer. If the node can be successfully added a pre-node add event is generated, if that event is not rejected in a vetoable listener then the node-add event is generated. If a node cannot be added at all (e.g. it is already added) then no events are generated at all.

  • int buildAdjacencyMatrix()

    Builds the adjacency matrix for this GraphObjectContainer. This operation maybe very expensive. In case it seems too expensive it is up to the implementation to throw an exception. The diagonal of the matrix is filled with 1 by convention (each edge is considered to be adjacent to itself).

  • Edge getEdgeByID(long)

    Returns the Edge with the given id.

  • int getEdgeCount()

    Retrieves the number of edges from the GraphObjectContainer.

  • Iterator getEdges()

    Retrieves an Iterator for all edges in the GraphObjectContainer.

  • Node getNodeByID(long)

    Returns the Node with the given id.

  • int getNodeCount()

    Retrieves the number of nodes from the GraphObjectContainer.

  • Iterator getNodes()

    Retrieves an Iterator for all nodes.

  • boolean isDirected()

    Indicates whether or not the GraphObjectContainer is directed.

  • void removeEdge(Edge)

    Removes the given edge from the GraphObjectContainer. The edge must be in the GraphObjectContainer for this operation to succeed.

  • void removeNode(Node)

    Removes the given node from the GraphObjectContainer. The node must in the GraphObjectContainer for this operation to succeed.

Interface Graph

This interface defines an abstract Graph without any notion of a visualization. This class can be used on its own for applying graph algorithms and for deriving specialized graph classes. Both directed and undirected graphs are supported. A Graph consists of Node and Edge objects. These are the parts of the graph model that can be accessed through the Graph interface and whose abstraction is commonly defined in the base interface GraphObject. This component interface defines the functionality common to both nodes and edges, as illustrated in the following class diagram.

Constructing a Graph and its related objects is done by means of a GraphModelFactory, which will hide the particular implementation classes from client code.

Upon adding, the ID of any GraphObject is automatically assigned. Before adding, however, the ID should be set to -1. Each GraphObject may only be added to at most one Graph instance. Adding it to a Graph instance instance while still managed by another will produce an exception. Removing a GraphObject will reset its ID to -1.

These are the more important methods defined in the interface:

  • void addGraphListener(GraphListener)

    Adds a graph listener to the graphs internal structure.

  • Subgraph getSubgraph()

    Returns a reference to the Subgraph this Graph is a part of. If this Graph is not part of a Subgraph then null is returned.

  • void visitSubgraphs(GraphVisitor)

    Traverse the sub graphs of this graph by means of the given GraphVisitor.

Factories

Next we will provide a small code sample to show how a graph model is built. Before we do so, however, we have to clarify yet another important architectural facet of the Tensegrity Graph Framework.

Instead of directly creating objects using constructors, we have provided factory classes specialized to provide this functional responsibility. In some implementations, application-specific code can be full of calls to create new objects, causing this code to become brittle over time: Introducing new implementation classes that are better-designed to handle specific requirements would cause the application code to be changed and recompiled. This is the usual place where bugs find their way into systems.

Factory classes have the limited responsibility of creating new objects and returning them to clients. Code which uses a factory does not have to worry about which implementation class is used. This information is part of the responsibility of the factory implementation and can be configured by the application engineer. In other words, implementation details can be changed without violating the client contract. Please have a look at [GoF99] if you are unfamiliar with this pattern.

Creating an instance of type Graph, as well as all related model objects, is done by using a class derived from GraphModelFactory. The same is true for view and visual objects: They are created by methods defined in the abstract base class GraphViewFactory.

Class GraphModelFactory

The abstract class GraphModelFactory defines the methods which allow you to create instances of all earlier discussed Graph model objects.

You may not create instances of a GraphModelFactory directly. Rather you must instantiate a concrete class derived from this one, best done by calling the static method newInstance found in this class. Our implementation returns a global instance that can be shared by multiple clients.

The following is a list of the methods you are most likely to use:

  • Edge newEdge(Node, Node)

    Constructs and returns an Edge object.

  • Graph newGraph()

    Constructs and returns a Graph object.

  • Node newNode(String, List)

    Creates a new Node object with the specified name. The Node contains the given Ports.

  • Port newPort(PortDenotation)

    Constructs and returns a new Port object. To get more information about the meaning and usage of them please see the documentation of Port.

  • PortDenotation newPortDenotation(String)

    Constructs and returns a new PortDenotation object. To get more information about the usage of them please see the documentation of PortDenotation.

  • Subgraph newSubgraph(String, Graph)

    Constructs and returns a new Subgraph object.

This class provides several static helper methods that create default model elements. You must pass in a GraphModelFactory instance to do so, however.

Class GraphModelFactory provides static methods to ease the creation of nodes:

  • Node makeDefaultNode(GraphModelFactory, String)

    Creates a new Node using the given GraphModelFactory instance and the given name.

Creating a Graph Model

Now that we have introduced to you the fundamental objects that are required for assembling a Graph, we provide the following example which illustrates how to create a simple directed graph with four nodes (N) and four edges (E), such that:

N = {a, b, c, d} and E = { (a, b), (a, c), (c, d), (d, a) }
      

Building this graph with the Tensegrity Graph Framework is easy:

Example 1.1. Assembling a simple graph

// First we have to obtain a factory to build our graph model
GraphModelFactory factory = GraphModelFactory.newInstance();
        
// instantiate a graph object
Graph graph = factory.newGraph();
        
// adding nodes and edges to the graph instance
try
{   
    // create nodes.
    // empty list of ports -> nodes have no ports
    List ports = new ArrayList();
    Node a = factory.newNode ("a", ports);
    Node b = factory.newNode ("b", ports);
    Node c = factory.newNode ("c", ports);
    Node d = factory.newNode ("d", ports);
    
    // add nodes to graph
    graph.addNode (a);
    graph.addNode (b);
    graph.addNode (c);
    graph.addNode (d);
    
    // create edges
    Edge ab = factory.newEdge (a, b);
    Edge ac = factory.newEdge (a, c);
    Edge cd = factory.newEdge (c, d);
    Edge da = factory.newEdge (d, a);
    
    // add the edges to the graph
    graph.addEdge (ab);
    graph.addEdge (ac);
    graph.addEdge (cd);
    graph.addEdge (da);
    
    System.out.println(graph);
}
catch (GraphException e)
{
    // handle graph exception here
    System.err.println(e);
}

In order to reduce the coding effort, as well as to have a more handy way of assembling a Graph manually, it would be more convienient to build two arrays holding Node and Edge instances and then use the addNodes and addEdges methods to add these model elements to the Graph in one call.

Example 1.2. Assembling a simple graph (2)

            
// create and add nodes
// empty list of ports -> nodes have no ports
List ports= new ArrayList();
Node nodes[]= { 
        factory.newNode("a", ports), 
        factory.newNode("b", ports), 
        factory.newNode("c", ports), 
        factory.newNode("d", ports)};
        
graph.addNodes(nodes);
            
// create and add edges
Edge edges[]= { 
        factory.newEdge(nodes[0], nodes[1]),
        factory.newEdge(nodes[0], nodes[2]),
        factory.newEdge(nodes[1], nodes[3]),
        factory.newEdge(nodes[3], nodes[0])};
        
graph.addEdges(edges);

Both examples result in identical graphs. Please have a look at the following screenshot to get an impression what this Graph might look like.

Figure 1.1. Example Graph

Example Graph

In most programming cases, it is far more convenient to use the later form, as one can always reference individual nodes by their array index.

Note

Removing a Node and an Edge is handled by the removeNode and removeEdge methods respectively. Note that when a Node is removed from a Graph, all attached edges are removed as well.