YGF, the "Y Graph Format," is a binary file format that supports the entire range of possible graph structure constellations. In particular, this means that beyond "flat" graphs also grouped graphs can be saved, preserving all relevant hierarchical information. This information includes, e.g., any inter-edges from the hierarchy, the structure of any inner graphs, and whether a node containing an inner graph is a group node or a folder node.

Common Usage

A YGF file is written using class YGFIOHandler. This class is responsible for properly (de)serializing all graph elements to an object stream which is written to file. For (de)serialization of classes NodeRealizer and EdgeRealizer, YGFIOHandler resorts to their respective (de)serialization methods. The code fragment in Example 9.18, “Instantiating a YGFIOHandler” shows how to instantiate a YGFIOHandler and using it to write a graph to file.

Example 9.18. Instantiating a YGFIOHandler

// Instantiate a YGF I/O handler and write the graph to file. 
IOHandler ioh = new YGFIOHandler();
writeGraphToFile(graph, ioh, "MyYGF.ygf");

Extending YGF With Custom Data

Adding custom data from a graph to a YGF file can be accomplished by overriding appropriate methods from class YGFIOHandler. There is a number of protected methods that cover all sorts of graph elements, i.e., nodes and edges, but also group nodes, folder nodes, and inter-edges. For each of these elements there is a method available for both input and output, i.e., to write the respective item and to read it. See below for all output methods for graph elements.

Note that the input/output methods for the respective realizer types are almost never suitable subjects for customization. Instead, the "Info" methods from class YGFIOHandler are customized. They, in turn, invoke the "Realizer" methods.

Some Rules

The important things to take care of when overriding any of the methods, can be summarized as follows:

  • Providing both input and output logic, i.e., write and read implementations at once make for a complete I/O handler.
  • Input and output have to be symmetric, i.e., everything written also has to be read (and vice versa). In particular, the sequence of items has to match.

Furthermore, to avoid possible backward compatibility problems that can arise from a customized file format that evolves over time, the following have proven to be good practices:

  • Using version identifier marks at the beginning of a section of appended custom data. (This will help in distinguishing different versions of a customized YGF file format.)
  • Providing default values for custom data that cannot be read. (For example, because the data is not present in older versions of a customized YGF file format.)

Example 9.19, “Customized version of a YGFIOHandler” presents a customized YGFIOHandler that writes and reads custom data to/from a generated YGF file. The custom data has to be provided by an edge map that can be set using the setter method. Note that the actual data that is to be (de)serialized has to implement the java.io.Serializable interface.

Example 9.19. Customized version of a YGFIOHandler

public class MyYGFIOHandler extends YGFIOHandler
  protected DataProvider em;
  public void setEdgeMap(EdgeMap em){ this.em = em; }
  // Customized version to append data to the normal edge information stuff. 
  protected void writeEdgeInfo(Graph2D graph, Edge e, ObjectOutputStream out)
  throws IOException
    // First, write out a version identifier mark. 
    super.writeEdgeInfo(graph, e, out);
    // After the normal stuff has been written, append the custom data. 
    // The data is retrieved from the data provider (which is an edge map). 
  // Customized version to read the normal edge information stuff and also the 
  // appended data. 
  protected void readEdgeInfo(Graph2D graph, Edge e, ObjectInputStream in)
  throws IOException
    String relationType;
    switch (in.readByte())
      case YVersion.VERSION_1:
        // The correct version identifier mark has been seen. Good! 
        super.readEdgeInfo(graph, e, in);
        // After the normal stuff, read the appended custom data also. 
        try {
          relationType = (String)in.readObject();
        catch (ClassNotFoundException cnfEx) {
          relationType = null;
        // The data is stored to the data provider (which is an edge map). 
        ((EdgeMap)em).set(e, relationType);
        // Bad! The version identifier mark does not match. 
        throw new IOException("Unknown file format.");

By means of the two methods writeNodeRealizer(Graph2D, Node, ObjectOutputStream) and writeEdgeRealizer(Graph2D, Edge, ObjectOutputStream) the respective realizer type is written to a YGF file. Properly adding data that is held by customized versions of abstract classes NodeRealizer or EdgeRealizer can then be accomplished by overriding the appropriate (de)serialization method of either realizer type:

void read(ObjectInputStream in)
void write(ObjectOutputStream out)
Description The node realizer's (de)serialization methods.
void read(ObjectInputStream in)
void write(ObjectOutputStream out)
Description The edge realizer's (de)serialization methods.

The same rules that apply to overriding I/O methods from class YGFIOHandler also apply to overriding the serialization methods from the realizer classes.

Tutorial Demo Code

For examples of customized realizers see the tutorial demo applications: