1   package demo.yext.graphml;
2   
3   import org.graphdrawing.graphml.GraphMLConstants;
4   import org.graphdrawing.graphml.reader.dom.DOMGraphMLParseContext;
5   import org.graphdrawing.graphml.reader.dom.DOMInputHandler;
6   import org.graphdrawing.graphml.writer.GraphMLWriteContext;
7   import org.graphdrawing.graphml.writer.OutputHandler;
8   import org.graphdrawing.graphml.writer.XmlWriter;
9   import org.w3c.dom.NamedNodeMap;
10  import org.w3c.dom.Node;
11  import y.base.DataMap;
12  import y.base.Graph;
13  import y.base.NodeMap;
14  import y.view.Graph2D;
15  import yext.graphml.graph2D.GraphMLIOHandler;
16  import yext.graphml.reader.AbstractDOMInputHandler;
17  import yext.graphml.writer.AbstractOutputHandler;
18  
19  import java.util.StringTokenizer;
20  
21  /**
22   * IOHandler for serializing graphs in GraphML format with complex extensions.
23   * <p>
24   * The node labels of the graph are serialized as lists.
25   * Each token of the label corresponds to a list item.
26   * </p>
27   */
28  public class ComplexExtensionGraphMLIOHandler extends GraphMLIOHandler {
29    /////////////////////////////////////////////////////////////////////////////
30    ////////////////////////////////    Init    /////////////////////////////////
31    /////////////////////////////////////////////////////////////////////////////  
32    NodeMap nodeDataMap;
33    DataMap graphDataMap;
34    
35  
36    /**
37     * Creates a new instance of the IOHandler.
38     */
39    public ComplexExtensionGraphMLIOHandler(NodeMap nodeDataMap, DataMap graphDataMap) {
40      this.nodeDataMap = nodeDataMap;
41      this.graphDataMap = graphDataMap;
42  
43      /** definition of handlers for extensions */
44      addInputHandler(new MyNodeInputHandler());
45      addOutputHandler(new MyNodeOutputHandler(), GraphMLConstants.SCOPE_NODE);
46  //    getOutputHandlers( GraphMLConstants.SCOPE_NODE ).add( new MyNodeOutputHandler() );
47  
48      GraphAttributeIOHandler graphAttributeIOHandler = new GraphAttributeIOHandler();
49      addInputHandler(graphAttributeIOHandler);
50      addOutputHandler(graphAttributeIOHandler, GraphMLConstants.SCOPE_GRAPH);
51    }
52  
53    /////////////////////////////////////////////////////////////////////////////
54    ////////////////////////////////    Parsing    ///////////////////////////
55    /////////////////////////////////////////////////////////////////////////////
56  
57    class MyNodeInputHandler extends AbstractDOMInputHandler {
58  
59      public boolean acceptKey(NamedNodeMap map, int scopeType) {
60        // accept only data-tags which have the extra XML-Attribute extension.type with value list. 
61        Node node = map.getNamedItem("extension.type");
62        if (node == null) {
63          return false;
64        }
65        if ("list".equals(node.getNodeValue())) {
66          return true;
67        } else {
68          return false;
69        }
70      }
71  
72  
73      /**
74       * Parses the data.
75       *
76       * @param context     The context in which the parser is.
77       * @param graph       The current graph in the parse context.
78       * @param nodeedge    The current node/edge in the parse context.
79       * @param defaultMode Whether default data or real data is read.
80       * @param domNode     The DOM node which is actually processed.
81       */
82      protected void parseData(DOMGraphMLParseContext context,
83                               Graph graph, Object nodeedge,
84                               boolean defaultMode,
85                               org.w3c.dom.Node domNode) {
86        // default value handling not implemented
87        if (defaultMode) {
88          return;
89        }
90  
91        org.w3c.dom.NodeList children = domNode.getChildNodes();
92        if (children != null) {
93          for (int i = 0; i < children.getLength(); i++) {
94            org.w3c.dom.Node n = children.item(i);
95            if (n.getNodeType() == org.w3c.dom.Node.ELEMENT_NODE) {
96              String name = n.getLocalName();
97              if ("ItemList".equals(name)) {
98                String items = "";
99                org.w3c.dom.NodeList children2 = n.getChildNodes();
100               if (children2 != null) {
101                 for (int i2 = 0; i2 < children2.getLength(); i2++) {
102                   org.w3c.dom.Node n2 = children2.item(i2);
103                   if (n2.getNodeType() == org.w3c.dom.Node.ELEMENT_NODE) {
104                     if ("Item".equals(n2.getLocalName())) {
105                       org.w3c.dom.NodeList children3 = n2.getChildNodes();
106                       for (int i3 = 0; i3 < children3.getLength(); i3++) {
107                         if (children3.item(i3).getNodeType() == Node.TEXT_NODE) {
108                           items += children3.item(i3).getNodeValue() + "\n";
109                         }
110                       }
111                     }
112                   }
113                 }
114               }
115               if (items.length() > 0) {
116                 nodeDataMap.set(nodeedge, items.substring(0, items.length() - 1));
117               }
118             }
119           }
120         }
121       }
122     }
123 
124     /**
125      * Parses the data.
126      *
127      * @param context  The context in which the parser is.
128      * @param graph    The current graph in the parse context.
129      * @param nodeedge The current node/edge in the parse context.
130      */
131     protected void applyDefault(DOMGraphMLParseContext context,
132                                 Graph graph, Object nodeedge) {
133       // default value handling not implemented
134     }
135   }
136 
137   /////////////////////////////////////////////////////////////////////////////
138   ////////////////////////////////    Writing    ///////////////////////////
139   /////////////////////////////////////////////////////////////////////////////
140 
141   class MyNodeOutputHandler extends AbstractOutputHandler {
142     public void printKeyAttributes(GraphMLWriteContext context, XmlWriter writer) {
143       // mark with type declaration
144       writer.writeAttribute("extension.type", "list");
145     }
146 
147     public void printDataOutput(GraphMLWriteContext context, Graph graph, Object nodeedge, XmlWriter writer) {
148       writer.writeStartElement(null, "ItemList", null);
149 
150       String items = (String) nodeDataMap.get(nodeedge);
151       if (items != null) {
152         StringTokenizer tok = new StringTokenizer(items, "\n\r");
153         while (tok.hasMoreTokens()) {
154           String item = tok.nextToken();
155           writer.writeStartElement("Item", null)
156               .writeText(item)
157               .writeEndElement();
158         }
159       }
160       writer.writeEndElement();
161     }
162 
163     // Writes default value, not used yet
164     public void printKeyOutput(GraphMLWriteContext context, XmlWriter writer) {
165       // default value handling not implemented
166     }
167   }
168 
169   ////////////////////////////////////////////////////////////////////////////
170   ///////////////////////////// Graph Attributes /////////////////////////////
171   ////////////////////////////////////////////////////////////////////////////
172 
173   class GraphAttributeIOHandler implements OutputHandler, DOMInputHandler {
174     // Output
175 
176     public void printKeyAttributes(GraphMLWriteContext context, XmlWriter writer) {
177       writer.writeAttribute("type", "MyGraphAttribute");
178     }
179 
180     public void printKeyOutput(GraphMLWriteContext context, XmlWriter writer) {
181     }
182 
183     public void printDataAttributes(GraphMLWriteContext context, XmlWriter writer) {
184     }
185 
186     public void printDataOutput(GraphMLWriteContext context, XmlWriter writer) {
187 //      List list = context.getContainers();
188       Graph2D graph = (Graph2D) context.getCurrentContainer();
189       String data = (String) graphDataMap.get(graph);
190       if (data != null) {
191         writer.writeText(data);
192       }
193     }
194 
195     // Input
196 
197     public boolean acceptKey(NamedNodeMap attributes, int scopeType) {
198       // accept only data-tags which have the extra XML-Attribute extension.type with value list. 
199       Node node = attributes.getNamedItem("type");
200       if (node == null) {
201         return false;
202       }
203       if (!"MyGraphAttribute".equals(node.getNodeValue())) {
204         return false;
205       } else {
206         return true;
207       }
208     }
209 
210     public void parseData(DOMGraphMLParseContext context, boolean defaultMode, Node node) {
211       if (defaultMode) {
212         return;
213       }
214 
215 //      List list = context.getContainers();
216       Graph2D graph = (Graph2D) context.getCurrentContainer();
217       org.w3c.dom.NodeList children = node.getChildNodes();
218       if (children != null) {
219         for (int i = 0; i < children.getLength(); i++) {
220           org.w3c.dom.Node n = children.item(i);
221           if (n.getNodeType() == org.w3c.dom.Node.TEXT_NODE) {
222             graphDataMap.set(graph, children.item(i).getNodeValue());
223           }
224         }
225       }
226     }
227 
228     public void applyDefault(DOMGraphMLParseContext context) {
229     }
230   }
231 }
232