diff options
author | Brian Jones <cbj@gnu.org> | 2003-02-01 02:10:07 +0000 |
---|---|---|
committer | Brian Jones <cbj@gnu.org> | 2003-02-01 02:10:07 +0000 |
commit | a286d8e992bbc0a6180638df2bfb55e433642b78 (patch) | |
tree | 6c7ff3ab361b5543e219d49f537b2ecb38de6eb7 /external/jaxp/source/gnu/xml/pipeline/EventFilter.java | |
parent | a1314139baa4e9cd89051573d3bb5a5b556f3b32 (diff) | |
download | classpath-a286d8e992bbc0a6180638df2bfb55e433642b78.tar.gz |
Initial revision
Diffstat (limited to 'external/jaxp/source/gnu/xml/pipeline/EventFilter.java')
-rwxr-xr-x | external/jaxp/source/gnu/xml/pipeline/EventFilter.java | 788 |
1 files changed, 788 insertions, 0 deletions
diff --git a/external/jaxp/source/gnu/xml/pipeline/EventFilter.java b/external/jaxp/source/gnu/xml/pipeline/EventFilter.java new file mode 100755 index 000000000..bee10d5e7 --- /dev/null +++ b/external/jaxp/source/gnu/xml/pipeline/EventFilter.java @@ -0,0 +1,788 @@ +/* + * $Id: EventFilter.java,v 1.1 2003-02-01 02:10:19 cbj Exp $ + * Copyright (C) 1999-2001 David Brownell + * + * This file is part of GNU JAXP, a library. + * + * GNU JAXP is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * GNU JAXP is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * As a special exception, if you link this library with other files to + * produce an executable, this library does not by itself cause the + * resulting executable to be covered by the GNU General Public License. + * This exception does not however invalidate any other reasons why the + * executable file might be covered by the GNU General Public License. + */ + +package gnu.xml.pipeline; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +import org.xml.sax.*; +import org.xml.sax.ext.*; +import org.xml.sax.helpers.XMLFilterImpl; + + +/** + * A customizable event consumer, used to assemble various kinds of filters + * using SAX handlers and an optional second consumer. It can be constructed + * in two ways: <ul> + * + * <li> To serve as a passthrough, sending all events to a second consumer. + * The second consumer may be identified through {@link #getNext}. + * + * <li> To serve as a dead end, with all handlers null; + * {@link #getNext} returns null. + * + * </ul> + * + * <p> Additionally, SAX handlers may be assigned, which completely replace + * the "upstream" view (through {@link EventConsumer}) of handlers, initially + * null or the "next" consumer provided to the constructor. To make + * it easier to build specialized filter classes, this class implements + * all the standard SAX consumer handlers, and those implementations + * delegate "downstream" to the consumer accessed by {@link #getNext}. + * + * <p> The simplest way to create a custom a filter class is to create a + * subclass which overrides one or more handler interface methods. The + * constructor for that subclass then registers itself as a handler for + * those interfaces using a call such as <em>setContentHandler(this)</em>, + * so the "upstream" view of event delivery is modified from the state + * established in the base class constructor. That way, + * the overridden methods intercept those event callbacks + * as they go "downstream", and + * all other event callbacks will pass events to any next consumer. + * Overridden methods may invoke superclass methods (perhaps after modifying + * parameters) if they wish to delegate such calls. Such subclasses + * should use {@link #getErrorHandler} to report errors using the + * common error reporting mechanism. + * + * <p> Another important technique is to construct a filter consisting + * of only a few specific types of handler. For example, one could easily + * prune out lexical events or various declarations by providing handlers + * which don't pass those events downstream, or by providing null handlers. + * + * <hr /> + * + * <p> This may be viewed as the consumer oriented analogue of the SAX2 + * {@link org.xml.sax.helpers.XMLFilterImpl XMLFilterImpl} class. + * Key differences include: <ul> + * + * <li> This fully separates consumer and producer roles: it + * does not implement the producer side <em>XMLReader</em> or + * <em>EntityResolver</em> interfaces, so it can only be used + * in "push" mode (it has no <em>parse()</em> methods). + * + * <li> "Extension" handlers are fully supported, enabling a + * richer set of application requirements. + * And it implements {@link EventConsumer}, which groups related + * consumer methods together, rather than leaving them separated. + * + * <li> The chaining which is visible is "downstream" to the next + * consumer, not "upstream" to the preceding producer. + * It supports "fan-in", where + * a consumer can be fed by several producers. (For "fan-out", + * see the {@link TeeConsumer} class.) + * + * <li> Event chaining is set up differently. It is intended to + * work "upstream" from terminus towards producer, during filter + * construction, as described above. + * This is part of an early binding model: + * events don't need to pass through stages which ignore them. + * + * <li> ErrorHandler support is separated, on the grounds that + * pipeline stages need to share the same error handling policy. + * For the same reason, error handler setup goes "downstream": + * when error handlers get set, they are passed to subsequent + * consumers. + * + * </ul> + * + * <p> The {@link #chainTo chainTo()} convenience routine supports chaining to + * an XMLFilterImpl, in its role as a limited functionality event + * consumer. Its event producer role ({@link XMLFilter}) is ignored. + * + * <hr /> + * + * <p> The {@link #bind bind()} routine may be used associate event pipelines + * with any kind of {@link XMLReader} that will produce the events. + * Such pipelines don't necessarily need to have any members which are + * implemented using this class. That routine has some intelligence + * which supports automatic changes to parser feature flags, letting + * event piplines become largely independent of the particular feature + * sets of parsers. + * + * @author David Brownell + * @version $Date: 2003-02-01 02:10:19 $ + */ +public class EventFilter + implements EventConsumer, ContentHandler, DTDHandler, + LexicalHandler, DeclHandler +{ + // SAX handlers + private ContentHandler docHandler, docNext; + private DTDHandler dtdHandler, dtdNext; + private LexicalHandler lexHandler, lexNext; + private DeclHandler declHandler, declNext; + // and ideally, one more for the stuff SAX2 doesn't show + + private Locator locator; + private EventConsumer next; + private ErrorHandler errHandler; + + + /** SAX2 URI prefix for standard feature flags. */ + public static final String FEATURE_URI + = "http://xml.org/sax/features/"; + /** SAX2 URI prefix for standard properties (mostly for handlers). */ + public static final String PROPERTY_URI + = "http://xml.org/sax/properties/"; + + /** SAX2 property identifier for {@link DeclHandler} events */ + public static final String DECL_HANDLER + = PROPERTY_URI + "declaration-handler"; + /** SAX2 property identifier for {@link LexicalHandler} events */ + public static final String LEXICAL_HANDLER + = PROPERTY_URI + "lexical-handler"; + + // + // These class objects will be null if the relevant class isn't linked. + // Small configurations (pJava and some kinds of embedded systems) need + // to facilitate smaller executables. So "instanceof" is undesirable + // when bind() sees if it can remove some stages. + // + // SECURITY NOTE: assuming all these classes are part of the same sealed + // package, there's no problem saving these in the instance of this class + // that's associated with "this" class loader. But that wouldn't be true + // for classes in another package. + // + private static boolean loaded; + private static Class nsClass; + private static Class validClass; + private static Class wfClass; + private static Class xincClass; + + static ClassLoader getClassLoader () + { + Method m = null; + + try { + m = Thread.class.getMethod("getContextClassLoader", null); + } catch (NoSuchMethodException e) { + // Assume that we are running JDK 1.1, use the current ClassLoader + return EventFilter.class.getClassLoader(); + } + + try { + return (ClassLoader) m.invoke(Thread.currentThread(), null); + } catch (IllegalAccessException e) { + // assert(false) + throw new UnknownError(e.getMessage()); + } catch (InvocationTargetException e) { + // assert(e.getTargetException() instanceof SecurityException) + throw new UnknownError(e.getMessage()); + } + } + + static Class loadClass (ClassLoader classLoader, String className) + { + try { + if (classLoader == null) + return Class.forName(className); + else + return classLoader.loadClass(className); + } catch (Exception e) { + return null; + } + } + + static private void loadClasses () + { + ClassLoader loader = getClassLoader (); + + nsClass = loadClass (loader, "gnu.xml.pipeline.NSFilter"); + validClass = loadClass (loader, "gnu.xml.pipeline.ValidationConsumer"); + wfClass = loadClass (loader, "gnu.xml.pipeline.WellFormednessFilter"); + xincClass = loadClass (loader, "gnu.xml.pipeline.XIncludeFilter"); + loaded = true; + } + + + /** + * Binds the standard SAX2 handlers from the specified consumer + * pipeline to the specified producer. These handlers include the core + * {@link ContentHandler} and {@link DTDHandler}, plus the extension + * {@link DeclHandler} and {@link LexicalHandler}. Any additional + * application-specific handlers need to be bound separately. + * The {@link ErrorHandler} is handled differently: the producer's + * error handler is passed through to the consumer pipeline. + * The producer is told to include namespace prefix information if it + * can, since many pipeline stages need that Infoset information to + * work well. + * + * <p> At the head of the pipeline, certain standard event filters are + * recognized and handled specially. This facilitates construction + * of processing pipelines that work regardless of the capabilities + * of the XMLReader implementation in use; for example, it permits + * validating output of a {@link gnu.xml.util.DomParser}. <ul> + * + * <li> {@link NSFilter} will be removed if the producer can be + * told not to discard namespace data, using the "namespace-prefixes" + * feature flag. + * + * <li> {@link ValidationConsumer} will be removed if the producer + * can be told to validate, using the "validation" feature flag. + * + * <li> {@link WellFormednessFilter} is always removed, on the + * grounds that no XMLReader is permitted to producee malformed + * event streams and this would just be processing overhead. + * + * <li> {@link XIncludeFilter} stops the special handling, except + * that it's told about the "namespace-prefixes" feature of the + * event producer so that the event stream is internally consistent. + * + * <li> The first consumer which is not one of those classes stops + * such special handling. This means that if you want to force + * one of those filters to be used, you could just precede it with + * an instance of {@link EventFilter} configured as a pass-through. + * You might need to do that if you are using an {@link NSFilter} + * subclass to fix names found in attributes or character data. + * + * </ul> + * + * <p> Other than that, this method works with any kind of event consumer, + * not just event filters. Note that in all cases, the standard handlers + * are assigned; any previous handler assignments for the handler will + * be overridden. + * + * @param producer will deliver events to the specified consumer + * @param consumer pipeline supplying event handlers to be associated + * with the producer (may not be null) + */ + public static void bind (XMLReader producer, EventConsumer consumer) + { + Class klass = null; + boolean prefixes; + + if (!loaded) + loadClasses (); + + // DOM building, printing, layered validation, and other + // things don't work well when prefix info is discarded. + // Include it by default, whenever possible. + try { + producer.setFeature (FEATURE_URI + "namespace-prefixes", + true); + prefixes = true; + } catch (SAXException e) { + prefixes = false; + } + + // NOTE: This loop doesn't use "instanceof", since that + // would prevent compiling/linking without those classes + // being present. + while (consumer != null) { + klass = consumer.getClass (); + + // we might have already changed this problematic SAX2 default. + if (nsClass != null && nsClass.isAssignableFrom (klass)) { + if (!prefixes) + break; + consumer = ((EventFilter)consumer).getNext (); + + // the parser _might_ do DTD validation by default ... + // if not, maybe we can change this setting. + } else if (validClass != null + && validClass.isAssignableFrom (klass)) { + try { + producer.setFeature (FEATURE_URI + "validation", + true); + consumer = ((ValidationConsumer)consumer).getNext (); + } catch (SAXException e) { + break; + } + + // parsers are required not to have such bugs + } else if (wfClass != null && wfClass.isAssignableFrom (klass)) { + consumer = ((WellFormednessFilter)consumer).getNext (); + + // stop on the first pipeline stage we can't remove + } else + break; + + if (consumer == null) + klass = null; + } + + // the actual setting here doesn't matter as much + // as that producer and consumer agree + if (xincClass != null && klass != null + && xincClass.isAssignableFrom (klass)) + ((XIncludeFilter)consumer).setSavingPrefixes (prefixes); + + // Some SAX parsers can't handle null handlers -- bleech + DefaultHandler2 h = new DefaultHandler2 (); + + if (consumer != null && consumer.getContentHandler () != null) + producer.setContentHandler (consumer.getContentHandler ()); + else + producer.setContentHandler (h); + if (consumer != null && consumer.getDTDHandler () != null) + producer.setDTDHandler (consumer.getDTDHandler ()); + else + producer.setDTDHandler (h); + + try { + Object dh; + + if (consumer != null) + dh = consumer.getProperty (DECL_HANDLER); + else + dh = null; + if (dh == null) + dh = h; + producer.setProperty (DECL_HANDLER, dh); + } catch (Exception e) { /* ignore */ } + try { + Object lh; + + if (consumer != null) + lh = consumer.getProperty (LEXICAL_HANDLER); + else + lh = null; + if (lh == null) + lh = h; + producer.setProperty (LEXICAL_HANDLER, lh); + } catch (Exception e) { /* ignore */ } + + // this binding goes the other way around + if (producer.getErrorHandler () == null) + producer.setErrorHandler (h); + if (consumer != null) + consumer.setErrorHandler (producer.getErrorHandler ()); + } + + /** + * Initializes all handlers to null. + */ + // constructor used by PipelineFactory + public EventFilter () { } + + + /** + * Handlers that are not otherwise set will default to those from + * the specified consumer, making it easy to pass events through. + * If the consumer is null, all handlers are initialzed to null. + */ + // constructor used by PipelineFactory + public EventFilter (EventConsumer consumer) + { + if (consumer == null) + return; + + next = consumer; + + // We delegate through the "xxNext" handlers, and + // report the "xxHandler" ones on our input side. + + // Normally a subclass would both override handler + // methods and register itself as the "xxHandler". + + docHandler = docNext = consumer.getContentHandler (); + dtdHandler = dtdNext = consumer.getDTDHandler (); + try { + declHandler = declNext = (DeclHandler) + consumer.getProperty (DECL_HANDLER); + } catch (SAXException e) { /* leave value null */ } + try { + lexHandler = lexNext = (LexicalHandler) + consumer.getProperty (LEXICAL_HANDLER); + } catch (SAXException e) { /* leave value null */ } + } + + /** + * Treats the XMLFilterImpl as a limited functionality event consumer, + * by arranging to deliver events to it; this lets such classes be + * "wrapped" as pipeline stages. + * + * <p> <em>Upstream Event Setup:</em> + * If no handlers have been assigned to this EventFilter, then the + * handlers from specified XMLFilterImpl are returned from this + * {@link EventConsumer}: the XMLFilterImpl is just "wrapped". + * Otherwise the specified handlers will be returned. + * + * <p> <em>Downstream Event Setup:</em> + * Subclasses may chain event delivery to the specified XMLFilterImpl + * by invoking the appropiate superclass methods, + * as if their constructor passed a "next" EventConsumer to the + * constructor for this class. + * If this EventFilter has an ErrorHandler, it is assigned as + * the error handler for the XMLFilterImpl, just as would be + * done for a next stage implementing {@link EventConsumer}. + * + * @param next the next downstream component of the pipeline. + * @exception IllegalStateException if the "next" consumer has + * already been set through the constructor. + */ + public void chainTo (XMLFilterImpl next) + { + if (this.next != null) + throw new IllegalStateException (); + + docNext = next.getContentHandler (); + if (docHandler == null) + docHandler = docNext; + dtdNext = next.getDTDHandler (); + if (dtdHandler == null) + dtdHandler = dtdNext; + + try { + declNext = (DeclHandler) next.getProperty (DECL_HANDLER); + if (declHandler == null) + declHandler = declNext; + } catch (SAXException e) { /* leave value null */ } + try { + lexNext = (LexicalHandler) next.getProperty (LEXICAL_HANDLER); + if (lexHandler == null) + lexHandler = lexNext; + } catch (SAXException e) { /* leave value null */ } + + if (errHandler != null) + next.setErrorHandler (errHandler); + } + + /** + * Records the error handler that should be used by this stage, and + * passes it "downstream" to any subsequent stage. + */ + final public void setErrorHandler (ErrorHandler handler) + { + errHandler = handler; + if (next != null) + next.setErrorHandler (handler); + } + + /** + * Returns the error handler assigned this filter stage, or null + * if no such assigment has been made. + */ + final public ErrorHandler getErrorHandler () + { + return errHandler; + } + + + /** + * Returns the next event consumer in sequence; or null if there + * is no such handler. + */ + final public EventConsumer getNext () + { return next; } + + + /** + * Assigns the content handler to use; a null handler indicates + * that these events will not be forwarded. + * This overrides the previous settting for this handler, which was + * probably pointed to the next consumer by the base class constructor. + */ + final public void setContentHandler (ContentHandler h) + { + docHandler = h; + } + + /** Returns the content handler being used. */ + final public ContentHandler getContentHandler () + { + return docHandler; + } + + /** + * Assigns the DTD handler to use; a null handler indicates + * that these events will not be forwarded. + * This overrides the previous settting for this handler, which was + * probably pointed to the next consumer by the base class constructor. + */ + final public void setDTDHandler (DTDHandler h) + { dtdHandler = h; } + + /** Returns the dtd handler being used. */ + final public DTDHandler getDTDHandler () + { + return dtdHandler; + } + + /** + * Stores the property, normally a handler; a null handler indicates + * that these events will not be forwarded. + * This overrides the previous handler settting, which was probably + * pointed to the next consumer by the base class constructor. + */ + final public void setProperty (String id, Object o) + throws SAXNotRecognizedException, SAXNotSupportedException + { + try { + Object value = getProperty (id); + + if (value == o) + return; + if (DECL_HANDLER.equals (id)) { + declHandler = (DeclHandler) o; + return; + } + if (LEXICAL_HANDLER.equals (id)) { + lexHandler = (LexicalHandler) o; + return; + } + throw new SAXNotSupportedException (id); + + } catch (ClassCastException e) { + throw new SAXNotSupportedException (id); + } + } + + /** Retrieves a property of unknown intent (usually a handler) */ + final public Object getProperty (String id) + throws SAXNotRecognizedException + { + if (DECL_HANDLER.equals (id)) + return declHandler; + if (LEXICAL_HANDLER.equals (id)) + return lexHandler; + + throw new SAXNotRecognizedException (id); + } + + /** + * Returns any locator provided to the next consumer, if this class + * (or a subclass) is handling {@link ContentHandler } events. + */ + public Locator getDocumentLocator () + { return locator; } + + + // CONTENT HANDLER DELEGATIONS + + /** <b>SAX2:</b> passes this callback to the next consumer, if any */ + public void setDocumentLocator (Locator locator) + { + this.locator = locator; + if (docNext != null) + docNext.setDocumentLocator (locator); + } + + /** <b>SAX2:</b> passes this callback to the next consumer, if any */ + public void startDocument () throws SAXException + { + if (docNext != null) + docNext.startDocument (); + } + + /** <b>SAX2:</b> passes this callback to the next consumer, if any */ + public void skippedEntity (String name) throws SAXException + { + if (docNext != null) + docNext.skippedEntity (name); + } + + /** <b>SAX2:</b> passes this callback to the next consumer, if any */ + public void processingInstruction (String target, String data) + throws SAXException + { + if (docNext != null) + docNext.processingInstruction (target, data); + } + + /** <b>SAX2:</b> passes this callback to the next consumer, if any */ + public void characters (char ch [], int start, int length) + throws SAXException + { + if (docNext != null) + docNext.characters (ch, start, length); + } + + /** <b>SAX2:</b> passes this callback to the next consumer, if any */ + public void ignorableWhitespace (char ch [], int start, int length) + throws SAXException + { + if (docNext != null) + docNext.ignorableWhitespace (ch, start, length); + } + + /** <b>SAX2:</b> passes this callback to the next consumer, if any */ + public void startPrefixMapping (String prefix, String uri) + throws SAXException + { + if (docNext != null) + docNext.startPrefixMapping (prefix, uri); + } + + /** <b>SAX2:</b> passes this callback to the next consumer, if any */ + public void startElement ( + String uri, String localName, + String qName, Attributes atts + ) throws SAXException + { + if (docNext != null) + docNext.startElement (uri, localName, qName, atts); + } + + /** <b>SAX2:</b> passes this callback to the next consumer, if any */ + public void endElement (String uri, String localName, String qName) + throws SAXException + { + if (docNext != null) + docNext.endElement (uri, localName, qName); + } + + /** <b>SAX2:</b> passes this callback to the next consumer, if any */ + public void endPrefixMapping (String prefix) throws SAXException + { + if (docNext != null) + docNext.endPrefixMapping (prefix); + } + + /** <b>SAX2:</b> passes this callback to the next consumer, if any */ + public void endDocument () throws SAXException + { + if (docNext != null) + docNext.endDocument (); + locator = null; + } + + + // DTD HANDLER DELEGATIONS + + /** <b>SAX1:</b> passes this callback to the next consumer, if any */ + public void unparsedEntityDecl ( + String name, + String publicId, + String systemId, + String notationName + ) throws SAXException + { + if (dtdNext != null) + dtdNext.unparsedEntityDecl (name, publicId, systemId, notationName); + } + + /** <b>SAX1:</b> passes this callback to the next consumer, if any */ + public void notationDecl (String name, String publicId, String systemId) + throws SAXException + { + if (dtdNext != null) + dtdNext.notationDecl (name, publicId, systemId); + } + + + // LEXICAL HANDLER DELEGATIONS + + /** <b>SAX2:</b> passes this callback to the next consumer, if any */ + public void startDTD (String name, String publicId, String systemId) + throws SAXException + { + if (lexNext != null) + lexNext.startDTD (name, publicId, systemId); + } + + /** <b>SAX2:</b> passes this callback to the next consumer, if any */ + public void endDTD () + throws SAXException + { + if (lexNext != null) + lexNext.endDTD (); + } + + /** <b>SAX2:</b> passes this callback to the next consumer, if any */ + public void comment (char ch [], int start, int length) + throws SAXException + { + if (lexNext != null) + lexNext.comment (ch, start, length); + } + + /** <b>SAX2:</b> passes this callback to the next consumer, if any */ + public void startCDATA () + throws SAXException + { + if (lexNext != null) + lexNext.startCDATA (); + } + + /** <b>SAX2:</b> passes this callback to the next consumer, if any */ + public void endCDATA () + throws SAXException + { + if (lexNext != null) + lexNext.endCDATA (); + } + + /** + * <b>SAX2:</b> passes this callback to the next consumer, if any. + */ + public void startEntity (String name) + throws SAXException + { + if (lexNext != null) + lexNext.startEntity (name); + } + + /** + * <b>SAX2:</b> passes this callback to the next consumer, if any. + */ + public void endEntity (String name) + throws SAXException + { + if (lexNext != null) + lexNext.endEntity (name); + } + + + // DECLARATION HANDLER DELEGATIONS + + + /** <b>SAX2:</b> passes this callback to the next consumer, if any */ + public void elementDecl (String name, String model) + throws SAXException + { + if (declNext != null) + declNext.elementDecl (name, model); + } + + /** <b>SAX2:</b> passes this callback to the next consumer, if any */ + public void attributeDecl (String eName, String aName, + String type, String mode, String value) + throws SAXException + { + if (declNext != null) + declNext.attributeDecl (eName, aName, type, mode, value); + } + + /** <b>SAX2:</b> passes this callback to the next consumer, if any */ + public void externalEntityDecl (String name, + String publicId, String systemId) + throws SAXException + { + if (declNext != null) + declNext.externalEntityDecl (name, publicId, systemId); + } + + /** <b>SAX2:</b> passes this callback to the next consumer, if any */ + public void internalEntityDecl (String name, String value) + throws SAXException + { + if (declNext != null) + declNext.internalEntityDecl (name, value); + } +} |