1// XMLReaderAdapter.java - adapt an SAX2 XMLReader to a SAX1 Parser
2// http://www.saxproject.org
3// Written by David Megginson
4// NO WARRANTY!  This class is in the public domain.
5// $Id: XMLReaderAdapter.java,v 1.9 2004/04/26 17:34:35 dmegginson Exp $
6
7package org.xml.sax.helpers;
8
9import java.io.IOException;
10import java.util.Locale;
11import org.xml.sax.AttributeList;
12import org.xml.sax.Attributes;
13import org.xml.sax.ContentHandler;
14import org.xml.sax.DTDHandler;
15import org.xml.sax.DocumentHandler;
16import org.xml.sax.EntityResolver;
17import org.xml.sax.ErrorHandler;
18import org.xml.sax.InputSource;
19import org.xml.sax.Locator;
20import org.xml.sax.Parser;
21import org.xml.sax.SAXException;
22import org.xml.sax.SAXNotSupportedException;
23import org.xml.sax.XMLReader;
24
25
26/**
27 * Adapt a SAX2 XMLReader as a SAX1 Parser.
28 *
29 * <blockquote>
30 * <em>This module, both source code and documentation, is in the
31 * Public Domain, and comes with <strong>NO WARRANTY</strong>.</em>
32 * See <a href='http://www.saxproject.org'>http://www.saxproject.org</a>
33 * for further information.
34 * </blockquote>
35 *
36 * <p>This class wraps a SAX2 {@link org.xml.sax.XMLReader XMLReader}
37 * and makes it act as a SAX1 {@link org.xml.sax.Parser Parser}.  The XMLReader
38 * must support a true value for the
39 * http://xml.org/sax/features/namespace-prefixes property or parsing will fail
40 * with a {@link org.xml.sax.SAXException SAXException}; if the XMLReader
41 * supports a false value for the http://xml.org/sax/features/namespaces
42 * property, that will also be used to improve efficiency.</p>
43 *
44 * @since SAX 2.0
45 * @author David Megginson
46 * @version 2.0.1 (sax2r2)
47 * @see org.xml.sax.Parser
48 * @see org.xml.sax.XMLReader
49 */
50public class XMLReaderAdapter implements Parser, ContentHandler
51{
52
53
54    ////////////////////////////////////////////////////////////////////
55    // Constructor.
56    ////////////////////////////////////////////////////////////////////
57
58
59    /**
60     * Create a new adapter.
61     *
62     * <p>Use the "org.xml.sax.driver" property to locate the SAX2
63     * driver to embed.</p>
64     *
65     * @exception org.xml.sax.SAXException If the embedded driver
66     *            cannot be instantiated or if the
67     *            org.xml.sax.driver property is not specified.
68     */
69    public XMLReaderAdapter ()
70      throws SAXException
71    {
72    setup(XMLReaderFactory.createXMLReader());
73    }
74
75
76    /**
77     * Create a new adapter.
78     *
79     * <p>Create a new adapter, wrapped around a SAX2 XMLReader.
80     * The adapter will make the XMLReader act like a SAX1
81     * Parser.</p>
82     *
83     * @param xmlReader The SAX2 XMLReader to wrap.
84     * @exception java.lang.NullPointerException If the argument is null.
85     */
86    public XMLReaderAdapter (XMLReader xmlReader)
87    {
88    setup(xmlReader);
89    }
90
91
92
93    /**
94     * Internal setup.
95     *
96     * @param xmlReader The embedded XMLReader.
97     */
98    private void setup (XMLReader xmlReader)
99    {
100    if (xmlReader == null) {
101        throw new NullPointerException("XMLReader must not be null");
102    }
103    this.xmlReader = xmlReader;
104    qAtts = new AttributesAdapter();
105    }
106
107
108
109    ////////////////////////////////////////////////////////////////////
110    // Implementation of org.xml.sax.Parser.
111    ////////////////////////////////////////////////////////////////////
112
113
114    /**
115     * Set the locale for error reporting.
116     *
117     * <p>This is not supported in SAX2, and will always throw
118     * an exception.</p>
119     *
120     * @param locale the locale for error reporting.
121     * @see org.xml.sax.Parser#setLocale
122     * @exception org.xml.sax.SAXException Thrown unless overridden.
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    if (documentHandler != null)
259        documentHandler.setDocumentLocator(locator);
260    }
261
262
263    /**
264     * Start document event.
265     *
266     * @exception org.xml.sax.SAXException The client may raise a
267     *            processing exception.
268     * @see org.xml.sax.ContentHandler#startDocument
269     */
270    public void startDocument ()
271    throws SAXException
272    {
273    if (documentHandler != null)
274        documentHandler.startDocument();
275    }
276
277
278    /**
279     * End document event.
280     *
281     * @exception org.xml.sax.SAXException The client may raise a
282     *            processing exception.
283     * @see org.xml.sax.ContentHandler#endDocument
284     */
285    public void endDocument ()
286    throws SAXException
287    {
288    if (documentHandler != null)
289        documentHandler.endDocument();
290    }
291
292
293    /**
294     * Adapt a SAX2 start prefix mapping event.
295     *
296     * @param prefix The prefix being mapped.
297     * @param uri The Namespace URI being mapped to.
298     * @see org.xml.sax.ContentHandler#startPrefixMapping
299     */
300    public void startPrefixMapping (String prefix, String uri)
301    {
302    }
303
304
305    /**
306     * Adapt a SAX2 end prefix mapping event.
307     *
308     * @param prefix The prefix being mapped.
309     * @see org.xml.sax.ContentHandler#endPrefixMapping
310     */
311    public void endPrefixMapping (String prefix)
312    {
313    }
314
315
316    /**
317     * Adapt a SAX2 start element event.
318     *
319     * @param uri The Namespace URI.
320     * @param localName The Namespace local name.
321     * @param qName The qualified (prefixed) name.
322     * @param atts The SAX2 attributes.
323     * @exception org.xml.sax.SAXException The client may raise a
324     *            processing exception.
325     * @see org.xml.sax.ContentHandler#endDocument
326     */
327    public void startElement (String uri, String localName,
328                  String qName, Attributes atts)
329    throws SAXException
330    {
331    if (documentHandler != null) {
332        qAtts.setAttributes(atts);
333        documentHandler.startElement(qName, qAtts);
334    }
335    }
336
337
338    /**
339     * Adapt a SAX2 end element event.
340     *
341     * @param uri The Namespace URI.
342     * @param localName The Namespace local name.
343     * @param qName The qualified (prefixed) name.
344     * @exception org.xml.sax.SAXException The client may raise a
345     *            processing exception.
346     * @see org.xml.sax.ContentHandler#endElement
347     */
348    public void endElement (String uri, String localName,
349                String qName)
350    throws SAXException
351    {
352    if (documentHandler != null)
353        documentHandler.endElement(qName);
354    }
355
356
357    /**
358     * Adapt a SAX2 characters event.
359     *
360     * @param ch An array of characters.
361     * @param start The starting position in the array.
362     * @param length The number of characters to use.
363     * @exception org.xml.sax.SAXException The client may raise a
364     *            processing exception.
365     * @see org.xml.sax.ContentHandler#characters
366     */
367    public void characters (char ch[], int start, int length)
368    throws SAXException
369    {
370    if (documentHandler != null)
371        documentHandler.characters(ch, start, length);
372    }
373
374
375    /**
376     * Adapt a SAX2 ignorable whitespace event.
377     *
378     * @param ch An array of characters.
379     * @param start The starting position in the array.
380     * @param length The number of characters to use.
381     * @exception org.xml.sax.SAXException The client may raise a
382     *            processing exception.
383     * @see org.xml.sax.ContentHandler#ignorableWhitespace
384     */
385    public void ignorableWhitespace (char ch[], int start, int length)
386    throws SAXException
387    {
388    if (documentHandler != null)
389        documentHandler.ignorableWhitespace(ch, start, length);
390    }
391
392
393    /**
394     * Adapt a SAX2 processing instruction event.
395     *
396     * @param target The processing instruction target.
397     * @param data The remainder of the processing instruction
398     * @exception org.xml.sax.SAXException The client may raise a
399     *            processing exception.
400     * @see org.xml.sax.ContentHandler#processingInstruction
401     */
402    public void processingInstruction (String target, String data)
403    throws SAXException
404    {
405    if (documentHandler != null)
406        documentHandler.processingInstruction(target, data);
407    }
408
409
410    /**
411     * Adapt a SAX2 skipped entity event.
412     *
413     * @param name The name of the skipped entity.
414     * @see org.xml.sax.ContentHandler#skippedEntity
415     * @exception org.xml.sax.SAXException Throwable by subclasses.
416     */
417    public void skippedEntity (String name)
418    throws SAXException
419    {
420    }
421
422
423
424    ////////////////////////////////////////////////////////////////////
425    // Internal state.
426    ////////////////////////////////////////////////////////////////////
427
428    XMLReader xmlReader;
429    DocumentHandler documentHandler;
430    AttributesAdapter qAtts;
431
432
433
434    ////////////////////////////////////////////////////////////////////
435    // Internal class.
436    ////////////////////////////////////////////////////////////////////
437
438
439    /**
440     * Internal class to wrap a SAX2 Attributes object for SAX1.
441     */
442    static final class AttributesAdapter implements AttributeList
443    {
444    AttributesAdapter ()
445    {
446    }
447
448
449    /**
450     * Set the embedded Attributes object.
451     *
452     * @param The embedded SAX2 Attributes.
453     */
454    void setAttributes (Attributes attributes)
455    {
456        this.attributes = attributes;
457    }
458
459
460    /**
461     * Return the number of attributes.
462     *
463     * @return The length of the attribute list.
464     * @see org.xml.sax.AttributeList#getLength
465     */
466    public int getLength ()
467    {
468        return attributes.getLength();
469    }
470
471
472    /**
473     * Return the qualified (prefixed) name of an attribute by position.
474     *
475     * @return The qualified name.
476     * @see org.xml.sax.AttributeList#getName
477     */
478    public String getName (int i)
479    {
480        return attributes.getQName(i);
481    }
482
483
484    /**
485     * Return the type of an attribute by position.
486     *
487     * @return The type.
488     * @see org.xml.sax.AttributeList#getType(int)
489     */
490    public String getType (int i)
491    {
492        return attributes.getType(i);
493    }
494
495
496    /**
497     * Return the value of an attribute by position.
498     *
499     * @return The value.
500     * @see org.xml.sax.AttributeList#getValue(int)
501     */
502    public String getValue (int i)
503    {
504        return attributes.getValue(i);
505    }
506
507
508    /**
509     * Return the type of an attribute by qualified (prefixed) name.
510     *
511     * @return The type.
512     * @see org.xml.sax.AttributeList#getType(java.lang.String)
513     */
514    public String getType (String qName)
515    {
516        return attributes.getType(qName);
517    }
518
519
520    /**
521     * Return the value of an attribute by qualified (prefixed) name.
522     *
523     * @return The value.
524     * @see org.xml.sax.AttributeList#getValue(java.lang.String)
525     */
526    public String getValue (String qName)
527    {
528        return attributes.getValue(qName);
529    }
530
531    private Attributes attributes;
532    }
533
534}
535
536// end of XMLReaderAdapter.java
537