001: /*
002: * Javolution - Java(TM) Solution for Real-Time and Embedded Systems
003: * Copyright (C) 2005 - Javolution (http://javolution.org/)
004: * All rights reserved.
005: *
006: * Permission to use, copy, modify, and distribute this software is
007: * freely granted, provided that this notice is preserved.
008: */
009: package javolution.xml.sax;
010:
011: import j2me.lang.CharSequence;
012: import j2me.lang.UnsupportedOperationException;
013:
014: import java.io.IOException;
015: import java.io.InputStream;
016: import java.io.Reader;
017:
018: import javolution.lang.Reflection;
019: import javolution.lang.Reusable;
020: import javolution.text.CharArray;
021: import javolution.xml.stream.XMLStreamConstants;
022: import javolution.xml.stream.XMLStreamException;
023: import javolution.xml.stream.XMLStreamReaderImpl;
024:
025: import org.xml.sax.DTDHandler;
026: import org.xml.sax.EntityResolver;
027: import org.xml.sax.ErrorHandler;
028: import org.xml.sax.InputSource;
029: import org.xml.sax.SAXException;
030: import org.xml.sax.SAXNotRecognizedException;
031: import org.xml.sax.SAXNotSupportedException;
032:
033: /**
034: * <p> This class provides a real-time SAX2-like XML parser; this parser is
035: * <i>extremely</i> fast and <b>does not create temporary objects</b>
036: * (no garbage generated and no GC interruption).</p>
037: *
038: * <p> The parser is implemented as a SAX2 wrapper around
039: * {@link XMLStreamReaderImpl} and share the same characteristics.</p>
040: *
041: * <p><i> Note: This parser is a <b>SAX2-like</b> parser with the
042: * <code>java.lang.String</code> type replaced by
043: * {@link CharArray}/{@link CharSequence} in the {@link ContentHandler},
044: * {@link Attributes} interfaces and {@link DefaultHandler} base class.
045: * If a standard SAX2 or JAXP parser is required, you may consider using
046: * the wrapping class {@link SAX2ReaderImpl}. Fast but not as fast as
047: * <code>java.lang.String</code> instances are dynamically allocated
048: * while parsing.</i></p>
049: *
050: * @author <a href="mailto:jean-marie@dautelle.com">Jean-Marie Dautelle</a>
051: * @version 4.0, June 16, 2006
052: */
053: public class XMLReaderImpl implements XMLReader, Reusable {
054:
055: /**
056: * Holds the default handler instance.
057: */
058: private static DefaultHandler DEFAULT_HANDLER = new DefaultHandler();
059:
060: /**
061: * Holds the content handler.
062: */
063: private ContentHandler _contentHandler;
064:
065: /**
066: * Holds the error handler.
067: */
068: private ErrorHandler _errorHandler;
069:
070: /**
071: * Holds reusable StAX reader.
072: */
073: private final XMLStreamReaderImpl _xmlReader = new XMLStreamReaderImpl();
074:
075: /**
076: * Default constructor.
077: */
078: public XMLReaderImpl() {
079: // Sets default handlers.
080: setContentHandler(DEFAULT_HANDLER);
081: setErrorHandler(DEFAULT_HANDLER);
082: }
083:
084: /**
085: * Parses an XML document from the specified input stream
086: * (encoding retrieved from input source and the XML prolog if any).
087: *
088: * @param in the input stream with unknown encoding.
089: * @throws org.xml.sax.SAXException any SAX exception, possibly
090: * wrapping another exception.
091: * @throws IOException an IO exception from the parser,
092: * possibly from a byte stream or character stream
093: * supplied by the application.
094: */
095: public void parse(InputStream in) throws IOException, SAXException {
096: try {
097: _xmlReader.setInput(in);
098: parseAll();
099: } catch (XMLStreamException e) {
100: if (e.getNestedException() instanceof IOException)
101: throw (IOException) e.getNestedException();
102: throw new SAXException(e);
103: } finally {
104: _xmlReader.reset();
105: }
106: }
107:
108: /**
109: * Parses an XML document from the specified input stream and encoding.
110: *
111: * @param in the input stream.
112: * @param encoding the input stream encoding.
113: * @throws org.xml.sax.SAXException any SAX exception, possibly
114: * wrapping another exception.
115: * @throws IOException an IO exception from the parser,
116: * possibly from a byte stream or character stream
117: * supplied by the application.
118: */
119: public void parse(InputStream in, String encoding)
120: throws IOException, SAXException {
121: try {
122: _xmlReader.setInput(in, encoding);
123: parseAll();
124: } catch (XMLStreamException e) {
125: if (e.getNestedException() instanceof IOException)
126: throw (IOException) e.getNestedException();
127: throw new SAXException(e);
128: } finally {
129: _xmlReader.reset();
130: }
131: }
132:
133: /**
134: * Parses an XML document using the specified reader.
135: *
136: * @param reader the document reader.
137: * @throws SAXException any SAX exception, possibly wrapping another
138: * exception.
139: * @throws IOException an IO exception from the parser, possibly from
140: * a byte stream or character stream supplied by the application.
141: * @see javolution.io.UTF8StreamReader
142: * @see javolution.io.UTF8ByteBufferReader
143: * @see javolution.io.CharSequenceReader
144: */
145: public void parse(Reader reader) throws IOException, SAXException {
146: try {
147: _xmlReader.setInput(reader);
148: parseAll();
149: } catch (XMLStreamException e) {
150: if (e.getNestedException() instanceof IOException)
151: throw (IOException) e.getNestedException();
152: throw new SAXException(e);
153: } finally {
154: _xmlReader.reset();
155: }
156: }
157:
158: // Implements XMLReader interface.
159: public void parse(InputSource input) throws IOException,
160: SAXException {
161: Reader reader = input.getCharacterStream();
162: if (reader != null) {
163: parse(reader);
164: } else {
165: InputStream inStream = input.getByteStream();
166: if (inStream != null) {
167: parse(inStream, input.getEncoding());
168: } else {
169: parse(input.getSystemId());
170: }
171: }
172: }
173:
174: // Implements XMLReader interface.
175: public void parse(String systemId) throws IOException, SAXException {
176: InputStream inStream;
177: try {
178: Object url = NEW_URL.newInstance(systemId);
179: inStream = (InputStream) OPEN_STREAM.invoke(url);
180: } catch (Exception urlException) { // Try as filename.
181: try {
182: inStream = (InputStream) NEW_FILE_INPUT_STREAM
183: .newInstance(systemId);
184: } catch (Exception fileException) {
185: throw new UnsupportedOperationException("Cannot parse "
186: + systemId);
187: }
188: }
189: parse(inStream);
190: }
191:
192: private static final Reflection.Constructor NEW_URL = Reflection
193: .getConstructor("java.net.URL(j2me.lang.String)");
194:
195: private static final Reflection.Method OPEN_STREAM = Reflection
196: .getMethod("java.net.URL.openStream()");
197:
198: private static final Reflection.Constructor NEW_FILE_INPUT_STREAM = Reflection
199: .getConstructor("j2me.io.FileInputStream(j2me.lang.String)");
200:
201: // Implements XMLReader interface.
202: public void setContentHandler(ContentHandler handler) {
203: if (handler != null) {
204: _contentHandler = handler;
205: } else {
206: throw new NullPointerException();
207: }
208: }
209:
210: // Implements XMLReader interface.
211: public ContentHandler getContentHandler() {
212: return (_contentHandler == DEFAULT_HANDLER) ? null
213: : _contentHandler;
214: }
215:
216: // Implements XMLReader interface.
217: public void setErrorHandler(ErrorHandler handler) {
218: if (handler != null) {
219: _errorHandler = handler;
220: } else {
221: throw new NullPointerException();
222: }
223: }
224:
225: // Implements XMLReader interface.
226: public ErrorHandler getErrorHandler() {
227: return (_errorHandler == DEFAULT_HANDLER) ? null
228: : _errorHandler;
229: }
230:
231: // Implements XMLReader interface.
232: public boolean getFeature(String name)
233: throws SAXNotRecognizedException, SAXNotSupportedException {
234: if (name.equals("http://xml.org/sax/features/namespaces")) {
235: return true;
236: } else if (name
237: .equals("http://xml.org/sax/features/namespace-prefixes")) {
238: return true;
239: } else {
240: throw new SAXNotRecognizedException("Feature " + name
241: + " not recognized");
242: }
243: }
244:
245: public void setFeature(String name, boolean value)
246: throws SAXNotRecognizedException, SAXNotSupportedException {
247: if (name.equals("http://xml.org/sax/features/namespaces")
248: || name
249: .equals("http://xml.org/sax/features/namespace-prefixes")) {
250: return; // Ignores, these features are always set.
251: } else {
252: throw new SAXNotRecognizedException("Feature " + name
253: + " not recognized");
254: }
255: }
256:
257: public Object getProperty(String name)
258: throws SAXNotRecognizedException, SAXNotSupportedException {
259: throw new SAXNotRecognizedException("Property " + name
260: + " not recognized");
261: }
262:
263: public void setProperty(String name, Object value)
264: throws SAXNotRecognizedException, SAXNotSupportedException {
265: throw new SAXNotRecognizedException("Property " + name
266: + " not recognized");
267: }
268:
269: public void setEntityResolver(EntityResolver resolver) {
270: _entityResolver = resolver;
271: }
272:
273: private EntityResolver _entityResolver;
274:
275: public EntityResolver getEntityResolver() {
276: return _entityResolver;
277: }
278:
279: public void setDTDHandler(DTDHandler handler) {
280: _dtdHandler = handler;
281: }
282:
283: private DTDHandler _dtdHandler;
284:
285: public DTDHandler getDTDHandler() {
286: return _dtdHandler;
287: }
288:
289: // Implements Reusable.
290: public void reset() {
291: setContentHandler(DEFAULT_HANDLER);
292: setErrorHandler(DEFAULT_HANDLER);
293: _xmlReader.reset();
294: }
295:
296: /**
297: * Parses the whole document using the real-time pull parser.
298: *
299: * @throws SAXException any SAX exception, possibly wrapping another
300: * exception.
301: * @throws IOException an IO exception from the parser, possibly from
302: * a byte stream or character stream supplied by the application.
303: */
304: private void parseAll() throws XMLStreamException, SAXException {
305: int eventType = _xmlReader.getEventType();
306: if (eventType != XMLStreamConstants.START_DOCUMENT)
307: throw new SAXException("Currently parsing");
308: _contentHandler.startDocument();
309:
310: boolean doContinue = true;
311: while (doContinue) {
312: CharArray uri, localName, qName, prefix, text;
313: switch (_xmlReader.next()) {
314: case XMLStreamConstants.START_ELEMENT:
315:
316: // Start prefix mapping.
317: for (int i = 0, count = _xmlReader.getNamespaceCount(); i < count; i++) {
318: prefix = _xmlReader.getNamespacePrefix(i);
319: prefix = (prefix == null) ? NO_CHAR : prefix; // Default namespace is ""
320: uri = _xmlReader.getNamespaceURI(i);
321: _contentHandler.startPrefixMapping(prefix, uri);
322: }
323:
324: // Start element.
325: uri = _xmlReader.getNamespaceURI();
326: uri = (uri == null) ? NO_CHAR : uri;
327: localName = _xmlReader.getLocalName();
328: qName = _xmlReader.getQName();
329: _contentHandler.startElement(uri, localName, qName,
330: _xmlReader.getAttributes());
331: break;
332:
333: case XMLStreamConstants.END_ELEMENT:
334:
335: // End element.
336: uri = _xmlReader.getNamespaceURI();
337: uri = (uri == null) ? NO_CHAR : uri;
338: localName = _xmlReader.getLocalName();
339: qName = _xmlReader.getQName();
340: _contentHandler.endElement(uri, localName, qName);
341:
342: // End prefix mapping.
343: for (int i = 0, count = _xmlReader.getNamespaceCount(); i < count; i++) {
344: prefix = _xmlReader.getNamespacePrefix(i);
345: prefix = (prefix == null) ? NO_CHAR : prefix; // Default namespace is ""
346: _contentHandler.endPrefixMapping(prefix);
347: }
348: break;
349:
350: case XMLStreamConstants.CDATA:
351: case XMLStreamConstants.CHARACTERS:
352: text = _xmlReader.getText();
353: _contentHandler.characters(text.array(), text.offset(),
354: text.length());
355: break;
356:
357: case XMLStreamConstants.SPACE:
358: text = _xmlReader.getText();
359: _contentHandler.ignorableWhitespace(text.array(), text
360: .offset(), text.length());
361: break;
362:
363: case XMLStreamConstants.PROCESSING_INSTRUCTION:
364: _contentHandler.processingInstruction(_xmlReader
365: .getPITarget(), _xmlReader.getPIData());
366: break;
367:
368: case XMLStreamConstants.COMMENT:
369: // Ignores.
370: break;
371:
372: case XMLStreamConstants.END_DOCUMENT:
373: doContinue = false;
374: _xmlReader.close();
375: break;
376:
377: }
378: }
379: }
380:
381: private static final CharArray NO_CHAR = new CharArray("");
382: }
|