001: /*
002: * (C) Copyright 2002-2003, Andy Clark. All rights reserved.
003: *
004: * This file is distributed under an Apache style license. Please
005: * refer to the LICENSE file for specific details.
006: */
007:
008: package org.cyberneko.pull.util;
009:
010: import org.cyberneko.pull.XMLEvent;
011:
012: import java.io.IOException;
013: import java.util.Locale;
014:
015: import org.apache.xerces.xni.XMLDocumentHandler;
016: import org.apache.xerces.xni.XMLDTDHandler;
017: import org.apache.xerces.xni.XMLDTDContentModelHandler;
018: import org.apache.xerces.xni.XMLLocator;
019: import org.apache.xerces.xni.XMLResourceIdentifier;
020: import org.apache.xerces.xni.XMLString;
021: import org.apache.xerces.xni.XNIException;
022: import org.apache.xerces.xni.parser.XMLConfigurationException;
023: import org.apache.xerces.xni.parser.XMLEntityResolver;
024: import org.apache.xerces.xni.parser.XMLErrorHandler;
025: import org.apache.xerces.xni.parser.XMLInputSource;
026: import org.apache.xerces.xni.parser.XMLParserConfiguration;
027: import org.apache.xerces.xni.parser.XMLPullParserConfiguration;
028:
029: /**
030: * An implementation of an XNI <code>XMLPullParserConfiguration</code>
031: * that buffers the events so that one and only callback is performed
032: * for each call to <code>parse(boolean):boolean</code>. In addition,
033: * this class can be used to turn any XNI parser configuration, even
034: * it doesn't implement <code>XMLPullParserConfiguration</code>, into
035: * a fully buffered, pull parser configuration.
036: * <p>
037: * <strong>Note:</strong>
038: * There is a performance hit to buffering the underlying XNI events.
039: * While the difference is negligible for small documents, it becomes
040: * more pronounced as the document size increases.
041: *
042: * @see EventCollector
043: * @see EventDispatcher
044: *
045: * @author Andy Clark
046: *
047: * @version $Id$
048: */
049: public class BufferedPullConfiguration implements
050: XMLPullParserConfiguration {
051:
052: //
053: // Data
054: //
055:
056: /** Parser configuration. */
057: protected XMLParserConfiguration fConfiguration;
058:
059: /** Pull parser configuration. */
060: protected XMLPullParserConfiguration fPullConfiguration;
061:
062: /** Input source. */
063: protected XMLInputSource fInputSource;
064:
065: /** Document handler. */
066: protected XMLDocumentHandler fDocumentHandler;
067:
068: /** Event collector. */
069: protected EventCollector fEventCollector;
070:
071: /** Event dispatcher. */
072: protected EventDispatcher fEventDispatcher;
073:
074: //
075: // Constructors
076: //
077:
078: /** Constructs a buffered pull parser from the specified configuration. */
079: public BufferedPullConfiguration(XMLParserConfiguration config) {
080: fConfiguration = config;
081: if (config instanceof XMLPullParserConfiguration) {
082: fPullConfiguration = (XMLPullParserConfiguration) config;
083: }
084: fEventCollector = new EventCollector();
085: fEventDispatcher = new EventDispatcher();
086: } // <init>(XMLPullParserConfiguration,EventQueue)
087:
088: //
089: // XMLPullParserConfiguration methods
090: //
091:
092: // parsing
093:
094: /**
095: * Sets the input source for the document to parse.
096: *
097: * @param inputSource The document's input source.
098: *
099: * @exception XMLConfigurationException Thrown if there is a
100: * configuration error when initializing the
101: * parser.
102: * @exception IOException Thrown on I/O error.
103: *
104: * @see #parse(boolean)
105: */
106: public void setInputSource(XMLInputSource inputSource)
107: throws XMLConfigurationException, IOException {
108: reset();
109: if (fPullConfiguration != null) {
110: fPullConfiguration.setInputSource(inputSource);
111: } else {
112: fInputSource = inputSource;
113: }
114: } // setInputSource(XMLInputSource)
115:
116: /**
117: * Parses the document in a pull parsing fashion.
118: *
119: * @param complete True if the pull parser should parse the
120: * remaining document completely.
121: *
122: * @returns True if there is more document to parse.
123: *
124: * @exception XNIException Any XNI exception, possibly wrapping
125: * another exception.
126: * @exception IOException An IO exception from the parser, possibly
127: * from a byte stream or character stream
128: * supplied by the parser.
129: *
130: * @see #setInputSource
131: */
132: public boolean parse(boolean complete) throws XNIException,
133: IOException {
134:
135: // complete parse
136: if (complete) {
137: XMLEvent event = fEventCollector.dequeue();
138: while (event != null) {
139: fEventDispatcher.dispatchEvent(event);
140: event = fEventCollector.dequeue();
141: }
142: fPullConfiguration.setDocumentHandler(fDocumentHandler);
143: if (fPullConfiguration != null) {
144: return fPullConfiguration.parse(true);
145: }
146: XMLInputSource inputSource = fInputSource;
147: if (inputSource != null) {
148: fInputSource = null;
149: }
150: fConfiguration.parse(inputSource);
151: return true;
152: }
153:
154: // buffered parse
155: XMLEvent event = fEventCollector.dequeue();
156: boolean more = true;
157: if (event == null) {
158: if (fPullConfiguration != null) {
159: while (event == null && more) {
160: more = fPullConfiguration.parse(false);
161: event = fEventCollector.dequeue();
162: }
163: } else {
164: XMLInputSource inputSource = fInputSource;
165: if (inputSource != null) {
166: fInputSource = null;
167: }
168: fConfiguration.parse(inputSource);
169: more = false;
170: }
171: }
172: if (event != null) {
173: fEventDispatcher.dispatchEvent(event);
174: }
175: return !fEventCollector.isEmpty() || more;
176:
177: } // parse(boolean):boolean
178:
179: /**
180: * If the application decides to terminate parsing before the xml document
181: * is fully parsed, the application should call this method to free any
182: * resource allocated during parsing. For example, close all opened streams.
183: */
184: public void cleanup() {
185: if (fPullConfiguration != null) {
186: fPullConfiguration.cleanup();
187: }
188: } // cleanup()
189:
190: //
191: // XMLParserConfiguration methods
192: //
193:
194: // parsing
195:
196: /**
197: * Parse an XML document.
198: * <p>
199: * The parser can use this method to instruct this configuration
200: * to begin parsing an XML document from any valid input source
201: * (a character stream, a byte stream, or a URI).
202: * <p>
203: * Parsers may not invoke this method while a parse is in progress.
204: * Once a parse is complete, the parser may then parse another XML
205: * document.
206: * <p>
207: * This method is synchronous: it will not return until parsing
208: * has ended. If a client application wants to terminate
209: * parsing early, it should throw an exception.
210: * <p>
211: * When this method returns, all characters streams and byte streams
212: * opened by the parser are closed.
213: *
214: * @param source The input source for the top-level of the
215: * XML document.
216: *
217: * @exception XNIException Any XNI exception, possibly wrapping
218: * another exception.
219: * @exception IOException An IO exception from the parser, possibly
220: * from a byte stream or character stream
221: * supplied by the parser.
222: */
223: public void parse(XMLInputSource inputSource) throws XNIException,
224: IOException {
225: setInputSource(inputSource);
226: parse(true);
227: } // parse(XMLInputSource)
228:
229: // generic configuration
230:
231: /**
232: * Allows a parser to add parser specific features to be recognized
233: * and managed by the parser configuration.
234: *
235: * @param featureIds An array of the additional feature identifiers
236: * to be recognized.
237: */
238: public void addRecognizedFeatures(String[] featureIds) {
239: fConfiguration.addRecognizedFeatures(featureIds);
240: } // addRecognizedFeatures(String[])
241:
242: /**
243: * Sets the state of a feature. This method is called by the parser
244: * and gets propagated to components in this parser configuration.
245: *
246: * @param featureId The feature identifier.
247: * @param state The state of the feature.
248: *
249: * @throws XMLConfigurationException Thrown if there is a configuration
250: * error.
251: */
252: public void setFeature(String featureId, boolean state)
253: throws XMLConfigurationException {
254: fConfiguration.setFeature(featureId, state);
255: } // setFeature(String,boolean)
256:
257: /**
258: * Returns the state of a feature.
259: *
260: * @param featureId The feature identifier.
261: *
262: * @throws XMLConfigurationException Thrown if there is a configuration
263: * error.
264: */
265: public boolean getFeature(String featureId)
266: throws XMLConfigurationException {
267: return fConfiguration.getFeature(featureId);
268: } // getFeature(String):boolean
269:
270: /**
271: * Allows a parser to add parser specific properties to be recognized
272: * and managed by the parser configuration.
273: *
274: * @param propertyIds An array of the additional property identifiers
275: * to be recognized.
276: */
277: public void addRecognizedProperties(String[] propertyIds) {
278: fConfiguration.addRecognizedProperties(propertyIds);
279: } // addRecognizedProperties(String[])
280:
281: /**
282: * Sets the value of a property. This method is called by the parser
283: * and gets propagated to components in this parser configuration.
284: *
285: * @param propertyId The property identifier.
286: * @param value The value of the property.
287: *
288: * @throws XMLConfigurationException Thrown if there is a configuration
289: * error.
290: */
291: public void setProperty(String propertyId, Object value)
292: throws XMLConfigurationException {
293: fConfiguration.setProperty(propertyId, value);
294: } // setProperty(String,Object)
295:
296: /**
297: * Returns the value of a property.
298: *
299: * @param propertyId The property identifier.
300: *
301: * @throws XMLConfigurationException Thrown if there is a configuration
302: * error.
303: */
304: public Object getProperty(String propertyId)
305: throws XMLConfigurationException {
306: return fConfiguration.getProperty(propertyId);
307: } // getProperty(String):Object
308:
309: // handlers
310:
311: /**
312: * Sets the error handler.
313: *
314: * @param errorHandler The error resolver.
315: */
316: public void setErrorHandler(XMLErrorHandler errorHandler) {
317: fConfiguration.setErrorHandler(errorHandler);
318: } // setErrorHandler(XMLErrorHandler)
319:
320: /** Returns the registered error handler. */
321: public XMLErrorHandler getErrorHandler() {
322: return fConfiguration.getErrorHandler();
323: } // getErrorHandler():XMLErrorHandler
324:
325: /**
326: * Sets the document handler to receive information about the document.
327: *
328: * @param documentHandler The document handler.
329: */
330: public void setDocumentHandler(XMLDocumentHandler documentHandler) {
331: fDocumentHandler = documentHandler;
332: } // setDocumentHandler(XMLDocumentHandler)
333:
334: /** Returns the registered document handler. */
335: public XMLDocumentHandler getDocumentHandler() {
336: return fDocumentHandler;
337: } // getDocumentHandler():XMLDocumentHandler
338:
339: /**
340: * Sets the DTD handler.
341: *
342: * @param dtdHandler The DTD handler.
343: */
344: public void setDTDHandler(XMLDTDHandler dtdHandler) {
345: fConfiguration.setDTDHandler(dtdHandler);
346: } // setDTDHandler(XMLDTDHandler)
347:
348: /** Returns the registered DTD handler. */
349: public XMLDTDHandler getDTDHandler() {
350: return fConfiguration.getDTDHandler();
351: } // getDTDHandler():XMLDTDHandler
352:
353: /**
354: * Sets the DTD content model handler.
355: *
356: * @param dtdContentModelHandler The DTD content model handler.
357: */
358: public void setDTDContentModelHandler(
359: XMLDTDContentModelHandler dtdContentModelHandler) {
360: fConfiguration
361: .setDTDContentModelHandler(dtdContentModelHandler);
362: } // setDTDContentModelHandler(XMLDTDContentModelHandler)
363:
364: /** Returns the registered DTD content model handler. */
365: public XMLDTDContentModelHandler getDTDContentModelHandler() {
366: return fConfiguration.getDTDContentModelHandler();
367: } // getDTDContentModelHandler():XMLDTDContentModelHandler
368:
369: // other settings
370:
371: /**
372: * Sets the entity resolver.
373: *
374: * @param entityResolver The new entity resolver.
375: */
376: public void setEntityResolver(XMLEntityResolver entityResolver) {
377: fConfiguration.setEntityResolver(entityResolver);
378: } // setEntityResolver():XMLEntityResolver
379:
380: /** Returns the registered entity resolver. */
381: public XMLEntityResolver getEntityResolver() {
382: return fConfiguration.getEntityResolver();
383: } // getEntityResolver():XMLEntityResolver
384:
385: /**
386: * Set the locale to use for messages.
387: *
388: * @param locale The locale object to use for localization of messages.
389: *
390: * @exception XNIException Thrown if the parser does not support the
391: * specified locale.
392: */
393: public void setLocale(Locale locale) throws XNIException {
394: fConfiguration.setLocale(locale);
395: } // setLocale():Locale
396:
397: /** Returns the locale. */
398: public Locale getLocale() {
399: return fConfiguration.getLocale();
400: } // getLocale():Locale
401:
402: //
403: // Protected methods
404: //
405:
406: protected void reset() throws XMLConfigurationException {
407: fEventCollector.clear();
408: fConfiguration.setDocumentHandler(fEventCollector);
409: fEventDispatcher.setDocumentHandler(fDocumentHandler);
410: } // reset()
411:
412: } // class BufferedPullConfiguration
|