001: /*
002: * GeoTools - OpenSource mapping toolkit
003: * http://geotools.org
004: * (C) 2002-2006, GeoTools Project Managment Committee (PMC)
005: *
006: * This library is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU Lesser General Public
008: * License as published by the Free Software Foundation;
009: * version 2.1 of the License.
010: *
011: * This library is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * Lesser General Public License for more details.
015: */
016: package org.geotools.xml;
017:
018: import java.io.InputStream;
019:
020: import javax.xml.namespace.QName;
021: import javax.xml.parsers.ParserConfigurationException;
022: import javax.xml.parsers.SAXParser;
023: import javax.xml.parsers.SAXParserFactory;
024:
025: import org.geotools.xml.impl.ElementNameStreamingParserHandler;
026: import org.geotools.xml.impl.StreamingParserHandler;
027: import org.geotools.xml.impl.TypeStreamingParserHandler;
028: import org.geotools.xml.impl.jxpath.JXPathStreamingParserHandler;
029: import org.xml.sax.SAXException;
030:
031: /**
032: * XML parser capable of streaming.
033: * <p>
034: * Performs the same task as {@link org.geotools.xml.Parser}, with the addition
035: * that objects are streamed back to the client. Streaming can occur in a
036: * number of different modes.
037: * </p>
038: * <p>
039: * As an example consider the following gml document:
040: * <pre>
041: * <test:TestFeatureCollection xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
042: * xmlns:gml="http://www.opengis.net/gml"
043: * xmlns:test="http://www.geotools.org/test"
044: * xsi:schemaLocation="http://www.geotools.org/test test.xsd">
045: *
046: * <gml:featureMember>
047: * <test:TestFeature fid="0">
048: * ...
049: * </test:TestFeature>
050: * </gml:featureMember>
051: *
052: * <gml:featureMember>
053: * <test:TestFeature fid="1">
054: * ...
055: * </test:TestFeature>
056: * </gml:featureMember>
057: *
058: * <gml:featureMember>
059: * <test:TestFeature fid="2">
060: * ....
061: * </test:TestFeature>
062: * </gml:featureMember>
063: *
064: * </test:TestFeatureCollection>
065: * </pre>
066: * And suppose we want to stream back each feature as it is parsed.
067: * </p>
068: * <p>
069: * <h3>1. Element Name</h3>
070: * Objects are streamed back when an element of a particular name has been
071: * parsed.
072: * <pre>
073: * Configuration configuration = new GMLConfiguration();
074: * QName elementName = new QName( "http://www.geotools.org/test", "TestFeature" );
075: *
076: * StreamingParser parser = new StreamingParser( configuration, elementName );
077: *
078: * Feature f = null;
079: * while ( ( f = parser.parse() ) != null ) {
080: * ...
081: * }
082: * </pre>
083: * </p>
084: * <p>
085: * <h3>2. Type</h3>
086: * Objects are streamed back when an element has been parsed into an object
087: * of a particular type.
088: * <pre>
089: * Configuration configuration = new GMLConfiguration();
090: * StreamingParser parser = new StreamingParser( configuration, Feature.class );
091: *
092: * Feature f = null;
093: * while ( ( f = parser.parse() ) != null ) {
094: * ...
095: * }
096: * </pre>
097: * </p>
098: * <p>
099: * <h3>3. Xpath Expression</h3>
100: * Objects are streamed back when an element has been parsed which matches
101: * a particular xpath expression.
102: * <pre>
103: * Configuration configuration = new GMLConfiguration();
104: * String xpath = "//TestFeature";
105: * StreamingParser parser = new StreamingParser( configuration, xpath );
106: *
107: * Feature f = null;
108: * while ( ( f = parser.parse() ) != null ) {
109: * ...
110: * }
111: * </pre>
112: * </p>
113: *
114: * @author Justin Deoliveira, The Open Planning Project
115: */
116: public class StreamingParser {
117: /**
118: * The sax driver / handler.
119: */
120: private StreamingParserHandler handler;
121: /**
122: * The sax parser.
123: */
124: private SAXParser parser;
125: /**
126: * The xml input.
127: */
128: private InputStream input;
129: /**
130: * The parsing thread.
131: */
132: private Thread thread;
133:
134: /**
135: * Creates a new instance of the type based streaming parser.
136: *
137: * @param configuration Object representing the configuration of the parser.
138: * @param input The input stream representing the instance document to be parsed.
139: * @param type The type of parsed objects to stream back.
140: *
141: * @throws ParserConfigurationException
142: * @throws SAXException
143: */
144: public StreamingParser(Configuration configuration,
145: InputStream input, Class type)
146: throws ParserConfigurationException, SAXException {
147:
148: this (configuration, input, new TypeStreamingParserHandler(
149: configuration, type));
150: }
151:
152: /**
153: * Creates a new instance of the element name based streaming parser.
154: *
155: * @param configuration Object representing the configuration of the parser.
156: * @param input The input stream representing the instance document to be parsed.
157: * @param elementName The name of elements to stream back.
158: *
159: * @throws ParserConfigurationException
160: * @throws SAXException
161: */
162: public StreamingParser(Configuration configuration,
163: InputStream input, QName elementName)
164: throws ParserConfigurationException, SAXException {
165:
166: this (configuration, input,
167: new ElementNameStreamingParserHandler(configuration,
168: elementName));
169: }
170:
171: /**
172: * Creates a new instance of the xpath based streaming parser.
173: *
174: * @param configuration Object representing the configuration of the parser.
175: * @param input The input stream representing the instance document to be parsed.
176: * @param xpath An xpath expression which dictates how the parser streams
177: * objects back to the client.
178: *
179: * @throws ParserConfigurationException
180: * @throws SAXException
181: */
182: public StreamingParser(Configuration configuration,
183: InputStream input, String xpath)
184: throws ParserConfigurationException, SAXException {
185:
186: this (configuration, input, new JXPathStreamingParserHandler(
187: configuration, xpath));
188: }
189:
190: /**
191: * Internal constructor.
192: */
193: protected StreamingParser(Configuration configuration,
194: InputStream input, StreamingParserHandler handler)
195: throws ParserConfigurationException, SAXException {
196:
197: SAXParserFactory spf = SAXParserFactory.newInstance();
198: spf.setNamespaceAware(true);
199: parser = spf.newSAXParser();
200:
201: this .handler = handler;
202: this .input = input;
203: }
204:
205: /**
206: * Streams the parser to the next element in the instance document which
207: * matches the xpath query specified in the contstructor. This method
208: * returns null when there are no more objects to stream.
209: *
210: * @return The next object in the stream, or null if no such object is
211: * available.
212: */
213: public Object parse() {
214:
215: if (thread == null) {
216: Runnable runnable = new Runnable() {
217: public void run() {
218: try {
219: parser.parse(input, handler);
220: } catch (Exception e) {
221: //close the buffer
222: handler.getBuffer().close();
223: throw new RuntimeException(e);
224: }
225: };
226: };
227:
228: thread = new Thread(runnable);
229: thread.start();
230: }
231:
232: return handler.getBuffer().get();
233: }
234: }
|