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