Makefile.am: New file.
* external/w3c_dom/Makefile.am: New file. * external/w3c_dom/Makefile.in: New file. * external/sax/Makefile.am: New file. * external/sax/Makefile.in: New file. * external/Makefile.in: New file. * external/Makefile.am: New file. * external/README: New file from Classpath. * configure, aclocal.m4, Makefile.in: Rebuilt. * configure.ac (AC_CONFIG_FILES): Create new Makefiles. * Makefile.am (SUBDIRS): Added external. (toolexeclib_LTLIBRARIES): Removed lib-org-xml-sax.la, lib-org-w3c-dom.la, lib-org-ietf-gss.la. (BOOTCLASSPATH): New variable. (AM_GCJFLAGS): Use it. (gnu_xml_source_files): New variable. (libgcj_la_SOURCES): Use it. (libgcj_la_LDADD): Include new convenience libraries. (lib_org_ietf_jgss_la_SOURCES): Removed. (lib_org_ietf_jgss_la_DEPENDENCIES): Likewise. (lib_org_ietf_jgss_la_LIBADD): Likewise. (lib_org_w3c_dom_la_SOURCES): Removed. (lib_org_w3c_dom_la_DEPENDENCIES): Likewise. (lib_org_w3c_dom_la_LIBADD): Likewise. (lib_org_xml_sax_la_SOURCES): Removed. (lib_org_xml_sax_la_DEPENDENCIES): Likewise. (lib_org_xml_sax_la_LIBADD): Likewise. (all_java_source_files): Updated. (property_files): Likewise. (libgcj-@gcc_version@.jar): Include classes from external/. (javax_source_files): Updated. (ordinary_java_source_files): Updated. * javax/xml/XMLConstants.java, javax/xml/datatype/DatatypeConfigurationException.java, javax/xml/datatype/DatatypeConstants.java, javax/xml/datatype/DatatypeFactory.java, javax/xml/datatype/Duration.java, javax/xml/datatype/XMLGregorianCalendar.java, javax/xml/datatype/package.html, javax/xml/namespace/NamespaceContext.java, javax/xml/namespace/QName.java, javax/xml/namespace/package.html, javax/xml/parsers/DocumentBuilder.java, javax/xml/parsers/DocumentBuilderFactory.java, javax/xml/parsers/FactoryConfigurationError.java, javax/xml/parsers/ParserConfigurationException.java, javax/xml/parsers/SAXParser.java, javax/xml/parsers/SAXParserFactory.java, javax/xml/parsers/package.html, javax/xml/transform/ErrorListener.java, javax/xml/transform/OutputKeys.java, javax/xml/transform/Result.java, javax/xml/transform/Source.java, javax/xml/transform/SourceLocator.java, javax/xml/transform/Templates.java, javax/xml/transform/Transformer.java, javax/xml/transform/TransformerConfigurationException.java, javax/xml/transform/TransformerException.java, javax/xml/transform/TransformerFactory.java, javax/xml/transform/TransformerFactoryConfigurationError.java, javax/xml/transform/URIResolver.java, javax/xml/transform/package.html, javax/xml/transform/dom/DOMLocator.java, javax/xml/transform/dom/DOMResult.java, javax/xml/transform/dom/DOMSource.java, javax/xml/transform/dom/package.html, javax/xml/transform/sax/SAXResult.java, javax/xml/transform/sax/SAXSource.java, javax/xml/transform/sax/SAXTransformerFactory.java, javax/xml/transform/sax/TemplatesHandler.java, javax/xml/transform/sax/TransformerHandler.java, javax/xml/transform/sax/package.html, javax/xml/transform/stream/StreamResult.java, javax/xml/transform/stream/StreamSource.java, javax/xml/transform/stream/package.html, javax/xml/validation/Schema.java, javax/xml/validation/SchemaFactory.java, javax/xml/validation/TypeInfoProvider.java, javax/xml/validation/Validator.java, javax/xml/validation/ValidatorHandler.java, javax/xml/validation/package.html, javax/xml/xpath/XPath.java, javax/xml/xpath/XPathConstants.java, javax/xml/xpath/XPathException.java, javax/xml/xpath/XPathExpression.java, javax/xml/xpath/XPathExpressionException.java, javax/xml/xpath/XPathFactory.java, javax/xml/xpath/XPathFactoryConfigurationException.java, javax/xml/xpath/XPathFunction.java, javax/xml/xpath/XPathFunctionException.java, javax/xml/xpath/XPathFunctionResolver.java, javax/xml/xpath/XPathVariableResolver.java, javax/xml/xpath/package.html: New files, from GNU JAXP. * org/w3c/dom/Attr.java, org/w3c/dom/CDATASection.java, org/w3c/dom/CharacterData.java, org/w3c/dom/Comment.java, org/w3c/dom/DOMException.java, org/w3c/dom/DOMImplementation.java, org/w3c/dom/Document.java, org/w3c/dom/DocumentFragment.java, org/w3c/dom/DocumentType.java, org/w3c/dom/Element.java, org/w3c/dom/Entity.java, org/w3c/dom/EntityReference.java, org/w3c/dom/NamedNodeMap.java, org/w3c/dom/Node.java, org/w3c/dom/NodeList.java, org/w3c/dom/Notation.java, org/w3c/dom/ProcessingInstruction.java, org/w3c/dom/Text.java, org/w3c/dom/ranges/DocumentRange.java, org/w3c/dom/ranges/Range.java, org/w3c/dom/ranges/RangeException.java, org/w3c/dom/traversal/DocumentTraversal.java, org/w3c/dom/traversal/NodeFilter.java, org/w3c/dom/traversal/NodeIterator.java, org/w3c/dom/traversal/TreeWalker.java, org/xml/sax/AttributeList.java, org/xml/sax/Attributes.java, org/xml/sax/ContentHandler.java, org/xml/sax/DTDHandler.java, org/xml/sax/DocumentHandler.java, org/xml/sax/EntityResolver.java, org/xml/sax/ErrorHandler.java, org/xml/sax/HandlerBase.java, org/xml/sax/InputSource.java, org/xml/sax/Locator.java, org/xml/sax/Parser.java, org/xml/sax/SAXException.java, org/xml/sax/SAXNotRecognizedException.java, org/xml/sax/SAXNotSupportedException.java, org/xml/sax/SAXParseException.java, org/xml/sax/XMLFilter.java, org/xml/sax/XMLReader.java, org/xml/sax/package.html, org/xml/sax/ext/DeclHandler.java, org/xml/sax/ext/LexicalHandler.java, org/xml/sax/ext/package.html, org/xml/sax/helpers/AttributeListImpl.java, org/xml/sax/helpers/AttributesImpl.java, org/xml/sax/helpers/DefaultHandler.java, org/xml/sax/helpers/LocatorImpl.java, org/xml/sax/helpers/NamespaceSupport.java, org/xml/sax/helpers/NewInstance.java, org/xml/sax/helpers/ParserAdapter.java, org/xml/sax/helpers/ParserFactory.java, org/xml/sax/helpers/XMLFilterImpl.java, org/xml/sax/helpers/XMLReaderAdapter.java, org/xml/sax/helpers/XMLReaderFactory.java, org/xml/sax/helpers/package.html: Moved to external/. * external/sax/README, external/sax/org/xml/sax/AttributeList.java, external/sax/org/xml/sax/Attributes.java, external/sax/org/xml/sax/ContentHandler.java, external/sax/org/xml/sax/DTDHandler.java, external/sax/org/xml/sax/DocumentHandler.java, external/sax/org/xml/sax/EntityResolver.java, external/sax/org/xml/sax/ErrorHandler.java, external/sax/org/xml/sax/HandlerBase.java, external/sax/org/xml/sax/InputSource.java, external/sax/org/xml/sax/Locator.java, external/sax/org/xml/sax/Parser.java, external/sax/org/xml/sax/SAXException.java, external/sax/org/xml/sax/SAXNotRecognizedException.java, external/sax/org/xml/sax/SAXNotSupportedException.java, external/sax/org/xml/sax/SAXParseException.java, external/sax/org/xml/sax/XMLFilter.java, external/sax/org/xml/sax/XMLReader.java, external/sax/org/xml/sax/package.html, external/sax/org/xml/sax/ext/Attributes2.java, external/sax/org/xml/sax/ext/Attributes2Impl.java, external/sax/org/xml/sax/ext/DeclHandler.java, external/sax/org/xml/sax/ext/DefaultHandler2.java, external/sax/org/xml/sax/ext/EntityResolver2.java, external/sax/org/xml/sax/ext/LexicalHandler.java, external/sax/org/xml/sax/ext/Locator2.java, external/sax/org/xml/sax/ext/Locator2Impl.java, external/sax/org/xml/sax/ext/package.html, external/sax/org/xml/sax/helpers/AttributeListImpl.java, external/sax/org/xml/sax/helpers/AttributesImpl.java, external/sax/org/xml/sax/helpers/DefaultHandler.java, external/sax/org/xml/sax/helpers/LocatorImpl.java, external/sax/org/xml/sax/helpers/NamespaceSupport.java, external/sax/org/xml/sax/helpers/NewInstance.java, external/sax/org/xml/sax/helpers/ParserAdapter.java, external/sax/org/xml/sax/helpers/ParserFactory.java, external/sax/org/xml/sax/helpers/XMLFilterImpl.java, external/sax/org/xml/sax/helpers/XMLReaderAdapter.java, external/sax/org/xml/sax/helpers/XMLReaderFactory.java, external/sax/org/xml/sax/helpers/package.html, external/w3c_dom/COPYRIGHT.html, external/w3c_dom/README, external/w3c_dom/org/w3c/dom/Attr.java, external/w3c_dom/org/w3c/dom/CDATASection.java, external/w3c_dom/org/w3c/dom/CharacterData.java, external/w3c_dom/org/w3c/dom/Comment.java, external/w3c_dom/org/w3c/dom/DOMConfiguration.java, external/w3c_dom/org/w3c/dom/DOMError.java, external/w3c_dom/org/w3c/dom/DOMErrorHandler.java, external/w3c_dom/org/w3c/dom/DOMException.java, external/w3c_dom/org/w3c/dom/DOMImplementation.java, external/w3c_dom/org/w3c/dom/DOMImplementationList.java, external/w3c_dom/org/w3c/dom/DOMImplementationSource.java, external/w3c_dom/org/w3c/dom/DOMLocator.java, external/w3c_dom/org/w3c/dom/DOMStringList.java, external/w3c_dom/org/w3c/dom/Document.java, external/w3c_dom/org/w3c/dom/DocumentFragment.java, external/w3c_dom/org/w3c/dom/DocumentType.java, external/w3c_dom/org/w3c/dom/Element.java, external/w3c_dom/org/w3c/dom/Entity.java, external/w3c_dom/org/w3c/dom/EntityReference.java, external/w3c_dom/org/w3c/dom/NameList.java, external/w3c_dom/org/w3c/dom/NamedNodeMap.java, external/w3c_dom/org/w3c/dom/Node.java, external/w3c_dom/org/w3c/dom/NodeList.java, external/w3c_dom/org/w3c/dom/Notation.java, external/w3c_dom/org/w3c/dom/ProcessingInstruction.java, external/w3c_dom/org/w3c/dom/Text.java, external/w3c_dom/org/w3c/dom/TypeInfo.java, external/w3c_dom/org/w3c/dom/UserDataHandler.java, external/w3c_dom/org/w3c/dom/bootstrap/DOMImplementationRegistry.java, external/w3c_dom/org/w3c/dom/css/CSS2Properties.java, external/w3c_dom/org/w3c/dom/css/CSSCharsetRule.java, external/w3c_dom/org/w3c/dom/css/CSSFontFaceRule.java, external/w3c_dom/org/w3c/dom/css/CSSImportRule.java, external/w3c_dom/org/w3c/dom/css/CSSMediaRule.java, external/w3c_dom/org/w3c/dom/css/CSSPageRule.java, external/w3c_dom/org/w3c/dom/css/CSSPrimitiveValue.java, external/w3c_dom/org/w3c/dom/css/CSSRule.java, external/w3c_dom/org/w3c/dom/css/CSSRuleList.java, external/w3c_dom/org/w3c/dom/css/CSSStyleDeclaration.java, external/w3c_dom/org/w3c/dom/css/CSSStyleRule.java, external/w3c_dom/org/w3c/dom/css/CSSStyleSheet.java, external/w3c_dom/org/w3c/dom/css/CSSUnknownRule.java, external/w3c_dom/org/w3c/dom/css/CSSValue.java, external/w3c_dom/org/w3c/dom/css/CSSValueList.java, external/w3c_dom/org/w3c/dom/css/Counter.java, external/w3c_dom/org/w3c/dom/css/DOMImplementationCSS.java, external/w3c_dom/org/w3c/dom/css/DocumentCSS.java, external/w3c_dom/org/w3c/dom/css/ElementCSSInlineStyle.java, external/w3c_dom/org/w3c/dom/css/RGBColor.java, external/w3c_dom/org/w3c/dom/css/Rect.java, external/w3c_dom/org/w3c/dom/css/ViewCSS.java, external/w3c_dom/org/w3c/dom/events/DocumentEvent.java, external/w3c_dom/org/w3c/dom/events/Event.java, external/w3c_dom/org/w3c/dom/events/EventException.java, external/w3c_dom/org/w3c/dom/events/EventListener.java, external/w3c_dom/org/w3c/dom/events/EventTarget.java, external/w3c_dom/org/w3c/dom/events/MouseEvent.java, external/w3c_dom/org/w3c/dom/events/MutationEvent.java, external/w3c_dom/org/w3c/dom/events/UIEvent.java, external/w3c_dom/org/w3c/dom/html2/HTMLAnchorElement.java, external/w3c_dom/org/w3c/dom/html2/HTMLAppletElement.java, external/w3c_dom/org/w3c/dom/html2/HTMLAreaElement.java, external/w3c_dom/org/w3c/dom/html2/HTMLBRElement.java, external/w3c_dom/org/w3c/dom/html2/HTMLBaseElement.java, external/w3c_dom/org/w3c/dom/html2/HTMLBaseFontElement.java, external/w3c_dom/org/w3c/dom/html2/HTMLBodyElement.java, external/w3c_dom/org/w3c/dom/html2/HTMLButtonElement.java, external/w3c_dom/org/w3c/dom/html2/HTMLCollection.java, external/w3c_dom/org/w3c/dom/html2/HTMLDListElement.java, external/w3c_dom/org/w3c/dom/html2/HTMLDirectoryElement.java, external/w3c_dom/org/w3c/dom/html2/HTMLDivElement.java, external/w3c_dom/org/w3c/dom/html2/HTMLDocument.java, external/w3c_dom/org/w3c/dom/html2/HTMLElement.java, external/w3c_dom/org/w3c/dom/html2/HTMLFieldSetElement.java, external/w3c_dom/org/w3c/dom/html2/HTMLFontElement.java, external/w3c_dom/org/w3c/dom/html2/HTMLFormElement.java, external/w3c_dom/org/w3c/dom/html2/HTMLFrameElement.java, external/w3c_dom/org/w3c/dom/html2/HTMLFrameSetElement.java, external/w3c_dom/org/w3c/dom/html2/HTMLHRElement.java, external/w3c_dom/org/w3c/dom/html2/HTMLHeadElement.java, external/w3c_dom/org/w3c/dom/html2/HTMLHeadingElement.java, external/w3c_dom/org/w3c/dom/html2/HTMLHtmlElement.java, external/w3c_dom/org/w3c/dom/html2/HTMLIFrameElement.java, external/w3c_dom/org/w3c/dom/html2/HTMLImageElement.java, external/w3c_dom/org/w3c/dom/html2/HTMLInputElement.java, external/w3c_dom/org/w3c/dom/html2/HTMLIsIndexElement.java, external/w3c_dom/org/w3c/dom/html2/HTMLLIElement.java, external/w3c_dom/org/w3c/dom/html2/HTMLLabelElement.java, external/w3c_dom/org/w3c/dom/html2/HTMLLegendElement.java, external/w3c_dom/org/w3c/dom/html2/HTMLLinkElement.java, external/w3c_dom/org/w3c/dom/html2/HTMLMapElement.java, external/w3c_dom/org/w3c/dom/html2/HTMLMenuElement.java, external/w3c_dom/org/w3c/dom/html2/HTMLMetaElement.java, external/w3c_dom/org/w3c/dom/html2/HTMLModElement.java, external/w3c_dom/org/w3c/dom/html2/HTMLOListElement.java, external/w3c_dom/org/w3c/dom/html2/HTMLObjectElement.java, external/w3c_dom/org/w3c/dom/html2/HTMLOptGroupElement.java, external/w3c_dom/org/w3c/dom/html2/HTMLOptionElement.java, external/w3c_dom/org/w3c/dom/html2/HTMLOptionsCollection.java, external/w3c_dom/org/w3c/dom/html2/HTMLParagraphElement.java, external/w3c_dom/org/w3c/dom/html2/HTMLParamElement.java, external/w3c_dom/org/w3c/dom/html2/HTMLPreElement.java, external/w3c_dom/org/w3c/dom/html2/HTMLQuoteElement.java, external/w3c_dom/org/w3c/dom/html2/HTMLScriptElement.java, external/w3c_dom/org/w3c/dom/html2/HTMLSelectElement.java, external/w3c_dom/org/w3c/dom/html2/HTMLStyleElement.java, external/w3c_dom/org/w3c/dom/html2/HTMLTableCaptionElement.java, external/w3c_dom/org/w3c/dom/html2/HTMLTableCellElement.java, external/w3c_dom/org/w3c/dom/html2/HTMLTableColElement.java, external/w3c_dom/org/w3c/dom/html2/HTMLTableElement.java, external/w3c_dom/org/w3c/dom/html2/HTMLTableRowElement.java, external/w3c_dom/org/w3c/dom/html2/HTMLTableSectionElement.java, external/w3c_dom/org/w3c/dom/html2/HTMLTextAreaElement.java, external/w3c_dom/org/w3c/dom/html2/HTMLTitleElement.java, external/w3c_dom/org/w3c/dom/html2/HTMLUListElement.java, external/w3c_dom/org/w3c/dom/ls/DOMImplementationLS.java, external/w3c_dom/org/w3c/dom/ls/LSException.java, external/w3c_dom/org/w3c/dom/ls/LSInput.java, external/w3c_dom/org/w3c/dom/ls/LSLoadEvent.java, external/w3c_dom/org/w3c/dom/ls/LSOutput.java, external/w3c_dom/org/w3c/dom/ls/LSParser.java, external/w3c_dom/org/w3c/dom/ls/LSParserFilter.java, external/w3c_dom/org/w3c/dom/ls/LSProgressEvent.java, external/w3c_dom/org/w3c/dom/ls/LSResourceResolver.java, external/w3c_dom/org/w3c/dom/ls/LSSerializer.java, external/w3c_dom/org/w3c/dom/ls/LSSerializerFilter.java, external/w3c_dom/org/w3c/dom/ranges/DocumentRange.java, external/w3c_dom/org/w3c/dom/ranges/Range.java, external/w3c_dom/org/w3c/dom/ranges/RangeException.java, external/w3c_dom/org/w3c/dom/stylesheets/DocumentStyle.java, external/w3c_dom/org/w3c/dom/stylesheets/LinkStyle.java, external/w3c_dom/org/w3c/dom/stylesheets/MediaList.java, external/w3c_dom/org/w3c/dom/stylesheets/StyleSheet.java, external/w3c_dom/org/w3c/dom/stylesheets/StyleSheetList.java, external/w3c_dom/org/w3c/dom/traversal/DocumentTraversal.java, external/w3c_dom/org/w3c/dom/traversal/NodeFilter.java, external/w3c_dom/org/w3c/dom/traversal/NodeIterator.java, external/w3c_dom/org/w3c/dom/traversal/TreeWalker.java, external/w3c_dom/org/w3c/dom/views/AbstractView.java, external/w3c_dom/org/w3c/dom/views/DocumentView.java, external/w3c_dom/org/w3c/dom/xpath/XPathEvaluator.java, external/w3c_dom/org/w3c/dom/xpath/XPathException.java, external/w3c_dom/org/w3c/dom/xpath/XPathExpression.java, external/w3c_dom/org/w3c/dom/xpath/XPathNSResolver.java, external/w3c_dom/org/w3c/dom/xpath/XPathNamespace.java, external/w3c_dom/org/w3c/dom/xpath/XPathResult.java: New files from Classpath. From-SVN: r94577
This commit is contained in:
@@ -0,0 +1,250 @@
|
||||
/* CallFilter.java --
|
||||
Copyright (C) 1999,2000,2001 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Classpath.
|
||||
|
||||
GNU Classpath 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, or (at your option)
|
||||
any later version.
|
||||
|
||||
GNU Classpath 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 GNU Classpath; see the file COPYING. If not, write to the
|
||||
Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
02111-1307 USA.
|
||||
|
||||
Linking this library statically or dynamically with other modules is
|
||||
making a combined work based on this library. Thus, the terms and
|
||||
conditions of the GNU General Public License cover the whole
|
||||
combination.
|
||||
|
||||
As a special exception, the copyright holders of this library give you
|
||||
permission to link this library with independent modules to produce an
|
||||
executable, regardless of the license terms of these independent
|
||||
modules, and to copy and distribute the resulting executable under
|
||||
terms of your choice, provided that you also meet, for each linked
|
||||
independent module, the terms and conditions of the license of that
|
||||
module. An independent module is a module which is not derived from
|
||||
or based on this library. If you modify this library, you may extend
|
||||
this exception to your version of the library, but you are not
|
||||
obligated to do so. If you do not wish to do so, delete this
|
||||
exception statement from your version. */
|
||||
|
||||
package gnu.xml.pipeline;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.*;
|
||||
|
||||
import org.xml.sax.*;
|
||||
import org.xml.sax.ext.*;
|
||||
import org.xml.sax.helpers.XMLReaderFactory;
|
||||
|
||||
import gnu.xml.util.Resolver;
|
||||
import gnu.xml.util.XMLWriter;
|
||||
|
||||
|
||||
/**
|
||||
* Input is sent as an XML request to given URI, and the output of this
|
||||
* filter is the parsed response to that request.
|
||||
* A connection is opened to the remote URI when the startDocument call is
|
||||
* issued through this filter, and the request is finished when the
|
||||
* endDocument call is issued. Events should be written quickly enough to
|
||||
* prevent the remote HTTP server from aborting the connection due to
|
||||
* inactivity; you may want to buffer text in an earlier pipeline stage.
|
||||
* If your application requires validity checking of such
|
||||
* outputs, have the output pipeline include a validation stage.
|
||||
*
|
||||
* <p>In effect, this makes a remote procedure call to the URI, with the
|
||||
* request and response document syntax as chosen by the application.
|
||||
* <em>Note that all the input events must be seen, and sent to the URI,
|
||||
* before the first output event can be seen. </em> Clients are delayed
|
||||
* at least by waiting for the server to respond, constraining concurrency.
|
||||
* Services can thus be used to synchronize concurrent activities, and
|
||||
* even to prioritize service among different clients.
|
||||
*
|
||||
* <p> You are advised to avoid restricting yourself to an "RPC" model
|
||||
* for distributed computation. With a World Wide Web, network latencies
|
||||
* and failures (e.g. non-availability)
|
||||
* are significant; adopting a "procedure" model, rather than a workflow
|
||||
* model where bulk requests are sent and worked on asynchronously, is not
|
||||
* generally an optimal system-wide architecture. When the messages may
|
||||
* need authentication, such as with an OpenPGP signature, or when server
|
||||
* loads don't argue in favor of immediate responses, non-RPC models can
|
||||
* be advantageous. (So-called "peer to peer" computing models are one
|
||||
* additional type of model, though too often that term is applied to
|
||||
* systems that still have a centralized control structure.)
|
||||
*
|
||||
* <p> <em>Be strict in what you send, liberal in what you accept,</em> as
|
||||
* the Internet tradition goes. Strictly conformant data should never cause
|
||||
* problems to its receiver; make your request pipeline be very strict, and
|
||||
* don't compromise on that. Make your response pipeline strict as well,
|
||||
* but be ready to tolerate specific mild, temporary, and well-documented
|
||||
* variations from specific communications peers.
|
||||
*
|
||||
* @see XmlServlet
|
||||
*
|
||||
* @author David Brownell
|
||||
*/
|
||||
final public class CallFilter implements EventConsumer
|
||||
{
|
||||
private Requestor req;
|
||||
private EventConsumer next;
|
||||
private URL target;
|
||||
private URLConnection conn;
|
||||
private ErrorHandler errHandler;
|
||||
|
||||
|
||||
/**
|
||||
* Initializes a call filter so that its inputs are sent to the
|
||||
* specified URI, and its outputs are sent to the next consumer
|
||||
* provided.
|
||||
*
|
||||
* @exception IOException if the URI isn't accepted as a URL
|
||||
*/
|
||||
// constructor used by PipelineFactory
|
||||
public CallFilter (String uri, EventConsumer next)
|
||||
throws IOException
|
||||
{
|
||||
this.next = next;
|
||||
req = new Requestor ();
|
||||
setCallTarget (uri);
|
||||
}
|
||||
|
||||
/**
|
||||
* Assigns the URI of the call target to be used.
|
||||
* Does not affect calls currently being made.
|
||||
*/
|
||||
final public void setCallTarget (String uri)
|
||||
throws IOException
|
||||
{
|
||||
target = new URL (uri);
|
||||
}
|
||||
|
||||
/**
|
||||
* Assigns the error handler to be used to present most fatal
|
||||
* errors.
|
||||
*/
|
||||
public void setErrorHandler (ErrorHandler handler)
|
||||
{
|
||||
req.setErrorHandler (handler);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the call target's URI.
|
||||
*/
|
||||
final public String getCallTarget ()
|
||||
{
|
||||
return target.toString ();
|
||||
}
|
||||
|
||||
/** Returns the content handler currently in use. */
|
||||
final public org.xml.sax.ContentHandler getContentHandler ()
|
||||
{
|
||||
return req;
|
||||
}
|
||||
|
||||
/** Returns the DTD handler currently in use. */
|
||||
final public DTDHandler getDTDHandler ()
|
||||
{
|
||||
return req;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the declaration or lexical handler currently in
|
||||
* use, or throws an exception for other properties.
|
||||
*/
|
||||
final public Object getProperty (String id)
|
||||
throws SAXNotRecognizedException
|
||||
{
|
||||
if (EventFilter.DECL_HANDLER.equals (id))
|
||||
return req;
|
||||
if (EventFilter.LEXICAL_HANDLER.equals (id))
|
||||
return req;
|
||||
throw new SAXNotRecognizedException (id);
|
||||
}
|
||||
|
||||
|
||||
// JDK 1.1 seems to need it to be done this way, sigh
|
||||
ErrorHandler getErrorHandler () { return errHandler; }
|
||||
|
||||
//
|
||||
// Takes input and echoes to server as POST input.
|
||||
// Then sends the POST reply to the next pipeline element.
|
||||
//
|
||||
final class Requestor extends XMLWriter
|
||||
{
|
||||
Requestor ()
|
||||
{
|
||||
super ((Writer)null);
|
||||
}
|
||||
|
||||
public synchronized void startDocument () throws SAXException
|
||||
{
|
||||
// Connect to remote object and set up to send it XML text
|
||||
try {
|
||||
if (conn != null)
|
||||
throw new IllegalStateException ("call is being made");
|
||||
|
||||
conn = target.openConnection ();
|
||||
conn.setDoOutput (true);
|
||||
conn.setRequestProperty ("Content-Type",
|
||||
"application/xml;charset=UTF-8");
|
||||
|
||||
setWriter (new OutputStreamWriter (
|
||||
conn.getOutputStream (),
|
||||
"UTF8"), "UTF-8");
|
||||
|
||||
} catch (IOException e) {
|
||||
fatal ("can't write (POST) to URI: " + target, e);
|
||||
}
|
||||
|
||||
// NOW base class can safely write that text!
|
||||
super.startDocument ();
|
||||
}
|
||||
|
||||
public void endDocument () throws SAXException
|
||||
{
|
||||
//
|
||||
// Finish writing the request (for HTTP, a POST);
|
||||
// this closes the output stream.
|
||||
//
|
||||
super.endDocument ();
|
||||
|
||||
//
|
||||
// Receive the response.
|
||||
// Produce events for the next stage.
|
||||
//
|
||||
InputSource source;
|
||||
XMLReader producer;
|
||||
String encoding;
|
||||
|
||||
try {
|
||||
|
||||
source = new InputSource (conn.getInputStream ());
|
||||
|
||||
// FIXME if status is anything but success, report it!! It'd be good to
|
||||
// save the request data just in case we need to deal with a forward.
|
||||
|
||||
encoding = Resolver.getEncoding (conn.getContentType ());
|
||||
if (encoding != null)
|
||||
source.setEncoding (encoding);
|
||||
|
||||
producer = XMLReaderFactory.createXMLReader ();
|
||||
producer.setErrorHandler (getErrorHandler ());
|
||||
EventFilter.bind (producer, next);
|
||||
producer.parse (source);
|
||||
conn = null;
|
||||
|
||||
} catch (IOException e) {
|
||||
fatal ("I/O Exception reading response, " + e.getMessage (), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,969 @@
|
||||
/* DomConsumer.java --
|
||||
Copyright (C) 1999,2000,2001 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Classpath.
|
||||
|
||||
GNU Classpath 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, or (at your option)
|
||||
any later version.
|
||||
|
||||
GNU Classpath 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 GNU Classpath; see the file COPYING. If not, write to the
|
||||
Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
02111-1307 USA.
|
||||
|
||||
Linking this library statically or dynamically with other modules is
|
||||
making a combined work based on this library. Thus, the terms and
|
||||
conditions of the GNU General Public License cover the whole
|
||||
combination.
|
||||
|
||||
As a special exception, the copyright holders of this library give you
|
||||
permission to link this library with independent modules to produce an
|
||||
executable, regardless of the license terms of these independent
|
||||
modules, and to copy and distribute the resulting executable under
|
||||
terms of your choice, provided that you also meet, for each linked
|
||||
independent module, the terms and conditions of the license of that
|
||||
module. An independent module is a module which is not derived from
|
||||
or based on this library. If you modify this library, you may extend
|
||||
this exception to your version of the library, but you are not
|
||||
obligated to do so. If you do not wish to do so, delete this
|
||||
exception statement from your version. */
|
||||
|
||||
package gnu.xml.pipeline;
|
||||
|
||||
import java.util.Hashtable;
|
||||
|
||||
import org.w3c.dom.*;
|
||||
import org.xml.sax.*;
|
||||
import org.xml.sax.ext.DeclHandler;
|
||||
import org.xml.sax.ext.LexicalHandler;
|
||||
import org.xml.sax.helpers.AttributesImpl;
|
||||
|
||||
import gnu.xml.aelfred2.ContentHandler2;
|
||||
import gnu.xml.util.DomParser;
|
||||
|
||||
|
||||
/**
|
||||
* This consumer builds a DOM Document from its input, acting either as a
|
||||
* pipeline terminus or as an intermediate buffer. When a document's worth
|
||||
* of events has been delivered to this consumer, that document is read with
|
||||
* a {@link DomParser} and sent to the next consumer. It is also available
|
||||
* as a read-once property.
|
||||
*
|
||||
* <p>The DOM tree is constructed as faithfully as possible. There are some
|
||||
* complications since a DOM should expose behaviors that can't be implemented
|
||||
* without API backdoors into that DOM, and because some SAX parsers don't
|
||||
* report all the information that DOM permits to be exposed. The general
|
||||
* problem areas involve information from the Document Type Declaration (DTD).
|
||||
* DOM only represents a limited subset, but has some behaviors that depend
|
||||
* on much deeper knowledge of a document's DTD. You shouldn't have much to
|
||||
* worry about unless you change handling of "noise" nodes from its default
|
||||
* setting (which ignores them all); note if you use JAXP to populate your
|
||||
* DOM trees, it wants to save "noise" nodes by default. (Such nodes include
|
||||
* ignorable whitespace, comments, entity references and CDATA boundaries.)
|
||||
* Otherwise, your
|
||||
* main worry will be if you use a SAX parser that doesn't flag ignorable
|
||||
* whitespace unless it's validating (few don't).
|
||||
*
|
||||
* <p> The SAX2 events used as input must contain XML Names for elements
|
||||
* and attributes, with original prefixes. In SAX2,
|
||||
* this is optional unless the "namespace-prefixes" parser feature is set.
|
||||
* Moreover, many application components won't provide completely correct
|
||||
* structures anyway. <em>Before you convert a DOM to an output document,
|
||||
* you should plan to postprocess it to create or repair such namespace
|
||||
* information.</em> The {@link NSFilter} pipeline stage does such work.
|
||||
*
|
||||
* <p> <em>Note: changes late in DOM L2 process made it impractical to
|
||||
* attempt to create the DocumentType node in any implementation-neutral way,
|
||||
* much less to populate it (L1 didn't support even creating such nodes).
|
||||
* To create and populate such a node, subclass the inner
|
||||
* {@link DomConsumer.Handler} class and teach it about the backdoors into
|
||||
* whatever DOM implementation you want. It's possible that some revised
|
||||
* DOM API (L3?) will make this problem solvable again. </em>
|
||||
*
|
||||
* @see DomParser
|
||||
*
|
||||
* @author David Brownell
|
||||
*/
|
||||
public class DomConsumer implements EventConsumer
|
||||
{
|
||||
private Class domImpl;
|
||||
|
||||
private boolean hidingCDATA = true;
|
||||
private boolean hidingComments = true;
|
||||
private boolean hidingWhitespace = true;
|
||||
private boolean hidingReferences = true;
|
||||
|
||||
private Handler handler;
|
||||
private ErrorHandler errHandler;
|
||||
|
||||
private EventConsumer next;
|
||||
|
||||
// FIXME: this can't be a generic pipeline stage just now,
|
||||
// since its input became a Class not a String (to be turned
|
||||
// into a class, using the right class loader)
|
||||
|
||||
|
||||
/**
|
||||
* Configures this pipeline terminus to use the specified implementation
|
||||
* of DOM when constructing its result value.
|
||||
*
|
||||
* @param impl class implementing {@link org.w3c.dom.Document Document}
|
||||
* which publicly exposes a default constructor
|
||||
*
|
||||
* @exception SAXException when there is a problem creating an
|
||||
* empty DOM document using the specified implementation
|
||||
*/
|
||||
public DomConsumer (Class impl)
|
||||
throws SAXException
|
||||
{
|
||||
domImpl = impl;
|
||||
handler = new Handler (this);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is the hook through which a subclass provides a handler
|
||||
* which knows how to access DOM extensions, specific to some
|
||||
* implementation, to record additional data in a DOM.
|
||||
* Treat this as part of construction; don't call it except
|
||||
* before (or between) parses.
|
||||
*/
|
||||
protected void setHandler (Handler h)
|
||||
{
|
||||
handler = h;
|
||||
}
|
||||
|
||||
|
||||
private Document emptyDocument ()
|
||||
throws SAXException
|
||||
{
|
||||
try {
|
||||
return (Document) domImpl.newInstance ();
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new SAXException ("can't access constructor: "
|
||||
+ e.getMessage ());
|
||||
} catch (InstantiationException e) {
|
||||
throw new SAXException ("can't instantiate Document: "
|
||||
+ e.getMessage ());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Configures this consumer as a buffer/filter, using the specified
|
||||
* DOM implementation when constructing its result value.
|
||||
*
|
||||
* <p> This event consumer acts as a buffer and filter, in that it
|
||||
* builds a DOM tree and then writes it out when <em>endDocument</em>
|
||||
* is invoked. Because of the limitations of DOM, much information
|
||||
* will as a rule not be seen in that replay. To get a full fidelity
|
||||
* copy of the input event stream, use a {@link TeeConsumer}.
|
||||
*
|
||||
* @param impl class implementing {@link org.w3c.dom.Document Document}
|
||||
* which publicly exposes a default constructor
|
||||
* @param next receives a "replayed" sequence of parse events when
|
||||
* the <em>endDocument</em> method is invoked.
|
||||
*
|
||||
* @exception SAXException when there is a problem creating an
|
||||
* empty DOM document using the specified DOM implementation
|
||||
*/
|
||||
public DomConsumer (Class impl, EventConsumer n)
|
||||
throws SAXException
|
||||
{
|
||||
this (impl);
|
||||
next = n;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the document constructed from the preceding
|
||||
* sequence of events. This method should not be
|
||||
* used again until another sequence of events has been
|
||||
* given to this EventConsumer.
|
||||
*/
|
||||
final public Document getDocument ()
|
||||
{
|
||||
return handler.clearDocument ();
|
||||
}
|
||||
|
||||
public void setErrorHandler (ErrorHandler handler)
|
||||
{
|
||||
errHandler = handler;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns true if the consumer is hiding entity references nodes
|
||||
* (the default), and false if EntityReference nodes should
|
||||
* instead be created. Such EntityReference nodes will normally be
|
||||
* empty, unless an implementation arranges to populate them and then
|
||||
* turn them back into readonly objects.
|
||||
*
|
||||
* @see #setHidingReferences
|
||||
*/
|
||||
final public boolean isHidingReferences ()
|
||||
{ return hidingReferences; }
|
||||
|
||||
/**
|
||||
* Controls whether the consumer will hide entity expansions,
|
||||
* or will instead mark them with entity reference nodes.
|
||||
*
|
||||
* @see #isHidingReferences
|
||||
* @param flag False if entity reference nodes will appear
|
||||
*/
|
||||
final public void setHidingReferences (boolean flag)
|
||||
{ hidingReferences = flag; }
|
||||
|
||||
|
||||
/**
|
||||
* Returns true if the consumer is hiding comments (the default),
|
||||
* and false if they should be placed into the output document.
|
||||
*
|
||||
* @see #setHidingComments
|
||||
*/
|
||||
public final boolean isHidingComments ()
|
||||
{ return hidingComments; }
|
||||
|
||||
/**
|
||||
* Controls whether the consumer is hiding comments.
|
||||
*
|
||||
* @see #isHidingComments
|
||||
*/
|
||||
public final void setHidingComments (boolean flag)
|
||||
{ hidingComments = flag; }
|
||||
|
||||
|
||||
/**
|
||||
* Returns true if the consumer is hiding ignorable whitespace
|
||||
* (the default), and false if such whitespace should be placed
|
||||
* into the output document as children of element nodes.
|
||||
*
|
||||
* @see #setHidingWhitespace
|
||||
*/
|
||||
public final boolean isHidingWhitespace ()
|
||||
{ return hidingWhitespace; }
|
||||
|
||||
/**
|
||||
* Controls whether the consumer hides ignorable whitespace
|
||||
*
|
||||
* @see #isHidingComments
|
||||
*/
|
||||
public final void setHidingWhitespace (boolean flag)
|
||||
{ hidingWhitespace = flag; }
|
||||
|
||||
|
||||
/**
|
||||
* Returns true if the consumer is saving CDATA boundaries, or
|
||||
* false (the default) otherwise.
|
||||
*
|
||||
* @see #setHidingCDATA
|
||||
*/
|
||||
final public boolean isHidingCDATA ()
|
||||
{ return hidingCDATA; }
|
||||
|
||||
/**
|
||||
* Controls whether the consumer will save CDATA boundaries.
|
||||
*
|
||||
* @see #isHidingCDATA
|
||||
* @param flag True to treat CDATA text differently from other
|
||||
* text nodes
|
||||
*/
|
||||
final public void setHidingCDATA (boolean flag)
|
||||
{ hidingCDATA = flag; }
|
||||
|
||||
|
||||
|
||||
/** Returns the document handler being used. */
|
||||
final public ContentHandler getContentHandler ()
|
||||
{ return handler; }
|
||||
|
||||
/** Returns the DTD handler being used. */
|
||||
final public DTDHandler getDTDHandler ()
|
||||
{ return handler; }
|
||||
|
||||
/**
|
||||
* Returns the lexical handler being used.
|
||||
* (DOM construction can't really use declaration handlers.)
|
||||
*/
|
||||
final public Object getProperty (String id)
|
||||
throws SAXNotRecognizedException
|
||||
{
|
||||
if ("http://xml.org/sax/properties/lexical-handler".equals (id))
|
||||
return handler;
|
||||
if ("http://xml.org/sax/properties/declaration-handler".equals (id))
|
||||
return handler;
|
||||
throw new SAXNotRecognizedException (id);
|
||||
}
|
||||
|
||||
EventConsumer getNext () { return next; }
|
||||
|
||||
ErrorHandler getErrorHandler () { return errHandler; }
|
||||
|
||||
/**
|
||||
* Class used to intercept various parsing events and use them to
|
||||
* populate a DOM document. Subclasses would typically know and use
|
||||
* backdoors into specific DOM implementations, used to implement
|
||||
* DTD-related functionality.
|
||||
*
|
||||
* <p> Note that if this ever throws a DOMException (runtime exception)
|
||||
* that will indicate a bug in the DOM (e.g. doesn't support something
|
||||
* per specification) or the parser (e.g. emitted an illegal name, or
|
||||
* accepted illegal input data). </p>
|
||||
*/
|
||||
public static class Handler
|
||||
implements ContentHandler2, LexicalHandler,
|
||||
DTDHandler, DeclHandler
|
||||
{
|
||||
protected DomConsumer consumer;
|
||||
|
||||
private DOMImplementation impl;
|
||||
private Document document;
|
||||
private boolean isL2;
|
||||
|
||||
private Locator locator;
|
||||
private Node top;
|
||||
private boolean inCDATA;
|
||||
private boolean mergeCDATA;
|
||||
private boolean inDTD;
|
||||
private String currentEntity;
|
||||
|
||||
private boolean recreatedAttrs;
|
||||
private AttributesImpl attributes = new AttributesImpl ();
|
||||
|
||||
/**
|
||||
* Subclasses may use SAX2 events to provide additional
|
||||
* behaviors in the resulting DOM.
|
||||
*/
|
||||
protected Handler (DomConsumer consumer)
|
||||
throws SAXException
|
||||
{
|
||||
this.consumer = consumer;
|
||||
document = consumer.emptyDocument ();
|
||||
impl = document.getImplementation ();
|
||||
isL2 = impl.hasFeature ("XML", "2.0");
|
||||
}
|
||||
|
||||
private void fatal (String message, Exception x)
|
||||
throws SAXException
|
||||
{
|
||||
SAXParseException e;
|
||||
ErrorHandler errHandler = consumer.getErrorHandler ();;
|
||||
|
||||
if (locator == null)
|
||||
e = new SAXParseException (message, null, null, -1, -1, x);
|
||||
else
|
||||
e = new SAXParseException (message, locator, x);
|
||||
if (errHandler != null)
|
||||
errHandler.fatalError (e);
|
||||
throw e;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns and forgets the document produced. If the handler is
|
||||
* reused, a new document may be created.
|
||||
*/
|
||||
Document clearDocument ()
|
||||
{
|
||||
Document retval = document;
|
||||
document = null;
|
||||
locator = null;
|
||||
return retval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the document under construction.
|
||||
*/
|
||||
protected Document getDocument ()
|
||||
{ return document; }
|
||||
|
||||
/**
|
||||
* Returns the current node being populated. This is usually
|
||||
* an Element or Document, but it might be an EntityReference
|
||||
* node if some implementation-specific code knows how to put
|
||||
* those into the result tree and later mark them as readonly.
|
||||
*/
|
||||
protected Node getTop ()
|
||||
{ return top; }
|
||||
|
||||
|
||||
// SAX1
|
||||
public void setDocumentLocator (Locator locator)
|
||||
{
|
||||
this.locator = locator;
|
||||
}
|
||||
|
||||
// SAX1
|
||||
public void startDocument ()
|
||||
throws SAXException
|
||||
{
|
||||
if (document == null)
|
||||
try {
|
||||
if (isL2) {
|
||||
// couple to original implementation
|
||||
document = impl.createDocument (null, "foo", null);
|
||||
document.removeChild (document.getFirstChild ());
|
||||
} else {
|
||||
document = consumer.emptyDocument ();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
fatal ("DOM create document", e);
|
||||
}
|
||||
top = document;
|
||||
}
|
||||
|
||||
// ContentHandler2
|
||||
public void xmlDecl(String version,
|
||||
String encoding,
|
||||
boolean standalone,
|
||||
String inputEncoding)
|
||||
throws SAXException
|
||||
{
|
||||
if (document != null)
|
||||
{
|
||||
document.setXmlVersion(version);
|
||||
document.setXmlStandalone(standalone);
|
||||
}
|
||||
}
|
||||
|
||||
// SAX1
|
||||
public void endDocument ()
|
||||
throws SAXException
|
||||
{
|
||||
try {
|
||||
if (consumer.getNext () != null && document != null) {
|
||||
DomParser parser = new DomParser (document);
|
||||
|
||||
EventFilter.bind (parser, consumer.getNext ());
|
||||
parser.parse ("ignored");
|
||||
}
|
||||
} finally {
|
||||
top = null;
|
||||
}
|
||||
}
|
||||
|
||||
// SAX1
|
||||
public void processingInstruction (String target, String data)
|
||||
throws SAXException
|
||||
{
|
||||
// we can't create populated entity ref nodes using
|
||||
// only public DOM APIs (they've got to be readonly)
|
||||
if (currentEntity != null)
|
||||
return;
|
||||
|
||||
ProcessingInstruction pi;
|
||||
|
||||
if (isL2
|
||||
// && consumer.isUsingNamespaces ()
|
||||
&& target.indexOf (':') != -1)
|
||||
namespaceError (
|
||||
"PI target name is namespace nonconformant: "
|
||||
+ target);
|
||||
if (inDTD)
|
||||
return;
|
||||
pi = document.createProcessingInstruction (target, data);
|
||||
top.appendChild (pi);
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses may overrride this method to provide a more efficient
|
||||
* way to construct text nodes.
|
||||
* Typically, copying the text into a single character array will
|
||||
* be more efficient than doing that as well as allocating other
|
||||
* needed for a String, including an internal StringBuffer.
|
||||
* Those additional memory and CPU costs can be incurred later,
|
||||
* if ever needed.
|
||||
* Unfortunately the standard DOM factory APIs encourage those costs
|
||||
* to be incurred early.
|
||||
*/
|
||||
protected Text createText (
|
||||
boolean isCDATA,
|
||||
char ch [],
|
||||
int start,
|
||||
int length
|
||||
) {
|
||||
String value = new String (ch, start, length);
|
||||
|
||||
if (isCDATA)
|
||||
return document.createCDATASection (value);
|
||||
else
|
||||
return document.createTextNode (value);
|
||||
}
|
||||
|
||||
// SAX1
|
||||
public void characters (char ch [], int start, int length)
|
||||
throws SAXException
|
||||
{
|
||||
// we can't create populated entity ref nodes using
|
||||
// only public DOM APIs (they've got to be readonly
|
||||
// at creation time)
|
||||
if (currentEntity != null)
|
||||
return;
|
||||
|
||||
Node lastChild = top.getLastChild ();
|
||||
|
||||
// merge consecutive text or CDATA nodes if appropriate.
|
||||
if (lastChild instanceof Text) {
|
||||
if (consumer.isHidingCDATA ()
|
||||
// consecutive Text content ... always merge
|
||||
|| (!inCDATA
|
||||
&& !(lastChild instanceof CDATASection))
|
||||
// consecutive CDATASection content ... don't
|
||||
// merge between sections, only within them
|
||||
|| (inCDATA && mergeCDATA
|
||||
&& lastChild instanceof CDATASection)
|
||||
) {
|
||||
CharacterData last = (CharacterData) lastChild;
|
||||
String value = new String (ch, start, length);
|
||||
|
||||
last.appendData (value);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (inCDATA && !consumer.isHidingCDATA ()) {
|
||||
top.appendChild (createText (true, ch, start, length));
|
||||
mergeCDATA = true;
|
||||
} else
|
||||
top.appendChild (createText (false, ch, start, length));
|
||||
}
|
||||
|
||||
// SAX2
|
||||
public void skippedEntity (String name)
|
||||
throws SAXException
|
||||
{
|
||||
// this callback is useless except to report errors, since
|
||||
// we can't know if the ref was in content, within an
|
||||
// attribute, within a declaration ... only one of those
|
||||
// cases supports more intelligent action than a panic.
|
||||
fatal ("skipped entity: " + name, null);
|
||||
}
|
||||
|
||||
// SAX2
|
||||
public void startPrefixMapping (String prefix, String uri)
|
||||
throws SAXException
|
||||
{
|
||||
// reconstruct "xmlns" attributes deleted by all
|
||||
// SAX2 parsers without "namespace-prefixes" = true
|
||||
if ("".equals (prefix))
|
||||
attributes.addAttribute ("", "", "xmlns",
|
||||
"CDATA", uri);
|
||||
else
|
||||
attributes.addAttribute ("", "", "xmlns:" + prefix,
|
||||
"CDATA", uri);
|
||||
recreatedAttrs = true;
|
||||
}
|
||||
|
||||
// SAX2
|
||||
public void endPrefixMapping (String prefix)
|
||||
throws SAXException
|
||||
{ }
|
||||
|
||||
// SAX2
|
||||
public void startElement (
|
||||
String uri,
|
||||
String localName,
|
||||
String qName,
|
||||
Attributes atts
|
||||
) throws SAXException
|
||||
{
|
||||
// we can't create populated entity ref nodes using
|
||||
// only public DOM APIs (they've got to be readonly)
|
||||
if (currentEntity != null)
|
||||
return;
|
||||
|
||||
// parser discarded basic information; DOM tree isn't writable
|
||||
// without massaging to assign prefixes to all nodes.
|
||||
// the "NSFilter" class does that massaging.
|
||||
if (qName.length () == 0)
|
||||
qName = localName;
|
||||
|
||||
|
||||
Element element;
|
||||
int length = atts.getLength ();
|
||||
|
||||
if (!isL2) {
|
||||
element = document.createElement (qName);
|
||||
|
||||
// first the explicit attributes ...
|
||||
length = atts.getLength ();
|
||||
for (int i = 0; i < length; i++)
|
||||
element.setAttribute (atts.getQName (i),
|
||||
atts.getValue (i));
|
||||
// ... then any recreated ones (DOM deletes duplicates)
|
||||
if (recreatedAttrs) {
|
||||
recreatedAttrs = false;
|
||||
length = attributes.getLength ();
|
||||
for (int i = 0; i < length; i++)
|
||||
element.setAttribute (attributes.getQName (i),
|
||||
attributes.getValue (i));
|
||||
attributes.clear ();
|
||||
}
|
||||
|
||||
top.appendChild (element);
|
||||
top = element;
|
||||
return;
|
||||
}
|
||||
|
||||
// For an L2 DOM when namespace use is enabled, use
|
||||
// createElementNS/createAttributeNS except when
|
||||
// (a) it's an element in the default namespace, or
|
||||
// (b) it's an attribute with no prefix
|
||||
String namespace;
|
||||
|
||||
if (localName.length () != 0)
|
||||
namespace = (uri.length () == 0) ? null : uri;
|
||||
else
|
||||
namespace = getNamespace (getPrefix (qName), atts);
|
||||
|
||||
if (namespace == null)
|
||||
element = document.createElement (qName);
|
||||
else
|
||||
element = document.createElementNS (namespace, qName);
|
||||
|
||||
populateAttributes (element, atts);
|
||||
if (recreatedAttrs) {
|
||||
recreatedAttrs = false;
|
||||
// ... DOM deletes any duplicates
|
||||
populateAttributes (element, attributes);
|
||||
attributes.clear ();
|
||||
}
|
||||
|
||||
top.appendChild (element);
|
||||
top = element;
|
||||
}
|
||||
|
||||
final static String xmlnsURI = "http://www.w3.org/2000/xmlns/";
|
||||
|
||||
private void populateAttributes (Element element, Attributes attrs)
|
||||
throws SAXParseException
|
||||
{
|
||||
int length = attrs.getLength ();
|
||||
|
||||
for (int i = 0; i < length; i++) {
|
||||
String type = attrs.getType (i);
|
||||
String value = attrs.getValue (i);
|
||||
String name = attrs.getQName (i);
|
||||
String local = attrs.getLocalName (i);
|
||||
String uri = attrs.getURI (i);
|
||||
|
||||
// parser discarded basic information, DOM tree isn't writable
|
||||
if (name.length () == 0)
|
||||
name = local;
|
||||
|
||||
// all attribute types other than these three may not
|
||||
// contain scoped names... enumerated attributes get
|
||||
// reported as NMTOKEN, except for NOTATION values
|
||||
if (!("CDATA".equals (type)
|
||||
|| "NMTOKEN".equals (type)
|
||||
|| "NMTOKENS".equals (type))) {
|
||||
if (value.indexOf (':') != -1) {
|
||||
namespaceError (
|
||||
"namespace nonconformant attribute value: "
|
||||
+ "<" + element.getNodeName ()
|
||||
+ " " + name + "='" + value + "' ...>");
|
||||
}
|
||||
}
|
||||
|
||||
// xmlns="" is legal (undoes default NS)
|
||||
// xmlns:foo="" is illegal
|
||||
String prefix = getPrefix (name);
|
||||
String namespace;
|
||||
|
||||
if ("xmlns".equals (prefix)) {
|
||||
if ("".equals (value))
|
||||
namespaceError ("illegal null namespace decl, " + name);
|
||||
namespace = xmlnsURI;
|
||||
} else if ("xmlns".equals (name))
|
||||
namespace = xmlnsURI;
|
||||
|
||||
else if (prefix == null)
|
||||
namespace = null;
|
||||
else if (!"".equals(uri) && uri.length () != 0)
|
||||
namespace = uri;
|
||||
else
|
||||
namespace = getNamespace (prefix, attrs);
|
||||
|
||||
if (namespace == null)
|
||||
element.setAttribute (name, value);
|
||||
else
|
||||
element.setAttributeNS (namespace, name, value);
|
||||
}
|
||||
}
|
||||
|
||||
private String getPrefix (String name)
|
||||
{
|
||||
int temp;
|
||||
|
||||
if ((temp = name.indexOf (':')) > 0)
|
||||
return name.substring (0, temp);
|
||||
return null;
|
||||
}
|
||||
|
||||
// used with SAX1-level parser output
|
||||
private String getNamespace (String prefix, Attributes attrs)
|
||||
throws SAXParseException
|
||||
{
|
||||
String namespace;
|
||||
String decl;
|
||||
|
||||
// defaulting
|
||||
if (prefix == null) {
|
||||
decl = "xmlns";
|
||||
namespace = attrs.getValue (decl);
|
||||
if ("".equals (namespace))
|
||||
return null;
|
||||
else if (namespace != null)
|
||||
return namespace;
|
||||
|
||||
// "xmlns" is like a keyword
|
||||
// ... according to the Namespace REC, but DOM L2 CR2+
|
||||
// and Infoset violate that by assigning a namespace.
|
||||
// that conflict is resolved elsewhere.
|
||||
} else if ("xmlns".equals (prefix))
|
||||
return null;
|
||||
|
||||
// "xml" prefix is fixed
|
||||
else if ("xml".equals (prefix))
|
||||
return "http://www.w3.org/XML/1998/namespace";
|
||||
|
||||
// otherwise, expect a declaration
|
||||
else {
|
||||
decl = "xmlns:" + prefix;
|
||||
namespace = attrs.getValue (decl);
|
||||
}
|
||||
|
||||
// if we found a local declaration, great
|
||||
if (namespace != null)
|
||||
return namespace;
|
||||
|
||||
|
||||
// ELSE ... search up the tree we've been building
|
||||
for (Node n = top;
|
||||
n != null && n.getNodeType () != Node.DOCUMENT_NODE;
|
||||
n = (Node) n.getParentNode ()) {
|
||||
if (n.getNodeType () == Node.ENTITY_REFERENCE_NODE)
|
||||
continue;
|
||||
Element e = (Element) n;
|
||||
Attr attr = e.getAttributeNode (decl);
|
||||
if (attr != null)
|
||||
return attr.getNodeValue ();
|
||||
}
|
||||
// see above re "xmlns" as keyword
|
||||
if ("xmlns".equals (decl))
|
||||
return null;
|
||||
|
||||
namespaceError ("Undeclared namespace prefix: " + prefix);
|
||||
return null;
|
||||
}
|
||||
|
||||
// SAX2
|
||||
public void endElement (String uri, String localName, String qName)
|
||||
throws SAXException
|
||||
{
|
||||
// we can't create populated entity ref nodes using
|
||||
// only public DOM APIs (they've got to be readonly)
|
||||
if (currentEntity != null)
|
||||
return;
|
||||
|
||||
top = top.getParentNode ();
|
||||
}
|
||||
|
||||
// SAX1 (mandatory reporting if validating)
|
||||
public void ignorableWhitespace (char ch [], int start, int length)
|
||||
throws SAXException
|
||||
{
|
||||
if (consumer.isHidingWhitespace ())
|
||||
return;
|
||||
characters (ch, start, length);
|
||||
}
|
||||
|
||||
// SAX2 lexical event
|
||||
public void startCDATA ()
|
||||
throws SAXException
|
||||
{
|
||||
inCDATA = true;
|
||||
// true except for the first fragment of a cdata section
|
||||
mergeCDATA = false;
|
||||
}
|
||||
|
||||
// SAX2 lexical event
|
||||
public void endCDATA ()
|
||||
throws SAXException
|
||||
{
|
||||
inCDATA = false;
|
||||
}
|
||||
|
||||
// SAX2 lexical event
|
||||
//
|
||||
// this SAX2 callback merges two unrelated things:
|
||||
// - Declaration of the root element type ... belongs with
|
||||
// the other DTD declaration methods, NOT HERE.
|
||||
// - IDs for the optional external subset ... belongs here
|
||||
// with other lexical information.
|
||||
//
|
||||
// ...and it doesn't include the internal DTD subset, desired
|
||||
// both to support DOM L2 and to enable "pass through" processing
|
||||
//
|
||||
public void startDTD (String name, String publicId, String SystemId)
|
||||
throws SAXException
|
||||
{
|
||||
// need to filter out comments and PIs within the DTD
|
||||
inDTD = true;
|
||||
}
|
||||
|
||||
// SAX2 lexical event
|
||||
public void endDTD ()
|
||||
throws SAXException
|
||||
{
|
||||
inDTD = false;
|
||||
}
|
||||
|
||||
// SAX2 lexical event
|
||||
public void comment (char ch [], int start, int length)
|
||||
throws SAXException
|
||||
{
|
||||
Node comment;
|
||||
|
||||
// we can't create populated entity ref nodes using
|
||||
// only public DOM APIs (they've got to be readonly)
|
||||
if (consumer.isHidingComments ()
|
||||
|| inDTD
|
||||
|| currentEntity != null)
|
||||
return;
|
||||
comment = document.createComment (new String (ch, start, length));
|
||||
top.appendChild (comment);
|
||||
}
|
||||
|
||||
/**
|
||||
* May be overridden by subclasses to return true, indicating
|
||||
* that entity reference nodes can be populated and then made
|
||||
* read-only.
|
||||
*/
|
||||
public boolean canPopulateEntityRefs ()
|
||||
{ return false; }
|
||||
|
||||
// SAX2 lexical event
|
||||
public void startEntity (String name)
|
||||
throws SAXException
|
||||
{
|
||||
// are we ignoring what would be contents of an
|
||||
// entity ref, since we can't populate it?
|
||||
if (currentEntity != null)
|
||||
return;
|
||||
|
||||
// Are we hiding all entity boundaries?
|
||||
if (consumer.isHidingReferences ())
|
||||
return;
|
||||
|
||||
// SAX2 shows parameter entities; DOM hides them
|
||||
if (name.charAt (0) == '%' || "[dtd]".equals (name))
|
||||
return;
|
||||
|
||||
// Since we can't create a populated entity ref node in any
|
||||
// standard way, we create an unpopulated one.
|
||||
EntityReference ref = document.createEntityReference (name);
|
||||
top.appendChild (ref);
|
||||
top = ref;
|
||||
|
||||
// ... allowing subclasses to populate them
|
||||
if (!canPopulateEntityRefs ())
|
||||
currentEntity = name;
|
||||
}
|
||||
|
||||
// SAX2 lexical event
|
||||
public void endEntity (String name)
|
||||
throws SAXException
|
||||
{
|
||||
if (name.charAt (0) == '%' || "[dtd]".equals (name))
|
||||
return;
|
||||
if (name.equals (currentEntity))
|
||||
currentEntity = null;
|
||||
if (!consumer.isHidingReferences ())
|
||||
top = top.getParentNode ();
|
||||
}
|
||||
|
||||
|
||||
// SAX1 DTD event
|
||||
public void notationDecl (
|
||||
String name,
|
||||
String publicId, String SystemId
|
||||
) throws SAXException
|
||||
{
|
||||
/* IGNORE -- no public DOM API lets us store these
|
||||
* into the doctype node
|
||||
*/
|
||||
}
|
||||
|
||||
// SAX1 DTD event
|
||||
public void unparsedEntityDecl (
|
||||
String name,
|
||||
String publicId, String SystemId,
|
||||
String notationName
|
||||
) throws SAXException
|
||||
{
|
||||
/* IGNORE -- no public DOM API lets us store these
|
||||
* into the doctype node
|
||||
*/
|
||||
}
|
||||
|
||||
// SAX2 declaration event
|
||||
public void elementDecl (String name, String model)
|
||||
throws SAXException
|
||||
{
|
||||
/* IGNORE -- no content model support in DOM L2 */
|
||||
}
|
||||
|
||||
// SAX2 declaration event
|
||||
public void attributeDecl (
|
||||
String eName,
|
||||
String aName,
|
||||
String type,
|
||||
String mode,
|
||||
String value
|
||||
) throws SAXException
|
||||
{
|
||||
/* IGNORE -- no attribute model support in DOM L2 */
|
||||
}
|
||||
|
||||
// SAX2 declaration event
|
||||
public void internalEntityDecl (String name, String value)
|
||||
throws SAXException
|
||||
{
|
||||
/* IGNORE -- no public DOM API lets us store these
|
||||
* into the doctype node
|
||||
*/
|
||||
}
|
||||
|
||||
// SAX2 declaration event
|
||||
public void externalEntityDecl (
|
||||
String name,
|
||||
String publicId,
|
||||
String SystemId
|
||||
) throws SAXException
|
||||
{
|
||||
/* IGNORE -- no public DOM API lets us store these
|
||||
* into the doctype node
|
||||
*/
|
||||
}
|
||||
|
||||
//
|
||||
// These really should offer the option of nonfatal handling,
|
||||
// like other validity errors, though that would cause major
|
||||
// chaos in the DOM data structures. DOM is already spec'd
|
||||
// to treat many of these as fatal, so this is consistent.
|
||||
//
|
||||
private void namespaceError (String description)
|
||||
throws SAXParseException
|
||||
{
|
||||
SAXParseException err;
|
||||
|
||||
err = new SAXParseException (description, locator);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
/* EventConsumer.java --
|
||||
Copyright (C) 1999,2000,2001 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Classpath.
|
||||
|
||||
GNU Classpath 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, or (at your option)
|
||||
any later version.
|
||||
|
||||
GNU Classpath 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 GNU Classpath; see the file COPYING. If not, write to the
|
||||
Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
02111-1307 USA.
|
||||
|
||||
Linking this library statically or dynamically with other modules is
|
||||
making a combined work based on this library. Thus, the terms and
|
||||
conditions of the GNU General Public License cover the whole
|
||||
combination.
|
||||
|
||||
As a special exception, the copyright holders of this library give you
|
||||
permission to link this library with independent modules to produce an
|
||||
executable, regardless of the license terms of these independent
|
||||
modules, and to copy and distribute the resulting executable under
|
||||
terms of your choice, provided that you also meet, for each linked
|
||||
independent module, the terms and conditions of the license of that
|
||||
module. An independent module is a module which is not derived from
|
||||
or based on this library. If you modify this library, you may extend
|
||||
this exception to your version of the library, but you are not
|
||||
obligated to do so. If you do not wish to do so, delete this
|
||||
exception statement from your version. */
|
||||
|
||||
package gnu.xml.pipeline;
|
||||
|
||||
import org.xml.sax.*;
|
||||
|
||||
|
||||
/**
|
||||
* Collects the event consumption apparatus of a SAX pipeline stage.
|
||||
* Consumers which permit some handlers or other characteristics to be
|
||||
* configured will provide methods to support that configuration.
|
||||
*
|
||||
* <p> Two important categories of consumers include <em>filters</em>, which
|
||||
* process events and pass them on to other consumers, and <em>terminus</em>
|
||||
* (or <em>terminal</em>) stages, which don't pass events on. Filters are not
|
||||
* necessarily derived from the {@link EventFilter} class, although that
|
||||
* class can substantially simplify their construction by automating the
|
||||
* most common activities.
|
||||
*
|
||||
* <p> Event consumers which follow certain conventions for the signatures
|
||||
* of their constructors can be automatically assembled into pipelines
|
||||
* by the {@link PipelineFactory} class.
|
||||
*
|
||||
* @author David Brownell
|
||||
*/
|
||||
public interface EventConsumer
|
||||
{
|
||||
/** Most stages process these core SAX callbacks. */
|
||||
public ContentHandler getContentHandler ();
|
||||
|
||||
/** Few stages will use unparsed entities. */
|
||||
public DTDHandler getDTDHandler ();
|
||||
|
||||
/**
|
||||
* This method works like the SAX2 XMLReader method of the same name,
|
||||
* and is used to retrieve the optional lexical and declaration handlers
|
||||
* in a pipeline.
|
||||
*
|
||||
* @param id This is a URI identifying the type of property desired.
|
||||
* @return The value of that property, if it is defined.
|
||||
*
|
||||
* @exception SAXNotRecognizedException Thrown if the particular
|
||||
* pipeline stage does not understand the specified identifier.
|
||||
*/
|
||||
public Object getProperty (String id)
|
||||
throws SAXNotRecognizedException;
|
||||
|
||||
/**
|
||||
* This method provides a filter stage with a handler that abstracts
|
||||
* presentation of warnings and both recoverable and fatal errors.
|
||||
* Most pipeline stages should share a single policy and mechanism
|
||||
* for such reports, since application components require consistency
|
||||
* in such activities. Accordingly, typical responses to this method
|
||||
* invocation involve saving the handler for use; filters will pass
|
||||
* it on to any other consumers they use.
|
||||
*
|
||||
* @param handler encapsulates error handling policy for this stage
|
||||
*/
|
||||
public void setErrorHandler (ErrorHandler handler);
|
||||
}
|
||||
@@ -0,0 +1,809 @@
|
||||
/* EventFilter.java --
|
||||
Copyright (C) 1999,2000,2001 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Classpath.
|
||||
|
||||
GNU Classpath 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, or (at your option)
|
||||
any later version.
|
||||
|
||||
GNU Classpath 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 GNU Classpath; see the file COPYING. If not, write to the
|
||||
Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
02111-1307 USA.
|
||||
|
||||
Linking this library statically or dynamically with other modules is
|
||||
making a combined work based on this library. Thus, the terms and
|
||||
conditions of the GNU General Public License cover the whole
|
||||
combination.
|
||||
|
||||
As a special exception, the copyright holders of this library give you
|
||||
permission to link this library with independent modules to produce an
|
||||
executable, regardless of the license terms of these independent
|
||||
modules, and to copy and distribute the resulting executable under
|
||||
terms of your choice, provided that you also meet, for each linked
|
||||
independent module, the terms and conditions of the license of that
|
||||
module. An independent module is a module which is not derived from
|
||||
or based on this library. If you modify this library, you may extend
|
||||
this exception to your version of the library, but you are not
|
||||
obligated to do so. If you do not wish to do so, delete this
|
||||
exception statement from your version. */
|
||||
|
||||
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;
|
||||
|
||||
import gnu.xml.aelfred2.ContentHandler2;
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public class EventFilter
|
||||
implements EventConsumer, ContentHandler2, 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 ();
|
||||
}
|
||||
|
||||
public void xmlDecl(String version, String encoding, boolean standalone,
|
||||
String inputEncoding)
|
||||
throws SAXException
|
||||
{
|
||||
if (docNext != null && docNext instanceof ContentHandler2)
|
||||
{
|
||||
((ContentHandler2) docNext).xmlDecl(version, encoding, standalone,
|
||||
inputEncoding);
|
||||
}
|
||||
}
|
||||
|
||||
/** <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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,243 @@
|
||||
/* LinkFilter.java --
|
||||
Copyright (C) 1999,2000,2001 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Classpath.
|
||||
|
||||
GNU Classpath 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, or (at your option)
|
||||
any later version.
|
||||
|
||||
GNU Classpath 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 GNU Classpath; see the file COPYING. If not, write to the
|
||||
Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
02111-1307 USA.
|
||||
|
||||
Linking this library statically or dynamically with other modules is
|
||||
making a combined work based on this library. Thus, the terms and
|
||||
conditions of the GNU General Public License cover the whole
|
||||
combination.
|
||||
|
||||
As a special exception, the copyright holders of this library give you
|
||||
permission to link this library with independent modules to produce an
|
||||
executable, regardless of the license terms of these independent
|
||||
modules, and to copy and distribute the resulting executable under
|
||||
terms of your choice, provided that you also meet, for each linked
|
||||
independent module, the terms and conditions of the license of that
|
||||
module. An independent module is a module which is not derived from
|
||||
or based on this library. If you modify this library, you may extend
|
||||
this exception to your version of the library, but you are not
|
||||
obligated to do so. If you do not wish to do so, delete this
|
||||
exception statement from your version. */
|
||||
|
||||
package gnu.xml.pipeline;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.util.Enumeration;
|
||||
import java.util.Vector;
|
||||
|
||||
import org.xml.sax.Attributes;
|
||||
import org.xml.sax.Locator;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
|
||||
/**
|
||||
* Pipeline filter to remember XHTML links found in a document,
|
||||
* so they can later be crawled. Fragments are not counted, and duplicates
|
||||
* are ignored. Callers are responsible for filtering out URLs they aren't
|
||||
* interested in. Events are passed through unmodified.
|
||||
*
|
||||
* <p> Input MUST include a setDocumentLocator() call, as it's used to
|
||||
* resolve relative links in the absence of a "base" element. Input MUST
|
||||
* also include namespace identifiers, since it is the XHTML namespace
|
||||
* identifier which is used to identify the relevant elements.
|
||||
*
|
||||
* <p><em>FIXME:</em> handle xml:base attribute ... in association with
|
||||
* a stack of base URIs. Similarly, recognize/support XLink data.
|
||||
*
|
||||
* @author David Brownell
|
||||
*/
|
||||
public class LinkFilter extends EventFilter
|
||||
{
|
||||
// for storing URIs
|
||||
private Vector vector = new Vector ();
|
||||
|
||||
// struct for "full" link record (tbd)
|
||||
// these for troubleshooting original source:
|
||||
// original uri
|
||||
// uri as resolved (base, relative, etc)
|
||||
// URI of originating doc
|
||||
// line #
|
||||
// original element + attrs (img src, desc, etc)
|
||||
|
||||
// XLink model of the link ... for inter-site pairups ?
|
||||
|
||||
private String baseURI;
|
||||
|
||||
private boolean siteRestricted = false;
|
||||
|
||||
//
|
||||
// XXX leverage blacklist info (like robots.txt)
|
||||
//
|
||||
// XXX constructor w/param ... pipeline for sending link data
|
||||
// probably XHTML --> XLink, providing info as sketched above
|
||||
//
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a new event filter, which collects links in private data
|
||||
* structure for later enumeration.
|
||||
*/
|
||||
// constructor used by PipelineFactory
|
||||
public LinkFilter ()
|
||||
{
|
||||
super.setContentHandler (this);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a new event filter, which collects links in private data
|
||||
* structure for later enumeration and passes all events, unmodified,
|
||||
* to the next consumer.
|
||||
*/
|
||||
// constructor used by PipelineFactory
|
||||
public LinkFilter (EventConsumer next)
|
||||
{
|
||||
super (next);
|
||||
super.setContentHandler (this);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns an enumeration of the links found since the filter
|
||||
* was constructed, or since removeAllLinks() was called.
|
||||
*
|
||||
* @return enumeration of strings.
|
||||
*/
|
||||
public Enumeration getLinks ()
|
||||
{
|
||||
return vector.elements ();
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes records about all links reported to the event
|
||||
* stream, as if the filter were newly created.
|
||||
*/
|
||||
public void removeAllLinks ()
|
||||
{
|
||||
vector = new Vector ();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Collects URIs for (X)HTML content from elements which hold them.
|
||||
*/
|
||||
public void startElement (
|
||||
String uri,
|
||||
String localName,
|
||||
String qName,
|
||||
Attributes atts
|
||||
) throws SAXException
|
||||
{
|
||||
String link;
|
||||
|
||||
// Recognize XHTML links.
|
||||
if ("http://www.w3.org/1999/xhtml".equals (uri)) {
|
||||
|
||||
if ("a".equals (localName) || "base".equals (localName)
|
||||
|| "area".equals (localName))
|
||||
link = atts.getValue ("href");
|
||||
else if ("iframe".equals (localName) || "frame".equals (localName))
|
||||
link = atts.getValue ("src");
|
||||
else if ("blockquote".equals (localName) || "q".equals (localName)
|
||||
|| "ins".equals (localName) || "del".equals (localName))
|
||||
link = atts.getValue ("cite");
|
||||
else
|
||||
link = null;
|
||||
link = maybeAddLink (link);
|
||||
|
||||
// "base" modifies designated baseURI
|
||||
if ("base".equals (localName) && link != null)
|
||||
baseURI = link;
|
||||
|
||||
if ("iframe".equals (localName) || "img".equals (localName))
|
||||
maybeAddLink (atts.getValue ("longdesc"));
|
||||
}
|
||||
|
||||
super.startElement (uri, localName, qName, atts);
|
||||
}
|
||||
|
||||
private String maybeAddLink (String link)
|
||||
{
|
||||
int index;
|
||||
|
||||
// ignore empty links and fragments inside docs
|
||||
if (link == null)
|
||||
return null;
|
||||
if ((index = link.indexOf ("#")) >= 0)
|
||||
link = link.substring (0, index);
|
||||
if (link.equals (""))
|
||||
return null;
|
||||
|
||||
try {
|
||||
// get the real URI
|
||||
URL base = new URL ((baseURI != null)
|
||||
? baseURI
|
||||
: getDocumentLocator ().getSystemId ());
|
||||
URL url = new URL (base, link);
|
||||
|
||||
link = url.toString ();
|
||||
|
||||
// ignore duplicates
|
||||
if (vector.contains (link))
|
||||
return link;
|
||||
|
||||
// other than what "base" does, stick to original site:
|
||||
if (siteRestricted) {
|
||||
// don't switch protocols
|
||||
if (!base.getProtocol ().equals (url.getProtocol ()))
|
||||
return link;
|
||||
// don't switch servers
|
||||
if (base.getHost () != null
|
||||
&& !base.getHost ().equals (url.getHost ()))
|
||||
return link;
|
||||
}
|
||||
|
||||
vector.addElement (link);
|
||||
|
||||
return link;
|
||||
|
||||
} catch (IOException e) {
|
||||
// bad URLs we don't want
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reports an error if no Locator has been made available.
|
||||
*/
|
||||
public void startDocument ()
|
||||
throws SAXException
|
||||
{
|
||||
if (getDocumentLocator () == null)
|
||||
throw new SAXException ("no Locator!");
|
||||
}
|
||||
|
||||
/**
|
||||
* Forgets about any base URI information that may be recorded.
|
||||
* Applications will often want to call removeAllLinks(), likely
|
||||
* after examining the links which were reported.
|
||||
*/
|
||||
public void endDocument ()
|
||||
throws SAXException
|
||||
{
|
||||
baseURI = null;
|
||||
super.endDocument ();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,340 @@
|
||||
/* NSFilter.java --
|
||||
Copyright (C) 1999,2000,2001 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Classpath.
|
||||
|
||||
GNU Classpath 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, or (at your option)
|
||||
any later version.
|
||||
|
||||
GNU Classpath 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 GNU Classpath; see the file COPYING. If not, write to the
|
||||
Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
02111-1307 USA.
|
||||
|
||||
Linking this library statically or dynamically with other modules is
|
||||
making a combined work based on this library. Thus, the terms and
|
||||
conditions of the GNU General Public License cover the whole
|
||||
combination.
|
||||
|
||||
As a special exception, the copyright holders of this library give you
|
||||
permission to link this library with independent modules to produce an
|
||||
executable, regardless of the license terms of these independent
|
||||
modules, and to copy and distribute the resulting executable under
|
||||
terms of your choice, provided that you also meet, for each linked
|
||||
independent module, the terms and conditions of the license of that
|
||||
module. An independent module is a module which is not derived from
|
||||
or based on this library. If you modify this library, you may extend
|
||||
this exception to your version of the library, but you are not
|
||||
obligated to do so. If you do not wish to do so, delete this
|
||||
exception statement from your version. */
|
||||
|
||||
package gnu.xml.pipeline;
|
||||
|
||||
import java.util.EmptyStackException;
|
||||
import java.util.Enumeration;
|
||||
import java.util.Stack;
|
||||
|
||||
import org.xml.sax.*;
|
||||
import org.xml.sax.ext.*;
|
||||
import org.xml.sax.helpers.AttributesImpl;
|
||||
import org.xml.sax.helpers.NamespaceSupport;
|
||||
|
||||
|
||||
/**
|
||||
* This filter ensures that element and attribute names are properly prefixed,
|
||||
* and that such prefixes are declared. Such data is critical for operations
|
||||
* like writing XML text, and validating against DTDs: names or their prefixes
|
||||
* may have been discarded, although they are essential to the exchange of
|
||||
* information using XML. There are various common ways that such data
|
||||
* gets discarded: <ul>
|
||||
*
|
||||
* <li> By default, SAX2 parsers must discard the "xmlns*"
|
||||
* attributes, and may also choose not to report properly prefixed
|
||||
* names for elements or attributes. (Some parsers may support
|
||||
* changing the <em>namespace-prefixes</em> value from the default
|
||||
* to <em>true</em>, effectively eliminating the need to use this
|
||||
* filter on their output.)
|
||||
*
|
||||
* <li> When event streams are generated from a DOM tree, they may
|
||||
* have never have had prefixes or declarations for namespaces; or
|
||||
* the existing prefixes or declarations may have been invalidated
|
||||
* by structural modifications to that DOM tree.
|
||||
*
|
||||
* <li> Other software writing SAX event streams won't necessarily
|
||||
* be worrying about prefix management, and so they will need to
|
||||
* have a transparent solution for managing them.
|
||||
*
|
||||
* </ul>
|
||||
*
|
||||
* <p> This filter uses a heuristic to choose the prefix to assign to any
|
||||
* particular name which wasn't already corectly prefixed. The associated
|
||||
* namespace will be correct, and the prefix will be declared. Original
|
||||
* structures facilitating text editing, such as conventions about use of
|
||||
* mnemonic prefix names or the scoping of prefixes, can't always be
|
||||
* reconstructed after they are discarded, as strongly encouraged by the
|
||||
* current SAX2 defaults.
|
||||
*
|
||||
* <p> Note that this can't possibly know whether values inside attribute
|
||||
* value or document content involve prefixed names. If your application
|
||||
* requires using prefixed names in such locations you'll need to add some
|
||||
* appropriate logic (perhaps adding additional heuristics in a subclass).
|
||||
*
|
||||
* @author David Brownell
|
||||
*/
|
||||
public class NSFilter extends EventFilter
|
||||
{
|
||||
private NamespaceSupport nsStack = new NamespaceSupport ();
|
||||
private Stack elementStack = new Stack ();
|
||||
|
||||
private boolean pushedContext;
|
||||
private String nsTemp [] = new String [3];
|
||||
private AttributesImpl attributes = new AttributesImpl ();
|
||||
private boolean usedDefault;
|
||||
|
||||
// gensymmed prefixes use this root name
|
||||
private static final String prefixRoot = "prefix-";
|
||||
|
||||
|
||||
/**
|
||||
* Passes events through to the specified consumer, after first
|
||||
* processing them.
|
||||
*
|
||||
* @param next the next event consumer to receive events.
|
||||
*/
|
||||
// constructor used by PipelineFactory
|
||||
public NSFilter (EventConsumer next)
|
||||
{
|
||||
super (next);
|
||||
|
||||
setContentHandler (this);
|
||||
}
|
||||
|
||||
private void fatalError (String message)
|
||||
throws SAXException
|
||||
{
|
||||
SAXParseException e;
|
||||
ErrorHandler handler = getErrorHandler ();
|
||||
Locator locator = getDocumentLocator ();
|
||||
|
||||
if (locator == null)
|
||||
e = new SAXParseException (message, null, null, -1, -1);
|
||||
else
|
||||
e = new SAXParseException (message, locator);
|
||||
if (handler != null)
|
||||
handler.fatalError (e);
|
||||
throw e;
|
||||
}
|
||||
|
||||
|
||||
public void startDocument () throws SAXException
|
||||
{
|
||||
elementStack.removeAllElements ();
|
||||
nsStack.reset ();
|
||||
pushedContext = false;
|
||||
super.startDocument ();
|
||||
}
|
||||
|
||||
/**
|
||||
* This call is not passed to the next consumer in the chain.
|
||||
* Prefix declarations and scopes are only exposed in the form
|
||||
* of attributes; this callback just records a declaration that
|
||||
* will be exposed as an attribute.
|
||||
*/
|
||||
public void startPrefixMapping (String prefix, String uri)
|
||||
throws SAXException
|
||||
{
|
||||
if (pushedContext == false) {
|
||||
nsStack.pushContext ();
|
||||
pushedContext = true;
|
||||
}
|
||||
|
||||
// this check is awkward, but the paranoia prevents big trouble
|
||||
for (Enumeration e = nsStack.getDeclaredPrefixes ();
|
||||
e.hasMoreElements ();
|
||||
/* NOP */ ) {
|
||||
String declared = (String) e.nextElement ();
|
||||
|
||||
if (!declared.equals (prefix))
|
||||
continue;
|
||||
if (uri.equals (nsStack.getURI (prefix)))
|
||||
return;
|
||||
fatalError ("inconsistent binding for prefix '" + prefix
|
||||
+ "' ... " + uri + " (was " + nsStack.getURI (prefix) + ")");
|
||||
}
|
||||
|
||||
if (!nsStack.declarePrefix (prefix, uri))
|
||||
fatalError ("illegal prefix declared: " + prefix);
|
||||
}
|
||||
|
||||
private String fixName (String ns, String l, String name, boolean isAttr)
|
||||
throws SAXException
|
||||
{
|
||||
if ("".equals (name) || name == null) {
|
||||
name = l;
|
||||
if ("".equals (name) || name == null)
|
||||
fatalError ("empty/null name");
|
||||
}
|
||||
|
||||
// can we correctly process the name as-is?
|
||||
// handles "element scope" attribute names here.
|
||||
if (nsStack.processName (name, nsTemp, isAttr) != null
|
||||
&& nsTemp [0].equals (ns)
|
||||
) {
|
||||
return nsTemp [2];
|
||||
}
|
||||
|
||||
// nope, gotta modify the name or declare a default mapping
|
||||
int temp;
|
||||
|
||||
// get rid of any current prefix
|
||||
if ((temp = name.indexOf (':')) >= 0) {
|
||||
name = name.substring (temp + 1);
|
||||
|
||||
// ... maybe that's enough (use/prefer default namespace) ...
|
||||
if (!isAttr && nsStack.processName (name, nsTemp, false) != null
|
||||
&& nsTemp [0].equals (ns)
|
||||
) {
|
||||
return nsTemp [2];
|
||||
}
|
||||
}
|
||||
|
||||
// must we define and use the default/undefined prefix?
|
||||
if ("".equals (ns)) {
|
||||
if (isAttr)
|
||||
fatalError ("processName bug");
|
||||
if (attributes.getIndex ("xmlns") != -1)
|
||||
fatalError ("need to undefine default NS, but it's bound: "
|
||||
+ attributes.getValue ("xmlns"));
|
||||
|
||||
nsStack.declarePrefix ("", "");
|
||||
attributes.addAttribute ("", "", "xmlns", "CDATA", "");
|
||||
return name;
|
||||
}
|
||||
|
||||
// is there at least one non-null prefix we can use?
|
||||
for (Enumeration e = nsStack.getDeclaredPrefixes ();
|
||||
e.hasMoreElements ();
|
||||
/* NOP */) {
|
||||
String prefix = (String) e.nextElement ();
|
||||
String uri = nsStack.getURI (prefix);
|
||||
|
||||
if (uri == null || !uri.equals (ns))
|
||||
continue;
|
||||
return prefix + ":" + name;
|
||||
}
|
||||
|
||||
// no such luck. create a prefix name, declare it, use it.
|
||||
for (temp = 0; temp >= 0; temp++) {
|
||||
String prefix = prefixRoot + temp;
|
||||
|
||||
if (nsStack.getURI (prefix) == null) {
|
||||
nsStack.declarePrefix (prefix, ns);
|
||||
attributes.addAttribute ("", "", "xmlns:" + prefix,
|
||||
"CDATA", ns);
|
||||
return prefix + ":" + name;
|
||||
}
|
||||
}
|
||||
fatalError ("too many prefixes genned");
|
||||
// NOTREACHED
|
||||
return null;
|
||||
}
|
||||
|
||||
public void startElement (
|
||||
String uri, String localName,
|
||||
String qName, Attributes atts
|
||||
) throws SAXException
|
||||
{
|
||||
if (!pushedContext)
|
||||
nsStack.pushContext ();
|
||||
pushedContext = false;
|
||||
|
||||
// make sure we have all NS declarations handy before we start
|
||||
int length = atts.getLength ();
|
||||
|
||||
for (int i = 0; i < length; i++) {
|
||||
String aName = atts.getQName (i);
|
||||
|
||||
if (!aName.startsWith ("xmlns"))
|
||||
continue;
|
||||
|
||||
String prefix;
|
||||
|
||||
if ("xmlns".equals (aName))
|
||||
prefix = "";
|
||||
else if (aName.indexOf (':') == 5)
|
||||
prefix = aName.substring (6);
|
||||
else // "xmlnsfoo" etc.
|
||||
continue;
|
||||
startPrefixMapping (prefix, atts.getValue (i));
|
||||
}
|
||||
|
||||
// put namespace decls at the start of our regenned attlist
|
||||
attributes.clear ();
|
||||
for (Enumeration e = nsStack.getDeclaredPrefixes ();
|
||||
e.hasMoreElements ();
|
||||
/* NOP */) {
|
||||
String prefix = (String) e.nextElement ();
|
||||
|
||||
attributes.addAttribute ("", "",
|
||||
("".equals (prefix)
|
||||
? "xmlns"
|
||||
: "xmlns:" + prefix),
|
||||
"CDATA",
|
||||
nsStack.getURI (prefix));
|
||||
}
|
||||
|
||||
// name fixups: element, then attributes.
|
||||
// fixName may declare a new prefix or, for the element,
|
||||
// redeclare the default (if element name needs it).
|
||||
qName = fixName (uri, localName, qName, false);
|
||||
|
||||
for (int i = 0; i < length; i++) {
|
||||
String aName = atts.getQName (i);
|
||||
String aNS = atts.getURI (i);
|
||||
String aLocal = atts.getLocalName (i);
|
||||
String aType = atts.getType (i);
|
||||
String aValue = atts.getValue (i);
|
||||
|
||||
if (aName.startsWith ("xmlns"))
|
||||
continue;
|
||||
aName = fixName (aNS, aLocal, aName, true);
|
||||
attributes.addAttribute (aNS, aLocal, aName, aType, aValue);
|
||||
}
|
||||
|
||||
elementStack.push (qName);
|
||||
|
||||
// pass event along, with cleaned-up names and decls.
|
||||
super.startElement (uri, localName, qName, attributes);
|
||||
}
|
||||
|
||||
public void endElement (String uri, String localName, String qName)
|
||||
throws SAXException
|
||||
{
|
||||
nsStack.popContext ();
|
||||
qName = (String) elementStack.pop ();
|
||||
super.endElement (uri, localName, qName);
|
||||
}
|
||||
|
||||
/**
|
||||
* This call is not passed to the next consumer in the chain.
|
||||
* Prefix declarations and scopes are only exposed in their
|
||||
* attribute form.
|
||||
*/
|
||||
public void endPrefixMapping (String prefix)
|
||||
throws SAXException
|
||||
{ }
|
||||
|
||||
public void endDocument () throws SAXException
|
||||
{
|
||||
elementStack.removeAllElements ();
|
||||
nsStack.reset ();
|
||||
super.endDocument ();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,723 @@
|
||||
/* PipelineFactory.java --
|
||||
Copyright (C) 1999,2000,2001 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Classpath.
|
||||
|
||||
GNU Classpath 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, or (at your option)
|
||||
any later version.
|
||||
|
||||
GNU Classpath 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 GNU Classpath; see the file COPYING. If not, write to the
|
||||
Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
02111-1307 USA.
|
||||
|
||||
Linking this library statically or dynamically with other modules is
|
||||
making a combined work based on this library. Thus, the terms and
|
||||
conditions of the GNU General Public License cover the whole
|
||||
combination.
|
||||
|
||||
As a special exception, the copyright holders of this library give you
|
||||
permission to link this library with independent modules to produce an
|
||||
executable, regardless of the license terms of these independent
|
||||
modules, and to copy and distribute the resulting executable under
|
||||
terms of your choice, provided that you also meet, for each linked
|
||||
independent module, the terms and conditions of the license of that
|
||||
module. An independent module is a module which is not derived from
|
||||
or based on this library. If you modify this library, you may extend
|
||||
this exception to your version of the library, but you are not
|
||||
obligated to do so. If you do not wish to do so, delete this
|
||||
exception statement from your version. */
|
||||
|
||||
package gnu.xml.pipeline;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import org.xml.sax.*;
|
||||
import org.xml.sax.ext.*;
|
||||
|
||||
|
||||
/**
|
||||
* This provides static factory methods for creating simple event pipelines.
|
||||
* These pipelines are specified by strings, suitable for passing on
|
||||
* command lines or embedding in element attributes. For example, one way
|
||||
* to write a pipeline that restores namespace syntax, validates (stopping
|
||||
* the pipeline on validity errors) and then writes valid data to standard
|
||||
* output is this: <pre>
|
||||
* nsfix | validate | write ( stdout )</pre>
|
||||
*
|
||||
* <p> In this syntax, the tokens are always separated by whitespace, and each
|
||||
* stage of the pipeline may optionally have a parameter (which can be a
|
||||
* pipeline) in parentheses. Interior stages are called filters, and the
|
||||
* rightmost end of a pipeline is called a terminus.
|
||||
*
|
||||
* <p> Stages are usually implemented by a single class, which may not be
|
||||
* able to act as both a filter and a terminus; but any terminus can be
|
||||
* automatically turned into a filter, through use of a {@link TeeConsumer}.
|
||||
* The stage identifiers are either class names, or are one of the following
|
||||
* short identifiers built into this class. (Most of these identifiers are
|
||||
* no more than aliases for classes.) The built-in identifiers include:</p>
|
||||
|
||||
<table border="1" cellpadding="3" cellspacing="0">
|
||||
<tr bgcolor="#ccccff" class="TableHeadingColor">
|
||||
<th align="center" width="5%">Stage</th>
|
||||
<th align="center" width="9%">Parameter</th>
|
||||
<th align="center" width="1%">Terminus</th>
|
||||
<th align="center">Description</th>
|
||||
</tr>
|
||||
|
||||
<tr valign="top" align="center">
|
||||
<td><a href="../dom/Consumer.html">dom</a></td>
|
||||
<td><em>none</em></td>
|
||||
<td> yes </td>
|
||||
<td align="left"> Applications code can access a DOM Document built
|
||||
from the input event stream. When used as a filter, this buffers
|
||||
data up to an <em>endDocument</em> call, and then uses a DOM parser
|
||||
to report everything that has been recorded (which can easily be
|
||||
less than what was reported to it). </td>
|
||||
</tr>
|
||||
<tr valign="top" align="center">
|
||||
<td><a href="NSFilter.html">nsfix</a></td>
|
||||
<td><em>none</em></td>
|
||||
<td>no</td>
|
||||
<td align="left">This stage ensures that the XML element and attribute
|
||||
names in its output use namespace prefixes and declarations correctly.
|
||||
That is, so that they match the "Namespace plus LocalName" naming data
|
||||
with which each XML element and attribute is already associated. </td>
|
||||
</tr>
|
||||
<tr valign="top" align="center">
|
||||
<td><a href="EventFilter.html">null</a></td>
|
||||
<td><em>none</em></td>
|
||||
<td>yes</td>
|
||||
<td align="left">This stage ignores all input event data.</td>
|
||||
</tr>
|
||||
<tr valign="top" align="center">
|
||||
<td><a href="CallFilter.html">server</a></td>
|
||||
<td><em>required</em><br> server URL </td>
|
||||
<td>no</td>
|
||||
<td align="left">Sends its input as XML request to a remote server,
|
||||
normally a web application server using the HTTP or HTTPS protocols.
|
||||
The output of this stage is the parsed response from that server.</td>
|
||||
</tr>
|
||||
<tr valign="top" align="center">
|
||||
<td><a href="TeeConsumer.html">tee</a></td>
|
||||
<td><em>required</em><br> first pipeline</td>
|
||||
<td>no</td>
|
||||
<td align="left">This sends its events down two paths; its parameter
|
||||
is a pipeline descriptor for the first path, and the second path
|
||||
is the output of this stage.</td>
|
||||
</tr>
|
||||
|
||||
<tr valign="top" align="center">
|
||||
<td><a href="ValidationConsumer.html">validate</a></td>
|
||||
<td><em>none</em></td>
|
||||
<td>yes</td>
|
||||
<td align="left">This checks for validity errors, and reports them
|
||||
through its error handler. The input must include declaration events
|
||||
and some lexical events. </td>
|
||||
</tr>
|
||||
<tr valign="top" align="center">
|
||||
<td><a href="WellFormednessFilter.html">wf</a></td>
|
||||
<td><em>none</em></td>
|
||||
<td>yes</td>
|
||||
<td align="left"> This class provides some basic "well formedness"
|
||||
tests on the input event stream, and reports a fatal error if any
|
||||
of them fail. One example: start/end calls for elements must match.
|
||||
No SAX parser is permitted to produce malformed output, but other
|
||||
components can easily do so.</td>
|
||||
</tr>
|
||||
<tr valign="top" align="center">
|
||||
<td>write</td>
|
||||
<td><em>required</em><br> "stdout", "stderr", or filename</td>
|
||||
<td>yes</td>
|
||||
<td align="left"> Writes its input to the specified output, as pretty
|
||||
printed XML text encoded using UTF-8. Input events must be well
|
||||
formed and "namespace fixed", else the output won't be XML (or possibly
|
||||
namespace) conformant. The symbolic names represent
|
||||
<em>System.out</em> and <em>System.err</em> respectively; names must
|
||||
correspond to files which don't yet exist.</td>
|
||||
</tr>
|
||||
<tr valign="top" align="center">
|
||||
<td>xhtml</td>
|
||||
<td><em>required</em><br> "stdout", "stderr", or filename</td>
|
||||
<td>yes</td>
|
||||
<td align="left"> Like <em>write</em> (above), except that XHTML rules
|
||||
are followed. The XHTML 1.0 Transitional document type is declared,
|
||||
and only ASCII characters are written (for interoperability). Other
|
||||
characters are written as entity or character references; the text is
|
||||
pretty printed.</td>
|
||||
</tr>
|
||||
<tr valign="top" align="center">
|
||||
<td><a href="XIncludeFilter.html">xinclude</a></td>
|
||||
<td><em>none</em></td>
|
||||
<td>no</td>
|
||||
<td align="left">This stage handles XInclude processing.
|
||||
This is like entity inclusion, except that the included content
|
||||
is declared in-line rather than in the DTD at the beginning of
|
||||
a document.
|
||||
</td>
|
||||
</tr>
|
||||
<tr valign="top" align="center">
|
||||
<td><a href="XsltFilter.html">xslt</a></td>
|
||||
<td><em>required</em><br> XSLT stylesheet URI</td>
|
||||
<td>no</td>
|
||||
<td align="left">This stage handles XSLT transformation
|
||||
according to a stylesheet.
|
||||
The implementation of the transformation may not actually
|
||||
stream data, although if such an XSLT engine is in use
|
||||
then that can happen.
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
</table>
|
||||
|
||||
* <p> Note that {@link EventFilter#bind} can automatically eliminate
|
||||
* some filters by setting SAX2 parser features appropriately. This means
|
||||
* that you can routinely put filters like "nsfix", "validate", or "wf" at the
|
||||
* front of a pipeline (for components that need inputs conditioned to match
|
||||
* that level of correctness), and know that it won't actually be used unless
|
||||
* it's absolutely necessary.
|
||||
*
|
||||
* @author David Brownell
|
||||
*/
|
||||
public class PipelineFactory
|
||||
{
|
||||
/**
|
||||
* Creates a simple pipeline according to the description string passed in.
|
||||
*/
|
||||
public static EventConsumer createPipeline (String description)
|
||||
throws IOException
|
||||
{
|
||||
return createPipeline (description, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extends an existing pipeline by prepending the filter pipeline to the
|
||||
* specified consumer. Some pipelines need more customization than can
|
||||
* be done through this simplified syntax. When they are set up with
|
||||
* direct API calls, use this method to merge more complex pipeline
|
||||
* segments with easily configured ones.
|
||||
*/
|
||||
public static EventConsumer createPipeline (
|
||||
String description,
|
||||
EventConsumer next
|
||||
) throws IOException
|
||||
{
|
||||
// tokens are (for now) what's separated by whitespace;
|
||||
// very easy to parse, but IDs never have spaces.
|
||||
|
||||
StringTokenizer tokenizer;
|
||||
String tokens [];
|
||||
|
||||
tokenizer = new StringTokenizer (description);
|
||||
tokens = new String [tokenizer.countTokens ()];
|
||||
for (int i = 0; i < tokens.length; i++)
|
||||
tokens [i] = tokenizer.nextToken ();
|
||||
|
||||
PipelineFactory factory = new PipelineFactory ();
|
||||
Pipeline pipeline = factory.parsePipeline (tokens, next);
|
||||
|
||||
return pipeline.createPipeline ();
|
||||
}
|
||||
|
||||
|
||||
private PipelineFactory () { /* NYET */ }
|
||||
|
||||
|
||||
/**
|
||||
* Extends an existing pipeline by prepending a pre-tokenized filter
|
||||
* pipeline to the specified consumer. Tokens are class names (or the
|
||||
* predefined aliases) left and right parenthesis, and the vertical bar.
|
||||
*/
|
||||
public static EventConsumer createPipeline (
|
||||
String tokens [],
|
||||
EventConsumer next
|
||||
) throws IOException
|
||||
{
|
||||
PipelineFactory factory = new PipelineFactory ();
|
||||
Pipeline pipeline = factory.parsePipeline (tokens, next);
|
||||
|
||||
return pipeline.createPipeline ();
|
||||
}
|
||||
|
||||
|
||||
private String tokens [];
|
||||
private int index;
|
||||
|
||||
private Pipeline parsePipeline (String toks [], EventConsumer next)
|
||||
{
|
||||
tokens = toks;
|
||||
index = 0;
|
||||
|
||||
Pipeline retval = parsePipeline (next);
|
||||
|
||||
if (index != toks.length)
|
||||
throw new ArrayIndexOutOfBoundsException (
|
||||
"extra token: " + tokens [index]);
|
||||
return retval;
|
||||
}
|
||||
|
||||
// pipeline ::= stage | stage '|' pipeline
|
||||
private Pipeline parsePipeline (EventConsumer next)
|
||||
{
|
||||
Pipeline retval = new Pipeline (parseStage ());
|
||||
|
||||
// minimal pipelines: "stage" and "... | id"
|
||||
if (index > (tokens.length - 2)
|
||||
|| !"|".equals (tokens [index])
|
||||
) {
|
||||
retval.next = next;
|
||||
return retval;
|
||||
}
|
||||
index++;
|
||||
retval.rest = parsePipeline (next);
|
||||
return retval;
|
||||
}
|
||||
|
||||
// stage ::= id | id '(' pipeline ')'
|
||||
private Stage parseStage ()
|
||||
{
|
||||
Stage retval = new Stage (tokens [index++]);
|
||||
|
||||
// minimal stages: "id" and "id ( id )"
|
||||
if (index > (tokens.length - 2)
|
||||
|| !"(".equals (tokens [index]) /*)*/
|
||||
)
|
||||
return retval;
|
||||
|
||||
index++;
|
||||
retval.param = parsePipeline (null);
|
||||
if (index >= tokens.length)
|
||||
throw new ArrayIndexOutOfBoundsException (
|
||||
"missing right paren");
|
||||
if (/*(*/ !")".equals (tokens [index++]))
|
||||
throw new ArrayIndexOutOfBoundsException (
|
||||
"required right paren, not: " + tokens [index - 1]);
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// these classes obey the conventions for constructors, so they're
|
||||
// only built in to this table of shortnames
|
||||
//
|
||||
// - filter (one or two types of arglist)
|
||||
// * last constructor is 'next' element
|
||||
// * optional (first) string parameter
|
||||
//
|
||||
// - terminus (one or types of arglist)
|
||||
// * optional (only) string parameter
|
||||
//
|
||||
// terminus stages are transformed into filters if needed, by
|
||||
// creating a "tee". filter stages aren't turned to terminus
|
||||
// stages though; either eliminate such stages, or add some
|
||||
// terminus explicitly.
|
||||
//
|
||||
private static final String builtinStages [][] = {
|
||||
{ "dom", "gnu.xml.dom.Consumer" },
|
||||
{ "nsfix", "gnu.xml.pipeline.NSFilter" },
|
||||
{ "null", "gnu.xml.pipeline.EventFilter" },
|
||||
{ "server", "gnu.xml.pipeline.CallFilter" },
|
||||
{ "tee", "gnu.xml.pipeline.TeeConsumer" },
|
||||
{ "validate", "gnu.xml.pipeline.ValidationConsumer" },
|
||||
{ "wf", "gnu.xml.pipeline.WellFormednessFilter" },
|
||||
{ "xinclude", "gnu.xml.pipeline.XIncludeFilter" },
|
||||
{ "xslt", "gnu.xml.pipeline.XsltFilter" },
|
||||
|
||||
// XXX want: option for validate, to preload external part of a DTD
|
||||
|
||||
// xhtml, write ... nyet generic-ready
|
||||
};
|
||||
|
||||
private static class Stage
|
||||
{
|
||||
String id;
|
||||
Pipeline param;
|
||||
|
||||
Stage (String name)
|
||||
{ id = name; }
|
||||
|
||||
public String toString ()
|
||||
{
|
||||
if (param == null)
|
||||
return id;
|
||||
return id + " ( " + param + " )";
|
||||
}
|
||||
|
||||
private void fail (String message)
|
||||
throws IOException
|
||||
{
|
||||
throw new IOException ("in '" + id
|
||||
+ "' stage of pipeline, " + message);
|
||||
}
|
||||
|
||||
EventConsumer createStage (EventConsumer next)
|
||||
throws IOException
|
||||
{
|
||||
String name = id;
|
||||
|
||||
// most builtins are just class aliases
|
||||
for (int i = 0; i < builtinStages.length; i++) {
|
||||
if (id.equals (builtinStages [i][0])) {
|
||||
name = builtinStages [i][1];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Save output as XML or XHTML text
|
||||
if ("write".equals (name) || "xhtml".equals (name)) {
|
||||
String filename;
|
||||
boolean isXhtml = "xhtml".equals (name);
|
||||
OutputStream out = null;
|
||||
TextConsumer consumer;
|
||||
|
||||
if (param == null)
|
||||
fail ("parameter is required");
|
||||
|
||||
filename = param.toString ();
|
||||
if ("stdout".equals (filename))
|
||||
out = System.out;
|
||||
else if ("stderr".equals (filename))
|
||||
out = System.err;
|
||||
else {
|
||||
File f = new File (filename);
|
||||
|
||||
/*
|
||||
if (!f.isAbsolute ())
|
||||
fail ("require absolute file paths");
|
||||
*/
|
||||
if (f.exists ())
|
||||
fail ("file already exists: " + f.getName ());
|
||||
|
||||
// XXX this races against the existence test
|
||||
out = new FileOutputStream (f);
|
||||
}
|
||||
|
||||
if (!isXhtml)
|
||||
consumer = new TextConsumer (out);
|
||||
else
|
||||
consumer = new TextConsumer (
|
||||
new OutputStreamWriter (out, "8859_1"),
|
||||
true);
|
||||
|
||||
consumer.setPrettyPrinting (true);
|
||||
if (next == null)
|
||||
return consumer;
|
||||
return new TeeConsumer (consumer, next);
|
||||
|
||||
} else {
|
||||
//
|
||||
// Here go all the builtins that are just aliases for
|
||||
// classes, and all stage IDs that started out as such
|
||||
// class names. The following logic relies on several
|
||||
// documented conventions for constructor invocation.
|
||||
//
|
||||
String msg = null;
|
||||
|
||||
try {
|
||||
Class klass = Class.forName (name);
|
||||
Class argTypes [] = null;
|
||||
Constructor constructor = null;
|
||||
boolean filter = false;
|
||||
Object params [] = null;
|
||||
Object obj = null;
|
||||
|
||||
// do we need a filter stage?
|
||||
if (next != null) {
|
||||
// "next" consumer is always passed, with
|
||||
// or without the optional string param
|
||||
if (param == null) {
|
||||
argTypes = new Class [1];
|
||||
argTypes [0] = EventConsumer.class;
|
||||
|
||||
params = new Object [1];
|
||||
params [0] = next;
|
||||
|
||||
msg = "no-param filter";
|
||||
} else {
|
||||
argTypes = new Class [2];
|
||||
argTypes [0] = String.class;
|
||||
argTypes [1] = EventConsumer.class;
|
||||
|
||||
params = new Object [2];
|
||||
params [0] = param.toString ();
|
||||
params [1] = next;
|
||||
|
||||
msg = "one-param filter";
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
constructor = klass.getConstructor (argTypes);
|
||||
} catch (NoSuchMethodException e) {
|
||||
// try creating a filter from a
|
||||
// terminus and a tee
|
||||
filter = true;
|
||||
msg += " built from ";
|
||||
}
|
||||
}
|
||||
|
||||
// build from a terminus stage, with or
|
||||
// without the optional string param
|
||||
if (constructor == null) {
|
||||
String tmp;
|
||||
|
||||
if (param == null) {
|
||||
argTypes = new Class [0];
|
||||
params = new Object [0];
|
||||
|
||||
tmp = "no-param terminus";
|
||||
} else {
|
||||
argTypes = new Class [1];
|
||||
argTypes [0] = String.class;
|
||||
|
||||
params = new Object [1];
|
||||
params [0] = param.toString ();
|
||||
|
||||
tmp = "one-param terminus";
|
||||
}
|
||||
if (msg == null)
|
||||
msg = tmp;
|
||||
else
|
||||
msg += tmp;
|
||||
constructor = klass.getConstructor (argTypes);
|
||||
// NOT creating terminus by dead-ending
|
||||
// filters ... users should think about
|
||||
// that one, something's likely wrong
|
||||
}
|
||||
|
||||
obj = constructor.newInstance (params);
|
||||
|
||||
// return EventConsumers directly, perhaps after
|
||||
// turning them into a filter
|
||||
if (obj instanceof EventConsumer) {
|
||||
if (filter)
|
||||
return new TeeConsumer ((EventConsumer) obj, next);
|
||||
return (EventConsumer) obj;
|
||||
}
|
||||
|
||||
// if it's not a handler, it's an error
|
||||
// we can wrap handlers in a filter
|
||||
EventFilter retval = new EventFilter ();
|
||||
boolean updated = false;
|
||||
|
||||
if (obj instanceof ContentHandler) {
|
||||
retval.setContentHandler ((ContentHandler) obj);
|
||||
updated = true;
|
||||
}
|
||||
if (obj instanceof DTDHandler) {
|
||||
retval.setDTDHandler ((DTDHandler) obj);
|
||||
updated = true;
|
||||
}
|
||||
if (obj instanceof LexicalHandler) {
|
||||
retval.setProperty (
|
||||
EventFilter.PROPERTY_URI + "lexical-handler",
|
||||
obj);
|
||||
updated = true;
|
||||
}
|
||||
if (obj instanceof DeclHandler) {
|
||||
retval.setProperty (
|
||||
EventFilter.PROPERTY_URI + "declaration-handler",
|
||||
obj);
|
||||
updated = true;
|
||||
}
|
||||
|
||||
if (!updated)
|
||||
fail ("class is neither Consumer nor Handler");
|
||||
|
||||
if (filter)
|
||||
return new TeeConsumer (retval, next);
|
||||
return retval;
|
||||
|
||||
} catch (IOException e) {
|
||||
throw e;
|
||||
|
||||
} catch (NoSuchMethodException e) {
|
||||
fail (name + " constructor missing -- " + msg);
|
||||
|
||||
} catch (ClassNotFoundException e) {
|
||||
fail (name + " class not found");
|
||||
|
||||
} catch (Exception e) {
|
||||
// e.printStackTrace ();
|
||||
fail ("stage not available: " + e.getMessage ());
|
||||
}
|
||||
}
|
||||
// NOTREACHED
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static class Pipeline
|
||||
{
|
||||
Stage stage;
|
||||
|
||||
// rest may be null
|
||||
Pipeline rest;
|
||||
EventConsumer next;
|
||||
|
||||
Pipeline (Stage s)
|
||||
{ stage = s; }
|
||||
|
||||
public String toString ()
|
||||
{
|
||||
if (rest == null && next == null)
|
||||
return stage.toString ();
|
||||
if (rest != null)
|
||||
return stage + " | " + rest;
|
||||
throw new IllegalArgumentException ("next");
|
||||
}
|
||||
|
||||
EventConsumer createPipeline ()
|
||||
throws IOException
|
||||
{
|
||||
if (next == null) {
|
||||
if (rest == null)
|
||||
next = stage.createStage (null);
|
||||
else
|
||||
next = stage.createStage (rest.createPipeline ());
|
||||
}
|
||||
return next;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
public static void main (String argv [])
|
||||
{
|
||||
try {
|
||||
// three basic terminus cases
|
||||
createPipeline ("null");
|
||||
createPipeline ("validate");
|
||||
createPipeline ("write ( stdout )");
|
||||
|
||||
// four basic filters
|
||||
createPipeline ("nsfix | write ( stderr )");
|
||||
createPipeline ("wf | null");
|
||||
createPipeline ("null | null");
|
||||
createPipeline (
|
||||
"call ( http://www.example.com/services/xml-1a ) | xhtml ( stdout )");
|
||||
|
||||
// tee junctions
|
||||
createPipeline ("tee ( validate ) | write ( stdout )");
|
||||
createPipeline ("tee ( nsfix | write ( stdout ) ) | validate");
|
||||
|
||||
// longer pipeline
|
||||
createPipeline ("nsfix | tee ( validate ) | write ( stdout )");
|
||||
createPipeline (
|
||||
"null | wf | nsfix | tee ( validate ) | write ( stdout )");
|
||||
|
||||
// try some parsing error cases
|
||||
try {
|
||||
createPipeline ("null ("); // extra token '('
|
||||
System.err.println ("** didn't report error");
|
||||
} catch (Exception e) {
|
||||
System.err.println ("== err: " + e.getMessage ()); }
|
||||
|
||||
try {
|
||||
createPipeline ("nsfix |"); // extra token '|'
|
||||
System.err.println ("** didn't report error");
|
||||
} catch (Exception e) {
|
||||
System.err.println ("== err: " + e.getMessage ()); }
|
||||
|
||||
try {
|
||||
createPipeline ("xhtml ( foo"); // missing right paren
|
||||
System.err.println ("** didn't report error");
|
||||
} catch (Exception e) {
|
||||
System.err.println ("== err: " + e.getMessage ()); }
|
||||
|
||||
try {
|
||||
createPipeline ("xhtml ( foo bar"); // required right paren
|
||||
System.err.println ("** didn't report error");
|
||||
} catch (Exception e) {
|
||||
System.err.println ("== err: " + e.getMessage ()); }
|
||||
|
||||
try {
|
||||
createPipeline ("tee ( nsfix | validate");// missing right paren
|
||||
System.err.println ("** didn't report error");
|
||||
} catch (Exception e) {
|
||||
System.err.println ("== err: " + e.getMessage ()); }
|
||||
|
||||
// try some construction error cases
|
||||
|
||||
try {
|
||||
createPipeline ("call"); // missing param
|
||||
System.err.println ("** didn't report error");
|
||||
} catch (Exception e) {
|
||||
System.err.println ("== err: " + e.getMessage ()); }
|
||||
try {
|
||||
createPipeline ("call ( foobar )"); // broken param
|
||||
System.err.println ("** didn't report error");
|
||||
} catch (Exception e) {
|
||||
System.err.println ("== err: " + e.getMessage ()); }
|
||||
try {
|
||||
createPipeline ("nsfix ( foobar )"); // illegal param
|
||||
System.err.println ("** didn't report error");
|
||||
} catch (Exception e) {
|
||||
System.err.println ("== err: " + e.getMessage ()); }
|
||||
try {
|
||||
createPipeline ("null ( foobar )"); // illegal param
|
||||
System.err.println ("** didn't report error");
|
||||
} catch (Exception e) {
|
||||
System.err.println ("== err: " + e.getMessage ()); }
|
||||
try {
|
||||
createPipeline ("wf ( foobar )"); // illegal param
|
||||
System.err.println ("** didn't report error");
|
||||
} catch (Exception e) {
|
||||
System.err.println ("== err: " + e.getMessage ()); }
|
||||
try {
|
||||
createPipeline ("xhtml ( foobar.html )");
|
||||
new File ("foobar.html").delete ();
|
||||
// now supported
|
||||
} catch (Exception e) {
|
||||
System.err.println ("** err: " + e.getMessage ()); }
|
||||
try {
|
||||
createPipeline ("xhtml"); // missing param
|
||||
System.err.println ("** didn't report error");
|
||||
} catch (Exception e) {
|
||||
System.err.println ("== err: " + e.getMessage ()); }
|
||||
try {
|
||||
createPipeline ("write ( stdout ) | null"); // nonterminal
|
||||
System.err.println ("** didn't report error");
|
||||
} catch (Exception e) {
|
||||
System.err.println ("== err: " + e.getMessage ()); }
|
||||
try {
|
||||
createPipeline ("validate | null");
|
||||
// now supported
|
||||
} catch (Exception e) {
|
||||
System.err.println ("** err: " + e.getMessage ()); }
|
||||
try {
|
||||
createPipeline ("validate ( foo )"); // illegal param
|
||||
System.err.println ("** didn't report error");
|
||||
} catch (Exception e) {
|
||||
System.err.println ("== err: " + e.getMessage ()); }
|
||||
try {
|
||||
createPipeline ("tee"); // missing param
|
||||
System.err.println ("** didn't report error");
|
||||
} catch (Exception e) {
|
||||
System.err.println ("== err: " + e.getMessage ()); }
|
||||
try {
|
||||
// only builtins so far
|
||||
createPipeline ("com.example.xml.FilterClass");
|
||||
System.err.println ("** didn't report error");
|
||||
} catch (Exception e) {
|
||||
System.err.println ("== err: " + e.getMessage ()); }
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace ();
|
||||
}
|
||||
}
|
||||
/**/
|
||||
|
||||
}
|
||||
@@ -0,0 +1,413 @@
|
||||
/* TeeConsumer.java --
|
||||
Copyright (C) 1999,2000,2001 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Classpath.
|
||||
|
||||
GNU Classpath 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, or (at your option)
|
||||
any later version.
|
||||
|
||||
GNU Classpath 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 GNU Classpath; see the file COPYING. If not, write to the
|
||||
Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
02111-1307 USA.
|
||||
|
||||
Linking this library statically or dynamically with other modules is
|
||||
making a combined work based on this library. Thus, the terms and
|
||||
conditions of the GNU General Public License cover the whole
|
||||
combination.
|
||||
|
||||
As a special exception, the copyright holders of this library give you
|
||||
permission to link this library with independent modules to produce an
|
||||
executable, regardless of the license terms of these independent
|
||||
modules, and to copy and distribute the resulting executable under
|
||||
terms of your choice, provided that you also meet, for each linked
|
||||
independent module, the terms and conditions of the license of that
|
||||
module. An independent module is a module which is not derived from
|
||||
or based on this library. If you modify this library, you may extend
|
||||
this exception to your version of the library, but you are not
|
||||
obligated to do so. If you do not wish to do so, delete this
|
||||
exception statement from your version. */
|
||||
|
||||
package gnu.xml.pipeline;
|
||||
|
||||
import java.io.IOException;
|
||||
import org.xml.sax.*;
|
||||
import org.xml.sax.ext.*;
|
||||
//import gnu.xml.util;
|
||||
|
||||
|
||||
/**
|
||||
* Fans its events out to two other consumers, a "tee" filter stage in an
|
||||
* event pipeline. Networks can be assembled with multiple output points.
|
||||
*
|
||||
* <p> Error handling should be simple if you remember that exceptions
|
||||
* you throw will cancel later stages in that callback's pipeline, and
|
||||
* generally the producer will stop if it sees such an exception. You
|
||||
* may want to protect your pipeline against such backflows, making a
|
||||
* kind of reverse filter (or valve?) so that certain exceptions thrown by
|
||||
* your pipeline will caught and handled before the producer sees them.
|
||||
* Just use a "try/catch" block, rememebering that really important
|
||||
* cleanup tasks should be in "finally" clauses.
|
||||
*
|
||||
* <p> That issue isn't unique to "tee" consumers, but tee consumers have
|
||||
* the additional twist that exceptions thrown by the first consumer
|
||||
* will cause the second consumer not to see the callback (except for
|
||||
* the endDocument callback, which signals state cleanup).
|
||||
*
|
||||
* @author David Brownell
|
||||
*/
|
||||
final public class TeeConsumer
|
||||
implements EventConsumer,
|
||||
ContentHandler, DTDHandler,
|
||||
LexicalHandler,DeclHandler
|
||||
{
|
||||
private EventConsumer first, rest;
|
||||
|
||||
// cached to minimize time overhead
|
||||
private ContentHandler docFirst, docRest;
|
||||
private DeclHandler declFirst, declRest;
|
||||
private LexicalHandler lexFirst, lexRest;
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a consumer which sends all its events to the first
|
||||
* consumer, and then the second one. If the first consumer throws
|
||||
* an exception, the second one will not see the event which
|
||||
* caused that exception to be reported.
|
||||
*
|
||||
* @param car The first consumer to get the events
|
||||
* @param cdr The second consumer to get the events
|
||||
*/
|
||||
public TeeConsumer (EventConsumer car, EventConsumer cdr)
|
||||
{
|
||||
if (car == null || cdr == null)
|
||||
throw new NullPointerException ();
|
||||
first = car;
|
||||
rest = cdr;
|
||||
|
||||
//
|
||||
// Cache the handlers.
|
||||
//
|
||||
docFirst = first.getContentHandler ();
|
||||
docRest = rest.getContentHandler ();
|
||||
// DTD handler isn't cached (rarely needed)
|
||||
|
||||
try {
|
||||
declFirst = null;
|
||||
declFirst = (DeclHandler) first.getProperty (
|
||||
EventFilter.DECL_HANDLER);
|
||||
} catch (SAXException e) {}
|
||||
try {
|
||||
declRest = null;
|
||||
declRest = (DeclHandler) rest.getProperty (
|
||||
EventFilter.DECL_HANDLER);
|
||||
} catch (SAXException e) {}
|
||||
|
||||
try {
|
||||
lexFirst = null;
|
||||
lexFirst = (LexicalHandler) first.getProperty (
|
||||
EventFilter.LEXICAL_HANDLER);
|
||||
} catch (SAXException e) {}
|
||||
try {
|
||||
lexRest = null;
|
||||
lexRest = (LexicalHandler) rest.getProperty (
|
||||
EventFilter.LEXICAL_HANDLER);
|
||||
} catch (SAXException e) {}
|
||||
}
|
||||
|
||||
/* FIXME
|
||||
/**
|
||||
* Constructs a pipeline, and is otherwise a shorthand for the
|
||||
* two-consumer constructor for this class.
|
||||
*
|
||||
* @param first Description of the first pipeline to get events,
|
||||
* which will be passed to {@link PipelineFactory#createPipeline}
|
||||
* @param rest The second pipeline to get the events
|
||||
* /
|
||||
// constructor used by PipelineFactory
|
||||
public TeeConsumer (String first, EventConsumer rest)
|
||||
throws IOException
|
||||
{
|
||||
this (PipelineFactory.createPipeline (first), rest);
|
||||
}
|
||||
*/
|
||||
|
||||
/** Returns the first pipeline to get event calls. */
|
||||
public EventConsumer getFirst ()
|
||||
{ return first; }
|
||||
|
||||
/** Returns the second pipeline to get event calls. */
|
||||
public EventConsumer getRest ()
|
||||
{ return rest; }
|
||||
|
||||
/** Returns the content handler being used. */
|
||||
final public ContentHandler getContentHandler ()
|
||||
{
|
||||
if (docRest == null)
|
||||
return docFirst;
|
||||
if (docFirst == null)
|
||||
return docRest;
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Returns the dtd handler being used. */
|
||||
final public DTDHandler getDTDHandler ()
|
||||
{
|
||||
// not cached (hardly used)
|
||||
if (rest.getDTDHandler () == null)
|
||||
return first.getDTDHandler ();
|
||||
if (first.getDTDHandler () == null)
|
||||
return rest.getDTDHandler ();
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Returns the declaration or lexical handler being used. */
|
||||
final public Object getProperty (String id)
|
||||
throws SAXNotRecognizedException
|
||||
{
|
||||
//
|
||||
// in degenerate cases, we have no work to do.
|
||||
//
|
||||
Object firstProp = null, restProp = null;
|
||||
|
||||
try { firstProp = first.getProperty (id); }
|
||||
catch (SAXNotRecognizedException e) { /* ignore */ }
|
||||
try { restProp = rest.getProperty (id); }
|
||||
catch (SAXNotRecognizedException e) { /* ignore */ }
|
||||
|
||||
if (restProp == null)
|
||||
return firstProp;
|
||||
if (firstProp == null)
|
||||
return restProp;
|
||||
|
||||
//
|
||||
// we've got work to do; handle two builtin cases.
|
||||
//
|
||||
if (EventFilter.DECL_HANDLER.equals (id))
|
||||
return this;
|
||||
if (EventFilter.LEXICAL_HANDLER.equals (id))
|
||||
return this;
|
||||
|
||||
//
|
||||
// non-degenerate, handled by both consumers, but we don't know
|
||||
// how to handle this.
|
||||
//
|
||||
throw new SAXNotRecognizedException ("can't tee: " + id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides the error handler to both subsequent nodes of
|
||||
* this filter stage.
|
||||
*/
|
||||
public void setErrorHandler (ErrorHandler handler)
|
||||
{
|
||||
first.setErrorHandler (handler);
|
||||
rest.setErrorHandler (handler);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// ContentHandler
|
||||
//
|
||||
public void setDocumentLocator (Locator locator)
|
||||
{
|
||||
// this call is not made by all parsers
|
||||
docFirst.setDocumentLocator (locator);
|
||||
docRest.setDocumentLocator (locator);
|
||||
}
|
||||
|
||||
public void startDocument ()
|
||||
throws SAXException
|
||||
{
|
||||
docFirst.startDocument ();
|
||||
docRest.startDocument ();
|
||||
}
|
||||
|
||||
public void endDocument ()
|
||||
throws SAXException
|
||||
{
|
||||
try {
|
||||
docFirst.endDocument ();
|
||||
} finally {
|
||||
docRest.endDocument ();
|
||||
}
|
||||
}
|
||||
|
||||
public void startPrefixMapping (String prefix, String uri)
|
||||
throws SAXException
|
||||
{
|
||||
docFirst.startPrefixMapping (prefix, uri);
|
||||
docRest.startPrefixMapping (prefix, uri);
|
||||
}
|
||||
|
||||
public void endPrefixMapping (String prefix)
|
||||
throws SAXException
|
||||
{
|
||||
docFirst.endPrefixMapping (prefix);
|
||||
docRest.endPrefixMapping (prefix);
|
||||
}
|
||||
|
||||
public void skippedEntity (String name)
|
||||
throws SAXException
|
||||
{
|
||||
docFirst.skippedEntity (name);
|
||||
docRest.skippedEntity (name);
|
||||
}
|
||||
|
||||
public void startElement (String uri, String localName,
|
||||
String qName, Attributes atts)
|
||||
throws SAXException
|
||||
{
|
||||
docFirst.startElement (uri, localName, qName, atts);
|
||||
docRest.startElement (uri, localName, qName, atts);
|
||||
}
|
||||
|
||||
public void endElement (String uri, String localName, String qName)
|
||||
throws SAXException
|
||||
{
|
||||
docFirst.endElement (uri, localName, qName);
|
||||
docRest.endElement (uri, localName, qName);
|
||||
}
|
||||
|
||||
public void processingInstruction (String target, String data)
|
||||
throws SAXException
|
||||
{
|
||||
docFirst.processingInstruction (target, data);
|
||||
docRest.processingInstruction (target, data);
|
||||
}
|
||||
|
||||
public void characters (char ch [], int start, int length)
|
||||
throws SAXException
|
||||
{
|
||||
docFirst.characters (ch, start, length);
|
||||
docRest.characters (ch, start, length);
|
||||
}
|
||||
|
||||
public void ignorableWhitespace (char ch [], int start, int length)
|
||||
throws SAXException
|
||||
{
|
||||
docFirst.ignorableWhitespace (ch, start, length);
|
||||
docRest.ignorableWhitespace (ch, start, length);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// DTDHandler
|
||||
//
|
||||
public void notationDecl (String name, String publicId, String systemId)
|
||||
throws SAXException
|
||||
{
|
||||
DTDHandler l1 = first.getDTDHandler ();
|
||||
DTDHandler l2 = rest.getDTDHandler ();
|
||||
|
||||
l1.notationDecl (name, publicId, systemId);
|
||||
l2.notationDecl (name, publicId, systemId);
|
||||
}
|
||||
|
||||
public void unparsedEntityDecl (String name,
|
||||
String publicId, String systemId,
|
||||
String notationName
|
||||
) throws SAXException
|
||||
{
|
||||
DTDHandler l1 = first.getDTDHandler ();
|
||||
DTDHandler l2 = rest.getDTDHandler ();
|
||||
|
||||
l1.unparsedEntityDecl (name, publicId, systemId, notationName);
|
||||
l2.unparsedEntityDecl (name, publicId, systemId, notationName);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// DeclHandler
|
||||
//
|
||||
public void attributeDecl (String eName, String aName,
|
||||
String type,
|
||||
String mode, String value)
|
||||
throws SAXException
|
||||
{
|
||||
declFirst.attributeDecl (eName, aName, type, mode, value);
|
||||
declRest.attributeDecl (eName, aName, type, mode, value);
|
||||
}
|
||||
|
||||
public void elementDecl (String name, String model)
|
||||
throws SAXException
|
||||
{
|
||||
declFirst.elementDecl (name, model);
|
||||
declRest.elementDecl (name, model);
|
||||
}
|
||||
|
||||
public void externalEntityDecl (String name,
|
||||
String publicId, String systemId)
|
||||
throws SAXException
|
||||
{
|
||||
declFirst.externalEntityDecl (name, publicId, systemId);
|
||||
declRest.externalEntityDecl (name, publicId, systemId);
|
||||
}
|
||||
|
||||
public void internalEntityDecl (String name, String value)
|
||||
throws SAXException
|
||||
{
|
||||
declFirst.internalEntityDecl (name, value);
|
||||
declRest.internalEntityDecl (name, value);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// LexicalHandler
|
||||
//
|
||||
public void comment (char ch [], int start, int length)
|
||||
throws SAXException
|
||||
{
|
||||
lexFirst.comment (ch, start, length);
|
||||
lexRest.comment (ch, start, length);
|
||||
}
|
||||
|
||||
public void startCDATA ()
|
||||
throws SAXException
|
||||
{
|
||||
lexFirst.startCDATA ();
|
||||
lexRest.startCDATA ();
|
||||
}
|
||||
|
||||
public void endCDATA ()
|
||||
throws SAXException
|
||||
{
|
||||
lexFirst.endCDATA ();
|
||||
lexRest.endCDATA ();
|
||||
}
|
||||
|
||||
public void startEntity (String name)
|
||||
throws SAXException
|
||||
{
|
||||
lexFirst.startEntity (name);
|
||||
lexRest.startEntity (name);
|
||||
}
|
||||
|
||||
public void endEntity (String name)
|
||||
throws SAXException
|
||||
{
|
||||
lexFirst.endEntity (name);
|
||||
lexRest.endEntity (name);
|
||||
}
|
||||
|
||||
public void startDTD (String name, String publicId, String systemId)
|
||||
throws SAXException
|
||||
{
|
||||
lexFirst.startDTD (name, publicId, systemId);
|
||||
lexRest.startDTD (name, publicId, systemId);
|
||||
}
|
||||
|
||||
public void endDTD ()
|
||||
throws SAXException
|
||||
{
|
||||
lexFirst.endDTD ();
|
||||
lexRest.endDTD ();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
/* TextConsumer.java --
|
||||
Copyright (C) 1999,2000,2001 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Classpath.
|
||||
|
||||
GNU Classpath 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, or (at your option)
|
||||
any later version.
|
||||
|
||||
GNU Classpath 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 GNU Classpath; see the file COPYING. If not, write to the
|
||||
Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
02111-1307 USA.
|
||||
|
||||
Linking this library statically or dynamically with other modules is
|
||||
making a combined work based on this library. Thus, the terms and
|
||||
conditions of the GNU General Public License cover the whole
|
||||
combination.
|
||||
|
||||
As a special exception, the copyright holders of this library give you
|
||||
permission to link this library with independent modules to produce an
|
||||
executable, regardless of the license terms of these independent
|
||||
modules, and to copy and distribute the resulting executable under
|
||||
terms of your choice, provided that you also meet, for each linked
|
||||
independent module, the terms and conditions of the license of that
|
||||
module. An independent module is a module which is not derived from
|
||||
or based on this library. If you modify this library, you may extend
|
||||
this exception to your version of the library, but you are not
|
||||
obligated to do so. If you do not wish to do so, delete this
|
||||
exception statement from your version. */
|
||||
|
||||
package gnu.xml.pipeline;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
import org.xml.sax.*;
|
||||
|
||||
import gnu.xml.util.XMLWriter;
|
||||
|
||||
|
||||
/**
|
||||
* Terminates a pipeline, consuming events to print them as well formed
|
||||
* XML (or XHTML) text.
|
||||
*
|
||||
* <p> Input must be well formed, and must include XML names (e.g. the
|
||||
* prefixes and prefix declarations must be present), or the output of
|
||||
* this class is undefined.
|
||||
*
|
||||
* @see NSFilter
|
||||
* @see WellFormednessFilter
|
||||
*
|
||||
* @author David Brownell
|
||||
*/
|
||||
public class TextConsumer extends XMLWriter implements EventConsumer
|
||||
{
|
||||
/**
|
||||
* Constructs an event consumer which echoes its input as text,
|
||||
* optionally adhering to some basic XHTML formatting options
|
||||
* which increase interoperability with old (v3) browsers.
|
||||
*
|
||||
* <p> For the best interoperability, when writing as XHTML only
|
||||
* ASCII characters are emitted; other characters are turned to
|
||||
* entity or character references as needed, and no XML declaration
|
||||
* is provided in the document.
|
||||
*/
|
||||
public TextConsumer (Writer w, boolean isXhtml)
|
||||
throws IOException
|
||||
{
|
||||
super (w, isXhtml ? "US-ASCII" : null);
|
||||
setXhtml (isXhtml);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a consumer that writes its input as XML text.
|
||||
* XHTML rules are not followed.
|
||||
*/
|
||||
public TextConsumer (Writer w)
|
||||
throws IOException
|
||||
{
|
||||
this (w, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a consumer that writes its input as XML text,
|
||||
* encoded in UTF-8. XHTML rules are not followed.
|
||||
*/
|
||||
public TextConsumer (OutputStream out)
|
||||
throws IOException
|
||||
{
|
||||
this (new OutputStreamWriter (out, "UTF8"), false);
|
||||
}
|
||||
|
||||
/** <b>EventConsumer</b> Returns the document handler being used. */
|
||||
public ContentHandler getContentHandler ()
|
||||
{ return this; }
|
||||
|
||||
/** <b>EventConsumer</b> Returns the dtd handler being used. */
|
||||
public DTDHandler getDTDHandler ()
|
||||
{ return this; }
|
||||
|
||||
/** <b>XMLReader</b>Retrieves a property (lexical and decl handlers) */
|
||||
public Object getProperty (String propertyId)
|
||||
throws SAXNotRecognizedException
|
||||
{
|
||||
if (EventFilter.LEXICAL_HANDLER.equals (propertyId))
|
||||
return this;
|
||||
if (EventFilter.DECL_HANDLER.equals (propertyId))
|
||||
return this;
|
||||
throw new SAXNotRecognizedException (propertyId);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,362 @@
|
||||
/* WellFormednessFilter.java --
|
||||
Copyright (C) 1999,2000,2001 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Classpath.
|
||||
|
||||
GNU Classpath 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, or (at your option)
|
||||
any later version.
|
||||
|
||||
GNU Classpath 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 GNU Classpath; see the file COPYING. If not, write to the
|
||||
Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
02111-1307 USA.
|
||||
|
||||
Linking this library statically or dynamically with other modules is
|
||||
making a combined work based on this library. Thus, the terms and
|
||||
conditions of the GNU General Public License cover the whole
|
||||
combination.
|
||||
|
||||
As a special exception, the copyright holders of this library give you
|
||||
permission to link this library with independent modules to produce an
|
||||
executable, regardless of the license terms of these independent
|
||||
modules, and to copy and distribute the resulting executable under
|
||||
terms of your choice, provided that you also meet, for each linked
|
||||
independent module, the terms and conditions of the license of that
|
||||
module. An independent module is a module which is not derived from
|
||||
or based on this library. If you modify this library, you may extend
|
||||
this exception to your version of the library, but you are not
|
||||
obligated to do so. If you do not wish to do so, delete this
|
||||
exception statement from your version. */
|
||||
|
||||
package gnu.xml.pipeline;
|
||||
|
||||
import java.util.EmptyStackException;
|
||||
import java.util.Stack;
|
||||
|
||||
import gnu.xml.aelfred2.SAXDriver;
|
||||
import org.xml.sax.*;
|
||||
import org.xml.sax.ext.*;
|
||||
|
||||
|
||||
/**
|
||||
* This filter reports fatal exceptions in the case of event streams that
|
||||
* are not well formed. The rules currently tested include: <ul>
|
||||
*
|
||||
* <li>setDocumentLocator ... may be called only before startDocument
|
||||
*
|
||||
* <li>startDocument/endDocument ... must be paired, and all other
|
||||
* calls (except setDocumentLocator) must be nested within these.
|
||||
*
|
||||
* <li>startElement/endElement ... must be correctly paired, and
|
||||
* may never appear within CDATA sections.
|
||||
*
|
||||
* <li>comment ... can't contain "--"
|
||||
*
|
||||
* <li>character data ... can't contain "]]>"
|
||||
*
|
||||
* <li>whitespace ... can't contain CR
|
||||
*
|
||||
* <li>whitespace and character data must be within an element
|
||||
*
|
||||
* <li>processing instruction ... can't contain "?>" or CR
|
||||
*
|
||||
* <li>startCDATA/endCDATA ... must be correctly paired.
|
||||
*
|
||||
* </ul>
|
||||
*
|
||||
* <p> Other checks for event stream correctness may be provided in
|
||||
* the future. For example, insisting that
|
||||
* entity boundaries nest correctly,
|
||||
* namespace scopes nest correctly,
|
||||
* namespace values never contain relative URIs,
|
||||
* attributes don't have "<" characters;
|
||||
* and more.
|
||||
*
|
||||
* @author David Brownell
|
||||
*/
|
||||
public final class WellFormednessFilter extends EventFilter
|
||||
{
|
||||
private boolean startedDoc;
|
||||
private Stack elementStack = new Stack ();
|
||||
private boolean startedCDATA;
|
||||
private String dtdState = "before";
|
||||
|
||||
|
||||
/**
|
||||
* Swallows all events after performing well formedness checks.
|
||||
*/
|
||||
// constructor used by PipelineFactory
|
||||
public WellFormednessFilter ()
|
||||
{ this (null); }
|
||||
|
||||
|
||||
/**
|
||||
* Passes events through to the specified consumer, after first
|
||||
* processing them.
|
||||
*/
|
||||
// constructor used by PipelineFactory
|
||||
public WellFormednessFilter (EventConsumer consumer)
|
||||
{
|
||||
super (consumer);
|
||||
|
||||
setContentHandler (this);
|
||||
setDTDHandler (this);
|
||||
|
||||
try {
|
||||
setProperty (LEXICAL_HANDLER, this);
|
||||
} catch (SAXException e) { /* can't happen */ }
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets state as if any preceding event stream was well formed.
|
||||
* Particularly useful if it ended through some sort of error,
|
||||
* and the endDocument call wasn't made.
|
||||
*/
|
||||
public void reset ()
|
||||
{
|
||||
startedDoc = false;
|
||||
startedCDATA = false;
|
||||
elementStack.removeAllElements ();
|
||||
}
|
||||
|
||||
|
||||
private SAXParseException getException (String message)
|
||||
{
|
||||
SAXParseException e;
|
||||
Locator locator = getDocumentLocator ();
|
||||
|
||||
if (locator == null)
|
||||
return new SAXParseException (message, null, null, -1, -1);
|
||||
else
|
||||
return new SAXParseException (message, locator);
|
||||
}
|
||||
|
||||
private void fatalError (String message)
|
||||
throws SAXException
|
||||
{
|
||||
SAXParseException e = getException (message);
|
||||
ErrorHandler handler = getErrorHandler ();
|
||||
|
||||
if (handler != null)
|
||||
handler.fatalError (e);
|
||||
throw e;
|
||||
}
|
||||
|
||||
/**
|
||||
* Throws an exception when called after startDocument.
|
||||
*
|
||||
* @param locator the locator, to be used in error reporting or relative
|
||||
* URI resolution.
|
||||
*
|
||||
* @exception IllegalStateException when called after the document
|
||||
* has already been started
|
||||
*/
|
||||
public void setDocumentLocator (Locator locator)
|
||||
{
|
||||
if (startedDoc)
|
||||
throw new IllegalStateException (
|
||||
"setDocumentLocator called after startDocument");
|
||||
super.setDocumentLocator (locator);
|
||||
}
|
||||
|
||||
public void startDocument () throws SAXException
|
||||
{
|
||||
if (startedDoc)
|
||||
fatalError ("startDocument called more than once");
|
||||
startedDoc = true;
|
||||
startedCDATA = false;
|
||||
elementStack.removeAllElements ();
|
||||
super.startDocument ();
|
||||
}
|
||||
|
||||
public void startElement (
|
||||
String uri, String localName,
|
||||
String qName, Attributes atts
|
||||
) throws SAXException
|
||||
{
|
||||
if (!startedDoc)
|
||||
fatalError ("callback outside of document?");
|
||||
if ("inside".equals (dtdState))
|
||||
fatalError ("element inside DTD?");
|
||||
else
|
||||
dtdState = "after";
|
||||
if (startedCDATA)
|
||||
fatalError ("element inside CDATA section");
|
||||
if (qName == null || "".equals (qName))
|
||||
fatalError ("startElement name missing");
|
||||
elementStack.push (qName);
|
||||
super.startElement (uri, localName, qName, atts);
|
||||
}
|
||||
|
||||
public void endElement (String uri, String localName, String qName)
|
||||
throws SAXException
|
||||
{
|
||||
if (!startedDoc)
|
||||
fatalError ("callback outside of document?");
|
||||
if (startedCDATA)
|
||||
fatalError ("element inside CDATA section");
|
||||
if (qName == null || "".equals (qName))
|
||||
fatalError ("endElement name missing");
|
||||
|
||||
try {
|
||||
String top = (String) elementStack.pop ();
|
||||
|
||||
if (!qName.equals (top))
|
||||
fatalError ("<" + top + " ...>...</" + qName + ">");
|
||||
// XXX could record/test namespace info
|
||||
} catch (EmptyStackException e) {
|
||||
fatalError ("endElement without startElement: </" + qName + ">");
|
||||
}
|
||||
super.endElement (uri, localName, qName);
|
||||
}
|
||||
|
||||
public void endDocument () throws SAXException
|
||||
{
|
||||
if (!startedDoc)
|
||||
fatalError ("callback outside of document?");
|
||||
dtdState = "before";
|
||||
startedDoc = false;
|
||||
super.endDocument ();
|
||||
}
|
||||
|
||||
|
||||
public void startDTD (String root, String publicId, String systemId)
|
||||
throws SAXException
|
||||
{
|
||||
if (!startedDoc)
|
||||
fatalError ("callback outside of document?");
|
||||
if ("before" != dtdState)
|
||||
fatalError ("two DTDs?");
|
||||
if (!elementStack.empty ())
|
||||
fatalError ("DTD must precede root element");
|
||||
dtdState = "inside";
|
||||
super.startDTD (root, publicId, systemId);
|
||||
}
|
||||
|
||||
public void notationDecl (String name, String publicId, String systemId)
|
||||
throws SAXException
|
||||
{
|
||||
// FIXME: not all parsers will report startDTD() ...
|
||||
// we'd rather insist we're "inside".
|
||||
if ("after" == dtdState)
|
||||
fatalError ("not inside DTD");
|
||||
super.notationDecl (name, publicId, systemId);
|
||||
}
|
||||
|
||||
public void unparsedEntityDecl (String name,
|
||||
String publicId, String systemId, String notationName)
|
||||
throws SAXException
|
||||
{
|
||||
// FIXME: not all parsers will report startDTD() ...
|
||||
// we'd rather insist we're "inside".
|
||||
if ("after" == dtdState)
|
||||
fatalError ("not inside DTD");
|
||||
super.unparsedEntityDecl (name, publicId, systemId, notationName);
|
||||
}
|
||||
|
||||
// FIXME: add the four DeclHandler calls too
|
||||
|
||||
public void endDTD ()
|
||||
throws SAXException
|
||||
{
|
||||
if (!startedDoc)
|
||||
fatalError ("callback outside of document?");
|
||||
if ("inside" != dtdState)
|
||||
fatalError ("DTD ends without start?");
|
||||
dtdState = "after";
|
||||
super.endDTD ();
|
||||
}
|
||||
|
||||
public void characters (char ch [], int start, int length)
|
||||
throws SAXException
|
||||
{
|
||||
int here = start, end = start + length;
|
||||
if (elementStack.empty ())
|
||||
fatalError ("characters must be in an element");
|
||||
while (here < end) {
|
||||
if (ch [here++] != ']')
|
||||
continue;
|
||||
if (here == end) // potential problem ...
|
||||
continue;
|
||||
if (ch [here++] != ']')
|
||||
continue;
|
||||
if (here == end) // potential problem ...
|
||||
continue;
|
||||
if (ch [here++] == '>')
|
||||
fatalError ("character data can't contain \"]]>\"");
|
||||
}
|
||||
super.characters (ch, start, length);
|
||||
}
|
||||
|
||||
public void ignorableWhitespace (char ch [], int start, int length)
|
||||
throws SAXException
|
||||
{
|
||||
int here = start, end = start + length;
|
||||
if (elementStack.empty ())
|
||||
fatalError ("characters must be in an element");
|
||||
while (here < end) {
|
||||
if (ch [here++] == '\r')
|
||||
fatalError ("whitespace can't contain CR");
|
||||
}
|
||||
super.ignorableWhitespace (ch, start, length);
|
||||
}
|
||||
|
||||
public void processingInstruction (String target, String data)
|
||||
throws SAXException
|
||||
{
|
||||
if (data.indexOf ('\r') > 0)
|
||||
fatalError ("PIs can't contain CR");
|
||||
if (data.indexOf ("?>") > 0)
|
||||
fatalError ("PIs can't contain \"?>\"");
|
||||
}
|
||||
|
||||
public void comment (char ch [], int start, int length)
|
||||
throws SAXException
|
||||
{
|
||||
if (!startedDoc)
|
||||
fatalError ("callback outside of document?");
|
||||
if (startedCDATA)
|
||||
fatalError ("comments can't nest in CDATA");
|
||||
int here = start, end = start + length;
|
||||
while (here < end) {
|
||||
if (ch [here] == '\r')
|
||||
fatalError ("comments can't contain CR");
|
||||
if (ch [here++] != '-')
|
||||
continue;
|
||||
if (here == end)
|
||||
fatalError ("comments can't end with \"--->\"");
|
||||
if (ch [here++] == '-')
|
||||
fatalError ("comments can't contain \"--\"");
|
||||
}
|
||||
super.comment (ch, start, length);
|
||||
}
|
||||
|
||||
public void startCDATA ()
|
||||
throws SAXException
|
||||
{
|
||||
if (!startedDoc)
|
||||
fatalError ("callback outside of document?");
|
||||
if (startedCDATA)
|
||||
fatalError ("CDATA starts can't nest");
|
||||
startedCDATA = true;
|
||||
super.startCDATA ();
|
||||
}
|
||||
|
||||
public void endCDATA ()
|
||||
throws SAXException
|
||||
{
|
||||
if (!startedDoc)
|
||||
fatalError ("callback outside of document?");
|
||||
if (!startedCDATA)
|
||||
fatalError ("CDATA end without start?");
|
||||
startedCDATA = false;
|
||||
super.endCDATA ();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,580 @@
|
||||
/* XIncludeFilter.java --
|
||||
Copyright (C) 2001,2002 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Classpath.
|
||||
|
||||
GNU Classpath 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, or (at your option)
|
||||
any later version.
|
||||
|
||||
GNU Classpath 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 GNU Classpath; see the file COPYING. If not, write to the
|
||||
Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
02111-1307 USA.
|
||||
|
||||
Linking this library statically or dynamically with other modules is
|
||||
making a combined work based on this library. Thus, the terms and
|
||||
conditions of the GNU General Public License cover the whole
|
||||
combination.
|
||||
|
||||
As a special exception, the copyright holders of this library give you
|
||||
permission to link this library with independent modules to produce an
|
||||
executable, regardless of the license terms of these independent
|
||||
modules, and to copy and distribute the resulting executable under
|
||||
terms of your choice, provided that you also meet, for each linked
|
||||
independent module, the terms and conditions of the license of that
|
||||
module. An independent module is a module which is not derived from
|
||||
or based on this library. If you modify this library, you may extend
|
||||
this exception to your version of the library, but you are not
|
||||
obligated to do so. If you do not wish to do so, delete this
|
||||
exception statement from your version. */
|
||||
|
||||
package gnu.xml.pipeline;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.util.Enumeration;
|
||||
import java.util.Hashtable;
|
||||
import java.util.Stack;
|
||||
import java.util.Vector;
|
||||
|
||||
import org.xml.sax.Attributes;
|
||||
import org.xml.sax.ErrorHandler;
|
||||
import org.xml.sax.InputSource;
|
||||
import org.xml.sax.Locator;
|
||||
import org.xml.sax.SAXException;
|
||||
import org.xml.sax.SAXParseException;
|
||||
import org.xml.sax.XMLReader;
|
||||
import org.xml.sax.helpers.XMLReaderFactory;
|
||||
|
||||
import gnu.xml.util.Resolver;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Filter to process an XPointer-free subset of
|
||||
* <a href="http://www.w3.org/TR/xinclude">XInclude</a>, supporting its
|
||||
* use as a kind of replacement for parsed general entities.
|
||||
* XInclude works much like the <code>#include</code> of C/C++ but
|
||||
* works for XML documents as well as unparsed text files.
|
||||
* Restrictions from the 17-Sept-2002 CR draft of XInclude are as follows:
|
||||
*
|
||||
* <ul>
|
||||
*
|
||||
* <li> URIs must not include fragment identifiers.
|
||||
* The CR specifies support for XPointer <em>element()</em> fragment IDs,
|
||||
* which is not currently implemented here.
|
||||
*
|
||||
* <li> <em>xi:fallback</em> handling of resource errors is not
|
||||
* currently supported.
|
||||
*
|
||||
* <li> DTDs are not supported in included files, since the SAX DTD events
|
||||
* must have completely preceded any included file.
|
||||
* The CR explicitly allows the DTD related portions of the infoset to
|
||||
* grow as an effect of including XML documents.
|
||||
*
|
||||
* <li> <em>xml:base</em> fixup isn't done.
|
||||
*
|
||||
* </ul>
|
||||
*
|
||||
* <p> XML documents that are included will normally be processed using
|
||||
* the default SAX namespace rules, meaning that prefix information may
|
||||
* be discarded. This may be changed with {@link #setSavingPrefixes
|
||||
* setSavingPrefixes()}. <em>You are strongly advised to do this.</em>
|
||||
*
|
||||
* <p> Note that XInclude allows highly incompatible implementations, which
|
||||
* are specialized to handle application-specific infoset extensions. Some
|
||||
* such implementations can be implemented by subclassing this one, but
|
||||
* they may only be substituted in applications at "user option".
|
||||
*
|
||||
* <p>TBD: "IURI" handling.
|
||||
*
|
||||
* @author David Brownell
|
||||
*/
|
||||
public class XIncludeFilter extends EventFilter implements Locator
|
||||
{
|
||||
private Hashtable extEntities = new Hashtable (5, 5);
|
||||
private int ignoreCount;
|
||||
private Stack uris = new Stack ();
|
||||
private Locator locator;
|
||||
private Vector inclusions = new Vector (5, 5);
|
||||
private boolean savingPrefixes;
|
||||
|
||||
/**
|
||||
*/
|
||||
public XIncludeFilter (EventConsumer next)
|
||||
throws SAXException
|
||||
{
|
||||
super (next);
|
||||
setContentHandler (this);
|
||||
// DTDHandler callbacks pass straight through
|
||||
setProperty (DECL_HANDLER, this);
|
||||
setProperty (LEXICAL_HANDLER, this);
|
||||
}
|
||||
|
||||
private void fatal (SAXParseException e) throws SAXException
|
||||
{
|
||||
ErrorHandler eh;
|
||||
|
||||
eh = getErrorHandler ();
|
||||
if (eh != null)
|
||||
eh.fatalError (e);
|
||||
throw e;
|
||||
}
|
||||
|
||||
/**
|
||||
* Passes "this" down the filter chain as a proxy locator.
|
||||
*/
|
||||
public void setDocumentLocator (Locator locator)
|
||||
{
|
||||
this.locator = locator;
|
||||
super.setDocumentLocator (this);
|
||||
}
|
||||
|
||||
/** Used for proxy locator; do not call directly. */
|
||||
public String getSystemId ()
|
||||
{ return (locator == null) ? null : locator.getSystemId (); }
|
||||
/** Used for proxy locator; do not call directly. */
|
||||
public String getPublicId ()
|
||||
{ return (locator == null) ? null : locator.getPublicId (); }
|
||||
/** Used for proxy locator; do not call directly. */
|
||||
public int getLineNumber ()
|
||||
{ return (locator == null) ? -1 : locator.getLineNumber (); }
|
||||
/** Used for proxy locator; do not call directly. */
|
||||
public int getColumnNumber ()
|
||||
{ return (locator == null) ? -1 : locator.getColumnNumber (); }
|
||||
|
||||
/**
|
||||
* Assigns the flag controlling the setting of the SAX2
|
||||
* <em>namespace-prefixes</em> flag.
|
||||
*/
|
||||
public void setSavingPrefixes (boolean flag)
|
||||
{ savingPrefixes = flag; }
|
||||
|
||||
/**
|
||||
* Returns the flag controlling the setting of the SAX2
|
||||
* <em>namespace-prefixes</em> flag when parsing included documents.
|
||||
* The default value is the SAX2 default (false), which discards
|
||||
* information that can be useful.
|
||||
*/
|
||||
public boolean isSavingPrefixes ()
|
||||
{ return savingPrefixes; }
|
||||
|
||||
//
|
||||
// Two mechanisms are interacting here.
|
||||
//
|
||||
// - XML Base implies a stack of base URIs, updated both by
|
||||
// "real entity" boundaries and element boundaries.
|
||||
//
|
||||
// - Active "Real Entities" (for document and general entities,
|
||||
// and by xincluded files) are tracked to prevent circular
|
||||
// inclusions.
|
||||
//
|
||||
private String addMarker (String uri)
|
||||
throws SAXException
|
||||
{
|
||||
if (locator != null && locator.getSystemId () != null)
|
||||
uri = locator.getSystemId ();
|
||||
|
||||
// guard against InputSource objects without system IDs
|
||||
if (uri == null)
|
||||
fatal (new SAXParseException ("Entity URI is unknown", locator));
|
||||
|
||||
try {
|
||||
URL url = new URL (uri);
|
||||
|
||||
uri = url.toString ();
|
||||
if (inclusions.contains (uri))
|
||||
fatal (new SAXParseException (
|
||||
"XInclude, circular inclusion", locator));
|
||||
inclusions.addElement (uri);
|
||||
uris.push (url);
|
||||
} catch (IOException e) {
|
||||
// guard against illegal relative URIs (Xerces)
|
||||
fatal (new SAXParseException ("parser bug: relative URI",
|
||||
locator, e));
|
||||
}
|
||||
return uri;
|
||||
}
|
||||
|
||||
private void pop (String uri)
|
||||
{
|
||||
inclusions.removeElement (uri);
|
||||
uris.pop ();
|
||||
}
|
||||
|
||||
//
|
||||
// Document entity boundaries get both treatments.
|
||||
//
|
||||
public void startDocument () throws SAXException
|
||||
{
|
||||
ignoreCount = 0;
|
||||
addMarker (null);
|
||||
super.startDocument ();
|
||||
}
|
||||
|
||||
public void endDocument () throws SAXException
|
||||
{
|
||||
inclusions.setSize (0);
|
||||
extEntities.clear ();
|
||||
uris.setSize (0);
|
||||
super.endDocument ();
|
||||
}
|
||||
|
||||
//
|
||||
// External general entity boundaries get both treatments.
|
||||
//
|
||||
public void externalEntityDecl (String name,
|
||||
String publicId, String systemId)
|
||||
throws SAXException
|
||||
{
|
||||
if (name.charAt (0) == '%')
|
||||
return;
|
||||
try {
|
||||
URL url = new URL (locator.getSystemId ());
|
||||
systemId = new URL (url, systemId).toString ();
|
||||
} catch (IOException e) {
|
||||
// what could we do?
|
||||
}
|
||||
extEntities.put (name, systemId);
|
||||
}
|
||||
|
||||
public void startEntity (String name)
|
||||
throws SAXException
|
||||
{
|
||||
if (ignoreCount != 0) {
|
||||
ignoreCount++;
|
||||
return;
|
||||
}
|
||||
|
||||
String uri = (String) extEntities.get (name);
|
||||
if (uri != null)
|
||||
addMarker (uri);
|
||||
super.startEntity (name);
|
||||
}
|
||||
|
||||
public void endEntity (String name)
|
||||
throws SAXException
|
||||
{
|
||||
if (ignoreCount != 0) {
|
||||
if (--ignoreCount != 0)
|
||||
return;
|
||||
}
|
||||
|
||||
String uri = (String) extEntities.get (name);
|
||||
|
||||
if (uri != null)
|
||||
pop (uri);
|
||||
super.endEntity (name);
|
||||
}
|
||||
|
||||
//
|
||||
// element boundaries only affect the base URI stack,
|
||||
// unless they're XInclude elements.
|
||||
//
|
||||
public void
|
||||
startElement (String uri, String localName, String qName, Attributes atts)
|
||||
throws SAXException
|
||||
{
|
||||
if (ignoreCount != 0) {
|
||||
ignoreCount++;
|
||||
return;
|
||||
}
|
||||
|
||||
URL baseURI = (URL) uris.peek ();
|
||||
String base;
|
||||
|
||||
base = atts.getValue ("http://www.w3.org/XML/1998/namespace", "base");
|
||||
if (base == null)
|
||||
uris.push (baseURI);
|
||||
else {
|
||||
URL url;
|
||||
|
||||
if (base.indexOf ('#') != -1)
|
||||
fatal (new SAXParseException (
|
||||
"xml:base with fragment: " + base,
|
||||
locator));
|
||||
|
||||
try {
|
||||
baseURI = new URL (baseURI, base);
|
||||
uris.push (baseURI);
|
||||
} catch (Exception e) {
|
||||
fatal (new SAXParseException (
|
||||
"xml:base with illegal uri: " + base,
|
||||
locator, e));
|
||||
}
|
||||
}
|
||||
|
||||
if (!"http://www.w3.org/2001/XInclude".equals (uri)) {
|
||||
super.startElement (uri, localName, qName, atts);
|
||||
return;
|
||||
}
|
||||
|
||||
if ("include".equals (localName)) {
|
||||
String href = atts.getValue ("href");
|
||||
String parse = atts.getValue ("parse");
|
||||
String encoding = atts.getValue ("encoding");
|
||||
URL url = (URL) uris.peek ();
|
||||
SAXParseException x = null;
|
||||
|
||||
if (href == null)
|
||||
fatal (new SAXParseException (
|
||||
"XInclude missing href",
|
||||
locator));
|
||||
if (href.indexOf ('#') != -1)
|
||||
fatal (new SAXParseException (
|
||||
"XInclude with fragment: " + href,
|
||||
locator));
|
||||
|
||||
if (parse == null || "xml".equals (parse))
|
||||
x = xinclude (url, href);
|
||||
else if ("text".equals (parse))
|
||||
x = readText (url, href, encoding);
|
||||
else
|
||||
fatal (new SAXParseException (
|
||||
"unknown XInclude parsing mode: " + parse,
|
||||
locator));
|
||||
if (x == null) {
|
||||
// strip out all child content
|
||||
ignoreCount++;
|
||||
return;
|
||||
}
|
||||
|
||||
// FIXME the 17-Sept-2002 CR of XInclude says we "must"
|
||||
// use xi:fallback elements to handle resource errors,
|
||||
// if they exist.
|
||||
fatal (x);
|
||||
|
||||
} else if ("fallback".equals (localName)) {
|
||||
fatal (new SAXParseException (
|
||||
"illegal top level XInclude 'fallback' element",
|
||||
locator));
|
||||
} else {
|
||||
ErrorHandler eh = getErrorHandler ();
|
||||
|
||||
// CR doesn't say this is an error
|
||||
if (eh != null)
|
||||
eh.warning (new SAXParseException (
|
||||
"unrecognized toplevel XInclude element: " + localName,
|
||||
locator));
|
||||
super.startElement (uri, localName, qName, atts);
|
||||
}
|
||||
}
|
||||
|
||||
public void endElement (String uri, String localName, String qName)
|
||||
throws SAXException
|
||||
{
|
||||
if (ignoreCount != 0) {
|
||||
if (--ignoreCount != 0)
|
||||
return;
|
||||
}
|
||||
|
||||
uris.pop ();
|
||||
if (!("http://www.w3.org/2001/XInclude".equals (uri)
|
||||
&& "include".equals (localName)))
|
||||
super.endElement (uri, localName, qName);
|
||||
}
|
||||
|
||||
//
|
||||
// ignore all content within non-empty xi:include elements
|
||||
//
|
||||
public void characters (char ch [], int start, int length)
|
||||
throws SAXException
|
||||
{
|
||||
if (ignoreCount == 0)
|
||||
super.characters (ch, start, length);
|
||||
}
|
||||
|
||||
public void processingInstruction (String target, String value)
|
||||
throws SAXException
|
||||
{
|
||||
if (ignoreCount == 0)
|
||||
super.processingInstruction (target, value);
|
||||
}
|
||||
|
||||
public void ignorableWhitespace (char ch [], int start, int length)
|
||||
throws SAXException
|
||||
{
|
||||
if (ignoreCount == 0)
|
||||
super.ignorableWhitespace (ch, start, length);
|
||||
}
|
||||
|
||||
public void comment (char ch [], int start, int length)
|
||||
throws SAXException
|
||||
{
|
||||
if (ignoreCount == 0)
|
||||
super.comment (ch, start, length);
|
||||
}
|
||||
|
||||
public void startCDATA () throws SAXException
|
||||
{
|
||||
if (ignoreCount == 0)
|
||||
super.startCDATA ();
|
||||
}
|
||||
|
||||
public void endCDATA () throws SAXException
|
||||
{
|
||||
if (ignoreCount == 0)
|
||||
super.endCDATA ();
|
||||
}
|
||||
|
||||
public void startPrefixMapping (String prefix, String uri)
|
||||
throws SAXException
|
||||
{
|
||||
if (ignoreCount == 0)
|
||||
super.startPrefixMapping (prefix, uri);
|
||||
}
|
||||
|
||||
public void endPrefixMapping (String prefix) throws SAXException
|
||||
{
|
||||
if (ignoreCount == 0)
|
||||
super.endPrefixMapping (prefix);
|
||||
}
|
||||
|
||||
public void skippedEntity (String name) throws SAXException
|
||||
{
|
||||
if (ignoreCount == 0)
|
||||
super.skippedEntity (name);
|
||||
}
|
||||
|
||||
// JDK 1.1 seems to need it to be done this way, sigh
|
||||
void setLocator (Locator l) { locator = l; }
|
||||
Locator getLocator () { return locator; }
|
||||
|
||||
|
||||
//
|
||||
// for XIncluded entities, manage the current locator and
|
||||
// filter out events that would be incorrect to report
|
||||
//
|
||||
private class Scrubber extends EventFilter
|
||||
{
|
||||
Scrubber (EventFilter f)
|
||||
throws SAXException
|
||||
{
|
||||
// delegation passes to next in chain
|
||||
super (f);
|
||||
|
||||
// process all content events
|
||||
super.setContentHandler (this);
|
||||
super.setProperty (LEXICAL_HANDLER, this);
|
||||
|
||||
// drop all DTD events
|
||||
super.setDTDHandler (null);
|
||||
super.setProperty (DECL_HANDLER, null);
|
||||
}
|
||||
|
||||
// maintain proxy locator
|
||||
// only one startDocument()/endDocument() pair per event stream
|
||||
public void setDocumentLocator (Locator l)
|
||||
{ setLocator (l); }
|
||||
public void startDocument ()
|
||||
{ }
|
||||
public void endDocument ()
|
||||
{ }
|
||||
|
||||
private void reject (String message) throws SAXException
|
||||
{ fatal (new SAXParseException (message, getLocator ())); }
|
||||
|
||||
// only the DTD from the "base document" gets reported
|
||||
public void startDTD (String root, String publicId, String systemId)
|
||||
throws SAXException
|
||||
{ reject ("XIncluded DTD: " + systemId); }
|
||||
public void endDTD ()
|
||||
throws SAXException
|
||||
{ reject ("XIncluded DTD"); }
|
||||
// ... so this should never happen
|
||||
public void skippedEntity (String name) throws SAXException
|
||||
{ reject ("XInclude skipped entity: " + name); }
|
||||
|
||||
// since we rejected DTDs, only builtin entities can be reported
|
||||
}
|
||||
|
||||
// <xi:include parse='xml' ...>
|
||||
// relative to the base URI passed
|
||||
private SAXParseException xinclude (URL url, String href)
|
||||
throws SAXException
|
||||
{
|
||||
XMLReader helper;
|
||||
Scrubber scrubber;
|
||||
Locator savedLocator = locator;
|
||||
|
||||
// start with a parser acting just like our input
|
||||
// modulo DTD-ish stuff (validation flag, entity resolver)
|
||||
helper = XMLReaderFactory.createXMLReader ();
|
||||
helper.setErrorHandler (getErrorHandler ());
|
||||
helper.setFeature (FEATURE_URI + "namespace-prefixes", true);
|
||||
|
||||
// Set up the proxy locator and event filter.
|
||||
scrubber = new Scrubber (this);
|
||||
locator = null;
|
||||
bind (helper, scrubber);
|
||||
|
||||
// Merge the included document, except its DTD
|
||||
try {
|
||||
url = new URL (url, href);
|
||||
href = url.toString ();
|
||||
|
||||
if (inclusions.contains (href))
|
||||
fatal (new SAXParseException (
|
||||
"XInclude, circular inclusion", locator));
|
||||
|
||||
inclusions.addElement (href);
|
||||
uris.push (url);
|
||||
helper.parse (new InputSource (href));
|
||||
return null;
|
||||
} catch (java.io.IOException e) {
|
||||
return new SAXParseException (href, locator, e);
|
||||
} finally {
|
||||
pop (href);
|
||||
locator = savedLocator;
|
||||
}
|
||||
}
|
||||
|
||||
// <xi:include parse='text' ...>
|
||||
// relative to the base URI passed
|
||||
private SAXParseException readText (URL url, String href, String encoding)
|
||||
throws SAXException
|
||||
{
|
||||
InputStream in = null;
|
||||
|
||||
try {
|
||||
URLConnection conn;
|
||||
InputStreamReader reader;
|
||||
char buf [] = new char [4096];
|
||||
int count;
|
||||
|
||||
url = new URL (url, href);
|
||||
conn = url.openConnection ();
|
||||
in = conn.getInputStream ();
|
||||
if (encoding == null)
|
||||
encoding = Resolver.getEncoding (conn.getContentType ());
|
||||
if (encoding == null) {
|
||||
ErrorHandler eh = getErrorHandler ();
|
||||
if (eh != null)
|
||||
eh.warning (new SAXParseException (
|
||||
"guessing text encoding for URL: " + url,
|
||||
locator));
|
||||
reader = new InputStreamReader (in);
|
||||
} else
|
||||
reader = new InputStreamReader (in, encoding);
|
||||
|
||||
while ((count = reader.read (buf, 0, buf.length)) != -1)
|
||||
super.characters (buf, 0, count);
|
||||
in.close ();
|
||||
return null;
|
||||
} catch (IOException e) {
|
||||
return new SAXParseException (
|
||||
"can't XInclude text",
|
||||
locator, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
/* XsltFilter.java --
|
||||
Copyright (C) 2001 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU Classpath.
|
||||
|
||||
GNU Classpath 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, or (at your option)
|
||||
any later version.
|
||||
|
||||
GNU Classpath 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 GNU Classpath; see the file COPYING. If not, write to the
|
||||
Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
02111-1307 USA.
|
||||
|
||||
Linking this library statically or dynamically with other modules is
|
||||
making a combined work based on this library. Thus, the terms and
|
||||
conditions of the GNU General Public License cover the whole
|
||||
combination.
|
||||
|
||||
As a special exception, the copyright holders of this library give you
|
||||
permission to link this library with independent modules to produce an
|
||||
executable, regardless of the license terms of these independent
|
||||
modules, and to copy and distribute the resulting executable under
|
||||
terms of your choice, provided that you also meet, for each linked
|
||||
independent module, the terms and conditions of the license of that
|
||||
module. An independent module is a module which is not derived from
|
||||
or based on this library. If you modify this library, you may extend
|
||||
this exception to your version of the library, but you are not
|
||||
obligated to do so. If you do not wish to do so, delete this
|
||||
exception statement from your version. */
|
||||
|
||||
package gnu.xml.pipeline;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.xml.transform.TransformerFactory;
|
||||
import javax.xml.transform.TransformerConfigurationException;
|
||||
import javax.xml.transform.sax.*;
|
||||
import javax.xml.transform.stream.StreamSource;
|
||||
|
||||
import org.xml.sax.ContentHandler;
|
||||
import org.xml.sax.SAXException;
|
||||
import org.xml.sax.ext.LexicalHandler;
|
||||
|
||||
|
||||
/**
|
||||
* Packages an XSLT transform as a pipeline component.
|
||||
* Note that all DTD events (callbacks to DeclHandler and DTDHandler
|
||||
* interfaces) are discarded, although XSLT transforms may be set up to
|
||||
* use the LexicalHandler to write DTDs with only an external subset.
|
||||
* Not every XSLT engine will necessarily be usable with this filter,
|
||||
* but current versions of
|
||||
* <a href="http://saxon.sourceforge.net">SAXON</a> and
|
||||
* <a href="http://xml.apache.org/xalan-j">Xalan</a> should work well.
|
||||
*
|
||||
* @see TransformerFactory
|
||||
*
|
||||
* @author David Brownell
|
||||
*/
|
||||
final public class XsltFilter extends EventFilter
|
||||
{
|
||||
/**
|
||||
* Creates a filter that performs the specified transform.
|
||||
* Uses the JAXP 1.1 interfaces to access the default XSLT
|
||||
* engine configured for in the current execution context,
|
||||
* and parses the stylesheet without custom EntityResolver
|
||||
* or ErrorHandler support.
|
||||
*
|
||||
* @param stylesheet URI for the stylesheet specifying the
|
||||
* XSLT transform
|
||||
* @param next provides the ContentHandler and LexicalHandler
|
||||
* to receive XSLT output.
|
||||
* @exception SAXException if the stylesheet can't be parsed
|
||||
* @exception IOException if there are difficulties
|
||||
* bootstrapping the XSLT engine, such as it not supporting
|
||||
* SAX well enough to use this way.
|
||||
*/
|
||||
public XsltFilter (String stylesheet, EventConsumer next)
|
||||
throws SAXException, IOException
|
||||
{
|
||||
// First, get a transformer with the stylesheet preloaded
|
||||
TransformerFactory tf = null;
|
||||
TransformerHandler th;
|
||||
|
||||
try {
|
||||
SAXTransformerFactory stf;
|
||||
|
||||
tf = TransformerFactory.newInstance ();
|
||||
if (!tf.getFeature (SAXTransformerFactory.FEATURE) // sax inputs
|
||||
|| !tf.getFeature (SAXResult.FEATURE) // sax outputs
|
||||
|| !tf.getFeature (StreamSource.FEATURE) // stylesheet
|
||||
)
|
||||
throw new IOException ("XSLT factory ("
|
||||
+ tf.getClass ().getName ()
|
||||
+ ") does not support SAX");
|
||||
stf = (SAXTransformerFactory) tf;
|
||||
th = stf.newTransformerHandler (new StreamSource (stylesheet));
|
||||
} catch (TransformerConfigurationException e) {
|
||||
throw new IOException ("XSLT factory ("
|
||||
+ (tf == null
|
||||
? "none available"
|
||||
: tf.getClass ().getName ())
|
||||
+ ") configuration error, "
|
||||
+ e.getMessage ()
|
||||
);
|
||||
}
|
||||
|
||||
// Hook its outputs up to the pipeline ...
|
||||
SAXResult out = new SAXResult ();
|
||||
|
||||
out.setHandler (next.getContentHandler ());
|
||||
try {
|
||||
LexicalHandler lh;
|
||||
lh = (LexicalHandler) next.getProperty (LEXICAL_HANDLER);
|
||||
out.setLexicalHandler (lh);
|
||||
} catch (Exception e) {
|
||||
// ignore
|
||||
}
|
||||
th.setResult (out);
|
||||
|
||||
// ... and make sure its inputs look like ours.
|
||||
setContentHandler (th);
|
||||
setProperty (LEXICAL_HANDLER, th);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,255 @@
|
||||
<html><head><title>
|
||||
blah
|
||||
<!--
|
||||
/*
|
||||
* Copyright (C) 1999-2001 The Free Software Foundation, Inc.
|
||||
*/
|
||||
-->
|
||||
</title></head><body>
|
||||
|
||||
<p>This package exposes a kind of XML processing pipeline, based on sending
|
||||
SAX events, which can be used as components of application architectures.
|
||||
Pipelines are used to convey streams of processing events from a producer
|
||||
to one or more consumers, and to let each consumer control the data seen by
|
||||
later consumers.
|
||||
|
||||
<p> There is a <a href="PipelineFactory.html">PipelineFactory</a> class which
|
||||
accepts a syntax describing how to construct some simple pipelines. Strings
|
||||
describing such pipelines can be used in command line tools (see the
|
||||
<a href="../util/DoParse.html">DoParse</a> class)
|
||||
and in other places that it is
|
||||
useful to let processing be easily reconfigured. Pipelines can of course
|
||||
be constructed programmatically, providing access to options that the
|
||||
factory won't.
|
||||
|
||||
<p> Web applications are supported by making it easy for servlets (or
|
||||
non-Java web application components) to be part of a pipeline. They can
|
||||
originate XML (or XHTML) data through an <em>InputSource</em> or in
|
||||
response to XML messages sent from clients using <em>CallFilter</em>
|
||||
pipeline stages. Such facilities are available using the simple syntax
|
||||
for pipeline construction.
|
||||
|
||||
|
||||
<h2> Programming Models </h2>
|
||||
|
||||
<p> Pipelines should be simple to understand.
|
||||
|
||||
<ul>
|
||||
<li> XML content, typically entire documents,
|
||||
is pushed through consumers by producers.
|
||||
|
||||
<li> Pipelines are basically about consuming SAX2 callback events,
|
||||
where the events encapsulate XML infoset-level data.<ul>
|
||||
|
||||
<li> Pipelines are constructed by taking one or more consumer
|
||||
stages and combining them to produce a composite consumer.
|
||||
|
||||
<li> A pipeline is presumed to have pending tasks and state from
|
||||
the beginning of its ContentHandler.startDocument() callback until
|
||||
it's returned from its ContentHandler.doneDocument() callback.
|
||||
|
||||
<li> Pipelines may have multiple output stages ("fan-out")
|
||||
or multiple input stages ("fan-in") when appropriate.
|
||||
|
||||
<li> Pipelines may be long-lived, but need not be.
|
||||
|
||||
</ul>
|
||||
|
||||
<li> There is flexibility about event production. <ul>
|
||||
|
||||
<li> SAX2 XMLReader objects are producers, which
|
||||
provide a high level "pull" model: documents (text or DOM) are parsed,
|
||||
and the parser pushes individual events through the pipeline.
|
||||
|
||||
<li> Events can be pushed directly to event consumer components
|
||||
by application modules, if they invoke SAX2 callbacks directly.
|
||||
That is, application modules use the XML Infoset as exposed
|
||||
through SAX2 event callbacks.
|
||||
|
||||
</ul>
|
||||
|
||||
<li> Multiple producer threads may concurrently access a pipeline,
|
||||
if they coordinate appropriately.
|
||||
|
||||
<li> Pipeline processing is not the only framework applications
|
||||
will use.
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
<h3> Producers: XMLReader or Custom </h3>
|
||||
|
||||
<p> Many producers will be SAX2 XMLReader objects, and
|
||||
will read (pull) data which is then written (pushed) as events.
|
||||
Typically these will parse XML text (acquired from
|
||||
<code>org.xml.sax.helpers.XMLReaderFactory</code>) or a DOM tree
|
||||
(using a <code><a href="../util/DomParser.html">DomParser</a></code>)
|
||||
These may be bound to event consumer using a convenience routine,
|
||||
<em><a href="EventFilter.html">EventFilter</a>.bind()</em>.
|
||||
Once bound, these producers may be given additional documents to
|
||||
sent through its pipeline.
|
||||
|
||||
<p> In other cases, you will write producers yourself. For example, some
|
||||
data structures might know how to write themselves out using one or
|
||||
more XML models, expressed as sequences of SAX2 event callbacks.
|
||||
An application module might
|
||||
itself be a producer, issuing startDocument and endDocument events
|
||||
and then asking those data structures to write themselves out to a
|
||||
given EventConsumer, or walking data structures (such as JDBC query
|
||||
results) and applying its own conversion rules. WAP format XML
|
||||
(WBMXL) can be directly converted to producer output.
|
||||
|
||||
<p> SAX2 introduced an "XMLFilter" interface, which is a kind of XMLReader.
|
||||
It is most useful in conjunction with its XMLFilterImpl helper class;
|
||||
see the <em><a href="EventFilter.html">EventFilter</a></em> javadoc
|
||||
for information contrasting that XMLFilterImpl approach with the
|
||||
relevant parts of this pipeline framework. Briefly, such XMLFilterImpl
|
||||
children can be either producers or consumers, and are more limited in
|
||||
configuration flexibility. In this framework, the focus of filters is
|
||||
on the EventConsumer side; see the section on
|
||||
<a href="#fitting">pipe fitting</a> below.
|
||||
|
||||
|
||||
<h3> Consume to Standard or Custom Data Representations </h3>
|
||||
|
||||
<p> Many consumers will be used to create standard representations of XML
|
||||
data. The <a href="TextConsumer.html">TextConsumer</a> takes its events
|
||||
and writes them as text for a single XML document,
|
||||
using an internal <a href="../util/XMLWriter.html">XMLWriter</a>.
|
||||
The <a href="DomConsumer.html">DomConsumer</a> takes its events and uses
|
||||
them to create and populate a DOM Document.
|
||||
|
||||
<p> In other cases, you will write consumers yourself. For example,
|
||||
you might use a particular unmarshaling filter to produce objects
|
||||
that fit your application's requirements, instead of using DOM.
|
||||
Such consumers work at the level of XML data models, rather than with
|
||||
specific representations such as XML text or a DOM tree. You could
|
||||
convert your output directly to WAP format data (WBXML).
|
||||
|
||||
|
||||
<h3><a name="fitting">Pipe Fitting</a></h3>
|
||||
|
||||
<p> Pipelines are composite event consumers, with each stage having
|
||||
the opportunity to transform the data before delivering it to any
|
||||
subsequent stages.
|
||||
|
||||
<p> The <a href="PipelineFactory.html">PipelineFactory</a> class
|
||||
provides access to much of this functionality through a simple syntax.
|
||||
See the table in that class's javadoc describing a number of standard
|
||||
components. Direct API calls are still needed for many of the most
|
||||
interesting pipeline configurations, including ones leveraging actual
|
||||
or logical concurrency.
|
||||
|
||||
<p> Four basic types of pipe fitting are directly supported. These may
|
||||
be used to construct complex pipeline networks. <ul>
|
||||
|
||||
<li> <a href="TeeConsumer.html">TeeConsumer</a> objects split event
|
||||
flow so it goes to two two different consumers, one before the other.
|
||||
This is a basic form of event fan-out; you can use this class to
|
||||
copy events to any number of output pipelines.
|
||||
|
||||
<li> Clients can call remote components through HTTP or HTTPS using
|
||||
the <a href="CallFilter.html">CallFilter</a> component, and Servlets
|
||||
can implement such components by extending the
|
||||
<a href="XmlServlet.html">XmlServlet</a> component. Java is not
|
||||
required on either end, and transport protocols other than HTTP may
|
||||
also be used.
|
||||
|
||||
<li> <a href="EventFilter.html">EventFilter</a> objects selectively
|
||||
provide handling for callbacks, and can pass unhandled ones to a
|
||||
subsequent stage. They are often subclassed, since much of the
|
||||
basic filtering machinery is already in place in the base class.
|
||||
|
||||
<li> Applications can merge two event flows by just using the same
|
||||
consumer in each one. If multiple threads are in use, synchronization
|
||||
needs to be addressed by the appropriate application level policy.
|
||||
|
||||
</ul>
|
||||
|
||||
<p> Note that filters can be as complex as
|
||||
<a href="XsltFilter.html">XSLT transforms</a>
|
||||
available) on input data, or as simple as removing simple syntax data
|
||||
such as ignorable whitespace, comments, and CDATA delimiters.
|
||||
Some simple "built-in" filters are part of this package.
|
||||
|
||||
|
||||
<h3> Coding Conventions: Filter and Terminus Stages</h3>
|
||||
|
||||
<p> If you follow these coding conventions, your classes may be used
|
||||
directly (give the full class name) in pipeline descriptions as understood
|
||||
by the PipelineFactory. There are four constructors the factory may
|
||||
try to use; in order of decreasing numbers of parameters, these are: <ul>
|
||||
|
||||
<li> Filters that need a single String setup parameter should have
|
||||
a public constructor with two parameters: that string, then the
|
||||
EventConsumer holding the "next" consumer to get events.
|
||||
|
||||
<li> Filters that don't need setup parameters should have a public
|
||||
constructor that accepts a single EventConsumer holding the "next"
|
||||
consumer to get events when they are done.
|
||||
|
||||
<li> Terminus stages may have a public constructor taking a single
|
||||
paramter: the string value of that parameter.
|
||||
|
||||
<li> Terminus stages may have a public no-parameters constructor.
|
||||
|
||||
</ul>
|
||||
|
||||
<p> Of course, classes may support more than one such usage convention;
|
||||
if they do, they can automatically be used in multiple modes. If you
|
||||
try to use a terminus class as a filter, and that terminus has a constructor
|
||||
with the appropriate number of arguments, it is automatically wrapped in
|
||||
a "tee" filter.
|
||||
|
||||
|
||||
<h2> Debugging Tip: "Tee" Joints can Snapshot Data</h2>
|
||||
|
||||
<p> It can sometimes be hard to see what's happening, when something
|
||||
goes wrong. Easily fixed: just snapshot the data. Then you can find
|
||||
out where things start to go wrong.
|
||||
|
||||
<p> If you're using pipeline descriptors so that they're easily
|
||||
administered, just stick a <em>write ( filename )</em>
|
||||
filter into the pipeline at an appropriate point.
|
||||
|
||||
<p> Inside your programs, you can do the same thing directly: perhaps
|
||||
by saving a Writer (perhaps a StringWriter) in a variable, using that
|
||||
to create a TextConsumer, and making that the first part of a tee --
|
||||
splicing that into your pipeline at a convenient location.
|
||||
|
||||
<p> You can also use a DomConsumer to buffer the data, but remember
|
||||
that DOM doesn't save all the information that XML provides, so that DOM
|
||||
snapshots are relatively low fidelity. They also are substantially more
|
||||
expensive in terms of memory than a StringWriter holding similar data.
|
||||
|
||||
<h2> Debugging Tip: Non-XML Producers</h2>
|
||||
|
||||
<p> Producers in pipelines don't need to start from XML
|
||||
data structures, such as text in XML syntax (likely coming
|
||||
from some <em>XMLReader</em> that parses XML) or a
|
||||
DOM representation (perhaps with a
|
||||
<a href="../util/DomParser.html">DomParser</a>).
|
||||
|
||||
<p> One common type of event producer will instead make
|
||||
direct calls to SAX event handlers returned from an
|
||||
<a href="EventConsumer.html">EventConsumer</a>.
|
||||
For example, making <em>ContentHandler.startElement</em>
|
||||
calls and matching <em>ContentHandler.endElement</em> calls.
|
||||
|
||||
<p> Applications making such calls can catch certain
|
||||
common "syntax errors" by using a
|
||||
<a href="WellFormednessFilter.html">WellFormednessFilter</a>.
|
||||
That filter will detect (and report) erroneous input data
|
||||
such as mismatched document, element, or CDATA start/end calls.
|
||||
Use such a filter near the head of the pipeline that your
|
||||
producer feeds, at least while debugging, to help ensure that
|
||||
you're providing legal XML Infoset data.
|
||||
|
||||
<p> You can also arrange to validate data on the fly.
|
||||
For DTD validation, you can configure a
|
||||
<a href="ValidationConsumer.html">ValidationConsumer</a>
|
||||
to work as a filter, using any DTD you choose.
|
||||
Other validation schemes can be handled with other
|
||||
validation filters.
|
||||
|
||||
</body></html>
|
||||
Reference in New Issue
Block a user