package textbender.g.xml.sax; // Copyright 2005, Michael Allan. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Textbender Software"), to deal in the Textbender Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicence, and/or sell copies of the Textbender Software, and to permit persons to whom the Textbender Software is furnished to do so, subject to the following conditions: The preceding copyright notice and this permission notice shall be included in all copies or substantial portions of the Textbender Software. THE TEXTBENDER SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE TEXTBENDER SOFTWARE OR THE USE OR OTHER DEALINGS IN THE TEXTBENDER SOFTWARE. import org.xml.sax.*; /** Links together a chain of SAX filters. * Serves as a temporary scaffold to construct an intermediate filter chain, prior to use. * When the intermediate chain is finished, * you {@linkplain #terminateFirst terminate its first filter} with a parent reader, * and {@linkplain #terminateLast terminate its last} with a content handler. * Then you typically call parse() on the {@linkplain #last() last filter}. * The chainer itself may be discarded. *
* Not intended for use after a chain is terminated. *
** Not intended to join muliple chains. Accepts only single, unlinked filters for linking. *
** Not thread safe. *
*/ public class FilterChainer { /** Appends a new filter as the chain's last. * * @throws AssertionError if previous last filter already has a content handler * @throws AssertionError if newLast already has a content handler or parent reader */ public final void append( XMLFilterCH newLast ) { assert newLast.getParent() == null && newLast.getContentHandler() == null : "only unlinked filters are linked in"; if( last == null ) { assert first == null; first = newLast; } else { assert last.getContentHandler() == null : "last filter must have no content handler"; last.setContentHandler( newLast ); newLast.setParent( last ); } last = newLast; } /** The first filter of the chain. * * @return first filter; or null if no filter is yet appended/prepended */ public final XMLFilterCH first() { return first; } private XMLFilterCH first; /** The last filter of the chain. * * @return last filter; or null if no filter is yet appended/prepended */ public final XMLFilterCH last() { return last; } private XMLFilterCH last; /** Prepends a new filter as the chain's first. * * @throws AssertionError if previous first filter already has a parent reader * @throws AssertionError if newFirst already has a content handler or parent reader */ public final void prepend( XMLFilterCH newFirst ) { assert newFirst.getParent() == null && newFirst.getContentHandler() == null : "only unlinked filters are linked in"; if( first == null ) { assert last == null; last = newFirst; } else { assert first.getParent() == null : "first filter must have no parent reader"; newFirst.setContentHandler( first ); first.setParent( newFirst ); } first = newFirst; } /** Convenience method to terminate the chain's first filter, with a reader. * Afterwards, {@linkplain #first() first}() still points to the first filter, as before. */ public void terminateFirst( XMLReader reader ) { reader.setContentHandler( first ); first.setParent( reader ); } /** Convenience method to terminate the chain's last filter, with a content handler. * Afterwards, {@linkplain #last() last}() still points to the last filter, as before. */ public final void terminateLast( ContentHandler contentHandler ) { last.setContentHandler( contentHandler ); } }