1 /*
2 * Copyright (c) 2008, The JUNG Authors
3 *
4 * All rights reserved.
5 *
6 * This software is open-source under the BSD license; see either
7 * "license.txt" or
8 * https://github.com/jrtom/jung/blob/master/LICENSE 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 import java.io.InputStream;
16
17 import javax.xml.stream.XMLEventReader;
18 import javax.xml.stream.XMLInputFactory;
19 import javax.xml.stream.XMLStreamException;
20 import javax.xml.stream.events.StartElement;
21 import javax.xml.stream.events.XMLEvent;
22
23 import com.google.common.base.Function;
24
25 import edu.uci.ics.jung.graph.Hypergraph;
26 import edu.uci.ics.jung.io.GraphIOException;
27 import edu.uci.ics.jung.io.GraphReader;
28 import edu.uci.ics.jung.io.graphml.parser.ElementParserRegistry;
29 import edu.uci.ics.jung.io.graphml.parser.GraphMLEventFilter;
30
31 /**
32 * Reads in data from a GraphML-formatted file and generates graphs based on
33 * that data. Does not currently support nested graphs.
34 *
35 * <p>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> the vertex type
47 * The vertex type used by the graph
48 * @param <V> the edge type
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 Function<GraphMetadata, G> graphTransformer;
58 protected Function<NodeMetadata, V> vertexTransformer;
59 protected Function<EdgeMetadata, E> edgeTransformer;
60 protected Function<HyperEdgeMetadata, E> hyperEdgeTransformer;
61 protected boolean initialized;
62 final protected GraphMLDocument document = new GraphMLDocument();
63 final protected ElementParserRegistry<G,V,E> parserRegistry;
64 private InputStream inputStream ;
65
66 /**
67 * Constructs a GraphML reader around the given reader. This constructor
68 * requires the user to supply transformation functions to convert from the
69 * GraphML metadata to Graph, Vertex, Edge instances. These Function
70 * functions can be used as purely factories (i.e. the metadata is
71 * disregarded) or can use the metadata to set particular fields in the
72 * objects.
73 *
74 * @param fileReader the reader for the input GraphML document.
75 * @param graphTransformer Transformation function to convert from GraphML GraphMetadata
76 * to graph objects. This must be non-null.
77 * @param vertexTransformer Transformation function to convert from GraphML NodeMetadata
78 * to vertex objects. This must be non-null.
79 * @param edgeTransformer Transformation function to convert from GraphML EdgeMetadata
80 * to edge objects. This must be non-null.
81 * @param hyperEdgeTransformer Transformation function to convert from GraphML
82 * HyperEdgeMetadata to edge objects. This must be non-null.
83 * @throws IllegalArgumentException thrown if any of the arguments are null.
84 */
85 public GraphMLReader2(Reader fileReader,
86 Function<GraphMetadata, G> graphTransformer,
87 Function<NodeMetadata, V> vertexTransformer,
88 Function<EdgeMetadata, E> edgeTransformer,
89 Function<HyperEdgeMetadata, E> hyperEdgeTransformer) {
90
91 if (fileReader == null) {
92 throw new IllegalArgumentException(
93 "Argument fileReader must be non-null");
94 }
95
96 if (graphTransformer == null) {
97 throw new IllegalArgumentException(
98 "Argument graphTransformer must be non-null");
99 }
100
101 if (vertexTransformer == null) {
102 throw new IllegalArgumentException(
103 "Argument vertexTransformer must be non-null");
104 }
105
106 if (edgeTransformer == null) {
107 throw new IllegalArgumentException(
108 "Argument edgeTransformer must be non-null");
109 }
110
111 if (hyperEdgeTransformer == null) {
112 throw new IllegalArgumentException(
113 "Argument hyperEdgeTransformer must be non-null");
114 }
115
116 this.fileReader = fileReader;
117 this.graphTransformer = graphTransformer;
118 this.vertexTransformer = vertexTransformer;
119 this.edgeTransformer = edgeTransformer;
120 this.hyperEdgeTransformer = hyperEdgeTransformer;
121
122 // Create the parser registry.
123 this.parserRegistry = new ElementParserRegistry<G,V,E>(document.getKeyMap(),
124 graphTransformer, vertexTransformer, edgeTransformer, hyperEdgeTransformer);
125 }
126
127 /**
128 * Constructs a GraphML reader around the given reader. This constructor
129 * requires the user to supply transformation functions to convert from the
130 * GraphML metadata to Graph, Vertex, Edge instances. These Function
131 * functions can be used as purely factories (i.e. the metadata is
132 * disregarded) or can use the metadata to set particular fields in the
133 * objects.
134 *
135 * @param inputStream the inputstream for the input GraphML document.
136 * @param graphTransformer Transformation function to convert from GraphML GraphMetadata
137 * to graph objects. This must be non-null.
138 * @param vertexTransformer Transformation function to convert from GraphML NodeMetadata
139 * to vertex objects. This must be non-null.
140 * @param edgeTransformer Transformation function to convert from GraphML EdgeMetadata
141 * to edge objects. This must be non-null.
142 * @param hyperEdgeTransformer Transformation function to convert from GraphML
143 * HyperEdgeMetadata to edge objects. This must be non-null.
144 * @throws IllegalArgumentException thrown if any of the arguments are null.
145 */
146 public GraphMLReader2(InputStream inputStream,
147 Function<GraphMetadata, G> graphTransformer,
148 Function<NodeMetadata, V> vertexTransformer,
149 Function<EdgeMetadata, E> edgeTransformer,
150 Function<HyperEdgeMetadata, E> hyperEdgeTransformer) {
151
152 if (inputStream == null) {
153 throw new IllegalArgumentException(
154 "Argument inputStream must be non-null");
155 }
156
157 if (graphTransformer == null) {
158 throw new IllegalArgumentException(
159 "Argument graphTransformer must be non-null");
160 }
161
162 if (vertexTransformer == null) {
163 throw new IllegalArgumentException(
164 "Argument vertexTransformer must be non-null");
165 }
166
167 if (edgeTransformer == null) {
168 throw new IllegalArgumentException(
169 "Argument edgeTransformer must be non-null");
170 }
171
172 if (hyperEdgeTransformer == null) {
173 throw new IllegalArgumentException(
174 "Argument hyperEdgeTransformer must be non-null");
175 }
176
177 this.inputStream = inputStream;
178 this.graphTransformer = graphTransformer;
179 this.vertexTransformer = vertexTransformer;
180 this.edgeTransformer = edgeTransformer;
181 this.hyperEdgeTransformer = hyperEdgeTransformer;
182
183 // Create the parser registry.
184 this.parserRegistry = new ElementParserRegistry<G,V,E>(document.getKeyMap(),
185 graphTransformer, vertexTransformer, edgeTransformer, hyperEdgeTransformer);
186 }
187
188 /**
189 * Gets the current Function that is being used for graph objects.
190 *
191 * @return the current Function.
192 */
193 public Function<GraphMetadata, G> getGraphTransformer() {
194 return graphTransformer;
195 }
196
197 /**
198 * Gets the current Function that is being used for vertex objects.
199 *
200 * @return the current Function.
201 */
202 public Function<NodeMetadata, V> getVertexTransformer() {
203 return vertexTransformer;
204 }
205
206 /**
207 * Gets the current Function that is being used for edge objects.
208 *
209 * @return the current Function.
210 */
211 public Function<EdgeMetadata, E> getEdgeTransformer() {
212 return edgeTransformer;
213 }
214
215 /**
216 * Gets the current Function that is being used for hyperedge objects.
217 *
218 * @return the current Function.
219 */
220 public Function<HyperEdgeMetadata, E> getHyperEdgeTransformer() {
221 return hyperEdgeTransformer;
222 }
223
224 /**
225 * Verifies the object state and initializes this reader. All Function
226 * properties must be set and be non-null or a <code>GraphReaderException
227 * </code> will be thrown. This method may be called more than once.
228 * Successive calls will have no effect.
229 *
230 * @throws edu.uci.ics.jung.io.GraphIOException thrown if an error occurred.
231 */
232 public void init() throws GraphIOException {
233
234 try {
235
236 if (!initialized) {
237
238 // Create the event reader.
239 XMLInputFactory Supplier = XMLInputFactory.newInstance();
240 if(fileReader==null && inputStream != null) {
241 xmlEventReader = Supplier.createXMLEventReader(inputStream);
242 } else {
243 xmlEventReader = Supplier.createXMLEventReader(fileReader);
244 }
245 xmlEventReader = Supplier.createFilteredReader(xmlEventReader,
246 new GraphMLEventFilter());
247
248 initialized = true;
249 }
250
251 } catch( Exception e ) {
252 ExceptionConverter.convert(e);
253 }
254 }
255
256 /**
257 * Closes the GraphML reader and disposes of any resources.
258 *
259 * @throws edu.uci.ics.jung.io.GraphIOException thrown if an error occurs.
260 */
261 public void close() throws GraphIOException {
262 try {
263
264 // Clear the contents of the document.
265 document.clear();
266
267 if (xmlEventReader != null) {
268 xmlEventReader.close();
269 }
270
271 if (fileReader != null) {
272 fileReader.close();
273 }
274
275 if (inputStream != null) {
276 inputStream.close();
277 }
278
279 } catch (IOException e) {
280 throw new GraphIOException(e);
281 } catch (XMLStreamException e) {
282 throw new GraphIOException(e);
283 } finally {
284 fileReader = null;
285 inputStream = null;
286 xmlEventReader = null;
287 graphTransformer = null;
288 vertexTransformer = null;
289 edgeTransformer = null;
290 hyperEdgeTransformer = null;
291 }
292 }
293
294 /**
295 * Returns the object that contains the metadata read in from the GraphML
296 * document
297 *
298 * @return the GraphML document
299 */
300 public GraphMLDocument getGraphMLDocument() {
301 return document;
302 }
303
304 /**
305 * Reads a single graph object from the GraphML document. Automatically
306 * calls <code>init</code> to initialize the state of the reader.
307 *
308 * @return the graph that was read if one was found, otherwise null.
309 */
310 @SuppressWarnings("unchecked")
311 public G readGraph() throws GraphIOException {
312
313 try {
314
315 // Initialize if not already.
316 init();
317
318 while (xmlEventReader.hasNext()) {
319
320 XMLEvent event = xmlEventReader.nextEvent();
321 if (event.isStartElement()) {
322 StartElement element = (StartElement) event;
323 String name = element.getName().getLocalPart();
324
325 // The element should be one of: key, graph, graphml
326 if (GraphMLConstants.KEY_NAME.equals(name)) {
327
328 // Parse the key object.
329 Key key = (Key) parserRegistry.getParser(name).parse(
330 xmlEventReader, element);
331
332 // Add the key to the key map.
333 document.getKeyMap().addKey(key);
334
335 } else if (GraphMLConstants.GRAPH_NAME.equals(name)) {
336
337 // Parse the graph.
338 GraphMetadata graph = (GraphMetadata) parserRegistry
339 .getParser(name).parse(xmlEventReader, element);
340
341 // Add it to the graph metadata list.
342 document.getGraphMetadata().add(graph);
343
344 // Return the graph object.
345 return (G)graph.getGraph();
346
347 } else if (GraphMLConstants.GRAPHML_NAME.equals(name)) {
348 // Ignore the graphML object.
349 } else {
350
351 // Encounted an unknown element - just skip by it.
352 parserRegistry.getUnknownElementParser().parse(
353 xmlEventReader, element);
354 }
355
356 } else if (event.isEndDocument()) {
357 break;
358 }
359 }
360
361 } catch (Exception e) {
362 ExceptionConverter.convert(e);
363 }
364
365 // We didn't read anything from the document.
366 throw new GraphIOException("Unable to read Graph from document - the document could be empty");
367 }
368 }