001: /*
002: Copyright (c) 2005-2006, Dennis M. Sosnoski.
003: All rights reserved.
004:
005: Redistribution and use in source and binary forms, with or without modification,
006: are permitted provided that the following conditions are met:
007:
008: * Redistributions of source code must retain the above copyright notice, this
009: list of conditions and the following disclaimer.
010: * Redistributions in binary form must reproduce the above copyright notice,
011: this list of conditions and the following disclaimer in the documentation
012: and/or other materials provided with the distribution.
013: * Neither the name of JiBX nor the names of its contributors may be used
014: to endorse or promote products derived from this software without specific
015: prior written permission.
016:
017: THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
018: ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
019: WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
020: DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
021: ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
022: (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
023: LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
024: ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
025: (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
026: SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
027: */
028:
029: package org.jibx.runtime.impl;
030:
031: import java.io.IOException;
032: import java.io.InputStream;
033: import java.io.Reader;
034:
035: import org.jibx.runtime.IXMLReader;
036: import org.jibx.runtime.JiBXException;
037: import org.xmlpull.v1.XmlPullParser;
038: import org.xmlpull.v1.XmlPullParserException;
039: import org.xmlpull.v1.XmlPullParserFactory;
040:
041: /**
042: * Factory for creating XMLPull parser instances.
043: *
044: * @author Dennis M. Sosnoski
045: * @version 1.0
046: */
047: public class XMLPullReaderFactory implements IXMLReaderFactory {
048: /** Default parser factory name when nothing else found. */
049: private static final String DEFAULT_PARSER_NAME = "org.xmlpull.mxp1.MXParserFactory";
050:
051: /** Singleton instance of class. */
052: private static final XMLPullReaderFactory s_instance;
053: static {
054:
055: // first try getting factory defined by property value
056: XmlPullParserFactory factory = null;
057: ClassLoader loader = Thread.currentThread()
058: .getContextClassLoader();
059: if (loader == null) {
060: loader = XMLPullReaderFactory.class.getClassLoader();
061: }
062: try {
063: String name = System
064: .getProperty(XmlPullParserFactory.PROPERTY_NAME);
065: if (name != null && (name = name.trim()).length() > 0) {
066: factory = XmlPullParserFactory.newInstance(name, loader
067: .getClass());
068: }
069: } catch (Exception ex) { /* deliberately empty */
070: }
071:
072: // if no luck that way, try getting it directly
073: if (factory == null) {
074: try {
075: factory = XmlPullParserFactory.newInstance();
076: } catch (Exception ex) { /* deliberately empty */
077: }
078: if (factory == null) {
079: throw new RuntimeException(
080: "Unable to create XMLPull parser");
081: }
082: }
083: s_instance = new XMLPullReaderFactory(factory);
084: }
085:
086: /** Factory used for constructing parser instances. */
087: private final XmlPullParserFactory m_factory;
088:
089: /**
090: * Internal constructor.
091: */
092: private XMLPullReaderFactory(XmlPullParserFactory factory) {
093: m_factory = factory;
094: }
095:
096: /**
097: * Get instance of factory.
098: *
099: * @return factory instance
100: */
101: public static XMLPullReaderFactory getInstance() {
102: return s_instance;
103: }
104:
105: /**
106: * Create new parser instance.
107: *
108: * @param nsf enable namespace processing on parser flag
109: * @return parser instance
110: * @throws XmlPullParserException on error creating parser
111: */
112: private XmlPullParser createParser(boolean nsf)
113: throws XmlPullParserException {
114: XmlPullParser parser = m_factory.newPullParser();
115: if (nsf) {
116: parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES,
117: true);
118: }
119: return parser;
120: }
121:
122: /* (non-Javadoc)
123: * @see org.jibx.runtime.impl.IXMLReaderFactory#createReader(java.io.InputStream, java.lang.String, java.lang.String, boolean)
124: */
125: public IXMLReader createReader(InputStream is, String name,
126: String enc, boolean nsf) throws JiBXException {
127: try {
128: return recycleReader(new XMLPullReader(createParser(nsf)),
129: is, name, enc);
130: } catch (XmlPullParserException e) {
131: throw new JiBXException("Error creating parser", e);
132: }
133: }
134:
135: /* (non-Javadoc)
136: * @see org.jibx.runtime.impl.IXMLReaderFactory#createReader(java.io.Reader, java.lang.String, boolean)
137: */
138: public IXMLReader createReader(Reader rdr, String name, boolean nsf)
139: throws JiBXException {
140: try {
141: return recycleReader(new XMLPullReader(createParser(nsf)),
142: rdr, name);
143: } catch (XmlPullParserException e) {
144: throw new JiBXException("Error creating parser", e);
145: }
146: }
147:
148: /* (non-Javadoc)
149: * @see org.jibx.runtime.impl.IXMLReaderFactory#recycleReader(org.jibx.runtime.IXMLReader, java.io.InputStream, java.lang.String, java.lang.String)
150: */
151: public IXMLReader recycleReader(IXMLReader old, InputStream is,
152: String name, String enc) throws JiBXException {
153: ((XMLPullReader) old).setDocument(is, name, enc);
154: return old;
155: }
156:
157: /* (non-Javadoc)
158: * @see org.jibx.runtime.impl.IXMLReaderFactory#recycleReader(org.jibx.runtime.IXMLReader, java.io.Reader, java.lang.String)
159: */
160: public IXMLReader recycleReader(IXMLReader old, Reader rdr,
161: String name) throws JiBXException {
162: ((XMLPullReader) old).setDocument(rdr, name);
163: return old;
164: }
165:
166: /**
167: * Wrapper for an XMLPull parser implementation. Since the internal parser
168: * API was originally based on XMLPull, this basically just delegates all
169: * the calls with minimal processing.
170: */
171: private static class XMLPullReader implements IXMLReader {
172: /** Actual parser. */
173: private final XmlPullParser m_parser;
174:
175: /** Document name. */
176: private String m_docName;
177:
178: /** Wrapper for supplied input stream. */
179: private InputStreamWrapper m_streamWrapper;
180:
181: /** Input document character encoding (<code>null</code> if unknown) */
182: private String m_encoding;
183:
184: /**
185: * Constructor used by factory.
186: *
187: * @param parser
188: */
189: private XMLPullReader(XmlPullParser parser) {
190: m_parser = parser;
191: }
192:
193: /**
194: * Set document to be parsed from input stream.
195: *
196: * @param is document input stream
197: * @param name document name (<code>null</code> if unknown)
198: * @param enc document character encoding (<code>null</code> if unknown)
199: * @throws JiBXException on parser configuration error
200: */
201: private void setDocument(InputStream is, String name, String enc)
202: throws JiBXException {
203: try {
204: if (enc == null) {
205: if (m_streamWrapper == null) {
206: m_streamWrapper = new InputStreamWrapper();
207: }
208: m_streamWrapper.setInput(is, enc);
209: setDocument(m_streamWrapper.getReader(), name);
210: m_encoding = m_streamWrapper.getEncoding();
211: } else {
212: m_docName = name;
213: m_encoding = enc;
214: m_parser.setInput(is, enc);
215: }
216: } catch (XmlPullParserException e) {
217: throw new JiBXException("Error initializing parser", e);
218: } catch (IOException e) {
219: throw new JiBXException("Error reading from stream", e);
220: }
221: }
222:
223: /**
224: * Set document to be parsed from reader.
225: *
226: * @param rdr document reader
227: * @param name document name (<code>null</code> if unknown)
228: * @throws JiBXException on parser configuration error
229: */
230: private void setDocument(Reader rdr, String name)
231: throws JiBXException {
232: try {
233: m_docName = name;
234: m_encoding = null;
235: m_parser.setInput(rdr);
236: } catch (XmlPullParserException e) {
237: throw new JiBXException("Error initializing parser", e);
238: }
239: }
240:
241: /**
242: * Format error message from exception.
243: *
244: * @param e root cause exception
245: */
246: private String describeException(Exception e) {
247: return "Error parsing document " + buildPositionString()
248: + ": " + e.getMessage();
249: }
250:
251: /* (non-Javadoc)
252: * @see org.jibx.runtime.IXMLReader#buildPositionString()
253: */
254: public String buildPositionString() {
255: String base = "(line " + m_parser.getLineNumber()
256: + ", col " + m_parser.getColumnNumber();
257: if (m_docName != null) {
258: base += ", in " + m_docName;
259: }
260: return base + ')';
261: }
262:
263: /* (non-Javadoc)
264: * @see org.jibx.runtime.IXMLReader#nextToken()
265: */
266: public int nextToken() throws JiBXException {
267: try {
268: return m_parser.nextToken();
269: } catch (IOException e) {
270: throw new JiBXException("Error accessing document", e);
271: } catch (XmlPullParserException e) {
272: throw new JiBXException("Error parsing document "
273: + buildPositionString(), e);
274: }
275: }
276:
277: /* (non-Javadoc)
278: * @see org.jibx.runtime.IXMLReader#next()
279: */
280: public int next() throws JiBXException {
281: try {
282: return m_parser.next();
283: } catch (IOException e) {
284: throw new JiBXException("Error accessing document", e);
285: } catch (XmlPullParserException e) {
286: throw new JiBXException("Error parsing document "
287: + buildPositionString(), e);
288: }
289: }
290:
291: /* (non-Javadoc)
292: * @see org.jibx.runtime.IXMLReader#getEventType()
293: */
294: public int getEventType() throws JiBXException {
295: try {
296: return m_parser.getEventType();
297: } catch (XmlPullParserException e) {
298: throw new JiBXException("Error parsing document "
299: + buildPositionString(), e);
300: }
301: }
302:
303: /* (non-Javadoc)
304: * @see org.jibx.runtime.IXMLReader#getName()
305: */
306: public String getName() {
307: String name = m_parser.getName();
308: if (name == null) {
309: throw new IllegalStateException(
310: "Internal state error: not at start or end tag");
311: } else {
312: return name;
313: }
314: }
315:
316: /* (non-Javadoc)
317: * @see org.jibx.runtime.IXMLReader#getNamespace()
318: */
319: public String getNamespace() {
320: String uri = m_parser.getNamespace();
321: if (uri == null) {
322: throw new IllegalStateException(
323: "Internal state error: not at start or end tag");
324: } else {
325: return uri;
326: }
327: }
328:
329: /* (non-Javadoc)
330: * @see org.jibx.runtime.IXMLReader#getPrefix()
331: */
332: public String getPrefix() {
333: return m_parser.getPrefix();
334: }
335:
336: /* (non-Javadoc)
337: * @see org.jibx.runtime.IXMLReader#getAttributeCount()
338: */
339: public int getAttributeCount() {
340: int count = m_parser.getAttributeCount();
341: if (count < 0) {
342: throw new IllegalStateException(
343: "Internal state error: not at start tag");
344: } else {
345: return count;
346: }
347: }
348:
349: /* (non-Javadoc)
350: * @see org.jibx.runtime.IXMLReader#getAttributeName(int)
351: */
352: public String getAttributeName(int index) {
353: try {
354: return m_parser.getAttributeName(index);
355: } catch (ArrayIndexOutOfBoundsException e) {
356: throw new IllegalStateException(describeException(e));
357: }
358: }
359:
360: /* (non-Javadoc)
361: * @see org.jibx.runtime.IXMLReader#getAttributeNamespace(int)
362: */
363: public String getAttributeNamespace(int index) {
364: try {
365: return m_parser.getAttributeNamespace(index);
366: } catch (ArrayIndexOutOfBoundsException e) {
367: throw new IllegalStateException(describeException(e));
368: }
369: }
370:
371: /* (non-Javadoc)
372: * @see org.jibx.runtime.IXMLReader#getAttributePrefix(int)
373: */
374: public String getAttributePrefix(int index) {
375: try {
376: return m_parser.getAttributePrefix(index);
377: } catch (ArrayIndexOutOfBoundsException e) {
378: throw new IllegalStateException(describeException(e));
379: }
380: }
381:
382: /* (non-Javadoc)
383: * @see org.jibx.runtime.IXMLReader#getAttributeValue(int)
384: */
385: public String getAttributeValue(int index) {
386: try {
387: return m_parser.getAttributeValue(index);
388: } catch (ArrayIndexOutOfBoundsException e) {
389: throw new IllegalStateException(describeException(e));
390: }
391: }
392:
393: /* (non-Javadoc)
394: * @see org.jibx.runtime.IXMLReader#getAttributeValue(java.lang.String, java.lang.String)
395: */
396: public String getAttributeValue(String ns, String name) {
397: try {
398: return m_parser.getAttributeValue(ns, name);
399: } catch (ArrayIndexOutOfBoundsException e) {
400: throw new IllegalStateException(describeException(e));
401: }
402: }
403:
404: /* (non-Javadoc)
405: * @see org.jibx.runtime.IXMLReader#getText()
406: */
407: public String getText() {
408: return m_parser.getText();
409: }
410:
411: /* (non-Javadoc)
412: * @see org.jibx.runtime.IXMLReader#getNestingDepth()
413: */
414: public int getNestingDepth() {
415: return m_parser.getDepth();
416: }
417:
418: /* (non-Javadoc)
419: * @see org.jibx.runtime.IXMLReader#getNamespaceCount(int)
420: */
421: public int getNamespaceCount(int depth) {
422: try {
423: return m_parser.getNamespaceCount(depth);
424: } catch (XmlPullParserException e) {
425: throw new IllegalArgumentException(describeException(e));
426: }
427: }
428:
429: /* (non-Javadoc)
430: * @see org.jibx.runtime.IXMLReader#getNamespaceUri(int)
431: */
432: public String getNamespaceUri(int index) {
433: try {
434: return m_parser.getNamespaceUri(index);
435: } catch (XmlPullParserException e) {
436: throw new IllegalArgumentException(describeException(e));
437: }
438: }
439:
440: /* (non-Javadoc)
441: * @see org.jibx.runtime.IXMLReader#getNamespacePrefix(int)
442: */
443: public String getNamespacePrefix(int index) {
444: try {
445: return m_parser.getNamespacePrefix(index);
446: } catch (XmlPullParserException e) {
447: throw new IllegalArgumentException(describeException(e));
448: }
449: }
450:
451: /* (non-Javadoc)
452: * @see org.jibx.runtime.IXMLReader#getDocumentName()
453: */
454: public String getDocumentName() {
455: return m_docName;
456: }
457:
458: /* (non-Javadoc)
459: * @see org.jibx.runtime.IXMLReader#getLineNumber()
460: */
461: public int getLineNumber() {
462: return m_parser.getLineNumber();
463: }
464:
465: /* (non-Javadoc)
466: * @see org.jibx.runtime.IXMLReader#getColumnNumber()
467: */
468: public int getColumnNumber() {
469: return m_parser.getColumnNumber();
470: }
471:
472: /* (non-Javadoc)
473: * @see org.jibx.runtime.IXMLReader#getNamespace(java.lang.String)
474: */
475: public String getNamespace(String prefix) {
476: return m_parser.getNamespace(prefix);
477: }
478:
479: /* (non-Javadoc)
480: * @see org.jibx.runtime.IXMLReader#getInputEncoding()
481: */
482: public String getInputEncoding() {
483: return m_encoding;
484: }
485:
486: /* (non-Javadoc)
487: * @see org.jibx.runtime.IXMLReader#isNamespaceAware()
488: */
489: public boolean isNamespaceAware() {
490: return m_parser
491: .getFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES);
492: }
493: }
494: }
|