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