001// XMLFilterImpl.java - base SAX2 filter implementation.
002// Written by David Megginson, sax@megginson.com
003// NO WARRANTY!  This class is in the Public Domain.
004
005// $Id: XMLFilterImpl.java,v 1.1 2001/03/05 21:40:06 jstrachan Exp $
006
007package org.xml.sax.helpers;
008
009import java.io.IOException;
010
011import org.xml.sax.XMLReader;
012import org.xml.sax.XMLFilter;
013import org.xml.sax.InputSource;
014import org.xml.sax.Locator;
015import org.xml.sax.Attributes;
016import org.xml.sax.EntityResolver;
017import org.xml.sax.DTDHandler;
018import org.xml.sax.ContentHandler;
019import org.xml.sax.ErrorHandler;
020import org.xml.sax.SAXException;
021import org.xml.sax.SAXParseException;
022import org.xml.sax.SAXNotSupportedException;
023import org.xml.sax.SAXNotRecognizedException;
024
025
026/**
027 * Base class for deriving an XML filter.
028 *
029 * <blockquote>
030 * <em>This module, both source code and documentation, is in the
031 * Public Domain, and comes with <strong>NO WARRANTY</strong>.</em>
032 * </blockquote>
033 *
034 * <p>This class is designed to sit between an {@link org.xml.sax.XMLReader
035 * XMLReader} and the client application's event handlers.  By default, it
036 * does nothing but pass requests up to the reader and events
037 * on to the handlers unmodified, but subclasses can override
038 * specific methods to modify the event stream or the configuration
039 * requests as they pass through.</p>
040 *
041 * @since SAX 2.0
042 * @author David Megginson, 
043 *         <a href="mailto:sax@megginson.com">sax@megginson.com</a>
044 * @version 2.0
045 * @see org.xml.sax.XMLFilter
046 * @see org.xml.sax.XMLReader
047 * @see org.xml.sax.EntityResolver
048 * @see org.xml.sax.DTDHandler
049 * @see org.xml.sax.ContentHandler
050 * @see org.xml.sax.ErrorHandler
051 */
052public class XMLFilterImpl
053    implements XMLFilter, EntityResolver, DTDHandler, ContentHandler, ErrorHandler
054{
055
056
057    ////////////////////////////////////////////////////////////////////
058    // Constructors.
059    ////////////////////////////////////////////////////////////////////
060
061
062    /**
063     * Construct an empty XML filter, with no parent.
064     *
065     * <p>This filter will have no parent: you must assign a parent
066     * before you start a parse or do any configuration with
067     * setFeature or setProperty.</p>
068     *
069     * @see org.xml.sax.XMLReader#setFeature
070     * @see org.xml.sax.XMLReader#setProperty
071     */
072    public XMLFilterImpl ()
073    {
074        super();
075    }
076
077
078    /**
079     * Construct an XML filter with the specified parent.
080     *
081     * @see #setParent
082     * @see #getParent
083     */
084    public XMLFilterImpl (XMLReader parent)
085    {
086        super();
087        setParent(parent);
088    }
089
090
091
092    ////////////////////////////////////////////////////////////////////
093    // Implementation of org.xml.sax.XMLFilter.
094    ////////////////////////////////////////////////////////////////////
095
096
097    /**
098     * Set the parent reader.
099     *
100     * <p>This is the {@link org.xml.sax.XMLReader XMLReader} from which 
101     * this filter will obtain its events and to which it will pass its 
102     * configuration requests.  The parent may itself be another filter.</p>
103     *
104     * <p>If there is no parent reader set, any attempt to parse
105     * or to set or get a feature or property will fail.</p>
106     *
107     * @param parent The parent XML reader.
108     * @exception java.lang.NullPointerException If the parent is null.
109     * @see #getParent
110     */
111    public void setParent (XMLReader parent)
112    {
113        if (parent == null) {
114            throw new NullPointerException("Null parent");
115        }
116        this.parent = parent;
117    }
118
119
120    /**
121     * Get the parent reader.
122     *
123     * @return The parent XML reader, or null if none is set.
124     * @see #setParent
125     */
126    public XMLReader getParent ()
127    {
128        return parent;
129    }
130
131
132
133    ////////////////////////////////////////////////////////////////////
134    // Implementation of org.xml.sax.XMLReader.
135    ////////////////////////////////////////////////////////////////////
136
137
138    /**
139     * Set the state of a feature.
140     *
141     * <p>This will always fail if the parent is null.</p>
142     *
143     * @param name The feature name.
144     * @param state The requested feature state.
145     * @exception org.xml.sax.SAXNotRecognizedException When the
146     *            XMLReader does not recognize the feature name.
147     * @exception org.xml.sax.SAXNotSupportedException When the
148     *            XMLReader recognizes the feature name but 
149     *            cannot set the requested value.
150     * @see org.xml.sax.XMLReader#setFeature
151     */
152    public void setFeature (String name, boolean state)
153        throws SAXNotRecognizedException, SAXNotSupportedException
154    {
155        if (parent != null) {
156            parent.setFeature(name, state);
157        } else {
158            throw new SAXNotRecognizedException("Feature: " + name);
159        }
160    }
161
162
163    /**
164     * Look up the state of a feature.
165     *
166     * <p>This will always fail if the parent is null.</p>
167     *
168     * @param name The feature name.
169     * @return The current state of the feature.
170     * @exception org.xml.sax.SAXNotRecognizedException When the
171     *            XMLReader does not recognize the feature name.
172     * @exception org.xml.sax.SAXNotSupportedException When the
173     *            XMLReader recognizes the feature name but 
174     *            cannot determine its state at this time.
175     * @see org.xml.sax.XMLReader#getFeature
176     */
177    public boolean getFeature (String name)
178        throws SAXNotRecognizedException, SAXNotSupportedException
179    {
180        if (parent != null) {
181            return parent.getFeature(name);
182        } else {
183            throw new SAXNotRecognizedException("Feature: " + name);
184        }
185    }
186
187
188    /**
189     * Set the value of a property.
190     *
191     * <p>This will always fail if the parent is null.</p>
192     *
193     * @param name The property name.
194     * @param state The requested property value.
195     * @exception org.xml.sax.SAXNotRecognizedException When the
196     *            XMLReader does not recognize the property name.
197     * @exception org.xml.sax.SAXNotSupportedException When the
198     *            XMLReader recognizes the property name but 
199     *            cannot set the requested value.
200     * @see org.xml.sax.XMLReader#setProperty
201     */
202    public void setProperty (String name, Object value)
203        throws SAXNotRecognizedException, SAXNotSupportedException
204    {
205        if (parent != null) {
206            parent.setProperty(name, value);
207        } else {
208            throw new SAXNotRecognizedException("Property: " + name);
209        }
210    }
211
212
213    /**
214     * Look up the value of a property.
215     *
216     * @param name The property name.
217     * @return The current value of the property.
218     * @exception org.xml.sax.SAXNotRecognizedException When the
219     *            XMLReader does not recognize the feature name.
220     * @exception org.xml.sax.SAXNotSupportedException When the
221     *            XMLReader recognizes the property name but 
222     *            cannot determine its value at this time.
223     * @see org.xml.sax.XMLReader#setFeature
224     */
225    public Object getProperty (String name)
226        throws SAXNotRecognizedException, SAXNotSupportedException
227    {
228        if (parent != null) {
229            return parent.getProperty(name);
230        } else {
231            throw new SAXNotRecognizedException("Property: " + name);
232        }
233    }
234
235
236    /**
237     * Set the entity resolver.
238     *
239     * @param resolver The new entity resolver.
240     * @exception java.lang.NullPointerException If the resolver
241     *            is null.
242     * @see org.xml.sax.XMLReader#setEntityResolver
243     */
244    public void setEntityResolver (EntityResolver resolver)
245    {
246        if (resolver == null) {
247            throw new NullPointerException("Null entity resolver");
248        } else {
249            entityResolver = resolver;
250        }
251    }
252
253
254    /**
255     * Get the current entity resolver.
256     *
257     * @return The current entity resolver, or null if none was set.
258     * @see org.xml.sax.XMLReader#getEntityResolver
259     */
260    public EntityResolver getEntityResolver ()
261    {
262        return entityResolver;
263    }
264
265
266    /**
267     * Set the DTD event handler.
268     *
269     * @param resolver The new DTD handler.
270     * @exception java.lang.NullPointerException If the handler
271     *            is null.
272     * @see org.xml.sax.XMLReader#setDTDHandler
273     */
274    public void setDTDHandler (DTDHandler handler)
275    {
276        if (handler == null) {
277            throw new NullPointerException("Null DTD handler");
278        } else {
279            dtdHandler = handler;
280        }
281    }
282
283
284    /**
285     * Get the current DTD event handler.
286     *
287     * @return The current DTD handler, or null if none was set.
288     * @see org.xml.sax.XMLReader#getDTDHandler
289     */
290    public DTDHandler getDTDHandler ()
291    {
292        return dtdHandler;
293    }
294
295
296    /**
297     * Set the content event handler.
298     *
299     * @param resolver The new content handler.
300     * @exception java.lang.NullPointerException If the handler
301     *            is null.
302     * @see org.xml.sax.XMLReader#setContentHandler
303     */
304    public void setContentHandler (ContentHandler handler)
305    {
306        if (handler == null) {
307            throw new NullPointerException("Null content handler");
308        } else {
309            contentHandler = handler;
310        }
311    }
312
313
314    /**
315     * Get the content event handler.
316     *
317     * @return The current content handler, or null if none was set.
318     * @see org.xml.sax.XMLReader#getContentHandler
319     */
320    public ContentHandler getContentHandler ()
321    {
322        return contentHandler;
323    }
324
325
326    /**
327     * Set the error event handler.
328     *
329     * @param handle The new error handler.
330     * @exception java.lang.NullPointerException If the handler
331     *            is null.
332     * @see org.xml.sax.XMLReader#setErrorHandler
333     */
334    public void setErrorHandler (ErrorHandler handler)
335    {
336        if (handler == null) {
337            throw new NullPointerException("Null error handler");
338        } else {
339            errorHandler = handler;
340        }
341    }
342
343
344    /**
345     * Get the current error event handler.
346     *
347     * @return The current error handler, or null if none was set.
348     * @see org.xml.sax.XMLReader#getErrorHandler
349     */
350    public ErrorHandler getErrorHandler ()
351    {
352        return errorHandler;
353    }
354
355
356    /**
357     * Parse a document.
358     *
359     * @param input The input source for the document entity.
360     * @exception org.xml.sax.SAXException Any SAX exception, possibly
361     *            wrapping another exception.
362     * @exception java.io.IOException An IO exception from the parser,
363     *            possibly from a byte stream or character stream
364     *            supplied by the application.
365     * @see org.xml.sax.XMLReader#parse(org.xml.sax.InputSource)
366     */
367    public void parse (InputSource input)
368        throws SAXException, IOException
369    {
370        setupParse();
371        parent.parse(input);
372    }
373
374
375    /**
376     * Parse a document.
377     *
378     * @param systemId The system identifier as a fully-qualified URI.
379     * @exception org.xml.sax.SAXException Any SAX exception, possibly
380     *            wrapping another exception.
381     * @exception java.io.IOException An IO exception from the parser,
382     *            possibly from a byte stream or character stream
383     *            supplied by the application.
384     * @see org.xml.sax.XMLReader#parse(java.lang.String)
385     */
386    public void parse (String systemId)
387        throws SAXException, IOException
388    {
389        parse(new InputSource(systemId));
390    }
391
392
393
394    ////////////////////////////////////////////////////////////////////
395    // Implementation of org.xml.sax.EntityResolver.
396    ////////////////////////////////////////////////////////////////////
397
398
399    /**
400     * Filter an external entity resolution.
401     *
402     * @param publicId The entity's public identifier, or null.
403     * @param systemId The entity's system identifier.
404     * @return A new InputSource or null for the default.
405     * @exception org.xml.sax.SAXException The client may throw
406     *            an exception during processing.
407     * @exception java.io.IOException The client may throw an
408     *            I/O-related exception while obtaining the
409     *            new InputSource.
410     * @see org.xml.sax.EntityResolver#resolveEntity
411     */
412    public InputSource resolveEntity (String publicId, String systemId)
413        throws SAXException, IOException
414    {
415        if (entityResolver != null) {
416            return entityResolver.resolveEntity(publicId, systemId);
417        } else {
418            return null;
419        }
420    }
421
422
423
424    ////////////////////////////////////////////////////////////////////
425    // Implementation of org.xml.sax.DTDHandler.
426    ////////////////////////////////////////////////////////////////////
427
428    
429    /**
430     * Filter a notation declaration event.
431     *
432     * @param name The notation name.
433     * @param publicId The notation's public identifier, or null.
434     * @param systemId The notation's system identifier, or null.
435     * @exception org.xml.sax.SAXException The client may throw
436     *            an exception during processing.
437     * @see org.xml.sax.DTDHandler#notationDecl
438     */
439    public void notationDecl (String name, String publicId, String systemId)
440        throws SAXException
441    {
442        if (dtdHandler != null) {
443            dtdHandler.notationDecl(name, publicId, systemId);
444        }
445    }
446
447    
448    /**
449     * Filter an unparsed entity declaration event.
450     *
451     * @param name The entity name.
452     * @param publicId The entity's public identifier, or null.
453     * @param systemId The entity's system identifier, or null.
454     * @param notationName The name of the associated notation.
455     * @exception org.xml.sax.SAXException The client may throw
456     *            an exception during processing.
457     * @see org.xml.sax.DTDHandler#unparsedEntityDecl
458     */
459    public void unparsedEntityDecl (String name, String publicId,
460                                    String systemId, String notationName)
461        throws SAXException
462    {
463        if (dtdHandler != null) {
464            dtdHandler.unparsedEntityDecl(name, publicId, systemId,
465                                          notationName);
466        }
467    }
468
469
470
471    ////////////////////////////////////////////////////////////////////
472    // Implementation of org.xml.sax.ContentHandler.
473    ////////////////////////////////////////////////////////////////////
474
475
476    /**
477     * Filter a new document locator event.
478     *
479     * @param locator The document locator.
480     * @see org.xml.sax.ContentHandler#setDocumentLocator
481     */
482    public void setDocumentLocator (Locator locator)
483    {
484        this.locator = locator;
485        if (contentHandler != null) {
486            contentHandler.setDocumentLocator(locator);
487        }
488    }
489
490
491    /**
492     * Filter a start document event.
493     *
494     * @exception org.xml.sax.SAXException The client may throw
495     *            an exception during processing.
496     * @see org.xml.sax.ContentHandler#startDocument
497     */
498    public void startDocument ()
499        throws SAXException
500    {
501        if (contentHandler != null) {
502            contentHandler.startDocument();
503        }
504    }
505
506
507    /**
508     * Filter an end document event.
509     *
510     * @exception org.xml.sax.SAXException The client may throw
511     *            an exception during processing.
512     * @see org.xml.sax.ContentHandler#endDocument
513     */
514    public void endDocument ()
515        throws SAXException
516    {
517        if (contentHandler != null) {
518            contentHandler.endDocument();
519        }
520    }
521
522
523    /**
524     * Filter a start Namespace prefix mapping event.
525     *
526     * @param prefix The Namespace prefix.
527     * @param uri The Namespace URI.
528     * @exception org.xml.sax.SAXException The client may throw
529     *            an exception during processing.
530     * @see org.xml.sax.ContentHandler#startPrefixMapping
531     */
532    public void startPrefixMapping (String prefix, String uri)
533        throws SAXException
534    {
535        if (contentHandler != null) {
536            contentHandler.startPrefixMapping(prefix, uri);
537        }
538    }
539
540
541    /**
542     * Filter an end Namespace prefix mapping event.
543     *
544     * @param prefix The Namespace prefix.
545     * @exception org.xml.sax.SAXException The client may throw
546     *            an exception during processing.
547     * @see org.xml.sax.ContentHandler#endPrefixMapping
548     */
549    public void endPrefixMapping (String prefix)
550        throws SAXException
551    {
552        if (contentHandler != null) {
553            contentHandler.endPrefixMapping(prefix);
554        }
555    }
556
557
558    /**
559     * Filter a start element event.
560     *
561     * @param uri The element's Namespace URI, or the empty string.
562     * @param localName The element's local name, or the empty string.
563     * @param qName The element's qualified (prefixed) name, or the empty
564     *        string.
565     * @param atts The element's attributes.
566     * @exception org.xml.sax.SAXException The client may throw
567     *            an exception during processing.
568     * @see org.xml.sax.ContentHandler#startElement
569     */
570    public void startElement (String uri, String localName, String qName,
571                              Attributes atts)
572        throws SAXException
573    {
574        if (contentHandler != null) {
575            contentHandler.startElement(uri, localName, qName, atts);
576        }
577    }
578
579
580    /**
581     * Filter an end element event.
582     *
583     * @param uri The element's Namespace URI, or the empty string.
584     * @param localName The element's local name, or the empty string.
585     * @param qName The element's qualified (prefixed) name, or the empty
586     *        string.
587     * @exception org.xml.sax.SAXException The client may throw
588     *            an exception during processing.
589     * @see org.xml.sax.ContentHandler#endElement
590     */
591    public void endElement (String uri, String localName, String qName)
592        throws SAXException
593    {
594        if (contentHandler != null) {
595            contentHandler.endElement(uri, localName, qName);
596        }
597    }
598
599
600    /**
601     * Filter a character data event.
602     *
603     * @param ch An array of characters.
604     * @param start The starting position in the array.
605     * @param length The number of characters to use from the array.
606     * @exception org.xml.sax.SAXException The client may throw
607     *            an exception during processing.
608     * @see org.xml.sax.ContentHandler#characters
609     */
610    public void characters (char ch[], int start, int length)
611        throws SAXException
612    {
613        if (contentHandler != null) {
614            contentHandler.characters(ch, start, length);
615        }
616    }
617
618
619    /**
620     * Filter an ignorable whitespace event.
621     *
622     * @param ch An array of characters.
623     * @param start The starting position in the array.
624     * @param length The number of characters to use from the array.
625     * @exception org.xml.sax.SAXException The client may throw
626     *            an exception during processing.
627     * @see org.xml.sax.ContentHandler#ignorableWhitespace
628     */
629    public void ignorableWhitespace (char ch[], int start, int length)
630        throws SAXException
631    {
632        if (contentHandler != null) {
633            contentHandler.ignorableWhitespace(ch, start, length);
634        }
635    }
636
637
638    /**
639     * Filter a processing instruction event.
640     *
641     * @param target The processing instruction target.
642     * @param data The text following the target.
643     * @exception org.xml.sax.SAXException The client may throw
644     *            an exception during processing.
645     * @see org.xml.sax.ContentHandler#processingInstruction
646     */
647    public void processingInstruction (String target, String data)
648        throws SAXException
649    {
650        if (contentHandler != null) {
651            contentHandler.processingInstruction(target, data);
652        }
653    }
654
655
656    /**
657     * Filter a skipped entity event.
658     *
659     * @param name The name of the skipped entity.
660     * @exception org.xml.sax.SAXException The client may throw
661     *            an exception during processing.
662     * @see org.xml.sax.ContentHandler#skippedEntity
663     */
664    public void skippedEntity (String name)
665        throws SAXException
666    {
667        if (contentHandler != null) {
668            contentHandler.skippedEntity(name);
669        }
670    }
671
672
673
674    ////////////////////////////////////////////////////////////////////
675    // Implementation of org.xml.sax.ErrorHandler.
676    ////////////////////////////////////////////////////////////////////
677
678
679    /**
680     * Filter a warning event.
681     *
682     * @param e The nwarning as an exception.
683     * @exception org.xml.sax.SAXException The client may throw
684     *            an exception during processing.
685     * @see org.xml.sax.ErrorHandler#warning
686     */
687    public void warning (SAXParseException e)
688        throws SAXException
689    {
690        if (errorHandler != null) {
691            errorHandler.warning(e);
692        }
693    }
694
695
696    /**
697     * Filter an error event.
698     *
699     * @param e The error as an exception.
700     * @exception org.xml.sax.SAXException The client may throw
701     *            an exception during processing.
702     * @see org.xml.sax.ErrorHandler#error
703     */
704    public void error (SAXParseException e)
705        throws SAXException
706    {
707        if (errorHandler != null) {
708            errorHandler.error(e);
709        }
710    }
711
712
713    /**
714     * Filter a fatal error event.
715     *
716     * @param e The error as an exception.
717     * @exception org.xml.sax.SAXException The client may throw
718     *            an exception during processing.
719     * @see org.xml.sax.ErrorHandler#fatalError
720     */
721    public void fatalError (SAXParseException e)
722        throws SAXException
723    {
724        if (errorHandler != null) {
725            errorHandler.fatalError(e);
726        }
727    }
728
729
730
731    ////////////////////////////////////////////////////////////////////
732    // Internal methods.
733    ////////////////////////////////////////////////////////////////////
734
735
736    /**
737     * Set up before a parse.
738     *
739     * <p>Before every parse, check whether the parent is
740     * non-null, and re-register the filter for all of the 
741     * events.</p>
742     */
743    private void setupParse ()
744    {
745        if (parent == null) {
746            throw new NullPointerException("No parent for filter");
747        }
748        parent.setEntityResolver(this);
749        parent.setDTDHandler(this);
750        parent.setContentHandler(this);
751        parent.setErrorHandler(this);
752    }
753
754
755
756    ////////////////////////////////////////////////////////////////////
757    // Internal state.
758    ////////////////////////////////////////////////////////////////////
759
760    private XMLReader parent = null;
761    private Locator locator = null;
762    private EntityResolver entityResolver = null;
763    private DTDHandler dtdHandler = null;
764    private ContentHandler contentHandler = null;
765    private ErrorHandler errorHandler = null;
766
767}
768
769// end of XMLFilterImpl.java