Appendix B. Migration Notes

Table of Contents

Migrating From yFiles for Java 2.6 to 2.7
API Changes
Hierarchy-related Functionality Moved to Base Classes
Group Node Realizers now use Double-precision Insets
Graph2DClipboard Uses CopyFactory
Miscellaneous
Removed
GraphML Compatibility
Most Prominent Changes
GraphMLIOHandler Changes
Adding Custom Data to GraphML
(De)Serialization of Custom Realizers
GraphML Demos Moved to New Package

This appendix presents the necessary steps to migrate applications based on yFiles for Java 2.6.

Migrating From yFiles for Java 2.6 to 2.7

The migration of a yFiles for Java 2.6 application to yFiles for Java 2.7 comprises changes to your code as described in the section called “API Changes”. Additionally, if your application uses the yFiles GraphML extension package for graph structure I/O, see also the notes on GraphML compatibility.

API Changes

Hierarchy-related Functionality Moved to Base Classes

Change

Hierarchy-related ViewMode implementations from the y.view.hierarchy package have been removed. Their functionality has been incorporated into the corresponding base classes, which can now correctly deal with hierarchically organized Graph2D instances.

Remedy

Replace occurrences of hierarchy-related ViewMode classes as listed in the following table with the corresponding base class.

Table B.1. Hierarchy-related ViewMode functionality

Hierarchy-related ViewMode Base class
HierarchyEditMode EditMode
HierarchyMoveSelectionMode MoveSelectionMode
HierarchyCreateEdgeMode CreateEdgeMode
HierarchySelectionBoxMode SelectionBoxMode
HierarchyHotSpotMode HotSpotMode
Change

Similarly, the functionality of the following hierarchy-related classes has also been moved to the corresponding base class.

Remedy

Replace occurrences of these classes as listed in the following table with the corresponding base class.

Table B.2. Hierarchy-related application logic

Hierarchy-related class Base class
Graph2DHierarchyClipboard Graph2DClipboard
Graph2DHierarchyUndoManager Graph2DUndoManager

Example B.1. Registering a Graph2DUndoManager

2.6
// 'hm' is of type y.view.hierarchy.HierarchyManager.
// 'graph2D' is of type y.view.Graph2D.

Graph2DHierarchyUndoManager manager = new Graph2DHierarchyUndoManager(hm);
graph2D.addGraphListener(manager);
graph2D.setBackupRealizersHandler(manager);
hm.addHierarchyListener(manager);
2.7
// 'graph2D' is of type y.view.Graph2D.

Graph2DUndoManager manager = new Graph2DUndoManager(graph2D);

Group Node Realizers now use Double-precision Insets

Change

Group node realizers and grouping related data providers now use double-precision YInsets instead of integer-precision java.awt.Insets.

Remedy

If you are using custom insets for group node realizers, replace occurrences of integer-precision java.awt.Insets with double-precision YInsets in the methods listed in the following table.

You should also replace any Insets that you pass to layout algorithms by means of data providers and use YInsets instead.

Table B.3. Double-precision insets

Interface/class name Method
AutoBoundsFeature implementations: ProxyAutoBoundsNodeRealizer, GroupNodeRealizer, GenericGroupNodeRealizer.GenericAutoBoundsFeature YInsets getAutoBoundsInsets()
void setAutoBoundsInsets(YInsets insets)
GenericGroupNodeRealizer.GenericAutoBoundsFeature YInsets getAutoBoundsInsets(NodeRealizer nr)
GroupFeature implementations: GroupNodeRealizer and GenericGroupNodeRealizer YInsets getBorderInsets()
void setBorderInsets(YInsets insets)
YInsets getMinimalInsets()
void setMinimalInsets(YInsets insets)
OrientationLayouter YInsets createOrientedInsets(YInsets insets)

Graph2DClipboard Uses CopyFactory

Change

Class Graph2DClipboard now uses a GraphCopier.CopyFactory implementation instead of a GraphFactory implementation to create the clipboard graph and copy parts of the given original graph to and from that graph. As a consequence, the corresponding setter and getter methods have changed. See the following table.

Remedy

If you are setting a custom GraphFactory implementation in your code, you will have to switch to a GraphCopier.CopyFactory implementation instead.

Table B.4. Changed methods in Graph2DClipboard

Old method New method
GraphFactory getGraphFactory() GraphCopier.CopyFactory getCopyFactory()
void setGraphFactory(GraphFactory) void setCopyFactory(GraphCopier.CopyFactory)

For example, if your GraphFactory created graphs of custom type (e.g. MyCustomGraph2D) then a CopyFactory implementation would provide this functionality as shown in Example B.2, “GraphFactory that creates a custom graph type changed to CopyFactory”.

Example B.2. GraphFactory that creates a custom graph type changed to CopyFactory

2.6
// Custom y.base.GraphFactory implementation.
public class MyCustomGraphFactory extends Graph2DCopyFactory {
  public Graph createGraph(Object hint) {
    return new MyCustomGraph2D();
  }

  public Node createNode(Graph graph, Object hint) {
    if (hint instanceof Node) {
      Node originalNode = (Node)hint;
      if (originalNode.getGraph() instanceof MyCustomGraph2D) {
        MyCustomGraph2D otherGraph = 
            (MyCustomGraph2D)originalNode.getGraph();
        MyCustomGraph2D targetGraph2D = (MyCustomGraph2D)graph;
        Node copiedNode = super.createNode(graph, hint);
        targetGraph2D.setMyCustomAttribute(
            copiedNode, otherGraph.getMyCustomAttribute(originalNode));
        return copiedNode;
      }
    }
    return super.createNode(graph, hint);
  }
}
2.7
// Custom y.util.GraphCopier.CopyFactory implementation.
public class MyCustomGraph2DCopyFactory extends
             Graph2DCopyFactory.HierarchicGraph2DCopyFactory {
  public Graph createGraph() {
    return new MyCustomGraph2D();
  }

  public Node copyNode(Graph targetGraph, Node originalNode) {
    if (originalNode.getGraph() instanceof MyCustomGraph2D) {
      MyCustomGraph2D otherGraph = 
          (MyCustomGraph2D)originalNode.getGraph();
      MyCustomGraph2D targetGraph2D = (MyCustomGraph2D)targetGraph;
      Node copiedNode = super.copyNode(targetGraph, originalNode);
      targetGraph2D.setMyCustomAttribute(
          copiedNode, otherGraph.getMyCustomAttribute(originalNode));
      return copiedNode;
    }
    return super.copyNode(targetGraph, originalNode);
  }
}

Note that the createGraph method will be called to create the proper graph type in the clipboard necessary to hold graph elements during the clipboard actions.

Miscellaneous

Change

The protected deleteSelection(Graph2DView) method from class Graph2DViewActions has been deprecated. It is also final now and can no longer be overwritten in order to customize delete behavior.

Remedy

As a replacement, use the delete(Graph2DView) method from dedicated class Graph2DViewActions.DeleteSelectionAction.

Removed

The methods listed in the following table were obsolete and thus removed from Graph2DView.

Table B.5. Methods removed from Graph2DView

Method
void setGridCursorPosition(double x, double y)
void gridCursorActive(boolean enabled)
void setSecureDrawingMode(boolean enabled)

The following classes have been removed:

Table B.6. Removed classes

Class name Description
y.view.hierarchy.HierarchyEditMode The functionality of this class has been integrated into its base class EditMode.
y.view.hierarchy.HierarchyMoveSelectionMode The functionality of this class has been integrated into its base class MoveSelectionMode.
y.view.hierarchy.HierarchyCreateEdgeMode The functionality of this class has been integrated into its base class CreateEdgeMode.
y.view.hierarchy.HierarchySelectionBoxMode The functionality of this class has been integrated into its base class SelectionBoxMode.
y.view.hierarchy.HierarchyHotSpotMode The functionality of this class has been integrated into its base class HotSpotMode.
y.view.hierarchy.Graph2DHierarchyClipboard The functionality of this class has been integrated into its base class Graph2DClipboard.
y.view.hierarchy.Graph2DHierarchyUndoManager The functionality of this class has been integrated into its base class Graph2DUndoManager.

GraphML Compatibility

GraphML support is now an integrated part of the yFiles for Java diagramming library, replacing the GraphML extension package. Although it can still be used with yFiles, new features of yFiles 2.7 are not supported by the extension package. Generally, the GraphML format itself has not changed, i.e., old files can be still be read by the new GraphML support classes. Files written by the new GraphML support can be read by applications that use the yFiles GraphML extension package unless they use new features of yFiles 2.7, e.g. new realizer implementations.

Most Prominent Changes

The most basic usage of the GraphML support is also the same: if you are only relying on the read and write methods of class GraphMLIOHandler, all that you will need to change is the import statement for GraphMLIOHandler in your classes:

Table B.7. New package for class GraphMLIOHandler

Old import New import
yext.graphml.graph2D.GraphMLIOHandler y.io.GraphMLIOHandler

Note, however, that some of the more specialized read and write methods of class GraphMLIOHandler have changed, see below.

If you are storing custom data in your GraphML files, or are using custom realizer implementations in your application, which you also (de)serialize to/from GraphML format, then you will need to update your code as described in the following sections.

If you are using the GraphML post-processor support, then you will have to change the package name:

Table B.8. New package for GraphML post-processors

Old package name New package name
yext.graphml.processor y.io.graphml.graph2d

Note that class yext.graphml.processor.NodeSizeAdapter has been removed from the library. However, the source code for this class can be found in demo.io.graphml.

GraphMLIOHandler Changes

The GraphMLIOHandler class of the integrated GraphML support resides in the y.io package. Changes due to the new XML support include the methods in the following table:

Table B.9. GraphMLIOHandler method changes

Old method New method
read(Graph2D, org.w3c.dom.Node)

read(Graph2D, org.w3c.dom.Document)

If you want to pass a Node which is not a Document, you need to create a new Document and import the Node and its subtree.

The write(Graph2D, javax.xml.transform.Result) method has been removed. Instead, you need to write in a temporary buffer which can serve as InputSource for your StreamResult.

Adding Custom Data to GraphML

Persisting custom application data in GraphML files using the support for simple data types has slightly changed. Instead of the methods listed in the following table:

Table B.10. GraphML extension package support for simple data types

Class name Method name
yext.graphml.graph2D.GraphMLIOHandler addAttribute(NodeMap map, String attrName, int type)
addNodeAttribute(DataProvider out, DataAcceptor in, String attrName, int type)
addAttribute(EdgeMap map, String attrName, int type)
addEdgeAttribute(DataProvider out, DataAcceptor in, String attrName, int type)

... you will need to use the following methods from class GraphMLHandler:

Note that the actual scope of the data provider/data acceptor is now a parameter of the methods, i.e, there are no dedicated methods for the different scopes. A code example which demonstrates how to migrate the registration of a data map is shown below:

Example B.3. Adding a GraphML attribute for custom node data using GraphMLIOHandler

2.6
void addNodeColorGraphMLAttribute(GraphMLIOHandler handler, 
                                  NodeMap map) {
  // The given node map will be accessed by the reader and writer classes
  // whenever a GraphML file is read or written.

  handler.addAttribute(map, "color", AttributeConstants.TYPE_STRING);
}
2.7
// 'ioHandler' is of type y.io.GraphMLIOHandler.

void addNodeColorGraphMLAttribute(GraphMLIOHandler ioHandler, 
                                  NodeMap map) {
  // The given node map will be accessed by the reader and writer classes
  // whenever a GraphML file is read or written.

  GraphMLHandler graphmlH = ioHandler.getGraphMLHandler();
  graphmlH.addInputDataAcceptor("color", map, 
                                KeyScope.NODE, KeyType.STRING);
  graphmlH.addOutputDataProvider("color", map, 
                                 KeyScope.NODE, KeyType.STRING);
}

The KeyScope class replaces the GraphMLConstants from the org.graphdrawing.graphml package, which is no longer necessary with the new GraphML support. Similarly, the KeyType class replaces the AttributeConstants from the org.graphdrawing.graphml.attr package.

The SimpleAttributesDemo.java shows how to use the new methods.

The recommended new scheme for persisting custom application data of structured (non-simple) data types in GraphML files is event-based and makes use of the methods from the following table, which are similar to those used with simple data types:

Instead of registering subordinate input/output handlers you will need to create (de)serialization event listeners as described in the section called “Support for Structured Data” and register them with the GraphMLHandler class. The following examples show how to parse the XML fragment

<Color xmlns="demo" value="AABBEE"/>

to java.awt.Color and store it in a node map resp. how to serialize the value stored in map to such a GraphML attribute:

Example B.4. Adding subordinate input/output handlers: registering the handlers

2.6
NodeMap map = graph.createNodeMap();
GraphMLIOHandler ioHandler;
ioHandler.addInputHandler(new ColorInputHandler(map));
ioHandler.addOutputHandler(new ColorOutputHandler(map),
                           GraphMLConstants.SCOPE_NODE);
2.7
NodeMap map = graph.createNodeMap();
GraphMLIOHandler ioHandler;
ioHandler.getGraphMLHandler().addInputDataAcceptor(
    "myColorAttribute", map, KeyScope.NODE, new ColorDeserializer());
ioHandler.getGraphMLHandler().addOutputDataProvider(
    "myColorAttribute", map, KeyScope.NODE, new ColorSerializer());

With the GraphML extension package, complex data is parsed in an input handler. It is the input handler's responsibility to accept the data from a given key, to parse the data and to store the result in a data acceptor. With yFiles 2.7, complex data can be handled by registering an input data acceptor with a given DeserializationHandler. The handler will be automatically invoked when data with the given name and scope is to be parsed and the result will be stored in the given map. The deserialization logic can be re-used from the old input handler:

Example B.5. Adding subordinate input/output handlers: InputHandler/Deserializer

2.6
public class ColorInputHandler extends AbstractDOMInputHandler {
  private NodeMap myColorMap;

  public ColorInputHandler(NodeMap myColorMap) {
    this.myColorMap = myColorMap;
  }

  protected void parseData(DOMGraphMLParseContext context, Graph graph,
                           Object nodeedge, boolean defaultMode,
                           Node domNode) {
    if (!defaultMode) {
      NodeList children = domNode.getChildNodes();
      if (children != null) {
        for (int i = 0; i < children.getLength(); i++) {
          Node n = children.item(i);
          // deserialization logic to be re-used
          // in the yFiles 2.7 deserializer
          if (n.getNodeType() == Node.ELEMENT_NODE &&
                "demo".equals(n.getNamespaceURI()) &&
                "Color".equals(n.getLocalName())) {
            String valueAttr = ((Element)n).getAttribute("value");
            if (valueAttr.length() > 0) {
              myColorMap.set(nodeedge, Color.decode(valueAttr));
            }
          }
        }
      }
    }
  }

  protected void applyDefault(DOMGraphMLParseContext context,
                              Graph graph, Object nodeedge) {
  }

  public boolean acceptKey(NamedNodeMap attributes, int scopeType) {
    // accept only data-tags which have the
    // attr.name=myColorAttribute and scope SCOPE_NODE.
    Node node = attributes.getNamedItem("attr.name");
    if (node == null) {
      return false;
    }
    return "myColorAttribute".equals(node.getNodeValue()) && 
           scopeType == GraphMLConstants.SCOPE_NODE;
  }
}

2.7
public class ColorDeserializer extends NameBasedDeserializer {
  public String getNamespaceURI(GraphMLParseContext context) {
    return "demo";
  }

  public String getNodeName(GraphMLParseContext context) {
    return "Color";
  }

  public Object deserializeNode(org.w3c.dom.Node xmlNode,
              GraphMLParseContext context) throws GraphMLParseException {
    if (xmlNode.getNodeType() == org.w3c.dom.Node.ELEMENT_NODE &&
                      "demo".equals(xmlNode.getNamespaceURI()) &&
                      "Color".equals(xmlNode.getLocalName())) {
      String valueAttr = ((Element) xmlNode).getAttribute("value");
      if (valueAttr.length() > 0) {
        return Color.decode(valueAttr);
      }
    }
    return null;
  }
}

Migrating an output handler to a SerializationHandler follows the same rules: With yFiles 2.7, an output data provider is registered with a given SerializationHandler. The SerializationHandler will be automatically invoked when data from the given map has to be written. The serialization logic can be re-used from the old output handler:

Example B.6. Adding subordinate input/output handlers: OutputHandler/Serializer

2.6
public class ColorOutputHandler extends AbstractOutputHandler {
  private NodeMap myColorMap;

  public ColorOutputHandler(NodeMap myColorMap) {
    this.myColorMap = myColorMap;
  }

  public void printKeyAttributes(GraphMLWriteContext context,
                                 XmlWriter writer) {
    // mark with type declaration
    writer.writeAttribute("attr.name", "myColorAttribute");
  }

  public void printDataOutput(GraphMLWriteContext context,
                              Graph graph,
                              Object nodeedge,
                              XmlWriter writer) {
    writer.writeStartElement("Color", "demo");
    Color c = (Color) myColorMap.get(nodeedge);
    if (c != null) {
      writer.writeAttribute("value", c.getRGB());
    }
    writer.writeEndElement();
  }

  public void printKeyOutput(GraphMLWriteContext context, 
                             XmlWriter writer) {
  }
}
2.7
public class ColorSerializer extends TypeBasedSerializer {
  protected Class getSerializationType(GraphMLWriteContext context) {
    return Color.class;
  }

  public void serializeItem(Object item, XmlWriter writer, 
                            GraphMLWriteContext context) 
              throws GraphMLWriteException {
    Color c = (Color) item;
    if (c != null) {
      writer.writeStartElement("Color", "demo")
            .writeAttribute("value", c.getRGB()).writeEndElement();
    }
  }
}

If your input handler/output handler logic does more than just writing to a data acceptor/reading from a data provider, then you can create input handler/output handler listeners to register your handlers. The examples below demonstrate how to migrate the same output handlers as in the above example to yFiles 2.7 using explicit Output/InputHandlers instead of (de)serialization event handlers.

Example B.7. Adding subordinate input/output handlers: registering the handlers

2.6
NodeMap map = graph.createNodeMap();
GraphMLIOHandler ioHandler;
ioHandler.addInputHandler(new ColorInputHandler(map));
ioHandler.addOutputHandler(new ColorOutputHandler(map),
                           GraphMLConstants.SCOPE_NODE);
2.7
NodeMap map = graph.createNodeMap();
GraphMLIOHandler ioHandler;
ioHandler.getGraphMLHandler()
         .addInputHandlerProvider(new ColorInputHandler(map));
ioHandler.getGraphMLHandler()
         .addOutputHandlerProvider(new ColorOutputHandler(map));

Claiming parsing responsibility for a given key definition is no longer done inside the input handler (via the acceptKey method). Instead, the listener checks whether the key definition matches and only then registers the actual input handler. If you want to keep your code more or less structured as before, your handler can additionally implement the InputHandlerProvider interface to process the event there, as shown in the example. For matching of the standard attributes, GraphMLHandler offers several static convenience methods, as shown in the example below.

The contract for an input handler has also changed, however, you can conveniently use abstract class AbstractInputHandler as the base for your new input handler implementation. Among other things, the parseData method no longer bears a defaultMode parameter. Instead of using this parameter's value, you will need to explicitly call initializeFromKeyDefinition to set up your input handler appropriately, as shown in the following example. Code present in parseData needs to be split into code for setValue and parseDataCore, as shown in the example above. Since default value handling in the new framework is quite different from the extension package, migration must be done on a case-by-case base. The default implementation in AbstractInputHandler delegates to parseDataCore to parse the default value, which applyDefault retrieves and sets (via setValue). Therefore, in most use cases, it is not necessary to write any custom default value handling code at all.

Example B.8. Adding subordinate input/output handlers: InputHandler/InputHandlerProvider

2.6
public class ColorInputHandler extends AbstractDOMInputHandler {
  private NodeMap myColorMap;

  public ColorInputHandler(NodeMap myColorMap) {
    this.myColorMap = myColorMap;
  }

  protected void parseData(DOMGraphMLParseContext context, Graph graph,
                           Object nodeedge, boolean defaultMode,
                           Node domNode) {
    if (!defaultMode) {
      // parsing logic here:
      // "result" is the object which results from parsing "domNode"

      // apply the value:
      myColorMap.set(nodeedge, result);
    }
  }

  protected void applyDefault(DOMGraphMLParseContext context,
                              Graph graph, Object nodeedge) {
  }

  public boolean acceptKey(NamedNodeMap attributes, int scopeType) {
    // accept only data-tags which have the
    // attr.name=myColorAttribute and scope SCOPE_NODE.
    Node node = attributes.getNamedItem("attr.name");
    if (node == null) {
      return false;
    }
    return "myColorAttribute".equals(node.getNodeValue()) && 
           scopeType == GraphMLConstants.SCOPE_NODE;
  }
}
2.7
public class ColorInputHandler extends AbstractInputHandler
                               implements InputHandlerProvider {
  private NodeMap myColorMap;

  public ColorInputHandler(NodeMap myColorMap) {
    this.myColorMap = myColorMap;
  }

  // Input handler registration is done by methods
  // of interface InputHandlerProvider
  public void onQueryInputHandler(QueryInputHandlersEvent event)
              throws GraphMLParseException {
    if (acceptKey(event.getKeyDefinition())) {
      initializeFromKeyDefinition(event.getContext(),
                                  event.getKeyDefinition());
      event.addInputHandler(this);
    }
  }

  // Corresponds to the code in parseData that actually parses the value
  // Note that no special default value handling is required
  protected Object parseDataCore(GraphMLParseContext context,
                                 org.w3c.dom.Node node)
                                        throws GraphMLParseException {
    // parsing logic here (same as with yFiles 2.6):
    // "result" is the object which results from parsing "domNode"
    return result;
  }

  // Corresponds to the code in parseData that actually applies the value
  protected void setValue(GraphMLParseContext context,
                          Object key,
                          Object data) throws GraphMLParseException {
    // apply the value:
    myColorMap.set(key, data);
  }

  //Predicate for onQueryInputHandler
  public boolean acceptKey(Element keyDefinition) {
    return GraphMLHandler.matchesName(keyDefinition, "myColorAttribute")
        && GraphMLHandler.matchesScope(keyDefinition, KeyScope.NODE);
  }
}

Similarly, the contract for an output handler has also changed. Again, you can conveniently use abstract class AbstractOutputHandler as the base for your new output handler implementation. Note that since this class also implements the OutputHandlerProvider interface, it can be registered directly with GraphMLHandler.addOutputHandlerProvider. Code present in printDataOutput needs to be split into code for getValue and writeValueCore, as shown in the following example. A replacement for printKeyAttributes is only needed if you write custom attributes for the <key> element (i.e. anything except "attr.name", "for", "attr.type". In that case, return the corresponding attribute as a GraphMLXmlAttribute instance in getKeyDefinitionAttributes.

Example B.9. Adding subordinate input/output handlers: OutputHandler

2.6
public class ColorOutputHandler extends AbstractOutputHandler {
  private NodeMap myColorMap;

  public ColorOutputHandler(NodeMap myColorMap) {
    this.myColorMap = myColorMap;
  }

  public void printKeyAttributes(GraphMLWriteContext context,
                                 XmlWriter writer) {
    // mark with type declaration
    writer.writeAttribute("attr.name", "myColorAttribute");
  }

  public void printDataOutput(GraphMLWriteContext context,
                              Graph graph,
                              Object nodeedge,
                              XmlWriter writer) {
    // retrieve the data to serialize
    Color c = (Color) myColorMap.get(nodeedge);
    // writing logic:
    if (c != null) {
      context.getWriter().writeStartElement("Color", "demo")
                         .writeAttribute("value", c.getRGB())
                         .writeEndElement();
    }
  }

  public void printKeyOutput(GraphMLWriteContext context, 
                             XmlWriter writer) {
  }
}
2.7
public class ColorOutputHandler extends AbstractOutputHandler {
  private NodeMap myColorMap;

  public ColorOutputHandler(NodeMap myColorMap) {
    super("myNodeAttribute", KeyScope.NODE, KeyType.COMPLEX);
    this.myColorMap = myColorMap;
  }

  // Corresponds to the code in printDataOutput
  // that actually writes the value
  protected void writeValueCore(GraphMLWriteContext context, Object data) 
                 throws GraphMLWriteException {
    Color c = (Color) data;
    // writing logic. Note that the logic has not changed from 2.6 to 2.7
    if (c != null) {
      context.getWriter().writeStartElement("Color", "demo")
                         .writeAttribute("value", c.getRGB())
                         .writeEndElement();
    }
  }

  // Corresponds to the code in printDataOutput that retrieves
  // the atttibute value for a gieven graph element.
  protected Object getValue(GraphMLWriteContext context,
                            Object key) throws GraphMLWriteException {
    // retrieve the data to serialize
    return myColorMap.get(key);
  }
}

The listeners will be queried dynamically during reading/writing of the GraphML. In the listener you can then register your input handler/output handler as described in the section called “Dynamic Registration of Attributes”.

In this implementation, if you need to add XML attributes to the key definition, this is done using a java.util.Collection of GraphMLXmlAttribute objects instead of using the printKeyAttribute method. Also, the XmlWriter instance is no longer a parameter to the print-related methods. Instead, the writer can be retrieved from the GraphMLWriteContext, which also provides access to the current graph and the current GraphML element (<graph>, <node>, and <edge>).

The tutorial demo application ComplexExtensionGraphMLDemo.java shows how to read and write structured data using input handlers and output handlers.

XMLAttributesParser and XMLAttributesProvider have been replaced

Interface org.graphdrawing.graphml.reader.dom.XMLAttributesParser has been replaced by interface y.io.graphml.input.ParseEventListener. The methods parseXXXAttributes (e.g. parseNodeAttributes) map to the onXXXParsing (e.g. onNodeParsing) methods. Instances can be registered with GraphMLHandler.addParseEventListener.

Similarly, interface org.graphdrawing.graphml.writer.XMLAttributesProvider has been replaced by interface y.io.graphml.output.WriteEventListener. The methods printXXXAttributes map to the onXXXWriting methods. Instances can be registered with GraphMLHandler.addWriteEventListener.

(De)Serialization of Custom Realizers

Realizer serializer implementations that handle custom node realizers or edge realizers are no longer registered using the RealizerSerializerManager class. Instead, GraphMLIOHandler directly offers convenience methods that allow to register realizer serializer implementations.

Change your code that uses class RealizerSerializerManager like this:

Example B.10. Registering a realizer serializer

2.6
// 'graphmlIO' is of type yext.graphml.graph2D.GraphMLIOHandler.

graphmlIO.getRealizerSerializerManager().
    addNodeRealizerSerializer(new MyNodeRealizerSerializer());
2.7
// 'graphmlIO' is of type y.io.GraphMLIOHandler.

graphmlIO.addNodeRealizerSerializer(new MyNodeRealizerSerializer());

Note that the GraphMLIOHandler convenience methods for registering realizer serializers actually delegate to class Graph2DGraphMLHandler. This class also provides methods for de-registering any realizer serializers.

GraphML Demos Moved to New Package

You will find the GraphML demos in the following package:

Table B.13. New package for GraphML demos

Old package name New package name
demo.yext.graphml demo.io.graphml