View Javadoc
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 }