View Javadoc

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 }