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;
11
12import org.xml.sax.Parser;    // deprecated
13import org.xml.sax.Locator;
14import org.xml.sax.InputSource;
15import org.xml.sax.AttributeList; // deprecated
16import org.xml.sax.EntityResolver;
17import org.xml.sax.DTDHandler;
18import org.xml.sax.DocumentHandler; // deprecated
19import org.xml.sax.ErrorHandler;
20import org.xml.sax.SAXException;
21
22import org.xml.sax.XMLReader;
23import org.xml.sax.Attributes;
24import org.xml.sax.ContentHandler;
25import org.xml.sax.SAXNotSupportedException;
26
27
28/**
29 * Adapt a SAX2 XMLReader as a SAX1 Parser.
30 *
31 * <blockquote>
32 * <em>This module, both source code and documentation, is in the
33 * Public Domain, and comes with <strong>NO WARRANTY</strong>.</em>
34 * See <a href='http://www.saxproject.org'>http://www.saxproject.org</a>
35 * for further information.
36 * </blockquote>
37 *
38 * <p>This class wraps a SAX2 {@link org.xml.sax.XMLReader XMLReader}
39 * and makes it act as a SAX1 {@link org.xml.sax.Parser Parser}.  The XMLReader
40 * must support a true value for the
41 * http://xml.org/sax/features/namespace-prefixes property or parsing will fail
42 * with a {@link org.xml.sax.SAXException SAXException}; if the XMLReader
43 * supports a false value for the http://xml.org/sax/features/namespaces
44 * property, that will also be used to improve efficiency.</p>
45 *
46 * @since SAX 2.0
47 * @author David Megginson
48 * @version 2.0.1 (sax2r2)
49 * @see org.xml.sax.Parser
50 * @see org.xml.sax.XMLReader
51 */
52public class XMLReaderAdapter implements Parser, ContentHandler
53{
54
55
56    ////////////////////////////////////////////////////////////////////
57    // Constructor.
58    ////////////////////////////////////////////////////////////////////
59
60
61    /**
62     * Create a new adapter.
63     *
64     * <p>Use the "org.xml.sax.driver" property to locate the SAX2
65     * driver to embed.</p>
66     *
67     * @exception org.xml.sax.SAXException If the embedded driver
68     *            cannot be instantiated or if the
69     *            org.xml.sax.driver property is not specified.
70     */
71    public XMLReaderAdapter ()
72      throws SAXException
73    {
74    setup(XMLReaderFactory.createXMLReader());
75    }
76
77
78    /**
79     * Create a new adapter.
80     *
81     * <p>Create a new adapter, wrapped around a SAX2 XMLReader.
82     * The adapter will make the XMLReader act like a SAX1
83     * Parser.</p>
84     *
85     * @param xmlReader The SAX2 XMLReader to wrap.
86     * @exception java.lang.NullPointerException If the argument is null.
87     */
88    public XMLReaderAdapter (XMLReader xmlReader)
89    {
90    setup(xmlReader);
91    }
92
93
94
95    /**
96     * Internal setup.
97     *
98     * @param xmlReader The embedded XMLReader.
99     */
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
539