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