001// XMLReaderAdapter.java - adapt an SAX2 XMLReader to a SAX1 Parser
002// http://www.saxproject.org
003// Written by David Megginson
004// NO WARRANTY!  This class is in the public domain.
005// $Id: XMLReaderAdapter.java,v 1.2 2006-12-10 20:25:41 gnu_andrew Exp $
006
007package org.xml.sax.helpers;
008
009import java.io.IOException;
010import java.util.Locale;
011
012import org.xml.sax.Parser;      // deprecated
013import org.xml.sax.Locator;
014import org.xml.sax.InputSource;
015import org.xml.sax.AttributeList; // deprecated
016import org.xml.sax.EntityResolver;
017import org.xml.sax.DTDHandler;
018import org.xml.sax.DocumentHandler; // deprecated
019import org.xml.sax.ErrorHandler;
020import org.xml.sax.SAXException;
021
022import org.xml.sax.XMLReader;
023import org.xml.sax.Attributes;
024import org.xml.sax.ContentHandler;
025import org.xml.sax.SAXNotSupportedException;
026
027
028/**
029 * Adapt a SAX2 XMLReader as a SAX1 Parser.
030 *
031 * <blockquote>
032 * <em>This module, both source code and documentation, is in the
033 * Public Domain, and comes with <strong>NO WARRANTY</strong>.</em>
034 * See <a href='http://www.saxproject.org'>http://www.saxproject.org</a>
035 * for further information.
036 * </blockquote>
037 *
038 * <p>This class wraps a SAX2 {@link org.xml.sax.XMLReader XMLReader}
039 * and makes it act as a SAX1 {@link org.xml.sax.Parser Parser}.  The XMLReader
040 * must support a true value for the
041 * http://xml.org/sax/features/namespace-prefixes property or parsing will fail
042 * with a {@link org.xml.sax.SAXException SAXException}; if the XMLReader
043 * supports a false value for the http://xml.org/sax/features/namespaces
044 * property, that will also be used to improve efficiency.</p>
045 *
046 * @since SAX 2.0
047 * @author David Megginson
048 * @version 2.0.1 (sax2r2)
049 * @see org.xml.sax.Parser
050 * @see org.xml.sax.XMLReader
051 */
052public class XMLReaderAdapter implements Parser, ContentHandler
053{
054
055
056    ////////////////////////////////////////////////////////////////////
057    // Constructor.
058    ////////////////////////////////////////////////////////////////////
059
060
061    /**
062     * Create a new adapter.
063     *
064     * <p>Use the "org.xml.sax.driver" property to locate the SAX2
065     * driver to embed.</p>
066     *
067     * @exception org.xml.sax.SAXException If the embedded driver
068     *            cannot be instantiated or if the
069     *            org.xml.sax.driver property is not specified.
070     */
071    public XMLReaderAdapter ()
072      throws SAXException
073    {
074        setup(XMLReaderFactory.createXMLReader());
075    }
076
077
078    /**
079     * Create a new adapter.
080     *
081     * <p>Create a new adapter, wrapped around a SAX2 XMLReader.
082     * The adapter will make the XMLReader act like a SAX1
083     * Parser.</p>
084     *
085     * @param xmlReader The SAX2 XMLReader to wrap.
086     * @exception java.lang.NullPointerException If the argument is null.
087     */
088    public XMLReaderAdapter (XMLReader xmlReader)
089    {
090        setup(xmlReader);
091    }
092
093
094
095    /**
096     * Internal setup.
097     *
098     * @param xmlReader The embedded XMLReader.
099     */
100    private void setup (XMLReader xmlReader)
101    {
102        if (xmlReader == null) {
103            throw new NullPointerException("XMLReader must not be null");
104        }
105        this.xmlReader = xmlReader;
106        qAtts = new AttributesAdapter();
107    }
108
109
110
111    ////////////////////////////////////////////////////////////////////
112    // Implementation of org.xml.sax.Parser.
113    ////////////////////////////////////////////////////////////////////
114
115
116    /**
117     * Set the locale for error reporting.
118     *
119     * <p>This is not supported in SAX2, and will always throw
120     * an exception.</p>
121     *
122     * @param locale the locale for error reporting.
123     * @see org.xml.sax.Parser#setLocale
124     * @exception org.xml.sax.SAXException Thrown unless overridden.
125     */
126    public void setLocale (Locale locale)
127        throws SAXException
128    {
129        throw new SAXNotSupportedException("setLocale not supported");
130    }
131
132
133    /**
134     * Register the entity resolver.
135     *
136     * @param resolver The new resolver.
137     * @see org.xml.sax.Parser#setEntityResolver
138     */
139    public void setEntityResolver (EntityResolver resolver)
140    {
141        xmlReader.setEntityResolver(resolver);
142    }
143
144
145    /**
146     * Register the DTD event handler.
147     *
148     * @param handler The new DTD event handler.
149     * @see org.xml.sax.Parser#setDTDHandler
150     */
151    public void setDTDHandler (DTDHandler handler)
152    {
153        xmlReader.setDTDHandler(handler);
154    }
155
156
157    /**
158     * Register the SAX1 document event handler.
159     *
160     * <p>Note that the SAX1 document handler has no Namespace
161     * support.</p>
162     *
163     * @param handler The new SAX1 document event handler.
164     * @see org.xml.sax.Parser#setDocumentHandler
165     */
166    public void setDocumentHandler (DocumentHandler handler)
167    {
168        documentHandler = handler;
169    }
170
171
172    /**
173     * Register the error event handler.
174     *
175     * @param handler The new error event handler.
176     * @see org.xml.sax.Parser#setErrorHandler
177     */
178    public void setErrorHandler (ErrorHandler handler)
179    {
180        xmlReader.setErrorHandler(handler);
181    }
182
183
184    /**
185     * Parse the document.
186     *
187     * <p>This method will throw an exception if the embedded
188     * XMLReader does not support the
189     * http://xml.org/sax/features/namespace-prefixes property.</p>
190     *
191     * @param systemId The absolute URL of the document.
192     * @exception java.io.IOException If there is a problem reading
193     *            the raw content of the document.
194     * @exception org.xml.sax.SAXException If there is a problem
195     *            processing the document.
196     * @see #parse(org.xml.sax.InputSource)
197     * @see org.xml.sax.Parser#parse(java.lang.String)
198     */
199    public void parse (String systemId)
200        throws IOException, SAXException
201    {
202        parse(new InputSource(systemId));
203    }
204
205
206    /**
207     * Parse the document.
208     *
209     * <p>This method will throw an exception if the embedded
210     * XMLReader does not support the
211     * http://xml.org/sax/features/namespace-prefixes property.</p>
212     *
213     * @param input An input source for the document.
214     * @exception java.io.IOException If there is a problem reading
215     *            the raw content of the document.
216     * @exception org.xml.sax.SAXException If there is a problem
217     *            processing the document.
218     * @see #parse(java.lang.String)
219     * @see org.xml.sax.Parser#parse(org.xml.sax.InputSource)
220     */
221    public void parse (InputSource input)
222        throws IOException, SAXException
223    {
224        setupXMLReader();
225        xmlReader.parse(input);
226    }
227
228
229    /**
230     * Set up the XML reader.
231     */
232    private void setupXMLReader ()
233        throws SAXException
234    {
235        xmlReader.setFeature("http://xml.org/sax/features/namespace-prefixes", true);
236        try {
237            xmlReader.setFeature("http://xml.org/sax/features/namespaces",
238                                 false);
239        } catch (SAXException e) {
240            // NO OP: it's just extra information, and we can ignore it
241        }
242        xmlReader.setContentHandler(this);
243    }
244
245
246
247    ////////////////////////////////////////////////////////////////////
248    // Implementation of org.xml.sax.ContentHandler.
249    ////////////////////////////////////////////////////////////////////
250
251
252    /**
253     * Set a document locator.
254     *
255     * @param locator The document locator.
256     * @see org.xml.sax.ContentHandler#setDocumentLocator
257     */
258    public void setDocumentLocator (Locator locator)
259    {
260        if (documentHandler != null)
261            documentHandler.setDocumentLocator(locator);
262    }
263
264
265    /**
266     * Start document event.
267     *
268     * @exception org.xml.sax.SAXException The client may raise a
269     *            processing exception.
270     * @see org.xml.sax.ContentHandler#startDocument
271     */
272    public void startDocument ()
273        throws SAXException
274    {
275        if (documentHandler != null)
276            documentHandler.startDocument();
277    }
278
279
280    /**
281     * End document event.
282     *
283     * @exception org.xml.sax.SAXException The client may raise a
284     *            processing exception.
285     * @see org.xml.sax.ContentHandler#endDocument
286     */
287    public void endDocument ()
288        throws SAXException
289    {
290        if (documentHandler != null)
291            documentHandler.endDocument();
292    }
293
294
295    /**
296     * Adapt a SAX2 start prefix mapping event.
297     *
298     * @param prefix The prefix being mapped.
299     * @param uri The Namespace URI being mapped to.
300     * @see org.xml.sax.ContentHandler#startPrefixMapping
301     */
302    public void startPrefixMapping (String prefix, String uri)
303    {
304    }
305
306
307    /**
308     * Adapt a SAX2 end prefix mapping event.
309     *
310     * @param prefix The prefix being mapped.
311     * @see org.xml.sax.ContentHandler#endPrefixMapping
312     */
313    public void endPrefixMapping (String prefix)
314    {
315    }
316
317
318    /**
319     * Adapt a SAX2 start element event.
320     *
321     * @param uri The Namespace URI.
322     * @param localName The Namespace local name.
323     * @param qName The qualified (prefixed) name.
324     * @param atts The SAX2 attributes.
325     * @exception org.xml.sax.SAXException The client may raise a
326     *            processing exception.
327     * @see org.xml.sax.ContentHandler#endDocument
328     */
329    public void startElement (String uri, String localName,
330                              String qName, Attributes atts)
331        throws SAXException
332    {
333        if (documentHandler != null) {
334            qAtts.setAttributes(atts);
335            documentHandler.startElement(qName, qAtts);
336        }
337    }
338
339
340    /**
341     * Adapt a SAX2 end element event.
342     *
343     * @param uri The Namespace URI.
344     * @param localName The Namespace local name.
345     * @param qName The qualified (prefixed) name.
346     * @exception org.xml.sax.SAXException The client may raise a
347     *            processing exception.
348     * @see org.xml.sax.ContentHandler#endElement
349     */
350    public void endElement (String uri, String localName,
351                            String qName)
352        throws SAXException
353    {
354        if (documentHandler != null)
355            documentHandler.endElement(qName);
356    }
357
358
359    /**
360     * Adapt a SAX2 characters event.
361     *
362     * @param ch An array of characters.
363     * @param start The starting position in the array.
364     * @param length The number of characters to use.
365     * @exception org.xml.sax.SAXException The client may raise a
366     *            processing exception.
367     * @see org.xml.sax.ContentHandler#characters
368     */
369    public void characters (char ch[], int start, int length)
370        throws SAXException
371    {
372        if (documentHandler != null)
373            documentHandler.characters(ch, start, length);
374    }
375
376
377    /**
378     * Adapt a SAX2 ignorable whitespace event.
379     *
380     * @param ch An array of characters.
381     * @param start The starting position in the array.
382     * @param length The number of characters to use.
383     * @exception org.xml.sax.SAXException The client may raise a
384     *            processing exception.
385     * @see org.xml.sax.ContentHandler#ignorableWhitespace
386     */
387    public void ignorableWhitespace (char ch[], int start, int length)
388        throws SAXException
389    {
390        if (documentHandler != null)
391            documentHandler.ignorableWhitespace(ch, start, length);
392    }
393
394
395    /**
396     * Adapt a SAX2 processing instruction event.
397     *
398     * @param target The processing instruction target.
399     * @param data The remainder of the processing instruction
400     * @exception org.xml.sax.SAXException The client may raise a
401     *            processing exception.
402     * @see org.xml.sax.ContentHandler#processingInstruction
403     */
404    public void processingInstruction (String target, String data)
405        throws SAXException
406    {
407        if (documentHandler != null)
408            documentHandler.processingInstruction(target, data);
409    }
410
411
412    /**
413     * Adapt a SAX2 skipped entity event.
414     *
415     * @param name The name of the skipped entity.
416     * @see org.xml.sax.ContentHandler#skippedEntity
417     * @exception org.xml.sax.SAXException Throwable by subclasses.
418     */
419    public void skippedEntity (String name)
420        throws SAXException
421    {
422    }
423
424
425
426    ////////////////////////////////////////////////////////////////////
427    // Internal state.
428    ////////////////////////////////////////////////////////////////////
429
430    XMLReader xmlReader;
431    DocumentHandler documentHandler;
432    AttributesAdapter qAtts;
433
434
435
436    ////////////////////////////////////////////////////////////////////
437    // Internal class.
438    ////////////////////////////////////////////////////////////////////
439
440
441    /**
442     * Internal class to wrap a SAX2 Attributes object for SAX1.
443     */
444    final class AttributesAdapter implements AttributeList
445    {
446        AttributesAdapter ()
447        {
448        }
449
450
451        /**
452         * Set the embedded Attributes object.
453         *
454         * @param The embedded SAX2 Attributes.
455         */
456        void setAttributes (Attributes attributes)
457        {
458            this.attributes = attributes;
459        }
460
461
462        /**
463         * Return the number of attributes.
464         *
465         * @return The length of the attribute list.
466         * @see org.xml.sax.AttributeList#getLength
467         */
468        public int getLength ()
469        {
470            return attributes.getLength();
471        }
472
473
474        /**
475         * Return the qualified (prefixed) name of an attribute by position.
476         *
477         * @return The qualified name.
478         * @see org.xml.sax.AttributeList#getName
479         */
480        public String getName (int i)
481        {
482            return attributes.getQName(i);
483        }
484
485
486        /**
487         * Return the type of an attribute by position.
488         *
489         * @return The type.
490         * @see org.xml.sax.AttributeList#getType(int)
491         */
492        public String getType (int i)
493        {
494            return attributes.getType(i);
495        }
496
497
498        /**
499         * Return the value of an attribute by position.
500         *
501         * @return The value.
502         * @see org.xml.sax.AttributeList#getValue(int)
503         */
504        public String getValue (int i)
505        {
506            return attributes.getValue(i);
507        }
508
509
510        /**
511         * Return the type of an attribute by qualified (prefixed) name.
512         *
513         * @return The type.
514         * @see org.xml.sax.AttributeList#getType(java.lang.String)
515         */
516        public String getType (String qName)
517        {
518            return attributes.getType(qName);
519        }
520
521
522        /**
523         * Return the value of an attribute by qualified (prefixed) name.
524         *
525         * @return The value.
526         * @see org.xml.sax.AttributeList#getValue(java.lang.String)
527         */
528        public String getValue (String qName)
529        {
530            return attributes.getValue(qName);
531        }
532
533        private Attributes attributes;
534    }
535
536}
537
538// end of XMLReaderAdapter.java