In this tutorial we will learn how to use node and edge objects and see how to associate data to them. This will be used both to provide arbitrary data used by algorithms that operate on a graph and to change the way various components of GraphStream interact with a graph instance, most notably the graph viewer.

Nodes and edges
In the previous tutorial we have seen how to build a small graph. To do this, we created three nodes and three edges, and we have done this without seeing any node or edge object, they were created for us by the graph implementation.
Node and edge class are manipulated under the hood when you call Graph.addNode() and Graph.addEdge(). It is however possible to access them. This allows :
- to browse the graph starting from a particular location.
- to check a specific information provided by the node or the edge.
- to store user specific data on them.
In this tutorial we will concentrate on the last item. We will see the first point in detail in the next tutorial. The second item will be clarified all along these tutorials.
The easiest way to retrieve a node or edge in the graph is to use their identifier. We will take as example the triangle graph constructed in the previous tutorial :
Node node1 = graph.getNode( "node1" ); Edge edge1 = graph.getEdge( "edge1" );
Note that Node and Edge are in fact interfaces, like Graph. Indeed the graph implementation is responsible for their correct instantiation.
These two interfaces provide few methods in fact. The node provides a way to know its degree (number of connected edges), its neighbours, and connected edges. The edge provides ways to know its connected nodes and its optional direction. However, they both inherit another interface named Element that defines methods allowing to store arbitrary data on them.
Therefore an Element is a part of a graph, a node or an edge. In fact, the Graph interface also inherits Element. This means that it is also possible to store arbitrary data on the graph itself, although this is not as useful (this also means a graph can be inserted into another graph, but this is also not terribly useful and few graph implementations allow this).
The main way to store data on an element is to use Element.addAttribute(), for example :
node1.addAttribute( "ui.color", Color.RED ); edge1.addAttribute( "ui.weight", 5.4f ); graph.addAttribute( "type", "Scale-Free" );
An attribute has a name and a value. The name must be a string whose content is arbitrary. The value can be any object, or if your java version is equal or greater to 1.5 (also known as 5.0), any base type that will be boxed for you.
Knowing the node or edge you are working on, you can modify an attribute value using :
node1.changeAttribute( "ui.color", Color.BLUE );
And you can remove it definitively using :
node1.removeAttribute( "ui.color" );
Attributes and the graph viewer
The attribute names are not restricted, however some of them have specific meaning for some modules of GraphStream, notably the graph viewer. When they are added or changed, the graph viewer automatically change its display according to their value.
We have already seen one of these attributes : "ui.color". This attribute allows, as its name suggests, to colour an edge or node. It accepts several types of values :
- instances of the java.awt.Color class ;
- string values of colour names ("red", "green", "violet"...) according to a name database (the X11 colour names, that share a large set of names with the HTML colour names) ;
- a CSS colour specification, like "rgb(255,128,0)" or "rgba(255,128,0,128)" ;
- and a HTML colour specification, like "#FF9900" or "#FF990099".
Other kinds of values or malformed values are merely ignored. Let's try it in our triangle example :
public TutorialBase005a() {
Graph graph = new DefaultGraph( "Triangle" );
graph.addNode( "node1" );
graph.addNode( "node2" );
graph.addNode( "node3" );
graph.addEdge( "edge1", "node1", "node2" );
graph.addEdge( "edge2", "node2", "node3" );
graph.addEdge( "edge3", "node3", "node1" );
// Here we add attributes to nodes and edges :
graph.getNode( "node1" ).addAttribute( "ui.color", Color.RED );
graph.getNode( "node2" ).addAttribute( "ui.color", "green" );
graph.getNode( "node3" ).addAttribute( "ui.color", "#0000FF" );
graph.getEdge( "edge1" ).addAttribute( "ui.color", "cyan" );
graph.getEdge( "edge2" ).addAttribute( "ui.color", Color.MAGENTA );
graph.getEdge( "edge3" ).addAttribute( "ui.color", "rgb(255,255,0)" );
graph.display();
}
You should get something like this :

Artistic, is'nt it ? .. hum ok ...
There are other graphic attributes. One of the most useful is "label". It allows to annotate an edge or node with an arbitrary text. For example :
graph.getNode( "node1" ).addAttribute( "label", "Node 1" ); graph.getNode( "node2" ).addAttribute( "label", "Node 2" ); graph.getNode( "node3" ).addAttribute( "label", "Node 3" );
This should give you something like :

The "label" attribute uses the Object.toString() method, therefore you can pass any kind of object as value for "label" :
graph.getEdge( "edge1" ).addAttribute( "label", 1.5f ); graph.getEdge( "edge2" ).addAttribute( "label", 2f ); graph.getEdge( "edge3" ).addAttribute( "label", 3.4f );

Two other useful attributes are "ui.width" and "ui.edge-style". The width attribute allows to change the visual size of a node or edge. The edge-style attribute allows to change the way an edge is drawn. Let's try them :
graph.getNode( "node1" ).addAttribute( "ui.width", 3 ); graph.getNode( "node2" ).addAttribute( "ui.width", 6 ); graph.getNode( "node3" ).addAttribute( "ui.width", 12 ); graph.getEdge( "edge1" ).addAttribute( "ui.edge-style", "dotted" ); graph.getEdge( "edge2" ).addAttribute( "ui.edge-style", "dashed" ); graph.getEdge( "edge3" ).addAttribute( "ui.width", 4 );

Note that this method for changing colors, widths and edge style are deprecated by the style sheet mechanism introduced in GraphStream since version 0.4.0.
The attributes that are purely dedicated to the graph viewer are all prefixed by "ui.". As "label" is not only used by the graph viewer it is not prefixed by "ui.". This prefix allows to avoid name clashes with the attributes you could use in your own applications.
Coordinate attributes and the graph viewer
Actually our graph viewer automatically layouts the graph for us. This is the reason why each of the screen shot above present a triangle graph that may seem rotated or mirrored, since the layout used here starts from random positions.
However for certain occasions, it can be useful to specify the node positions explicitly. This is possible, but requires a different call to the viewer, to tell it that we do not want a layout process :
graph.display( false );
The boolean argument enables or disables the layout. Then you can equip nodes with three attributes "x", "y" and "z" (actually the viewer is configured to show a graph in two dimensions, but the interfaces are done to also work in three, hence the "z" coordinate). Here is an example :
graph.getNode( "node1" ).addAttribute( "x", -1 ); graph.getNode( "node1" ).addAttribute( "y", 0 ); graph.getNode( "node2" ).addAttribute( "x", 1 ); graph.getNode( "node2" ).addAttribute( "y", 0 ); graph.getNode( "node3" ).addAttribute( "x", 0 ); graph.getNode( "node3" ).addAttribute( "y", 1 ); graph.display( false );
But you can also use attributes whose name is "xy" or "xyz". In this case you can use a version of addAttribute() that takes several arguments:
graph.getNode( "node1" ).addAttribute( "xy", -1, 0 ); graph.getNode( "node2" ).addAttribute( "xy", 1, 0 ); graph.getNode( "node3" ).addAttribute( "xy", 0, 1 ); graph.display( false );
We have not seen it yet, but the addAttribute() method takes a variable list of arguments. The first argument is mandatory. If it is alone, a boolean value "true" is stored in the attribute. For example graph.getNode("node1).addAttribute("marked") will store an attribute named marked with value true (of type Boolean).
You already know the verion with one attribute value. If there are more than one attribute value, an Object array is created whose size is the number of attributes values passed. In each cell a value is stored. This interface allows an easy array creation. This is this features that is used above to specify a position for nodes. As positions are either a 2D point or 3D point, we can use the "xy" or "xyz" attributes to pass array of coordinates (either with at least two or three values respectively).
Some remarks on what have been done
Attributes can be very useful and are easy to use. However for more advanced programs, one may need more flexibility. GraphStream allows to subclass the Node and Edge created by a graph using factories. This allows to equip them with the desired behaviour and add specific contents in it. However, this is the subject of another tutorial.
Concerning attributes, it may be interesting to know that there are in fact no differences between addAttribute() and changeAttribute(), when this last one is used and the attribute does not yet exists, it is created. However using them as appropriate may make a program easier to read.
There is one more option that merely enabling or disabling the automatic graph layout feature. You can choose the layout algorithm. We will see how to do this in tutorials dedicated to the graph viewer.
The method shown here for adding colours, widths, etc. in a word to add styling to the graph view, is easy but may be not the best one. The graph viewer supports a "style sheet" feature, very much like CSS provides style sheets for HTML. With style sheets, we will be able to specify accurately every aspect of graph, node and edge rendering, as well as sprites (a notion we will introduce later that allows to put arbitrary informative elements on the graph display). This style sheet feature works like CSS and tries to mimic its syntax. More on this later.
Finally, the screen shots shown here use anti-aliasing. Your program may not use it. It is easy to change it by clicking on the graph display with the second or third mouse button and moving the quality slider to value 3 or 4. We will see later how to do this in the program.