1
2
3
4
5
6
7
8
9
10
11
12 package edu.uci.ics.jung.io;
13
14 import java.io.FileReader;
15 import java.io.IOException;
16 import java.io.Reader;
17 import java.util.ArrayList;
18 import java.util.Collection;
19 import java.util.HashMap;
20 import java.util.LinkedList;
21 import java.util.List;
22 import java.util.Map;
23
24 import javax.xml.parsers.ParserConfigurationException;
25 import javax.xml.parsers.SAXParser;
26 import javax.xml.parsers.SAXParserFactory;
27
28 import org.xml.sax.Attributes;
29 import org.xml.sax.InputSource;
30 import org.xml.sax.SAXException;
31 import org.xml.sax.SAXNotSupportedException;
32 import org.xml.sax.helpers.DefaultHandler;
33
34 import com.google.common.base.Supplier;
35 import com.google.common.collect.BiMap;
36 import com.google.common.collect.HashBiMap;
37
38 import edu.uci.ics.jung.algorithms.util.MapSettableTransformer;
39 import edu.uci.ics.jung.algorithms.util.SettableTransformer;
40 import edu.uci.ics.jung.graph.Graph;
41 import edu.uci.ics.jung.graph.Hypergraph;
42 import edu.uci.ics.jung.graph.util.EdgeType;
43 import edu.uci.ics.jung.graph.util.Pair;
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69 public class GraphMLReader<G extends Hypergraph<V,E>, V, E> extends DefaultHandler
70 {
71 protected enum TagState {NO_TAG, VERTEX, EDGE, HYPEREDGE, ENDPOINT, GRAPH,
72 DATA, KEY, DESC, DEFAULT_KEY, GRAPHML, OTHER}
73
74 protected enum KeyType {NONE, VERTEX, EDGE, GRAPH, ALL}
75
76 protected SAXParser saxp;
77 protected EdgeType default_edgetype;
78 protected G current_graph;
79 protected V current_vertex;
80 protected E current_edge;
81 protected String current_key;
82 protected LinkedList<TagState> current_states;
83 protected BiMap<String, TagState> tag_state;
84 protected Supplier<G> graph_factory;
85 protected Supplier<V> vertex_factory;
86 protected Supplier<E> edge_factory;
87 protected BiMap<V, String> vertex_ids;
88 protected BiMap<E, String> edge_ids;
89 protected Map<String, GraphMLMetadata<G>> graph_metadata;
90 protected Map<String, GraphMLMetadata<V>> vertex_metadata;
91 protected Map<String, GraphMLMetadata<E>> edge_metadata;
92 protected Map<V, String> vertex_desc;
93 protected Map<E, String> edge_desc;
94 protected Map<G, String> graph_desc;
95 protected KeyType key_type;
96 protected Collection<V> hyperedge_vertices;
97
98 protected List<G> graphs;
99
100 protected StringBuilder current_text = new StringBuilder();
101
102
103
104
105
106
107
108
109
110
111 public GraphMLReader(Supplier<V> vertex_factory,
112 Supplier<E> edge_factory)
113 throws ParserConfigurationException, SAXException
114 {
115 current_vertex = null;
116 current_edge = null;
117
118 SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();
119 saxp = saxParserFactory.newSAXParser();
120
121 current_states = new LinkedList<TagState>();
122
123 tag_state = HashBiMap.<String, TagState>create();
124 tag_state.put("node", TagState.VERTEX);
125 tag_state.put("edge", TagState.EDGE);
126 tag_state.put("hyperedge", TagState.HYPEREDGE);
127 tag_state.put("endpoint", TagState.ENDPOINT);
128 tag_state.put("graph", TagState.GRAPH);
129 tag_state.put("data", TagState.DATA);
130 tag_state.put("key", TagState.KEY);
131 tag_state.put("desc", TagState.DESC);
132 tag_state.put("default", TagState.DEFAULT_KEY);
133 tag_state.put("graphml", TagState.GRAPHML);
134
135 this.key_type = KeyType.NONE;
136
137 this.vertex_factory = vertex_factory;
138 this.edge_factory = edge_factory;
139 }
140
141
142
143
144
145
146
147
148
149
150
151 public GraphMLReader() throws ParserConfigurationException, SAXException
152 {
153 this(null, null);
154 }
155
156
157
158
159
160
161
162
163
164 public List<G> loadMultiple(Reader reader, Supplier<G> graph_factory)
165 throws IOException
166 {
167 this.graph_factory = graph_factory;
168 initializeData();
169 clearData();
170 parse(reader);
171
172 return graphs;
173 }
174
175
176
177
178
179
180
181
182
183 public List<G> loadMultiple(String filename, Supplier<G> graph_factory) throws IOException
184 {
185 return loadMultiple(new FileReader(filename), graph_factory);
186 }
187
188
189
190
191
192
193
194 public void load(Reader reader, G g) throws IOException
195 {
196 this.current_graph = g;
197 this.graph_factory = null;
198 initializeData();
199 clearData();
200
201 parse(reader);
202 }
203
204
205
206
207
208
209
210 public void load(String filename, G g) throws IOException
211 {
212 load(new FileReader(filename), g);
213 }
214
215 protected void clearData()
216 {
217 this.vertex_ids.clear();
218 this.vertex_desc.clear();
219
220 this.edge_ids.clear();
221 this.edge_desc.clear();
222
223 this.graph_desc.clear();
224
225 this.hyperedge_vertices.clear();
226 }
227
228
229
230
231
232 protected void initializeData()
233 {
234 this.vertex_ids = HashBiMap.<V, String>create();
235 this.vertex_desc = new HashMap<V, String>();
236 this.vertex_metadata = new HashMap<String, GraphMLMetadata<V>>();
237
238 this.edge_ids = HashBiMap.<E, String>create();
239 this.edge_desc = new HashMap<E, String>();
240 this.edge_metadata = new HashMap<String, GraphMLMetadata<E>>();
241
242 this.graph_desc = new HashMap<G, String>();
243 this.graph_metadata = new HashMap<String, GraphMLMetadata<G>>();
244
245 this.hyperedge_vertices = new ArrayList<V>();
246 }
247
248 protected void parse(Reader reader) throws IOException
249 {
250 try
251 {
252 saxp.parse(new InputSource(reader), this);
253 reader.close();
254 }
255 catch (SAXException saxe)
256 {
257 throw new IOException(saxe.getMessage());
258 }
259 }
260
261 @Override
262 public void startElement(String uri, String name, String qName, Attributes atts) throws SAXNotSupportedException
263 {
264 String tag = qName.toLowerCase();
265 TagState state = tag_state.get(tag);
266 if (state == null)
267 state = TagState.OTHER;
268
269 switch (state)
270 {
271 case GRAPHML:
272 break;
273
274 case VERTEX:
275 if (this.current_graph == null)
276 throw new SAXNotSupportedException("Graph must be defined prior to elements");
277 if (this.current_edge != null || this.current_vertex != null)
278 throw new SAXNotSupportedException("Nesting elements not supported");
279
280 createVertex(atts);
281
282 break;
283
284 case ENDPOINT:
285 if (this.current_graph == null)
286 throw new SAXNotSupportedException("Graph must be defined prior to elements");
287 if (this.current_edge == null)
288 throw new SAXNotSupportedException("No edge defined for endpoint");
289 if (this.current_states.getFirst() != TagState.HYPEREDGE)
290 throw new SAXNotSupportedException("Endpoints must be defined inside hyperedge");
291 Map<String, String> endpoint_atts = getAttributeMap(atts);
292 String node = endpoint_atts.remove("node");
293 if (node == null)
294 throw new SAXNotSupportedException("Endpoint must include an 'id' attribute");
295 V v = vertex_ids.inverse().get(node);
296 if (v == null)
297 throw new SAXNotSupportedException("Endpoint refers to nonexistent node ID: " + node);
298
299 this.current_vertex = v;
300 hyperedge_vertices.add(v);
301 break;
302
303 case EDGE:
304 case HYPEREDGE:
305 if (this.current_graph == null)
306 throw new SAXNotSupportedException("Graph must be defined prior to elements");
307 if (this.current_edge != null || this.current_vertex != null)
308 throw new SAXNotSupportedException("Nesting elements not supported");
309
310 createEdge(atts, state);
311 break;
312
313 case GRAPH:
314 if (this.current_graph != null && graph_factory != null)
315 throw new SAXNotSupportedException("Nesting graphs not currently supported");
316
317
318 if (graph_factory != null)
319 current_graph = graph_factory.get();
320
321
322 clearData();
323
324
325 Map<String, String> graph_atts = getAttributeMap(atts);
326 String default_direction = graph_atts.remove("edgedefault");
327 if (default_direction == null)
328 throw new SAXNotSupportedException("All graphs must specify a default edge direction");
329 if (default_direction.equals("directed"))
330 this.default_edgetype = EdgeType.DIRECTED;
331 else if (default_direction.equals("undirected"))
332 this.default_edgetype = EdgeType.UNDIRECTED;
333 else
334 throw new SAXNotSupportedException("Invalid or unrecognized default edge direction: " + default_direction);
335
336
337 addExtraData(graph_atts, graph_metadata, current_graph);
338
339 break;
340
341 case DATA:
342 if (this.current_states.contains(TagState.DATA))
343 throw new SAXNotSupportedException("Nested data not supported");
344 handleData(atts);
345 break;
346
347 case KEY:
348 createKey(atts);
349 break;
350
351
352 default:
353 break;
354 }
355
356 current_states.addFirst(state);
357 }
358
359
360
361
362
363
364
365
366 private <T> void addExtraData(Map<String, String> atts,
367 Map<String, GraphMLMetadata<T>> metadata_map, T current_elt)
368 {
369
370
371
372 for (Map.Entry<String, GraphMLMetadata<T>> entry: metadata_map.entrySet())
373 {
374 GraphMLMetadata<T> gmlm = entry.getValue();
375 if (gmlm.default_value != null)
376 {
377 SettableTransformer<T, String> st =
378 (SettableTransformer<T, String>)gmlm.transformer;
379 st.set(current_elt, gmlm.default_value);
380 }
381 }
382
383
384 for (Map.Entry<String, String> entry : atts.entrySet())
385 {
386 String key = entry.getKey();
387 GraphMLMetadata<T> key_data = metadata_map.get(key);
388 SettableTransformer<T, String> st;
389 if (key_data != null)
390 {
391
392 if (key_data.default_value != null)
393 continue;
394 st = (SettableTransformer<T, String>)key_data.transformer;
395 }
396 else
397 {
398 st = new MapSettableTransformer<T, String>(
399 new HashMap<T, String>());
400 key_data = new GraphMLMetadata<T>(null, null, st);
401 metadata_map.put(key, key_data);
402 }
403 st.set(current_elt, entry.getValue());
404 }
405 }
406
407
408 @Override
409 public void characters(char[] ch, int start, int length) throws SAXNotSupportedException
410 {
411 this.current_text.append(new String(ch, start, length));
412 }
413
414
415 protected <T>void addDatum(Map<String, GraphMLMetadata<T>> metadata,
416 T current_elt, String text) throws SAXNotSupportedException
417 {
418 if (metadata.containsKey(this.current_key))
419 {
420 SettableTransformer<T, String> st =
421 (SettableTransformer<T, String>)(metadata.get(this.current_key).transformer);
422 st.set(current_elt, text);
423 }
424 else
425 throw new SAXNotSupportedException("key " + this.current_key +
426 " not valid for element " + current_elt);
427 }
428
429 @Override
430 public void endElement(String uri, String name, String qName) throws SAXNotSupportedException
431 {
432 String text = current_text.toString().trim();
433 current_text.setLength(0);
434
435 String tag = qName.toLowerCase();
436 TagState state = tag_state.get(tag);
437 if (state == null)
438 state = TagState.OTHER;
439 if (state == TagState.OTHER)
440 return;
441
442 if (state != current_states.getFirst())
443 throw new SAXNotSupportedException("Unbalanced tags: opened " +
444 tag_state.inverse().get(current_states.getFirst()) +
445 ", closed " + tag);
446
447 switch(state)
448 {
449 case VERTEX:
450 case ENDPOINT:
451 current_vertex = null;
452 break;
453
454 case EDGE:
455 current_edge = null;
456 break;
457
458 case HYPEREDGE:
459 current_graph.addEdge(current_edge, hyperedge_vertices);
460 hyperedge_vertices.clear();
461 current_edge = null;
462 break;
463
464 case GRAPH:
465 current_graph = null;
466 break;
467
468 case KEY:
469 current_key = null;
470 break;
471
472 case DESC:
473 switch (this.current_states.get(1))
474 {
475 case GRAPH:
476 graph_desc.put(current_graph, text);
477 break;
478 case VERTEX:
479 case ENDPOINT:
480 vertex_desc.put(current_vertex, text);
481 break;
482 case EDGE:
483 case HYPEREDGE:
484 edge_desc.put(current_edge, text);
485 break;
486 case DATA:
487 switch (key_type)
488 {
489 case GRAPH:
490 graph_metadata.get(current_key).description = text;
491 break;
492 case VERTEX:
493 vertex_metadata.get(current_key).description = text;
494 break;
495 case EDGE:
496 edge_metadata.get(current_key).description = text;
497 break;
498 case ALL:
499 graph_metadata.get(current_key).description = text;
500 vertex_metadata.get(current_key).description = text;
501 edge_metadata.get(current_key).description = text;
502 break;
503 default:
504 throw new SAXNotSupportedException("Invalid key type" +
505 " specified for default: " + key_type);
506 }
507
508 break;
509 default:
510 break;
511 }
512 break;
513 case DATA:
514 this.key_type = KeyType.NONE;
515 switch (this.current_states.get(1))
516 {
517 case GRAPH:
518 addDatum(graph_metadata, current_graph, text);
519 break;
520 case VERTEX:
521 case ENDPOINT:
522 addDatum(vertex_metadata, current_vertex, text);
523 break;
524 case EDGE:
525 case HYPEREDGE:
526 addDatum(edge_metadata, current_edge, text);
527 break;
528 default:
529 break;
530 }
531 break;
532 case DEFAULT_KEY:
533 if (this.current_states.get(1) != TagState.KEY)
534 throw new SAXNotSupportedException("'default' only defined in context of 'key' tag: " +
535 "stack: " + current_states.toString());
536
537 switch (key_type)
538 {
539 case GRAPH:
540 graph_metadata.get(current_key).default_value = text;
541 break;
542 case VERTEX:
543 vertex_metadata.get(current_key).default_value = text;
544 break;
545 case EDGE:
546 edge_metadata.get(current_key).default_value = text;
547 break;
548 case ALL:
549 graph_metadata.get(current_key).default_value = text;
550 vertex_metadata.get(current_key).default_value = text;
551 edge_metadata.get(current_key).default_value = text;
552 break;
553 default:
554 throw new SAXNotSupportedException("Invalid key type" +
555 " specified for default: " + key_type);
556 }
557
558 break;
559 default:
560 break;
561 }
562
563 current_states.removeFirst();
564 }
565
566 protected Map<String, String> getAttributeMap(Attributes atts)
567 {
568 Map<String,String> att_map = new HashMap<String,String>();
569 for (int i = 0; i < atts.getLength(); i++)
570 att_map.put(atts.getQName(i), atts.getValue(i));
571
572 return att_map;
573 }
574
575 protected void handleData(Attributes atts) throws SAXNotSupportedException
576 {
577 switch (this.current_states.getFirst())
578 {
579 case GRAPH:
580 break;
581 case VERTEX:
582 case ENDPOINT:
583 break;
584 case EDGE:
585 break;
586 case HYPEREDGE:
587 break;
588 default:
589 throw new SAXNotSupportedException("'data' tag only defined " +
590 "if immediately containing tag is 'graph', 'node', " +
591 "'edge', or 'hyperedge'");
592 }
593 this.current_key = getAttributeMap(atts).get("key");
594 if (this.current_key == null)
595 throw new SAXNotSupportedException("'data' tag requires a key specification");
596 if (this.current_key.equals(""))
597 throw new SAXNotSupportedException("'data' tag requires a non-empty key");
598 if (!getGraphMetadata().containsKey(this.current_key) &&
599 !getVertexMetadata().containsKey(this.current_key) &&
600 !getEdgeMetadata().containsKey(this.current_key))
601 {
602 throw new SAXNotSupportedException("'data' tag's key specification must reference a defined key");
603 }
604
605 }
606
607 protected void createKey(Attributes atts) throws SAXNotSupportedException
608 {
609 Map<String, String> key_atts = getAttributeMap(atts);
610 String id = key_atts.remove("id");
611 String for_type = key_atts.remove("for");
612
613 if (for_type == null || for_type.equals("") || for_type.equals("all"))
614 {
615 vertex_metadata.put(id,
616 new GraphMLMetadata<V>(null, null,
617 new MapSettableTransformer<V, String>(new HashMap<V, String>())));
618 edge_metadata.put(id,
619 new GraphMLMetadata<E>(null, null,
620 new MapSettableTransformer<E, String>(new HashMap<E, String>())));
621 graph_metadata.put(id,
622 new GraphMLMetadata<G>(null, null,
623 new MapSettableTransformer<G, String>(new HashMap<G, String>())));
624 key_type = KeyType.ALL;
625 }
626 else
627 {
628 TagState type = tag_state.get(for_type);
629 switch (type)
630 {
631 case VERTEX:
632 vertex_metadata.put(id,
633 new GraphMLMetadata<V>(null, null,
634 new MapSettableTransformer<V, String>(new HashMap<V, String>())));
635 key_type = KeyType.VERTEX;
636 break;
637 case EDGE:
638 case HYPEREDGE:
639 edge_metadata.put(id,
640 new GraphMLMetadata<E>(null, null,
641 new MapSettableTransformer<E, String>(new HashMap<E, String>())));
642 key_type = KeyType.EDGE;
643 break;
644 case GRAPH:
645 graph_metadata.put(id,
646 new GraphMLMetadata<G>(null, null,
647 new MapSettableTransformer<G, String>(new HashMap<G, String>())));
648 key_type = KeyType.GRAPH;
649 break;
650 default:
651 throw new SAXNotSupportedException(
652 "Invalid metadata target type: " + for_type);
653 }
654 }
655
656 this.current_key = id;
657
658 }
659
660 @SuppressWarnings("unchecked")
661 protected void createVertex(Attributes atts) throws SAXNotSupportedException
662 {
663 Map<String, String> vertex_atts = getAttributeMap(atts);
664 String id = vertex_atts.remove("id");
665 if (id == null)
666 throw new SAXNotSupportedException("node attribute list missing " +
667 "'id': " + atts.toString());
668 V v = vertex_ids.inverse().get(id);
669
670 if (v == null)
671 {
672 if (vertex_factory != null)
673 v = vertex_factory.get();
674 else
675 v = (V)id;
676 vertex_ids.put(v, id);
677 this.current_graph.addVertex(v);
678
679
680 addExtraData(vertex_atts, vertex_metadata, v);
681 }
682 else
683 throw new SAXNotSupportedException("Node id \"" + id +
684 " is a duplicate of an existing node ID");
685
686 this.current_vertex = v;
687 }
688
689
690 @SuppressWarnings("unchecked")
691 protected void createEdge(Attributes atts, TagState state)
692 throws SAXNotSupportedException
693 {
694 Map<String,String> edge_atts = getAttributeMap(atts);
695
696 String id = edge_atts.remove("id");
697 E e;
698 if (edge_factory != null)
699 e = edge_factory.get();
700 else
701 if (id != null)
702 e = (E)id;
703 else
704 throw new IllegalArgumentException("If no edge Supplier is supplied, " +
705 "edge id may not be null: " + edge_atts);
706
707 if (id != null)
708 {
709 if (edge_ids.containsKey(e))
710 throw new SAXNotSupportedException("Edge id \"" + id +
711 "\" is a duplicate of an existing edge ID");
712 edge_ids.put(e, id);
713 }
714
715 if (state == TagState.EDGE)
716 assignEdgeSourceTarget(e, atts, edge_atts);
717
718
719 addExtraData(edge_atts, edge_metadata, e);
720
721 this.current_edge = e;
722 }
723
724 protected void assignEdgeSourceTarget(E e, Attributes atts,
725 Map<String, String> edge_atts)
726 throws SAXNotSupportedException
727 {
728 String source_id = edge_atts.remove("source");
729 if (source_id == null)
730 throw new SAXNotSupportedException("edge attribute list missing " +
731 "'source': " + atts.toString());
732 V source = vertex_ids.inverse().get(source_id);
733 if (source == null)
734 throw new SAXNotSupportedException("specified 'source' attribute " +
735 "\"" + source_id + "\" does not match any node ID");
736
737 String target_id = edge_atts.remove("target");
738 if (target_id == null)
739 throw new SAXNotSupportedException("edge attribute list missing " +
740 "'target': " + atts.toString());
741 V target = vertex_ids.inverse().get(target_id);
742 if (target == null)
743 throw new SAXNotSupportedException("specified 'target' attribute " +
744 "\"" + target_id + "\" does not match any node ID");
745
746 String directed = edge_atts.remove("directed");
747 EdgeType edge_type;
748 if (directed == null)
749 edge_type = default_edgetype;
750 else if (directed.equals("true"))
751 edge_type = EdgeType.DIRECTED;
752 else if (directed.equals("false"))
753 edge_type = EdgeType.UNDIRECTED;
754 else
755 throw new SAXNotSupportedException("Unrecognized edge direction specifier 'direction=\"" +
756 directed + "\"': " + "source: " + source_id + ", target: " + target_id);
757
758 if (current_graph instanceof Graph)
759 ((Graph<V,E>)this.current_graph).addEdge(e, source, target,
760 edge_type);
761 else
762 this.current_graph.addEdge(e, new Pair<V>(source, target));
763 }
764
765
766
767
768 public BiMap<V, String> getVertexIDs()
769 {
770 return vertex_ids;
771 }
772
773
774
775
776
777
778
779 public BiMap<E, String> getEdgeIDs()
780 {
781 return edge_ids;
782 }
783
784
785
786
787 public Map<String, GraphMLMetadata<G>> getGraphMetadata()
788 {
789 return graph_metadata;
790 }
791
792
793
794
795 public Map<String, GraphMLMetadata<V>> getVertexMetadata()
796 {
797 return vertex_metadata;
798 }
799
800
801
802
803 public Map<String, GraphMLMetadata<E>> getEdgeMetadata()
804 {
805 return edge_metadata;
806 }
807
808
809
810
811 public Map<G, String> getGraphDescriptions()
812 {
813 return graph_desc;
814 }
815
816
817
818
819 public Map<V, String> getVertexDescriptions()
820 {
821 return vertex_desc;
822 }
823
824
825
826
827 public Map<E, String> getEdgeDescriptions()
828 {
829 return edge_desc;
830 }
831 }