1 /*
2 * Copyright (c) 2008, the JUNG Project and the Regents of the University
3 * of California
4 * All rights reserved.
5 *
6 * This software is open-source under the BSD license; see either
7 * "license.txt" or
8 * http://jung.sourceforge.net/license.txt for a description.
9 */
10
11 package edu.uci.ics.jung.io.graphml;
12
13 import java.io.IOException;
14 import java.io.Reader;
15
16 import javax.xml.stream.XMLEventReader;
17 import javax.xml.stream.XMLInputFactory;
18 import javax.xml.stream.XMLStreamException;
19 import javax.xml.stream.events.StartElement;
20 import javax.xml.stream.events.XMLEvent;
21
22 import org.apache.commons.collections15.Transformer;
23
24 import edu.uci.ics.jung.graph.Hypergraph;
25 import edu.uci.ics.jung.io.GraphReader;
26 import edu.uci.ics.jung.io.GraphIOException;
27 import edu.uci.ics.jung.io.graphml.parser.ElementParserRegistry;
28 import edu.uci.ics.jung.io.graphml.parser.GraphMLEventFilter;
29
30 /**
31 * Reads in data from a GraphML-formatted file and generates graphs based on
32 * that data. Does not currently support nested graphs.
33 * <p/>
34 * <p/>
35 * Note that the user is responsible for supplying a graph
36 * <code>Transformer</code> that will create graphs capable of supporting the
37 * edge types in the supplied GraphML file. If the graph generated by the
38 * <code>Factory</code> is not compatible (for example: if the graph does not
39 * accept directed edges, and the GraphML file contains a directed edge) then
40 * the results are graph-implementation-dependent.
41 *
42 * @author Nathan Mittler - nathan.mittler@gmail.com
43 *
44 * @param <G>
45 * The graph type to be read from the GraphML file
46 * @param <V>
47 * The vertex type used by the graph
48 * @param <E>
49 * The edge type used by the graph
50 * @see "http://graphml.graphdrawing.org/specification.html"
51 */
52 public class GraphMLReader2<G extends Hypergraph<V, E>, V, E> implements
53 GraphReader<G, V, E> {
54
55 protected XMLEventReader xmlEventReader;
56 protected Reader fileReader;
57 protected Transformer<GraphMetadata, G> graphTransformer;
58 protected Transformer<NodeMetadata, V> vertexTransformer;
59 protected Transformer<EdgeMetadata, E> edgeTransformer;
60 protected Transformer<HyperEdgeMetadata, E> hyperEdgeTransformer;
61 protected boolean initialized;
62 final protected GraphMLDocument document = new GraphMLDocument();
63 final protected ElementParserRegistry<G,V,E> parserRegistry;
64
65 /**
66 * Constructs a GraphML reader around the given reader. This constructor
67 * requires the user to supply transformation functions to convert from the
68 * GraphML metadata to Graph, Vertex, Edge instances. These transformer
69 * functions can be used as purely factories (i.e. the metadata is
70 * disregarded) or can use the metadata to set particular fields in the
71 * objects.
72 *
73 * @param fileReader the reader for the input GraphML document.
74 * @param graphTransformer Transformation function to convert from GraphML GraphMetadata
75 * to graph objects. This must be non-null.
76 * @param vertexTransformer Transformation function to convert from GraphML NodeMetadata
77 * to vertex objects. This must be non-null.
78 * @param edgeTransformer Transformation function to convert from GraphML EdgeMetadata
79 * to edge objects. This must be non-null.
80 * @param hyperEdgeTransformer Transformation function to convert from GraphML
81 * HyperEdgeMetadata to edge objects. This must be non-null.
82 * @throws IllegalArgumentException thrown if any of the arguments are null.
83 */
84 public GraphMLReader2(Reader fileReader,
85 Transformer<GraphMetadata, G> graphTransformer,
86 Transformer<NodeMetadata, V> vertexTransformer,
87 Transformer<EdgeMetadata, E> edgeTransformer,
88 Transformer<HyperEdgeMetadata, E> hyperEdgeTransformer) {
89
90 if (fileReader == null) {
91 throw new IllegalArgumentException(
92 "Argument fileReader must be non-null");
93 }
94
95 if (graphTransformer == null) {
96 throw new IllegalArgumentException(
97 "Argument graphTransformer must be non-null");
98 }
99
100 if (vertexTransformer == null) {
101 throw new IllegalArgumentException(
102 "Argument vertexTransformer must be non-null");
103 }
104
105 if (edgeTransformer == null) {
106 throw new IllegalArgumentException(
107 "Argument edgeTransformer must be non-null");
108 }
109
110 if (hyperEdgeTransformer == null) {
111 throw new IllegalArgumentException(
112 "Argument hyperEdgeTransformer must be non-null");
113 }
114
115 this.fileReader = fileReader;
116 this.graphTransformer = graphTransformer;
117 this.vertexTransformer = vertexTransformer;
118 this.edgeTransformer = edgeTransformer;
119 this.hyperEdgeTransformer = hyperEdgeTransformer;
120
121 // Create the parser registry.
122 this.parserRegistry = new ElementParserRegistry<G,V,E>(document.getKeyMap(),
123 graphTransformer, vertexTransformer, edgeTransformer, hyperEdgeTransformer);
124 }
125
126 /**
127 * Gets the current transformer that is being used for graph objects.
128 *
129 * @return the current transformer.
130 */
131 public Transformer<GraphMetadata, G> getGraphTransformer() {
132 return graphTransformer;
133 }
134
135 /**
136 * Gets the current transformer that is being used for vertex objects.
137 *
138 * @return the current transformer.
139 */
140 public Transformer<NodeMetadata, V> getVertexTransformer() {
141 return vertexTransformer;
142 }
143
144 /**
145 * Gets the current transformer that is being used for edge objects.
146 *
147 * @return the current transformer.
148 */
149 public Transformer<EdgeMetadata, E> getEdgeTransformer() {
150 return edgeTransformer;
151 }
152
153 /**
154 * Gets the current transformer that is being used for hyperedge objects.
155 *
156 * @return the current transformer.
157 */
158 public Transformer<HyperEdgeMetadata, E> getHyperEdgeTransformer() {
159 return hyperEdgeTransformer;
160 }
161
162 /**
163 * Verifies the object state and initializes this reader. All transformer
164 * properties must be set and be non-null or a <code>GraphReaderException
165 * </code> will be thrown. This method may be called more than once.
166 * Successive calls will have no effect.
167 *
168 * @throws edu.uci.ics.jung.io.GraphIOException thrown if an error occurred.
169 */
170 public void init() throws GraphIOException {
171
172 try {
173
174 if (!initialized) {
175
176 // Create the event reader.
177 XMLInputFactory factory = XMLInputFactory.newInstance();
178 xmlEventReader = factory.createXMLEventReader(fileReader);
179 xmlEventReader = factory.createFilteredReader(xmlEventReader,
180 new GraphMLEventFilter());
181
182 initialized = true;
183 }
184
185 } catch( Exception e ) {
186 ExceptionConverter.convert(e);
187 }
188 }
189
190 /**
191 * Closes the GraphML reader and disposes of any resources.
192 *
193 * @throws edu.uci.ics.jung.io.GraphIOException thrown if an error occurs.
194 */
195 public void close() throws GraphIOException {
196 try {
197
198 // Clear the contents of the document.
199 document.clear();
200
201 if (xmlEventReader != null) {
202 xmlEventReader.close();
203 }
204
205 if (fileReader != null) {
206 fileReader.close();
207 }
208
209 } catch (IOException e) {
210 throw new GraphIOException(e);
211 } catch (XMLStreamException e) {
212 throw new GraphIOException(e);
213 } finally {
214 fileReader = null;
215 xmlEventReader = null;
216 graphTransformer = null;
217 vertexTransformer = null;
218 edgeTransformer = null;
219 hyperEdgeTransformer = null;
220 }
221 }
222
223 /**
224 * Returns the object that contains the metadata read in from the GraphML
225 * document
226 *
227 * @return the GraphML document
228 */
229 public GraphMLDocument getGraphMLDocument() {
230 return document;
231 }
232
233 /**
234 * Reads a single graph object from the GraphML document. Automatically
235 * calls <code>init</code> to initialize the state of the reader.
236 *
237 * @return the graph that was read if one was found, otherwise null.
238 */
239 @SuppressWarnings("unchecked")
240 public G readGraph() throws GraphIOException {
241
242 try {
243
244 // Initialize if not already.
245 init();
246
247 while (xmlEventReader.hasNext()) {
248
249 XMLEvent event = xmlEventReader.nextEvent();
250 if (event.isStartElement()) {
251 StartElement element = (StartElement) event;
252 String name = element.getName().getLocalPart();
253
254 // The element should be one of: key, graph, graphml
255 if (GraphMLConstants.KEY_NAME.equals(name)) {
256
257 // Parse the key object.
258 Key key = (Key) parserRegistry.getParser(name).parse(
259 xmlEventReader, element);
260
261 // Add the key to the key map.
262 document.getKeyMap().addKey(key);
263
264 } else if (GraphMLConstants.GRAPH_NAME.equals(name)) {
265
266 // Parse the graph.
267 GraphMetadata graph = (GraphMetadata) parserRegistry
268 .getParser(name).parse(xmlEventReader, element);
269
270 // Add it to the graph metadata list.
271 document.getGraphMetadata().add(graph);
272
273 // Return the graph object.
274 return (G)graph.getGraph();
275
276 } else if (GraphMLConstants.GRAPHML_NAME.equals(name)) {
277 // Ignore the graphML object.
278 } else {
279
280 // Encounted an unknown element - just skip by it.
281 parserRegistry.getUnknownElementParser().parse(
282 xmlEventReader, element);
283 }
284
285 } else if (event.isEndDocument()) {
286 break;
287 }
288 }
289
290 } catch (Exception e) {
291 ExceptionConverter.convert(e);
292 }
293
294 // We didn't read anything from the document.
295 throw new GraphIOException("Unable to read Graph from document - the document could be empty");
296 }
297 }