package nl.helixsoft.graph; import java.io.OutputStream; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Map; import nl.helixsoft.util.AttributesTable; import org.apache.commons.collections15.BidiMap; import edu.uci.ics.jung.algorithms.util.Indexer; import edu.uci.ics.jung.graph.DirectedGraph; /** * Note that the node attribute "label" will be used as the node identifier during GML import in Cytoscape. * The Node id itself will be converted to an integer. * The formatter will warn if you try to supply label as an attribute. */ public final class GmlFormat { /** tool class should never be instantiated */ private GmlFormat() {} /** same as other writeGml, but sorts nodes and edges, for stable output. */ public static <V, E> void writeGml(OutputStream fos, final DirectedGraph<V, E> graph, AttributesTable<V> nodeAttr, AttributesTable<E> edgeAttr, Comparator<V> comp) { List<V> sortedV = new ArrayList<V>(graph.getVertices()); Collections.sort (sortedV, comp); // GML uses integers as node ids, so start by creating integer inex. final BidiMap<V, Integer> idx = Indexer.create(sortedV); GmlEmitter gml = new GmlEmitter(fos); gml.startList("graph"); writeNodes (nodeAttr, idx, gml, sortedV); List<E> sortedE = new ArrayList<E>(graph.getEdges()); Collections.sort (sortedE, new Comparator<E>() { @Override public int compare (E e1, E e2) { int i1 = idx.get(graph.getSource(e1)); int i2 = idx.get(graph.getSource(e2)); if (i1 < i2) return -1; if (i1 > i2) return 1; int j1 = idx.get(graph.getDest(e1)); int j2 = idx.get(graph.getDest(e2)); if (j1 < j2) return -1; if (j1 > j2) return 1; return 0; } } ); writeEdges(graph, edgeAttr, idx, gml, sortedE); gml.closeList(); gml.close(); } private static <V, E> void writeEdges(final DirectedGraph<V, E> graph, AttributesTable<E> edgeAttr, final BidiMap<V, Integer> idx, GmlEmitter gml, Collection<E> edges) { for (E e : edges) { V s = graph.getSource(e); V t = graph.getDest(e); gml.startList("edge"); gml.intLiteral("source", idx.get(s)); gml.intLiteral("target", idx.get(t)); gml.stringLiteral ("interaction", e.toString()); for (Map.Entry<String, Object> entry : edgeAttr.getAttributes(e)) { if ("interaction".equals (entry.getKey())) { System.err.println ("WARNING, ignoring reserved attribute " + entry.getKey() + " from attribute table"); continue; } Object val = entry.getValue(); if (val instanceof Number) { gml.numberLiteral(entry.getKey(), (Number)val); } else { gml.stringLiteral(entry.getKey(), "" + val); } } gml.closeList(); } } public static <V, E> void writeGml(OutputStream fos, DirectedGraph<V, E> graph, AttributesTable<V> nodeAttr, AttributesTable<E> edgeAttr) { // GML uses integers as node ids, so start by creating integer inex. final BidiMap<V, Integer> idx = Indexer.create(graph.getVertices()); GmlEmitter gml = new GmlEmitter(fos); gml.startList("graph"); writeNodes(nodeAttr, idx, gml, graph.getVertices()); writeEdges(graph, edgeAttr, idx, gml, graph.getEdges()); gml.closeList(); gml.close(); } private static <V> void writeNodes(AttributesTable<V> nodeAttr, final BidiMap<V, Integer> idx, GmlEmitter gml, Collection<V> nodes) { for (V v : nodes) { gml.startList("node"); gml.intLiteral("id", idx.get(v)); // label will be used as ID during import in Cytoscape. gml.stringLiteral ("label", v.toString()); for (Map.Entry<String, Object> entry : nodeAttr.getAttributes(v)) { if ("label".equals(entry.getKey()) || "id".equals (entry.getKey())) { System.err.println ("WARNING, ignoring reserved attribute " + entry.getKey() + " from attribute table"); continue; } Object val = entry.getValue(); if (val instanceof Number) { gml.numberLiteral(entry.getKey(), (Number)val); } else { gml.stringLiteral(entry.getKey(), "" + val); } } gml.closeList(); } }; }