aboutsummaryrefslogtreecommitdiff
path: root/libjava/gnu/xml
diff options
context:
space:
mode:
authorTom Tromey <tromey@redhat.com>2005-02-02 00:42:32 +0000
committerTom Tromey <tromey@gcc.gnu.org>2005-02-02 00:42:32 +0000
commit9ecd03471f54de5411794e6fc58c48420564c192 (patch)
treed6f5216c5e8b86e821ab50a8cb46c7eec16e0a52 /libjava/gnu/xml
parentc1c5b5e383b7e566b49b4c160a1af16eb9222f52 (diff)
downloadgcc-9ecd03471f54de5411794e6fc58c48420564c192.zip
gcc-9ecd03471f54de5411794e6fc58c48420564c192.tar.gz
gcc-9ecd03471f54de5411794e6fc58c48420564c192.tar.bz2
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
Diffstat (limited to 'libjava/gnu/xml')
-rw-r--r--libjava/gnu/xml/aelfred2/ContentHandler2.java65
-rw-r--r--libjava/gnu/xml/aelfred2/JAXPFactory.java196
-rw-r--r--libjava/gnu/xml/aelfred2/SAXDriver.java1385
-rw-r--r--libjava/gnu/xml/aelfred2/XmlParser.java5113
-rw-r--r--libjava/gnu/xml/aelfred2/XmlReader.java315
-rw-r--r--libjava/gnu/xml/aelfred2/package.html506
-rw-r--r--libjava/gnu/xml/dom/Consumer.java373
-rw-r--r--libjava/gnu/xml/dom/DTDAttributeTypeInfo.java84
-rw-r--r--libjava/gnu/xml/dom/DTDElementTypeInfo.java112
-rw-r--r--libjava/gnu/xml/dom/DomAttr.java379
-rw-r--r--libjava/gnu/xml/dom/DomCDATA.java91
-rw-r--r--libjava/gnu/xml/dom/DomCharacterData.java311
-rw-r--r--libjava/gnu/xml/dom/DomComment.java81
-rw-r--r--libjava/gnu/xml/dom/DomDoctype.java454
-rw-r--r--libjava/gnu/xml/dom/DomDocument.java1376
-rw-r--r--libjava/gnu/xml/dom/DomDocumentBuilder.java160
-rw-r--r--libjava/gnu/xml/dom/DomDocumentBuilderFactory.java128
-rw-r--r--libjava/gnu/xml/dom/DomDocumentConfiguration.java259
-rw-r--r--libjava/gnu/xml/dom/DomElement.java522
-rw-r--r--libjava/gnu/xml/dom/DomEntity.java147
-rw-r--r--libjava/gnu/xml/dom/DomEntityReference.java130
-rw-r--r--libjava/gnu/xml/dom/DomEvent.java345
-rw-r--r--libjava/gnu/xml/dom/DomEx.java175
-rw-r--r--libjava/gnu/xml/dom/DomExtern.java117
-rw-r--r--libjava/gnu/xml/dom/DomFragment.java76
-rw-r--r--libjava/gnu/xml/dom/DomImpl.java260
-rw-r--r--libjava/gnu/xml/dom/DomIterator.java374
-rw-r--r--libjava/gnu/xml/dom/DomNSResolverContext.java90
-rw-r--r--libjava/gnu/xml/dom/DomNamedNodeMap.java416
-rw-r--r--libjava/gnu/xml/dom/DomNode.java2180
-rw-r--r--libjava/gnu/xml/dom/DomNodeIterator.java327
-rw-r--r--libjava/gnu/xml/dom/DomNotation.java103
-rw-r--r--libjava/gnu/xml/dom/DomNsNode.java197
-rw-r--r--libjava/gnu/xml/dom/DomPI.java145
-rw-r--r--libjava/gnu/xml/dom/DomText.java220
-rw-r--r--libjava/gnu/xml/dom/DomXPathExpression.java148
-rw-r--r--libjava/gnu/xml/dom/DomXPathNSResolver.java64
-rw-r--r--libjava/gnu/xml/dom/DomXPathResult.java235
-rw-r--r--libjava/gnu/xml/dom/ImplementationList.java70
-rw-r--r--libjava/gnu/xml/dom/ImplementationSource.java167
-rw-r--r--libjava/gnu/xml/dom/JAXPFactory.java287
-rw-r--r--libjava/gnu/xml/dom/ls/DomLSEx.java75
-rw-r--r--libjava/gnu/xml/dom/ls/DomLSInput.java158
-rw-r--r--libjava/gnu/xml/dom/ls/DomLSOutput.java98
-rw-r--r--libjava/gnu/xml/dom/ls/DomLSParser.java574
-rw-r--r--libjava/gnu/xml/dom/ls/DomLSSerializer.java354
-rw-r--r--libjava/gnu/xml/dom/ls/FilteredSAXEventSink.java354
-rw-r--r--libjava/gnu/xml/dom/ls/ReaderInputStream.java237
-rw-r--r--libjava/gnu/xml/dom/ls/SAXEventSink.java533
-rw-r--r--libjava/gnu/xml/dom/ls/WriterOutputStream.java98
-rw-r--r--libjava/gnu/xml/dom/package.html273
-rw-r--r--libjava/gnu/xml/pipeline/CallFilter.java250
-rw-r--r--libjava/gnu/xml/pipeline/DomConsumer.java969
-rw-r--r--libjava/gnu/xml/pipeline/EventConsumer.java95
-rw-r--r--libjava/gnu/xml/pipeline/EventFilter.java809
-rw-r--r--libjava/gnu/xml/pipeline/LinkFilter.java243
-rw-r--r--libjava/gnu/xml/pipeline/NSFilter.java340
-rw-r--r--libjava/gnu/xml/pipeline/PipelineFactory.java723
-rw-r--r--libjava/gnu/xml/pipeline/TeeConsumer.java413
-rw-r--r--libjava/gnu/xml/pipeline/TextConsumer.java117
-rw-r--r--libjava/gnu/xml/pipeline/ValidationConsumer.java1922
-rw-r--r--libjava/gnu/xml/pipeline/WellFormednessFilter.java362
-rw-r--r--libjava/gnu/xml/pipeline/XIncludeFilter.java580
-rw-r--r--libjava/gnu/xml/pipeline/XsltFilter.java131
-rw-r--r--libjava/gnu/xml/pipeline/package.html255
-rw-r--r--libjava/gnu/xml/transform/AbstractNumberNode.java321
-rw-r--r--libjava/gnu/xml/transform/ApplyImportsNode.java91
-rw-r--r--libjava/gnu/xml/transform/ApplyTemplatesNode.java193
-rw-r--r--libjava/gnu/xml/transform/AttributeNode.java246
-rw-r--r--libjava/gnu/xml/transform/AttributeSet.java67
-rw-r--r--libjava/gnu/xml/transform/Bindings.java246
-rw-r--r--libjava/gnu/xml/transform/CallTemplateNode.java130
-rw-r--r--libjava/gnu/xml/transform/ChooseNode.java93
-rw-r--r--libjava/gnu/xml/transform/CommentNode.java115
-rw-r--r--libjava/gnu/xml/transform/CopyNode.java179
-rw-r--r--libjava/gnu/xml/transform/CopyOfNode.java179
-rw-r--r--libjava/gnu/xml/transform/CurrentFunction.java98
-rw-r--r--libjava/gnu/xml/transform/DOMSourceLocator.java84
-rw-r--r--libjava/gnu/xml/transform/DocumentFunction.java249
-rw-r--r--libjava/gnu/xml/transform/ElementAvailableFunction.java169
-rw-r--r--libjava/gnu/xml/transform/ElementNode.java279
-rw-r--r--libjava/gnu/xml/transform/ErrorListenerErrorHandler.java101
-rw-r--r--libjava/gnu/xml/transform/ForEachNode.java149
-rw-r--r--libjava/gnu/xml/transform/FormatNumberFunction.java133
-rw-r--r--libjava/gnu/xml/transform/FunctionAvailableFunction.java175
-rw-r--r--libjava/gnu/xml/transform/GenerateIdFunction.java126
-rw-r--r--libjava/gnu/xml/transform/IfNode.java107
-rw-r--r--libjava/gnu/xml/transform/Key.java71
-rw-r--r--libjava/gnu/xml/transform/KeyFunction.java216
-rw-r--r--libjava/gnu/xml/transform/LiteralNode.java225
-rw-r--r--libjava/gnu/xml/transform/MessageNode.java97
-rw-r--r--libjava/gnu/xml/transform/NodeNumberNode.java261
-rw-r--r--libjava/gnu/xml/transform/NumberNode.java86
-rw-r--r--libjava/gnu/xml/transform/OtherwiseNode.java93
-rw-r--r--libjava/gnu/xml/transform/ParameterNode.java156
-rw-r--r--libjava/gnu/xml/transform/ProcessingInstructionNode.java123
-rw-r--r--libjava/gnu/xml/transform/SAXSerializer.java305
-rw-r--r--libjava/gnu/xml/transform/SortKey.java154
-rw-r--r--libjava/gnu/xml/transform/StreamSerializer.java632
-rw-r--r--libjava/gnu/xml/transform/Stylesheet.java1730
-rw-r--r--libjava/gnu/xml/transform/SystemPropertyFunction.java129
-rw-r--r--libjava/gnu/xml/transform/Template.java254
-rw-r--r--libjava/gnu/xml/transform/TemplateNode.java115
-rw-r--r--libjava/gnu/xml/transform/TemplatesImpl.java83
-rw-r--r--libjava/gnu/xml/transform/TextNode.java116
-rw-r--r--libjava/gnu/xml/transform/TransformerFactoryImpl.java345
-rw-r--r--libjava/gnu/xml/transform/TransformerImpl.java586
-rw-r--r--libjava/gnu/xml/transform/TransformerOutputProperties.java185
-rw-r--r--libjava/gnu/xml/transform/URIResolverEntityResolver.java83
-rw-r--r--libjava/gnu/xml/transform/UnparsedEntityUriFunction.java118
-rw-r--r--libjava/gnu/xml/transform/ValueOfNode.java129
-rw-r--r--libjava/gnu/xml/transform/WhenNode.java110
-rw-r--r--libjava/gnu/xml/transform/WithParam.java110
-rw-r--r--libjava/gnu/xml/transform/XSLComparator.java124
-rw-r--r--libjava/gnu/xml/transform/XSLURIResolver.java270
-rw-r--r--libjava/gnu/xml/transform/package.html77
-rw-r--r--libjava/gnu/xml/util/DoParse.java306
-rw-r--r--libjava/gnu/xml/util/DomParser.java804
-rw-r--r--libjava/gnu/xml/util/Resolver.java263
-rw-r--r--libjava/gnu/xml/util/SAXNullTransformerFactory.java675
-rw-r--r--libjava/gnu/xml/util/XCat.java1609
-rw-r--r--libjava/gnu/xml/util/XHTMLWriter.java112
-rw-r--r--libjava/gnu/xml/util/XMLWriter.java1927
-rw-r--r--libjava/gnu/xml/util/package.html20
-rw-r--r--libjava/gnu/xml/xpath/AndExpr.java81
-rw-r--r--libjava/gnu/xml/xpath/ArithmeticExpr.java148
-rw-r--r--libjava/gnu/xml/xpath/BooleanFunction.java89
-rw-r--r--libjava/gnu/xml/xpath/CeilingFunction.java83
-rw-r--r--libjava/gnu/xml/xpath/ConcatFunction.java100
-rw-r--r--libjava/gnu/xml/xpath/Constant.java92
-rw-r--r--libjava/gnu/xml/xpath/ContainsFunction.java86
-rw-r--r--libjava/gnu/xml/xpath/CountFunction.java82
-rw-r--r--libjava/gnu/xml/xpath/DocumentOrderComparator.java63
-rw-r--r--libjava/gnu/xml/xpath/EqualityExpr.java258
-rw-r--r--libjava/gnu/xml/xpath/Expr.java476
-rw-r--r--libjava/gnu/xml/xpath/FalseFunction.java66
-rw-r--r--libjava/gnu/xml/xpath/FloorFunction.java83
-rw-r--r--libjava/gnu/xml/xpath/Function.java57
-rw-r--r--libjava/gnu/xml/xpath/FunctionCall.java151
-rw-r--r--libjava/gnu/xml/xpath/IdFunction.java96
-rw-r--r--libjava/gnu/xml/xpath/LangFunction.java110
-rw-r--r--libjava/gnu/xml/xpath/LastFunction.java67
-rw-r--r--libjava/gnu/xml/xpath/LocalNameFunction.java87
-rw-r--r--libjava/gnu/xml/xpath/NameFunction.java95
-rw-r--r--libjava/gnu/xml/xpath/NameTest.java131
-rw-r--r--libjava/gnu/xml/xpath/NamespaceTest.java118
-rw-r--r--libjava/gnu/xml/xpath/NamespaceUriFunction.java87
-rw-r--r--libjava/gnu/xml/xpath/NegativeExpr.java75
-rw-r--r--libjava/gnu/xml/xpath/NodeTypeTest.java130
-rw-r--r--libjava/gnu/xml/xpath/NormalizeSpaceFunction.java99
-rw-r--r--libjava/gnu/xml/xpath/NotFunction.java81
-rw-r--r--libjava/gnu/xml/xpath/NumberFunction.java96
-rw-r--r--libjava/gnu/xml/xpath/OrExpr.java81
-rw-r--r--libjava/gnu/xml/xpath/ParenthesizedExpr.java84
-rw-r--r--libjava/gnu/xml/xpath/Path.java54
-rw-r--r--libjava/gnu/xml/xpath/Pattern.java54
-rw-r--r--libjava/gnu/xml/xpath/PositionFunction.java67
-rw-r--r--libjava/gnu/xml/xpath/Predicate.java79
-rw-r--r--libjava/gnu/xml/xpath/RelationalExpr.java102
-rw-r--r--libjava/gnu/xml/xpath/Root.java81
-rw-r--r--libjava/gnu/xml/xpath/RoundFunction.java90
-rw-r--r--libjava/gnu/xml/xpath/Selector.java500
-rw-r--r--libjava/gnu/xml/xpath/StartsWithFunction.java86
-rw-r--r--libjava/gnu/xml/xpath/Steps.java240
-rw-r--r--libjava/gnu/xml/xpath/StringFunction.java113
-rw-r--r--libjava/gnu/xml/xpath/StringLengthFunction.java85
-rw-r--r--libjava/gnu/xml/xpath/SubstringAfterFunction.java92
-rw-r--r--libjava/gnu/xml/xpath/SubstringBeforeFunction.java91
-rw-r--r--libjava/gnu/xml/xpath/SubstringFunction.java122
-rw-r--r--libjava/gnu/xml/xpath/SumFunction.java94
-rw-r--r--libjava/gnu/xml/xpath/Test.java55
-rw-r--r--libjava/gnu/xml/xpath/TranslateFunction.java126
-rw-r--r--libjava/gnu/xml/xpath/TrueFunction.java66
-rw-r--r--libjava/gnu/xml/xpath/UnionExpr.java102
-rw-r--r--libjava/gnu/xml/xpath/VariableReference.java88
-rw-r--r--libjava/gnu/xml/xpath/XPathFactoryImpl.java90
-rw-r--r--libjava/gnu/xml/xpath/XPathImpl.java164
-rw-r--r--libjava/gnu/xml/xpath/XPathParser.java1462
-rw-r--r--libjava/gnu/xml/xpath/XPathParser.y782
-rw-r--r--libjava/gnu/xml/xpath/XPathTokenizer.java592
180 files changed, 53204 insertions, 0 deletions
diff --git a/libjava/gnu/xml/aelfred2/ContentHandler2.java b/libjava/gnu/xml/aelfred2/ContentHandler2.java
new file mode 100644
index 0000000..7bb1e7d
--- /dev/null
+++ b/libjava/gnu/xml/aelfred2/ContentHandler2.java
@@ -0,0 +1,65 @@
+/* ContentHandler2.java --
+ Copyright (C) 2004 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.aelfred2;
+
+import org.xml.sax.ContentHandler;
+import org.xml.sax.SAXException;
+
+/**
+ * Extension to the SAX ContentHandler interface to report parsing events
+ * and parameters required by DOM Level 3 but not supported by SAX.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+public interface ContentHandler2
+ extends ContentHandler
+{
+
+ /**
+ * Reports the XML declaration.
+ * @param version the value of the version attribute in the XML
+ * declaration
+ * @param encoding the encoding specified in the XML declaration, if any
+ * @param standalone the standalone attribute from the XML declaration
+ * @param inputEncoding the encoding of the XML input
+ */
+ void xmlDecl(String version, String encoding, boolean standalone,
+ String inputEncoding)
+ throws SAXException;
+
+}
diff --git a/libjava/gnu/xml/aelfred2/JAXPFactory.java b/libjava/gnu/xml/aelfred2/JAXPFactory.java
new file mode 100644
index 0000000..006dc13
--- /dev/null
+++ b/libjava/gnu/xml/aelfred2/JAXPFactory.java
@@ -0,0 +1,196 @@
+/* JAXPFactory.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.aelfred2;
+
+import java.util.Enumeration;
+import java.util.Hashtable;
+
+import org.xml.sax.Parser;
+import org.xml.sax.XMLReader;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXNotRecognizedException;
+import org.xml.sax.SAXNotSupportedException;
+import org.xml.sax.helpers.XMLReaderAdapter;
+
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+
+
+/**
+ * Configurable factory to create an &AElig;lfred2 JAXP parser; required
+ * to bootstrap using JAXP. You should use SAX2 directly where possible,
+ * rather than through JAXP, since that gives you better control.
+ * This class would normally be configured as a platform default factory.
+ *
+ * @author David Brownell
+ */
+public final class JAXPFactory extends SAXParserFactory
+{
+ private Hashtable flags = new Hashtable ();
+
+ /**
+ * Constructs a factory which normally returns a non-validating
+ * parser.
+ */
+ public JAXPFactory () { }
+
+ public SAXParser newSAXParser ()
+ throws ParserConfigurationException, SAXException
+ {
+ JaxpParser jaxp = new JaxpParser ();
+ Enumeration e = flags.keys ();
+ XMLReader parser = jaxp.getXMLReader ();
+
+ parser.setFeature (
+ SAXDriver.FEATURE + "namespaces",
+ isNamespaceAware ());
+ parser.setFeature (
+ SAXDriver.FEATURE + "validation",
+ isValidating ());
+ // that makes SAX2 feature flags trump JAXP
+
+ while (e.hasMoreElements ()) {
+ String uri = (String) e.nextElement ();
+ Boolean value = (Boolean) flags.get (uri);
+ parser.setFeature (uri, value.booleanValue ());
+ }
+
+ return jaxp;
+ }
+
+ // yes, this "feature transfer" mechanism doesn't play well
+
+ public void setFeature (String name, boolean value)
+ throws
+ ParserConfigurationException,
+ SAXNotRecognizedException,
+ SAXNotSupportedException
+ {
+ try {
+ // force "early" detection of errors where possible
+ // (flags can't necessarily be set before parsing)
+ new JaxpParser ().getXMLReader ().setFeature (name, value);
+
+ flags.put (name, new Boolean (value));
+ } catch (SAXNotRecognizedException e) {
+ throw new SAXNotRecognizedException (name);
+ } catch (SAXNotSupportedException e) {
+ throw new SAXNotSupportedException (name);
+ } catch (Exception e) {
+ throw new ParserConfigurationException (
+ e.getClass ().getName ()
+ + ": "
+ + e.getMessage ());
+ }
+ }
+
+ public boolean getFeature (String name)
+ throws
+ ParserConfigurationException,
+ SAXNotRecognizedException,
+ SAXNotSupportedException
+ {
+ Boolean value = (Boolean) flags.get (name);
+
+ if (value != null)
+ return value.booleanValue ();
+ else
+ try {
+ return new JaxpParser ().getXMLReader ().getFeature (name);
+ } catch (SAXNotRecognizedException e) {
+ throw new SAXNotRecognizedException (name);
+ } catch (SAXNotSupportedException e) {
+ throw new SAXNotSupportedException (name);
+ } catch (SAXException e) {
+ throw new ParserConfigurationException (
+ e.getClass ().getName ()
+ + ": "
+ + e.getMessage ());
+ }
+ }
+
+ private static class JaxpParser extends SAXParser
+ {
+ private XmlReader ae2 = new XmlReader ();
+ private XMLReaderAdapter parser = null;
+
+ JaxpParser () { }
+
+ public void setProperty (String id, Object value)
+ throws SAXNotRecognizedException, SAXNotSupportedException
+ { ae2.setProperty (id, value); }
+
+ public Object getProperty (String id)
+ throws SAXNotRecognizedException, SAXNotSupportedException
+ { return ae2.getProperty (id); }
+
+ public Parser getParser ()
+ throws SAXException
+ {
+ if (parser == null)
+ parser = new XMLReaderAdapter (ae2);
+ return parser;
+ }
+
+ public XMLReader getXMLReader ()
+ throws SAXException
+ { return ae2; }
+
+ public boolean isNamespaceAware ()
+ {
+ try {
+ return ae2.getFeature (SAXDriver.FEATURE + "namespaces");
+ } catch (Exception e) {
+ throw new Error ();
+ }
+ }
+
+ public boolean isValidating ()
+ {
+ try {
+ return ae2.getFeature (SAXDriver.FEATURE + "validation");
+ } catch (Exception e) {
+ throw new Error ();
+ }
+ }
+
+ // TODO isXIncludeAware()
+
+ }
+}
diff --git a/libjava/gnu/xml/aelfred2/SAXDriver.java b/libjava/gnu/xml/aelfred2/SAXDriver.java
new file mode 100644
index 0000000..4912f27
--- /dev/null
+++ b/libjava/gnu/xml/aelfred2/SAXDriver.java
@@ -0,0 +1,1385 @@
+/* SAXDriver.java --
+ Copyright (C) 1999,2000,2001,2004 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.
+
+Portions derived from code which carried the following notice:
+
+ Copyright (c) 1997, 1998 by Microstar Software Ltd.
+
+ AElfred is free for both commercial and non-commercial use and
+ redistribution, provided that Microstar's copyright and disclaimer are
+ retained intact. You are free to modify AElfred for your own use and
+ to redistribute AElfred with your modifications, provided that the
+ modifications are clearly documented.
+
+ This program 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. Please use it AT
+ YOUR OWN RISK.
+*/
+
+package gnu.xml.aelfred2;
+
+import java.io.*;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Locale;
+import java.util.Stack;
+
+// maintaining 1.1 compatibility for now ... more portable, PJava, etc
+// Iterator, Hashmap and ArrayList ought to be faster
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Vector;
+
+import org.xml.sax.*;
+import org.xml.sax.ext.*;
+import org.xml.sax.helpers.NamespaceSupport;
+
+
+/**
+ * An enhanced SAX2 version of Microstar's &AElig;lfred XML parser.
+ * The enhancements primarily relate to significant improvements in
+ * conformance to the XML specification, and SAX2 support. Performance
+ * has been improved. See the package level documentation for more
+ * information.
+ *
+ * <table border="1" width='100%' cellpadding='3' cellspacing='0'>
+ * <tr bgcolor='#ccccff'>
+ * <th><font size='+1'>Name</font></th>
+ * <th><font size='+1'>Notes</font></th></tr>
+ *
+ * <tr><td colspan=2><center><em>Features ... URL prefix is
+ * <b>http://xml.org/sax/features/</b></em></center></td></tr>
+ *
+ * <tr><td>(URL)/external-general-entities</td>
+ * <td>Value defaults to <em>true</em></td></tr>
+ * <tr><td>(URL)/external-parameter-entities</td>
+ * <td>Value defaults to <em>true</em></td></tr>
+ * <tr><td>(URL)/is-standalone</td>
+ * <td>(PRELIMINARY) Returns true iff the document's parsing
+ * has started (some non-error event after <em>startDocument()</em>
+ * was reported) and the document's standalone flag is set.</td></tr>
+ * <tr><td>(URL)/namespace-prefixes</td>
+ * <td>Value defaults to <em>false</em> (but XML 1.0 names are
+ * always reported)</td></tr>
+ * <tr><td>(URL)/lexical-handler/parameter-entities</td>
+ * <td>Value is fixed at <em>true</em></td></tr>
+ * <tr><td>(URL)/namespaces</td>
+ * <td>Value defaults to <em>true</em></td></tr>
+ * <tr><td>(URL)/resolve-dtd-uris</td>
+ * <td>(PRELIMINARY) Value defaults to <em>true</em></td></tr>
+ * <tr><td>(URL)/string-interning</td>
+ * <td>Value is fixed at <em>true</em></td></tr>
+ * <tr><td>(URL)/use-attributes2</td>
+ * <td>(PRELIMINARY) Value is fixed at <em>true</em></td></tr>
+ * <tr><td>(URL)/use-entity-resolver2</td>
+ * <td>(PRELIMINARY) Value defaults to <em>true</em></td></tr>
+ * <tr><td>(URL)/validation</td>
+ * <td>Value is fixed at <em>false</em></td></tr>
+ *
+ * <tr><td colspan=2><center><em>Handler Properties ... URL prefix is
+ * <b>http://xml.org/sax/properties/</b></em></center></td></tr>
+ *
+ * <tr><td>(URL)/declaration-handler</td>
+ * <td>A declaration handler may be provided. </td></tr>
+ * <tr><td>(URL)/lexical-handler</td>
+ * <td>A lexical handler may be provided. </td></tr>
+ * </table>
+ *
+ * <p>This parser currently implements the SAX1 Parser API, but
+ * it may not continue to do so in the future.
+ *
+ * @author Written by David Megginson (version 1.2a from Microstar)
+ * @author Updated by David Brownell &lt;dbrownell@users.sourceforge.net&gt;
+ * @see org.xml.sax.Parser
+ */
+final public class SAXDriver
+ implements Locator, Attributes2, XMLReader, Parser, AttributeList
+{
+ private final DefaultHandler2 base = new DefaultHandler2 ();
+ private XmlParser parser;
+
+ private EntityResolver entityResolver = base;
+ private EntityResolver2 resolver2 = null;
+ private ContentHandler contentHandler = base;
+ private DTDHandler dtdHandler = base;
+ private ErrorHandler errorHandler = base;
+ private DeclHandler declHandler = base;
+ private LexicalHandler lexicalHandler = base;
+
+ private String elementName;
+ private Stack entityStack;
+
+ // one vector (of object/struct): faster, smaller
+ private List attributesList = Collections.synchronizedList(new ArrayList());
+
+ private boolean attributeSpecified [] = new boolean[10];
+ private boolean attributeDeclared [] = new boolean[10];
+
+ private boolean namespaces = true;
+ private boolean xmlNames = false;
+ private boolean extGE = true;
+ private boolean extPE = true;
+ private boolean resolveAll = true;
+ private boolean useResolver2 = true;
+ private boolean stringInterning = true;
+
+ private int attributeCount;
+ private boolean attributes;
+ private String nsTemp [];
+ private NamespaceSupport prefixStack;
+
+ //
+ // Constructor.
+ //
+
+ /** Constructs a SAX Parser. */
+ public SAXDriver ()
+ {
+ reset ();
+ }
+
+ private void reset ()
+ {
+ elementName = null;
+ entityStack = new Stack ();
+ attributesList = Collections.synchronizedList(new ArrayList());
+ attributeSpecified = new boolean[10];
+ attributeDeclared = new boolean[10];
+ attributeCount = 0;
+ attributes = false;
+ nsTemp = new String[3];
+ prefixStack = null;
+ }
+
+
+ //
+ // Implementation of org.xml.sax.Parser.
+ //
+
+ /**
+ * <b>SAX1</b>: Sets the locale used for diagnostics; currently,
+ * only locales using the English language are supported.
+ * @param locale The locale for which diagnostics will be generated
+ */
+ public void setLocale (Locale locale)
+ throws SAXException
+ {
+ if ("en".equals (locale.getLanguage ()))
+ return ;
+
+ throw new SAXException ("AElfred2 only supports English locales.");
+ }
+
+
+ /**
+ * <b>SAX2</b>: Returns the object used when resolving external
+ * entities during parsing (both general and parameter entities).
+ */
+ public EntityResolver getEntityResolver ()
+ {
+ return (entityResolver == base) ? null : entityResolver;
+ }
+
+ /**
+ * <b>SAX1, SAX2</b>: Set the entity resolver for this parser.
+ * @param handler The object to receive entity events.
+ */
+ public void setEntityResolver (EntityResolver resolver)
+ {
+ if (resolver instanceof EntityResolver2)
+ resolver2 = (EntityResolver2) resolver;
+ else
+ resolver2 = null;
+ if (resolver == null)
+ resolver = base;
+ entityResolver = resolver;
+ }
+
+
+ /**
+ * <b>SAX2</b>: Returns the object used to process declarations related
+ * to notations and unparsed entities.
+ */
+ public DTDHandler getDTDHandler ()
+ {
+ return (dtdHandler == base) ? null : dtdHandler;
+ }
+
+ /**
+ * <b>SAX1, SAX2</b>: Set the DTD handler for this parser.
+ * @param handler The object to receive DTD events.
+ */
+ public void setDTDHandler (DTDHandler handler)
+ {
+ if (handler == null)
+ handler = base;
+ this.dtdHandler = handler;
+ }
+
+
+ /**
+ * <b>SAX1</b>: Set the document handler for this parser. If a
+ * content handler was set, this document handler will supplant it.
+ * The parser is set to report all XML 1.0 names rather than to
+ * filter out "xmlns" attributes (the "namespace-prefixes" feature
+ * is set to true).
+ *
+ * @deprecated SAX2 programs should use the XMLReader interface
+ * and a ContentHandler.
+ *
+ * @param handler The object to receive document events.
+ */
+ public void setDocumentHandler (DocumentHandler handler)
+ {
+ contentHandler = new Adapter (handler);
+ xmlNames = true;
+ }
+
+ /**
+ * <b>SAX2</b>: Returns the object used to report the logical
+ * content of an XML document.
+ */
+ public ContentHandler getContentHandler ()
+ {
+ return contentHandler == base ? null : contentHandler;
+ }
+
+ /**
+ * <b>SAX2</b>: Assigns the object used to report the logical
+ * content of an XML document. If a document handler was set,
+ * this content handler will supplant it (but XML 1.0 style name
+ * reporting may remain enabled).
+ */
+ public void setContentHandler (ContentHandler handler)
+ {
+ if (handler == null)
+ handler = base;
+ contentHandler = handler;
+ }
+
+ /**
+ * <b>SAX1, SAX2</b>: Set the error handler for this parser.
+ * @param handler The object to receive error events.
+ */
+ public void setErrorHandler (ErrorHandler handler)
+ {
+ if (handler == null)
+ handler = base;
+ this.errorHandler = handler;
+ }
+
+ /**
+ * <b>SAX2</b>: Returns the object used to receive callbacks for XML
+ * errors of all levels (fatal, nonfatal, warning); this is never null;
+ */
+ public ErrorHandler getErrorHandler ()
+ { return errorHandler == base ? null : errorHandler; }
+
+
+ /**
+ * <b>SAX1, SAX2</b>: Auxiliary API to parse an XML document, used mostly
+ * when no URI is available.
+ * If you want anything useful to happen, you should set
+ * at least one type of handler.
+ * @param source The XML input source. Don't set 'encoding' unless
+ * you know for a fact that it's correct.
+ * @see #setEntityResolver
+ * @see #setDTDHandler
+ * @see #setContentHandler
+ * @see #setErrorHandler
+ * @exception SAXException The handlers may throw any SAXException,
+ * and the parser normally throws SAXParseException objects.
+ * @exception IOException IOExceptions are normally through through
+ * the parser if there are problems reading the source document.
+ */
+ public void parse (InputSource source)
+ throws SAXException, IOException
+ {
+ synchronized (base) {
+ parser = new XmlParser ();
+ if (namespaces)
+ prefixStack = new NamespaceSupport ();
+ else if (!xmlNames)
+ throw new IllegalStateException ();
+ parser.setHandler (this);
+
+ try {
+
+ Reader r = source.getCharacterStream();
+ InputStream in = source.getByteStream();
+
+
+ parser.doParse (source.getSystemId (),
+ source.getPublicId (),
+ r,
+ in,
+ source.getEncoding ());
+ } catch (SAXException e) {
+ throw e;
+ } catch (IOException e) {
+ throw e;
+ } catch (RuntimeException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new SAXParseException (e.getMessage (), this, e);
+ } finally {
+ contentHandler.endDocument ();
+ reset();
+ }
+ }
+ }
+
+
+ /**
+ * <b>SAX1, SAX2</b>: Preferred API to parse an XML document, using a
+ * system identifier (URI).
+ */
+ public void parse (String systemId)
+ throws SAXException, IOException
+ {
+ parse (new InputSource (systemId));
+ }
+
+ //
+ // Implementation of SAX2 "XMLReader" interface
+ //
+ static final String FEATURE = "http://xml.org/sax/features/";
+ static final String PROPERTY = "http://xml.org/sax/properties/";
+
+ /**
+ * <b>SAX2</b>: Tells the value of the specified feature flag.
+ *
+ * @exception SAXNotRecognizedException thrown if the feature flag
+ * is neither built in, nor yet assigned.
+ */
+ public boolean getFeature (String featureId)
+ throws SAXNotRecognizedException, SAXNotSupportedException
+ {
+ if ((FEATURE + "validation").equals (featureId))
+ return false;
+
+ // external entities (both types) are optionally included
+ if ((FEATURE + "external-general-entities").equals (featureId))
+ return extGE;
+ if ((FEATURE + "external-parameter-entities") .equals (featureId))
+ return extPE;
+
+ // element/attribute names are as written in document; no mangling
+ if ((FEATURE + "namespace-prefixes").equals (featureId))
+ return xmlNames;
+
+ // report element/attribute namespaces?
+ if ((FEATURE + "namespaces").equals (featureId))
+ return namespaces;
+
+ // all PEs and GEs are reported
+ if ((FEATURE + "lexical-handler/parameter-entities").equals (featureId))
+ return true;
+
+ // default is true
+ if ((FEATURE + "string-interning").equals (featureId))
+ return stringInterning;
+
+ // EXTENSIONS 1.1
+
+ // always returns isSpecified info
+ if ((FEATURE + "use-attributes2").equals (featureId))
+ return true;
+
+ // meaningful between startDocument/endDocument
+ if ((FEATURE + "is-standalone").equals (featureId)) {
+ if (parser == null)
+ throw new SAXNotSupportedException (featureId);
+ return parser.isStandalone ();
+ }
+
+ // optionally don't absolutize URIs in declarations
+ if ((FEATURE + "resolve-dtd-uris").equals (featureId))
+ return resolveAll;
+
+ // optionally use resolver2 interface methods, if possible
+ if ((FEATURE + "use-entity-resolver2").equals (featureId))
+ return useResolver2;
+
+ throw new SAXNotRecognizedException (featureId);
+ }
+
+ // package private
+ DeclHandler getDeclHandler () { return declHandler; }
+
+ // package private
+ boolean resolveURIs () { return resolveAll; }
+
+ /**
+ * <b>SAX2</b>: Returns the specified property.
+ *
+ * @exception SAXNotRecognizedException thrown if the property value
+ * is neither built in, nor yet stored.
+ */
+ public Object getProperty (String propertyId)
+ throws SAXNotRecognizedException
+ {
+ if ((PROPERTY + "declaration-handler").equals (propertyId))
+ return declHandler == base ? null : declHandler;
+
+ if ((PROPERTY + "lexical-handler").equals (propertyId))
+ return lexicalHandler == base ? null : lexicalHandler;
+
+ // unknown properties
+ throw new SAXNotRecognizedException (propertyId);
+ }
+
+ /**
+ * <b>SAX2</b>: Sets the state of feature flags in this parser. Some
+ * built-in feature flags are mutable.
+ */
+ public void setFeature (String featureId, boolean value)
+ throws SAXNotRecognizedException, SAXNotSupportedException
+ {
+ boolean state;
+
+ // Features with a defined value, we just change it if we can.
+ state = getFeature (featureId);
+
+ if (state == value)
+ return;
+ if (parser != null)
+ throw new SAXNotSupportedException ("not while parsing");
+
+ if ((FEATURE + "namespace-prefixes").equals (featureId)) {
+ // in this implementation, this only affects xmlns reporting
+ xmlNames = value;
+ // forcibly prevent illegal parser state
+ if (!xmlNames)
+ namespaces = true;
+ return;
+ }
+
+ if ((FEATURE + "namespaces").equals (featureId)) {
+ namespaces = value;
+ // forcibly prevent illegal parser state
+ if (!namespaces)
+ xmlNames = true;
+ return;
+ }
+
+ if ((FEATURE + "external-general-entities").equals (featureId)) {
+ extGE = value;
+ return;
+ }
+ if ((FEATURE + "external-parameter-entities") .equals (featureId)) {
+ extPE = value;
+ return;
+ }
+ if ((FEATURE + "resolve-dtd-uris").equals (featureId)) {
+ resolveAll = value;
+ return;
+ }
+
+ if ((FEATURE + "use-entity-resolver2").equals (featureId)) {
+ useResolver2 = value;
+ return;
+ }
+
+ throw new SAXNotRecognizedException (featureId);
+ }
+
+ /**
+ * <b>SAX2</b>: Assigns the specified property. Like SAX1 handlers,
+ * these may be changed at any time.
+ */
+ public void setProperty (String propertyId, Object value)
+ throws SAXNotRecognizedException, SAXNotSupportedException
+ {
+ // see if the property is recognized
+ getProperty (propertyId);
+
+ // Properties with a defined value, we just change it if we can.
+
+ if ((PROPERTY + "declaration-handler").equals (propertyId)) {
+ if (value == null)
+ declHandler = base;
+ else if (! (value instanceof DeclHandler))
+ throw new SAXNotSupportedException (propertyId);
+ else
+ declHandler = (DeclHandler) value;
+ return ;
+ }
+
+ if ((PROPERTY + "lexical-handler").equals (propertyId)) {
+ if (value == null)
+ lexicalHandler = base;
+ else if (! (value instanceof LexicalHandler))
+ throw new SAXNotSupportedException (propertyId);
+ else
+ lexicalHandler = (LexicalHandler) value;
+ return ;
+ }
+
+ throw new SAXNotSupportedException (propertyId);
+ }
+
+
+ //
+ // This is where the driver receives XmlParser callbacks and translates
+ // them into SAX callbacks. Some more callbacks have been added for
+ // SAX2 support.
+ //
+
+ void startDocument ()
+ throws SAXException
+ {
+ contentHandler.setDocumentLocator (this);
+ contentHandler.startDocument ();
+ attributesList.clear ();
+ }
+
+ void xmlDecl(String version,
+ String encoding,
+ boolean standalone,
+ String inputEncoding)
+ throws SAXException
+ {
+ if (contentHandler instanceof ContentHandler2)
+ {
+ ((ContentHandler2) contentHandler).xmlDecl(version,
+ encoding,
+ standalone,
+ inputEncoding);
+ }
+ }
+
+ void skippedEntity (String name)
+ throws SAXException
+ { contentHandler.skippedEntity (name); }
+
+ InputSource getExternalSubset (String name, String baseURI)
+ throws SAXException, IOException
+ {
+ if (resolver2 == null || !useResolver2 || !extPE)
+ return null;
+ return resolver2.getExternalSubset (name, baseURI);
+ }
+
+ InputSource resolveEntity (boolean isPE, String name,
+ InputSource in, String baseURI)
+ throws SAXException, IOException
+ {
+ InputSource source;
+
+ // external entities might be skipped
+ if (isPE && !extPE)
+ return null;
+ if (!isPE && !extGE)
+ return null;
+
+ // ... or not
+ lexicalHandler.startEntity (name);
+ if (resolver2 != null && useResolver2) {
+ source = resolver2.resolveEntity (name, in.getPublicId (),
+ baseURI, in.getSystemId ());
+ if (source == null) {
+ in.setSystemId (absolutize (baseURI,
+ in.getSystemId (), false));
+ source = in;
+ }
+ } else {
+ in.setSystemId (absolutize (baseURI, in.getSystemId (), false));
+ source = entityResolver.resolveEntity (in.getPublicId (),
+ in.getSystemId ());
+ if (source == null)
+ source = in;
+ }
+ startExternalEntity (name, source.getSystemId (), true);
+ return source;
+ }
+
+ // absolutize a system ID relative to the specified base URI
+ // (temporarily) package-visible for external entity decls
+ String absolutize (String baseURI, String systemId, boolean nice)
+ throws MalformedURLException, SAXException
+ {
+ // FIXME normalize system IDs -- when?
+ // - Convert to UTF-8
+ // - Map reserved and non-ASCII characters to %HH
+
+ try {
+ if (baseURI == null) {
+ warn ("No base URI; hope this SYSTEM id is absolute: "
+ + systemId);
+ return new URL (systemId).toString ();
+ } else
+ return new URL (new URL (baseURI), systemId).toString ();
+
+ } catch (MalformedURLException e) {
+
+ // Let unknown URI schemes pass through unless we need
+ // the JVM to map them to i/o streams for us...
+ if (!nice)
+ throw e;
+
+ // sometimes sysids for notations or unparsed entities
+ // aren't really URIs...
+ warn ("Can't absolutize SYSTEM id: " + e.getMessage ());
+ return systemId;
+ }
+ }
+
+ void startExternalEntity (String name, String systemId,
+ boolean stackOnly)
+ throws SAXException
+ {
+ // The following warning was deleted because the application has the
+ // option of not setting systemId. Sun's JAXP or Xerces seems to
+ // ignore this case.
+ /*
+ if (systemId == null)
+ warn ("URI was not reported to parser for entity " + name);
+ */
+ if (!stackOnly) // spliced [dtd] needs startEntity
+ lexicalHandler.startEntity (name);
+ entityStack.push (systemId);
+ }
+
+ void endExternalEntity (String name)
+ throws SAXException
+ {
+ if (!"[document]".equals (name))
+ lexicalHandler.endEntity (name);
+ entityStack.pop ();
+ }
+
+ void startInternalEntity (String name)
+ throws SAXException
+ {
+ lexicalHandler.startEntity (name);
+ }
+
+ void endInternalEntity (String name)
+ throws SAXException
+ {
+ lexicalHandler.endEntity (name);
+ }
+
+ void doctypeDecl (String name, String publicId, String systemId)
+ throws SAXException
+ {
+ lexicalHandler.startDTD (name, publicId, systemId);
+
+ // ... the "name" is a declaration and should be given
+ // to the DeclHandler (but sax2 doesn't).
+
+ // the IDs for the external subset are lexical details,
+ // as are the contents of the internal subset; but sax2
+ // doesn't provide the internal subset "pre-parse"
+ }
+
+ void notationDecl (String name, String ids [])
+ throws SAXException
+ {
+ try {
+ dtdHandler.notationDecl (name, ids [0],
+ (resolveAll && ids [1] != null)
+ ? absolutize (ids [2], ids [1], true)
+ : ids [1]);
+ } catch (IOException e) {
+ // "can't happen"
+ throw new SAXParseException (e.getMessage (), this, e);
+ }
+ }
+
+ void unparsedEntityDecl (String name, String ids [], String notation)
+ throws SAXException
+ {
+ try {
+ dtdHandler.unparsedEntityDecl (name, ids [0],
+ resolveAll
+ ? absolutize (ids [2], ids [1], true)
+ : ids [1],
+ notation);
+ } catch (IOException e) {
+ // "can't happen"
+ throw new SAXParseException (e.getMessage (), this, e);
+ }
+ }
+
+ void endDoctype ()
+ throws SAXException
+ {
+ lexicalHandler.endDTD ();
+ }
+
+ private void declarePrefix (String prefix, String uri)
+ throws SAXException
+ {
+ int index = uri.indexOf (':');
+
+ // many versions of nwalsh docbook stylesheets
+ // have bogus URLs; so this can't be an error...
+ if (index < 1 && uri.length () != 0)
+ warn ("relative URI for namespace: " + uri);
+
+ // FIXME: char [0] must be ascii alpha; chars [1..index]
+ // must be ascii alphanumeric or in "+-." [RFC 2396]
+
+ //Namespace Constraints
+ //name for xml prefix must be http://www.w3.org/XML/1998/namespace
+ boolean prefixEquality = prefix.equals("xml");
+ boolean uriEquality = uri.equals("http://www.w3.org/XML/1998/namespace");
+ if ((prefixEquality || uriEquality) && !(prefixEquality && uriEquality))
+ fatal ("xml is by definition bound to the namespace name " +
+ "http://www.w3.org/XML/1998/namespace");
+
+ //xmlns prefix declaration is illegal but xml prefix declaration is llegal...
+ if (prefixEquality && uriEquality)
+ return;
+
+ //name for xmlns prefix must be http://www.w3.org/2000/xmlns/
+ prefixEquality = prefix.equals("xmlns");
+ uriEquality = uri.equals("http://www.w3.org/2000/xmlns/");
+ if ((prefixEquality || uriEquality) && !(prefixEquality && uriEquality))
+ fatal("http://www.w3.org/2000/xmlns/ is by definition bound" +
+ " to prefix xmlns");
+
+ //even if the uri is http://www.w3.org/2000/xmlns/ it is illegal to declare it
+ if (prefixEquality && uriEquality)
+ fatal ("declaring the xmlns prefix is illegal");
+
+ uri = uri.intern ();
+ prefixStack.declarePrefix (prefix, uri);
+ contentHandler.startPrefixMapping (prefix, uri);
+ }
+
+ void attribute (String qname, String value, boolean isSpecified)
+ throws SAXException
+ {
+ if (!attributes) {
+ attributes = true;
+ if (namespaces)
+ prefixStack.pushContext ();
+ }
+
+ // process namespace decls immediately;
+ // then maybe forget this as an attribute
+ if (namespaces) {
+ int index;
+
+ // default NS declaration?
+ if (getFeature (FEATURE + "string-interning")) {
+ if ("xmlns" == qname) {
+ declarePrefix ("", value);
+ if (!xmlNames)
+ return;
+ }
+ // NS prefix declaration?
+ else if ((index = qname.indexOf (':')) == 5
+ && qname.startsWith ("xmlns")) {
+ String prefix = qname.substring (6);
+
+ if (prefix.equals(""))
+ fatal ("missing prefix in namespace declaration attribute");
+ if (value.length () == 0) {
+ verror ("missing URI in namespace declaration attribute: "
+ + qname);
+ } else
+ declarePrefix (prefix, value);
+ if (!xmlNames)
+ return;
+ }
+ } else {
+ if ("xmlns".equals(qname)) {
+ declarePrefix ("", value);
+ if (!xmlNames)
+ return;
+ }
+ // NS prefix declaration?
+ else if ((index = qname.indexOf (':')) == 5
+ && qname.startsWith ("xmlns")) {
+ String prefix = qname.substring (6);
+
+ if (value.length () == 0) {
+ verror ("missing URI in namespace decl attribute: "
+ + qname);
+ } else
+ declarePrefix (prefix, value);
+ if (!xmlNames)
+ return;
+ }
+ }
+ }
+ // remember this attribute ...
+
+ if (attributeCount == attributeSpecified.length) { // grow array?
+ boolean temp [] = new boolean [attributeSpecified.length + 5];
+ System.arraycopy (attributeSpecified, 0, temp, 0, attributeCount);
+ attributeSpecified = temp;
+ }
+ attributeSpecified [attributeCount] = isSpecified;
+
+ attributeCount++;
+
+ // attribute type comes from querying parser's DTD records
+ attributesList.add(new Attribute(qname, value));
+
+ }
+
+ void startElement (String elname)
+ throws SAXException
+ {
+ ContentHandler handler = contentHandler;
+
+ //
+ // NOTE: this implementation of namespace support adds something
+ // like six percent to parsing CPU time, in a large (~50 MB)
+ // document that doesn't use namespaces at all. (Measured by PC
+ // sampling, with a bug where endElement processing was omitted.)
+ // [Measurement referred to older implementation, older JVM ...]
+ //
+ // It ought to become notably faster in such cases. Most
+ // costs are the prefix stack calling Hashtable.get() (2%),
+ // String.hashCode() (1.5%) and about 1.3% each for pushing
+ // the context, and two chunks of name processing.
+ //
+
+ if (!attributes) {
+ if (namespaces)
+ prefixStack.pushContext ();
+ } else if (namespaces) {
+
+ // now we can patch up namespace refs; we saw all the
+ // declarations, so now we'll do the Right Thing
+ Iterator itt = attributesList.iterator ();
+ while(itt.hasNext())
+ {
+ Attribute attribute = (Attribute) itt.next();
+ String qname = attribute.name;
+ int index;
+
+ // default NS declaration?
+ if (getFeature (FEATURE + "string-interning")) {
+ if ("xmlns" == qname)
+ continue;
+ } else {
+ if ("xmlns".equals(qname))
+ continue;
+ }
+ //Illegal in the new Namespaces Draft
+ //should it be only in 1.1 docs??
+ if (qname.equals (":"))
+ fatal ("namespace names consisting of a single colon " +
+ "character are invalid");
+ index = qname.indexOf (':');
+
+ // NS prefix declaration?
+ if (index == 5 && qname.startsWith ("xmlns"))
+ continue;
+
+ // it's not a NS decl; patch namespace info items
+ if (prefixStack.processName (qname, nsTemp, true) == null)
+ fatal ("undeclared attribute prefix in: " + qname);
+ else {
+ attribute.nameSpace = nsTemp[0];
+ attribute.localName = nsTemp[1];
+ }
+ }
+ }
+
+ // save element name so attribute callbacks work
+ elementName = elname;
+ if (namespaces) {
+ if (prefixStack.processName (elname, nsTemp, false) == null) {
+ fatal ("undeclared element prefix in: " + elname);
+ nsTemp [0] = nsTemp [1] = "";
+ }
+ handler.startElement (nsTemp [0], nsTemp [1], elname, this);
+ } else
+ handler.startElement ("", "", elname, this);
+ // elementName = null;
+
+ // elements with no attributes are pretty common!
+ if (attributes) {
+ attributesList.clear();
+ attributeCount = 0;
+ attributes = false;
+ }
+ }
+
+ void endElement (String elname)
+ throws SAXException
+ {
+ ContentHandler handler = contentHandler;
+
+ if (!namespaces) {
+ handler.endElement ("", "", elname);
+ return;
+ }
+ prefixStack.processName (elname, nsTemp, false);
+ handler.endElement (nsTemp [0], nsTemp [1], elname);
+
+ Enumeration prefixes = prefixStack.getDeclaredPrefixes ();
+
+ while (prefixes.hasMoreElements ())
+ handler.endPrefixMapping ((String) prefixes.nextElement ());
+ prefixStack.popContext ();
+ }
+
+ void startCDATA ()
+ throws SAXException
+ {
+ lexicalHandler.startCDATA ();
+ }
+
+ void charData (char ch[], int start, int length)
+ throws SAXException
+ {
+ contentHandler.characters (ch, start, length);
+ }
+
+ void endCDATA ()
+ throws SAXException
+ {
+ lexicalHandler.endCDATA ();
+ }
+
+ void ignorableWhitespace (char ch[], int start, int length)
+ throws SAXException
+ {
+ contentHandler.ignorableWhitespace (ch, start, length);
+ }
+
+ void processingInstruction (String target, String data)
+ throws SAXException
+ {
+ contentHandler.processingInstruction (target, data);
+ }
+
+ void comment (char ch[], int start, int length)
+ throws SAXException
+ {
+ if (lexicalHandler != base)
+ lexicalHandler.comment (ch, start, length);
+ }
+
+ void fatal (String message)
+ throws SAXException
+ {
+ SAXParseException fatal;
+
+ fatal = new SAXParseException (message, this);
+ errorHandler.fatalError (fatal);
+
+ // Even if the application can continue ... we can't!
+ throw fatal;
+ }
+
+ // We can safely report a few validity errors that
+ // make layered SAX2 DTD validation more conformant
+ void verror (String message)
+ throws SAXException
+ {
+ SAXParseException err;
+
+ err = new SAXParseException (message, this);
+ errorHandler.error (err);
+ }
+
+ void warn (String message)
+ throws SAXException
+ {
+ SAXParseException err;
+
+ err = new SAXParseException (message, this);
+ errorHandler.warning (err);
+ }
+
+
+ //
+ // Implementation of org.xml.sax.Attributes.
+ //
+
+ /**
+ * <b>SAX1 AttributeList, SAX2 Attributes</b> method
+ * (don't invoke on parser);
+ */
+ public int getLength ()
+ {
+ return attributesList.size ();
+ }
+
+ /**
+ * <b>SAX2 Attributes</b> method (don't invoke on parser);
+ */
+ public String getURI (int index)
+ {
+ return ((Attribute) attributesList.get (index)).nameSpace;
+ }
+
+ /**
+ * <b>SAX2 Attributes</b> method (don't invoke on parser);
+ */
+ public String getLocalName (int index)
+ {
+ Attribute attr = (Attribute) attributesList.get (index);
+ // FIXME attr.localName is sometimes null, why?
+ if (namespaces && attr.localName == null)
+ {
+ // XXX fix this here for now
+ int ci = attr.name.indexOf(':');
+ attr.localName = (ci == -1) ? attr.name :
+ attr.name.substring(ci + 1);
+ }
+ return attr.localName;
+ }
+
+ /**
+ * <b>SAX2 Attributes</b> method (don't invoke on parser);
+ */
+ public String getQName (int i)
+ {
+ return ((Attribute) attributesList.get (i)).name;
+ }
+
+ /**
+ * <b>SAX1 AttributeList</b> method (don't invoke on parser);
+ */
+ public String getName (int i)
+ {
+ return ((Attribute) attributesList.get (i)).name;
+ }
+
+ /**
+ * <b>SAX1 AttributeList, SAX2 Attributes</b> method
+ * (don't invoke on parser);
+ */
+ public String getType (int i)
+ {
+ String type = parser.getAttributeType (elementName, getQName (i));
+ if (type == null)
+ return "CDATA";
+ // ... use DeclHandler.attributeDecl to see enumerations
+ if (type == "ENUMERATION")
+ return "NMTOKEN";
+ return type;
+ }
+
+
+ /**
+ * <b>SAX1 AttributeList, SAX2 Attributes</b> method
+ * (don't invoke on parser);
+ */
+ public String getValue (int i)
+ {
+ return ((Attribute) attributesList.get (i)).value;
+ }
+
+
+ /**
+ * <b>SAX2 Attributes</b> method (don't invoke on parser);
+ */
+ public int getIndex (String uri, String local)
+ {
+ int length = getLength ();
+
+ for (int i = 0; i < length; i++) {
+ if (!getURI (i).equals (uri))
+ continue;
+ if (getLocalName (i).equals (local))
+ return i;
+ }
+ return -1;
+ }
+
+
+ /**
+ * <b>SAX2 Attributes</b> method (don't invoke on parser);
+ */
+ public int getIndex (String xmlName)
+ {
+ int length = getLength ();
+
+ for (int i = 0; i < length; i++) {
+ if (getQName (i).equals (xmlName))
+ return i;
+ }
+ return -1;
+ }
+
+
+ /**
+ * <b>SAX2 Attributes</b> method (don't invoke on parser);
+ */
+ public String getType (String uri, String local)
+ {
+ int index = getIndex (uri, local);
+
+ if (index < 0)
+ return null;
+ return getType (index);
+ }
+
+
+ /**
+ * <b>SAX1 AttributeList, SAX2 Attributes</b> method
+ * (don't invoke on parser);
+ */
+ public String getType (String xmlName)
+ {
+ int index = getIndex (xmlName);
+
+ if (index < 0)
+ return null;
+ return getType (index);
+ }
+
+
+ /**
+ * <b>SAX Attributes</b> method (don't invoke on parser);
+ */
+ public String getValue (String uri, String local)
+ {
+ int index = getIndex (uri, local);
+
+ if (index < 0)
+ return null;
+ return getValue (index);
+ }
+
+
+ /**
+ * <b>SAX1 AttributeList, SAX2 Attributes</b> method
+ * (don't invoke on parser);
+ */
+ public String getValue (String xmlName)
+ {
+ int index = getIndex (xmlName);
+
+ if (index < 0)
+ return null;
+ return getValue (index);
+ }
+
+
+ //
+ // Implementation of org.xml.sax.ext.Attributes2
+ //
+
+
+ /** @return false unless the attribute was declared in the DTD.
+ * @throws java.lang.ArrayIndexOutOfBoundsException
+ * When the supplied index does not identify an attribute.
+ */
+ public boolean isDeclared (int index)
+ {
+ if (index < 0 || index >= attributeCount)
+ throw new ArrayIndexOutOfBoundsException ();
+ return attributeDeclared [index];
+ }
+
+ /** @return false unless the attribute was declared in the DTD.
+ * @throws java.lang.IllegalArgumentException
+ * When the supplied names do not identify an attribute.
+ */
+ public boolean isDeclared (java.lang.String qName)
+ {
+ int index = getIndex (qName);
+ if (index < 0)
+ throw new IllegalArgumentException ();
+ return attributeDeclared [index];
+ }
+
+ /** @return false unless the attribute was declared in the DTD.
+ * @throws java.lang.IllegalArgumentException
+ * When the supplied names do not identify an attribute.
+ */
+ public boolean isDeclared (java.lang.String uri, java.lang.String localName)
+ {
+ int index = getIndex (uri, localName);
+ if (index < 0)
+ throw new IllegalArgumentException ();
+ return attributeDeclared [index];
+ }
+
+
+ /**
+ * <b>SAX-ext Attributes2</b> method (don't invoke on parser);
+ */
+ public boolean isSpecified (int index)
+ {
+ if (index < 0 || index >= attributeCount)
+ throw new ArrayIndexOutOfBoundsException ();
+ return attributeSpecified [index];
+ }
+
+ /**
+ * <b>SAX-ext Attributes2</b> method (don't invoke on parser);
+ */
+ public boolean isSpecified (String uri, String local)
+ {
+ int index = getIndex (uri, local);
+
+ if (index < 0)
+ throw new IllegalArgumentException ();
+ return attributeSpecified [index];
+ }
+
+ /**
+ * <b>SAX-ext Attributes2</b> method (don't invoke on parser);
+ */
+ public boolean isSpecified (String xmlName)
+ {
+ int index = getIndex (xmlName);
+
+ if (index < 0)
+ throw new IllegalArgumentException ();
+ return attributeSpecified [index];
+ }
+
+
+ //
+ // Implementation of org.xml.sax.Locator.
+ //
+
+ /**
+ * <b>SAX Locator</b> method (don't invoke on parser);
+ */
+ public String getPublicId ()
+ {
+ return null; // FIXME track public IDs too
+ }
+
+ /**
+ * <b>SAX Locator</b> method (don't invoke on parser);
+ */
+ public String getSystemId ()
+ {
+ if (entityStack.empty ())
+ return null;
+ else
+ return (String) entityStack.peek ();
+ }
+
+ /**
+ * <b>SAX Locator</b> method (don't invoke on parser);
+ */
+ public int getLineNumber ()
+ {
+ return parser.getLineNumber ();
+ }
+
+ /**
+ * <b>SAX Locator</b> method (don't invoke on parser);
+ */
+ public int getColumnNumber ()
+ {
+ return parser.getColumnNumber ();
+ }
+
+ // adapter between SAX2 content handler and SAX1 document handler callbacks
+ private static class Adapter implements ContentHandler
+ {
+ private DocumentHandler docHandler;
+
+ Adapter (DocumentHandler dh)
+ { docHandler = dh; }
+
+
+ public void setDocumentLocator (Locator l)
+ { docHandler.setDocumentLocator (l); }
+
+ public void startDocument () throws SAXException
+ { docHandler.startDocument (); }
+
+ public void processingInstruction (String target, String data)
+ throws SAXException
+ { docHandler.processingInstruction (target, data); }
+
+ public void startPrefixMapping (String prefix, String uri)
+ { /* ignored */ }
+
+ public void startElement (
+ String namespace,
+ String local,
+ String name,
+ Attributes attrs
+ ) throws SAXException
+ { docHandler.startElement (name, (AttributeList) attrs); }
+
+ public void characters (char buf [], int offset, int len)
+ throws SAXException
+ { docHandler.characters (buf, offset, len); }
+
+ public void ignorableWhitespace (char buf [], int offset, int len)
+ throws SAXException
+ { docHandler.ignorableWhitespace (buf, offset, len); }
+
+ public void skippedEntity (String name)
+ { /* ignored */ }
+
+ public void endElement (String u, String l, String name)
+ throws SAXException
+ { docHandler.endElement (name); }
+
+ public void endPrefixMapping (String prefix)
+ { /* ignored */ }
+
+ public void endDocument () throws SAXException
+ { docHandler.endDocument (); }
+ }
+}
+
+class Attribute
+{
+
+ String name;
+ String value;
+ String nameSpace;
+ String localName;
+
+ Attribute(String name, String value)
+ {
+ this.name = name;
+ this.value = value;
+ this.nameSpace = "";
+ }
+}
+
diff --git a/libjava/gnu/xml/aelfred2/XmlParser.java b/libjava/gnu/xml/aelfred2/XmlParser.java
new file mode 100644
index 0000000..f4abf22
--- /dev/null
+++ b/libjava/gnu/xml/aelfred2/XmlParser.java
@@ -0,0 +1,5113 @@
+/* XmlParser.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.
+
+Partly derived from code which carried the following notice:
+
+ Copyright (c) 1997, 1998 by Microstar Software Ltd.
+
+ AElfred is free for both commercial and non-commercial use and
+ redistribution, provided that Microstar's copyright and disclaimer are
+ retained intact. You are free to modify AElfred for your own use and
+ to redistribute AElfred with your modifications, provided that the
+ modifications are clearly documented.
+
+ This program 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. Please use it AT
+ YOUR OWN RISK.
+*/
+
+package gnu.xml.aelfred2;
+
+import java.io.BufferedInputStream;
+import java.io.CharConversionException;
+import java.io.EOFException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.IOException;
+import java.io.Reader;
+import java.io.UnsupportedEncodingException;
+import java.net.URL;
+import java.net.URLConnection;
+
+// maintaining 1.1 compatibility for now ...
+// Iterator and Hashmap ought to be faster
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Stack;
+
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+
+
+/**
+ * Parse XML documents and return parse events through call-backs.
+ * Use the <code>SAXDriver</code> class as your entry point, as all
+ * internal parser interfaces are subject to change.
+ *
+ * @author Written by David Megginson &lt;dmeggins@microstar.com&gt;
+ * (version 1.2a with bugfixes)
+ * @author Updated by David Brownell &lt;dbrownell@users.sourceforge.net&gt;
+ * @see SAXDriver
+ */
+final class XmlParser
+{
+ // avoid slow per-character readCh()
+ private final static boolean USE_CHEATS = true;
+
+
+ //////////////////////////////////////////////////////////////////////
+ // Constructors.
+ ////////////////////////////////////////////////////////////////////////
+
+
+ /**
+ * Construct a new parser with no associated handler.
+ * @see #setHandler
+ * @see #parse
+ */
+ // package private
+ XmlParser ()
+ {
+ }
+
+
+ /**
+ * Set the handler that will receive parsing events.
+ * @param handler The handler to receive callback events.
+ * @see #parse
+ */
+ // package private
+ void setHandler (SAXDriver handler)
+ {
+ this.handler = handler;
+ }
+
+
+ /**
+ * Parse an XML document from the character stream, byte stream, or URI
+ * that you provide (in that order of preference). Any URI that you
+ * supply will become the base URI for resolving relative URI, and may
+ * be used to acquire a reader or byte stream.
+ *
+ * <p> Only one thread at a time may use this parser; since it is
+ * private to this package, post-parse cleanup is done by the caller,
+ * which MUST NOT REUSE the parser (just null it).
+ *
+ * @param systemId Absolute URI of the document; should never be null,
+ * but may be so iff a reader <em>or</em> a stream is provided.
+ * @param publicId The public identifier of the document, or null.
+ * @param reader A character stream; must be null if stream isn't.
+ * @param stream A byte input stream; must be null if reader isn't.
+ * @param encoding The suggested encoding, or null if unknown.
+ * @exception java.lang.Exception Basically SAXException or IOException
+ */
+ // package private
+ void doParse (
+ String systemId,
+ String publicId,
+ Reader reader,
+ InputStream stream,
+ String encoding
+ ) throws Exception
+ {
+ if (handler == null)
+ throw new IllegalStateException ("no callback handler");
+
+ initializeVariables ();
+
+ // predeclare the built-in entities here (replacement texts)
+ // we don't need to intern(), since we're guaranteed literals
+ // are always (globally) interned.
+ setInternalEntity ("amp", "&#38;");
+ setInternalEntity ("lt", "&#60;");
+ setInternalEntity ("gt", "&#62;");
+ setInternalEntity ("apos", "&#39;");
+ setInternalEntity ("quot", "&#34;");
+
+ try {
+ // pushURL first to ensure locator is correct in startDocument
+ // ... it might report an IO or encoding exception.
+ handler.startDocument ();
+ pushURL (false, "[document]",
+ // default baseURI: null
+ new String [] { publicId, systemId, null},
+ reader, stream, encoding, false);
+
+ parseDocument ();
+ } catch (EOFException e){
+ //empty input
+ error("empty document, with no root element.");
+ }finally {
+ if (reader != null)
+ try { reader.close ();
+ } catch (IOException e) { /* ignore */ }
+ if (stream != null)
+ try { stream.close ();
+ } catch (IOException e) { /* ignore */ }
+ if (is != null)
+ try { is.close ();
+ } catch (IOException e) { /* ignore */ }
+ if (reader != null)
+ try {
+ reader.close ();
+ } catch (IOException e) { /* ignore */
+ }
+ scratch = null;
+ }
+ }
+
+
+ ////////////////////////////////////////////////////////////////////////
+ // Constants.
+ ////////////////////////////////////////////////////////////////////////
+
+ //
+ // Constants for element content type.
+ //
+
+ /**
+ * Constant: an element has not been declared.
+ * @see #getElementContentType
+ */
+ public final static int CONTENT_UNDECLARED = 0;
+
+ /**
+ * Constant: the element has a content model of ANY.
+ * @see #getElementContentType
+ */
+ public final static int CONTENT_ANY = 1;
+
+ /**
+ * Constant: the element has declared content of EMPTY.
+ * @see #getElementContentType
+ */
+ public final static int CONTENT_EMPTY = 2;
+
+ /**
+ * Constant: the element has mixed content.
+ * @see #getElementContentType
+ */
+ public final static int CONTENT_MIXED = 3;
+
+ /**
+ * Constant: the element has element content.
+ * @see #getElementContentType
+ */
+ public final static int CONTENT_ELEMENTS = 4;
+
+
+ //
+ // Constants for the entity type.
+ //
+
+ /**
+ * Constant: the entity has not been declared.
+ * @see #getEntityType
+ */
+ public final static int ENTITY_UNDECLARED = 0;
+
+ /**
+ * Constant: the entity is internal.
+ * @see #getEntityType
+ */
+ public final static int ENTITY_INTERNAL = 1;
+
+ /**
+ * Constant: the entity is external, non-parsable data.
+ * @see #getEntityType
+ */
+ public final static int ENTITY_NDATA = 2;
+
+ /**
+ * Constant: the entity is external XML data.
+ * @see #getEntityType
+ */
+ public final static int ENTITY_TEXT = 3;
+
+
+ //
+ // Attribute type constants are interned literal strings.
+ //
+
+ //
+ // Constants for supported encodings. "external" is just a flag.
+ //
+ private final static int ENCODING_EXTERNAL = 0;
+ private final static int ENCODING_UTF_8 = 1;
+ private final static int ENCODING_ISO_8859_1 = 2;
+ private final static int ENCODING_UCS_2_12 = 3;
+ private final static int ENCODING_UCS_2_21 = 4;
+ private final static int ENCODING_UCS_4_1234 = 5;
+ private final static int ENCODING_UCS_4_4321 = 6;
+ private final static int ENCODING_UCS_4_2143 = 7;
+ private final static int ENCODING_UCS_4_3412 = 8;
+ private final static int ENCODING_ASCII = 9;
+
+
+ //
+ // Constants for attribute default value.
+ //
+
+ /**
+ * Constant: the attribute is not declared.
+ * @see #getAttributeDefaultValueType
+ */
+ public final static int ATTRIBUTE_DEFAULT_UNDECLARED = 30;
+
+ /**
+ * Constant: the attribute has a literal default value specified.
+ * @see #getAttributeDefaultValueType
+ * @see #getAttributeDefaultValue
+ */
+ public final static int ATTRIBUTE_DEFAULT_SPECIFIED = 31;
+
+ /**
+ * Constant: the attribute was declared #IMPLIED.
+ * @see #getAttributeDefaultValueType
+ */
+ public final static int ATTRIBUTE_DEFAULT_IMPLIED = 32;
+
+ /**
+ * Constant: the attribute was declared #REQUIRED.
+ * @see #getAttributeDefaultValueType
+ */
+ public final static int ATTRIBUTE_DEFAULT_REQUIRED = 33;
+
+ /**
+ * Constant: the attribute was declared #FIXED.
+ * @see #getAttributeDefaultValueType
+ * @see #getAttributeDefaultValue
+ */
+ public final static int ATTRIBUTE_DEFAULT_FIXED = 34;
+
+
+ //
+ // Constants for input.
+ //
+ private final static int INPUT_NONE = 0;
+ private final static int INPUT_INTERNAL = 1;
+ private final static int INPUT_STREAM = 3;
+ private final static int INPUT_READER = 5;
+
+
+ //
+ // Flags for reading literals.
+ //
+ // expand general entity refs (attribute values in dtd and content)
+ private final static int LIT_ENTITY_REF = 2;
+ // normalize this value (space chars) (attributes, public ids)
+ private final static int LIT_NORMALIZE = 4;
+ // literal is an attribute value
+ private final static int LIT_ATTRIBUTE = 8;
+ // don't expand parameter entities
+ private final static int LIT_DISABLE_PE = 16;
+ // don't expand [or parse] character refs
+ private final static int LIT_DISABLE_CREF = 32;
+ // don't parse general entity refs
+ private final static int LIT_DISABLE_EREF = 64;
+ // literal is a public ID value
+ private final static int LIT_PUBID = 256;
+
+
+ //
+ // Flags affecting PE handling in DTDs (if expandPE is true).
+ // PEs expand with space padding, except inside literals.
+ //
+ private final static int CONTEXT_NORMAL = 0;
+ private final static int CONTEXT_LITERAL = 1;
+
+
+ //////////////////////////////////////////////////////////////////////
+ // Error reporting.
+ //////////////////////////////////////////////////////////////////////
+
+
+ /**
+ * Report an error.
+ * @param message The error message.
+ * @param textFound The text that caused the error (or null).
+ * @see SAXDriver#error
+ * @see #line
+ */
+ private void error (String message, String textFound, String textExpected)
+ throws SAXException
+ {
+ if (textFound != null) {
+ message = message + " (found \"" + textFound + "\")";
+ }
+ if (textExpected != null) {
+ message = message + " (expected \"" + textExpected + "\")";
+ }
+ handler.fatal (message);
+
+ // "can't happen"
+ throw new SAXException (message);
+ }
+
+
+ /**
+ * Report a serious error.
+ * @param message The error message.
+ * @param textFound The text that caused the error (or null).
+ */
+ private void error (String message, char textFound, String textExpected)
+ throws SAXException
+ {
+ error (message, new Character (textFound).toString (), textExpected);
+ }
+
+ /** Report typical case fatal errors. */
+ private void error (String message)
+ throws SAXException
+ {
+ handler.fatal (message);
+ }
+
+
+ //////////////////////////////////////////////////////////////////////
+ // Major syntactic productions.
+ //////////////////////////////////////////////////////////////////////
+
+
+ /**
+ * Parse an XML document.
+ * <pre>
+ * [1] document ::= prolog element Misc*
+ * </pre>
+ * <p>This is the top-level parsing function for a single XML
+ * document. As a minimum, a well-formed document must have
+ * a document element, and a valid document must have a prolog
+ * (one with doctype) as well.
+ */
+ private void parseDocument ()
+ throws Exception
+ {
+ try { // added by MHK
+ boolean sawDTD = parseProlog ();
+ require ('<');
+ parseElement (!sawDTD);
+ } catch (EOFException ee) { // added by MHK
+ error("premature end of file", "[EOF]", null);
+ }
+
+ try {
+ parseMisc (); //skip all white, PIs, and comments
+ char c = readCh (); //if this doesn't throw an exception...
+ error ("unexpected characters after document end", c, null);
+ } catch (EOFException e) {
+ return;
+ }
+ }
+
+ static final char startDelimComment [] = { '<', '!', '-', '-' };
+ static final char endDelimComment [] = { '-', '-' };
+
+ /**
+ * Skip a comment.
+ * <pre>
+ * [15] Comment ::= '&lt;!--' ((Char - '-') | ('-' (Char - '-')))* "-->"
+ * </pre>
+ * <p> (The <code>&lt;!--</code> has already been read.)
+ */
+ private void parseComment ()
+ throws Exception
+ {
+ char c;
+ boolean saved = expandPE;
+
+ expandPE = false;
+ parseUntil (endDelimComment);
+ require ('>');
+ expandPE = saved;
+ handler.comment (dataBuffer, 0, dataBufferPos);
+ dataBufferPos = 0;
+ }
+
+ static final char startDelimPI [] = { '<', '?' };
+ static final char endDelimPI [] = { '?', '>' };
+
+ /**
+ * Parse a processing instruction and do a call-back.
+ * <pre>
+ * [16] PI ::= '&lt;?' PITarget
+ * (S (Char* - (Char* '?&gt;' Char*)))?
+ * '?&gt;'
+ * [17] PITarget ::= Name - ( ('X'|'x') ('M'|m') ('L'|l') )
+ * </pre>
+ * <p> (The <code>&lt;?</code> has already been read.)
+ */
+ private void parsePI ()
+ throws SAXException, IOException
+ {
+ String name;
+ boolean saved = expandPE;
+
+ expandPE = false;
+ name = readNmtoken (true);
+ //NE08
+ if (name.indexOf(':') >= 0)
+ error ("Illegal character(':') in processing instruction name ", name, null);
+ if ("xml".equalsIgnoreCase (name))
+ error ("Illegal processing instruction target", name, null);
+ if (!tryRead (endDelimPI)) {
+ requireWhitespace ();
+ parseUntil (endDelimPI);
+ }
+ expandPE = saved;
+ handler.processingInstruction (name, dataBufferToString ());
+ }
+
+
+ static final char endDelimCDATA [] = { ']', ']', '>' };
+
+ private boolean isDirtyCurrentElement;
+
+ /**
+ * Parse a CDATA section.
+ * <pre>
+ * [18] CDSect ::= CDStart CData CDEnd
+ * [19] CDStart ::= '&lt;![CDATA['
+ * [20] CData ::= (Char* - (Char* ']]&gt;' Char*))
+ * [21] CDEnd ::= ']]&gt;'
+ * </pre>
+ * <p> (The '&lt;![CDATA[' has already been read.)
+ */
+ private void parseCDSect ()
+ throws Exception
+ {
+ parseUntil (endDelimCDATA);
+ dataBufferFlush ();
+ }
+
+
+ /**
+ * Parse the prolog of an XML document.
+ * <pre>
+ * [22] prolog ::= XMLDecl? Misc* (Doctypedecl Misc*)?
+ * </pre>
+ * <p>We do not look for the XML declaration here, because it was
+ * handled by pushURL ().
+ * @see pushURL
+ * @return true if a DTD was read.
+ */
+ private boolean parseProlog ()
+ throws Exception
+ {
+ parseMisc ();
+
+ if (tryRead ("<!DOCTYPE")) {
+ parseDoctypedecl ();
+ parseMisc ();
+ return true;
+ }
+ return false;
+ }
+
+ private void checkLegalVersion (String version)
+ throws SAXException
+ {
+ int len = version.length ();
+ for (int i = 0; i < len; i++) {
+ char c = version.charAt (i);
+ if ('0' <= c && c <= '9')
+ continue;
+ if (c == '_' || c == '.' || c == ':' || c == '-')
+ continue;
+ if ('a' <= c && c <= 'z')
+ continue;
+ if ('A' <= c && c <= 'Z')
+ continue;
+ error ("illegal character in version", version, "1.0");
+ }
+ }
+
+
+ /**
+ * Parse the XML declaration.
+ * <pre>
+ * [23] XMLDecl ::= '&lt;?xml' VersionInfo EncodingDecl? SDDecl? S? '?&gt;'
+ * [24] VersionInfo ::= S 'version' Eq
+ * ("'" VersionNum "'" | '"' VersionNum '"' )
+ * [26] VersionNum ::= ([a-zA-Z0-9_.:] | '-')*
+ * [32] SDDecl ::= S 'standalone' Eq
+ * ( "'"" ('yes' | 'no') "'"" | '"' ("yes" | "no") '"' )
+ * [80] EncodingDecl ::= S 'encoding' Eq
+ * ( "'" EncName "'" | "'" EncName "'" )
+ * [81] EncName ::= [A-Za-z] ([A-Za-z0-9._] | '-')*
+ * </pre>
+ * <p> (The <code>&lt;?xml</code> and whitespace have already been read.)
+ * @return the encoding in the declaration, uppercased; or null
+ * @see #parseTextDecl
+ * @see #setupDecoding
+ */
+ private String parseXMLDecl (boolean ignoreEncoding)
+ throws SAXException, IOException
+ {
+ String version;
+ String encodingName = null;
+ String standalone = null;
+ int flags = LIT_DISABLE_CREF | LIT_DISABLE_PE | LIT_DISABLE_EREF;
+ String inputEncoding = null;
+
+ switch (this.encoding)
+ {
+ case ENCODING_EXTERNAL:
+ case ENCODING_UTF_8:
+ inputEncoding = "UTF-8";
+ break;
+ case ENCODING_ISO_8859_1:
+ inputEncoding = "ISO-8859-1";
+ break;
+ case ENCODING_UCS_2_12:
+ inputEncoding = "UTF-16BE";
+ break;
+ case ENCODING_UCS_2_21:
+ inputEncoding = "UTF-16LE";
+ break;
+ }
+
+ // Read the version.
+ require ("version");
+ parseEq ();
+ checkLegalVersion (version = readLiteral (flags));
+ if (!version.equals ("1.0")){
+ if(version.equals ("1.1")){
+ handler.warn ("expected XML version 1.0, not: " + version);
+ xmlVersion = XML_11;
+ }else {
+ error("illegal XML version", version, "1.0 or 1.1");
+ }
+ }
+ else
+ xmlVersion = XML_10;
+ // Try reading an encoding declaration.
+ boolean white = tryWhitespace ();
+
+ if (tryRead ("encoding")) {
+ if (!white)
+ error ("whitespace required before 'encoding='");
+ parseEq ();
+ encodingName = readLiteral (flags);
+ if (!ignoreEncoding)
+ setupDecoding (encodingName);
+ }
+
+ // Try reading a standalone declaration
+ if (encodingName != null)
+ white = tryWhitespace ();
+ if (tryRead ("standalone")) {
+ if (!white)
+ error ("whitespace required before 'standalone='");
+ parseEq ();
+ standalone = readLiteral (flags);
+ if ("yes".equals (standalone))
+ docIsStandalone = true;
+ else if (!"no".equals (standalone))
+ error ("standalone flag must be 'yes' or 'no'");
+ }
+
+ skipWhitespace ();
+ require ("?>");
+
+ if (inputEncoding == null)
+ {
+ inputEncoding = encodingName;
+ }
+ handler.xmlDecl(version, encodingName, "yes".equals(standalone),
+ inputEncoding);
+
+ return encodingName;
+ }
+
+
+ /**
+ * Parse a text declaration.
+ * <pre>
+ * [79] TextDecl ::= '&lt;?xml' VersionInfo? EncodingDecl S? '?&gt;'
+ * [80] EncodingDecl ::= S 'encoding' Eq
+ * ( '"' EncName '"' | "'" EncName "'" )
+ * [81] EncName ::= [A-Za-z] ([A-Za-z0-9._] | '-')*
+ * </pre>
+ * <p> (The <code>&lt;?xml</code>' and whitespace have already been read.)
+ * @return the encoding in the declaration, uppercased; or null
+ * @see #parseXMLDecl
+ * @see #setupDecoding
+ */
+ private String parseTextDecl (boolean ignoreEncoding)
+ throws SAXException, IOException
+ {
+ String encodingName = null;
+ int flags = LIT_DISABLE_CREF | LIT_DISABLE_PE | LIT_DISABLE_EREF;
+
+ // Read an optional version.
+ if (tryRead ("version")) {
+ String version;
+ parseEq ();
+ checkLegalVersion (version = readLiteral (flags));
+
+ if (version.equals ("1.1")){
+ if (xmlVersion == XML_10){
+ error ("external subset has later version number.", "1.0", version);
+ }
+ handler.warn ("expected XML version 1.0, not: " + version);
+ xmlVersion = XML_11;
+ }else if(!version.equals ("1.0")) {
+ error("illegal XML version", version, "1.0 or 1.1");
+ }
+ requireWhitespace ();
+ }
+
+
+ // Read the encoding.
+ require ("encoding");
+ parseEq ();
+ encodingName = readLiteral (flags);
+ if (!ignoreEncoding)
+ setupDecoding (encodingName);
+
+ skipWhitespace ();
+ require ("?>");
+
+ return encodingName;
+ }
+
+
+ /**
+ * Sets up internal state so that we can decode an entity using the
+ * specified encoding. This is used when we start to read an entity
+ * and we have been given knowledge of its encoding before we start to
+ * read any data (e.g. from a SAX input source or from a MIME type).
+ *
+ * <p> It is also used after autodetection, at which point only very
+ * limited adjustments to the encoding may be used (switching between
+ * related builtin decoders).
+ *
+ * @param encodingName The name of the encoding specified by the user.
+ * @exception IOException if the encoding isn't supported either
+ * internally to this parser, or by the hosting JVM.
+ * @see #parseXMLDecl
+ * @see #parseTextDecl
+ */
+ private void setupDecoding (String encodingName)
+ throws SAXException, IOException
+ {
+ encodingName = encodingName.toUpperCase ();
+
+ // ENCODING_EXTERNAL indicates an encoding that wasn't
+ // autodetected ... we can use builtin decoders, or
+ // ones from the JVM (InputStreamReader).
+
+ // Otherwise we can only tweak what was autodetected, and
+ // only for single byte (ASCII derived) builtin encodings.
+
+ // ASCII-derived encodings
+ if (encoding == ENCODING_UTF_8 || encoding == ENCODING_EXTERNAL) {
+ if (encodingName.equals ("ISO-8859-1")
+ || encodingName.equals ("8859_1")
+ || encodingName.equals ("ISO8859_1")
+ ) {
+ encoding = ENCODING_ISO_8859_1;
+ return;
+ } else if (encodingName.equals ("US-ASCII")
+ || encodingName.equals ("ASCII")) {
+ encoding = ENCODING_ASCII;
+ return;
+ } else if (encodingName.equals ("UTF-8")
+ || encodingName.equals ("UTF8")) {
+ encoding = ENCODING_UTF_8;
+ return;
+ } else if (encoding != ENCODING_EXTERNAL) {
+ // used to start with a new reader ...
+ throw new UnsupportedEncodingException (encodingName);
+ }
+ // else fallthrough ...
+ // it's ASCII-ish and something other than a builtin
+ }
+
+ // Unicode and such
+ if (encoding == ENCODING_UCS_2_12 || encoding == ENCODING_UCS_2_21) {
+ if (!(encodingName.equals ("ISO-10646-UCS-2")
+ || encodingName.equals ("UTF-16")
+ || encodingName.equals ("UTF-16BE")
+ || encodingName.equals ("UTF-16LE")))
+ error ("unsupported Unicode encoding",
+ encodingName,
+ "UTF-16");
+ return;
+ }
+
+ // four byte encodings
+ if (encoding == ENCODING_UCS_4_1234
+ || encoding == ENCODING_UCS_4_4321
+ || encoding == ENCODING_UCS_4_2143
+ || encoding == ENCODING_UCS_4_3412) {
+ // Strictly: "UCS-4" == "UTF-32BE"; also, "UTF-32LE" exists
+ if (!encodingName.equals ("ISO-10646-UCS-4"))
+ error ("unsupported 32-bit encoding",
+ encodingName,
+ "ISO-10646-UCS-4");
+ return;
+ }
+
+ // assert encoding == ENCODING_EXTERNAL
+ // if (encoding != ENCODING_EXTERNAL)
+ // throw new RuntimeException ("encoding = " + encoding);
+
+ if (encodingName.equals ("UTF-16BE")) {
+ encoding = ENCODING_UCS_2_12;
+ return;
+ }
+ if (encodingName.equals ("UTF-16LE")) {
+ encoding = ENCODING_UCS_2_21;
+ return;
+ }
+
+ // We couldn't use the builtin decoders at all. But we can try to
+ // create a reader, since we haven't messed up buffering. Tweak
+ // the encoding name if necessary.
+
+ if (encodingName.equals ("UTF-16")
+ || encodingName.equals ("ISO-10646-UCS-2"))
+ encodingName = "Unicode";
+ // Ignoring all the EBCDIC aliases here
+
+ reader = new InputStreamReader (is, encodingName);
+ sourceType = INPUT_READER;
+ }
+
+
+ /**
+ * Parse miscellaneous markup outside the document element and DOCTYPE
+ * declaration.
+ * <pre>
+ * [27] Misc ::= Comment | PI | S
+ * </pre>
+ */
+ private void parseMisc ()
+ throws Exception
+ {
+ while (true) {
+ skipWhitespace ();
+ if (tryRead (startDelimPI)) {
+ parsePI ();
+ } else if (tryRead (startDelimComment)) {
+ parseComment ();
+ } else {
+ return;
+ }
+ }
+ }
+
+
+ /**
+ * Parse a document type declaration.
+ * <pre>
+ * [28] doctypedecl ::= '&lt;!DOCTYPE' S Name (S ExternalID)? S?
+ * ('[' (markupdecl | PEReference | S)* ']' S?)? '&gt;'
+ * </pre>
+ * <p> (The <code>&lt;!DOCTYPE</code> has already been read.)
+ */
+ private void parseDoctypedecl ()
+ throws Exception
+ {
+ String rootName, ids[];
+
+ // Read the document type name.
+ requireWhitespace ();
+ rootName = readNmtoken (true);
+
+ // Read the External subset's IDs
+ skipWhitespace ();
+ ids = readExternalIds (false, true);
+
+ // report (a) declaration of name, (b) lexical info (ids)
+ handler.doctypeDecl (rootName, ids [0], ids [1]);
+
+ // Internal subset is parsed first, if present
+ skipWhitespace ();
+ if (tryRead ('[')) {
+
+ // loop until the subset ends
+ while (true) {
+ doReport = expandPE = true;
+ skipWhitespace ();
+ doReport = expandPE = false;
+ if (tryRead (']')) {
+ break; // end of subset
+ } else {
+ // WFC, PEs in internal subset (only between decls)
+ peIsError = expandPE = true;
+ parseMarkupdecl ();
+ peIsError = expandPE = false;
+ }
+ }
+ }
+ skipWhitespace ();
+ require ('>');
+
+ // Read the external subset, if any
+ InputSource subset;
+
+ if (ids [1] == null)
+ subset = handler.getExternalSubset (rootName,
+ handler.getSystemId ());
+ else
+ subset = null;
+ if (ids [1] != null || subset != null) {
+ pushString (null, ">");
+
+ // NOTE: [dtd] is so we say what SAX2 expects,
+ // though it's misleading (subset, not entire dtd)
+ if (ids [1] != null)
+ pushURL (true, "[dtd]", ids, null, null, null, true);
+ else {
+ handler.warn ("modifying document by adding external subset");
+ pushURL (true, "[dtd]",
+ new String [] { subset.getPublicId (),
+ subset.getSystemId (), null },
+ subset.getCharacterStream (),
+ subset.getByteStream (),
+ subset.getEncoding (),
+ false);
+ }
+
+ // Loop until we end up back at '>'
+ while (true) {
+ doReport = expandPE = true;
+ skipWhitespace ();
+ doReport = expandPE = false;
+ if (tryRead ('>')) {
+ break;
+ } else {
+ expandPE = true;
+ parseMarkupdecl ();
+ expandPE = false;
+ }
+ }
+
+ // the ">" string isn't popped yet
+ if (inputStack.size () != 1)
+ error ("external subset has unmatched '>'");
+ }
+
+ // done dtd
+ handler.endDoctype ();
+ expandPE = false;
+ doReport = true;
+ }
+
+
+ /**
+ * Parse a markup declaration in the internal or external DTD subset.
+ * <pre>
+ * [29] markupdecl ::= elementdecl | Attlistdecl | EntityDecl
+ * | NotationDecl | PI | Comment
+ * [30] extSubsetDecl ::= (markupdecl | conditionalSect
+ * | PEReference | S) *
+ * </pre>
+ * <p> Reading toplevel PE references is handled as a lexical issue
+ * by the caller, as is whitespace.
+ */
+ private void parseMarkupdecl ()
+ throws Exception
+ {
+ char saved [] = null;
+ boolean savedPE = expandPE;
+
+ // prevent "<%foo;" and ensures saved entity is right
+ require ('<');
+ unread ('<');
+ expandPE = false;
+
+ if (tryRead ("<!ELEMENT")) {
+ saved = readBuffer;
+ expandPE = savedPE;
+ parseElementDecl ();
+ } else if (tryRead ("<!ATTLIST")) {
+ saved = readBuffer;
+ expandPE = savedPE;
+ parseAttlistDecl ();
+ } else if (tryRead ("<!ENTITY")) {
+ saved = readBuffer;
+ expandPE = savedPE;
+ parseEntityDecl ();
+ } else if (tryRead ("<!NOTATION")) {
+ saved = readBuffer;
+ expandPE = savedPE;
+ parseNotationDecl ();
+ } else if (tryRead (startDelimPI)) {
+ saved = readBuffer;
+ expandPE = savedPE;
+ parsePI ();
+ } else if (tryRead (startDelimComment)) {
+ saved = readBuffer;
+ expandPE = savedPE;
+ parseComment ();
+ } else if (tryRead ("<![")) {
+ saved = readBuffer;
+ expandPE = savedPE;
+ if (inputStack.size () > 0)
+ parseConditionalSect (saved);
+ else
+ error ("conditional sections illegal in internal subset");
+ } else {
+ error ("expected markup declaration");
+ }
+
+ // VC: Proper Decl/PE Nesting
+ if (readBuffer != saved)
+ handler.verror ("Illegal Declaration/PE nesting");
+ }
+
+
+ /**
+ * Parse an element, with its tags.
+ * <pre>
+ * [39] element ::= EmptyElementTag | STag content ETag
+ * [40] STag ::= '&lt;' Name (S Attribute)* S? '&gt;'
+ * [44] EmptyElementTag ::= '&lt;' Name (S Attribute)* S? '/&gt;'
+ * </pre>
+ * <p> (The '&lt;' has already been read.)
+ * <p>NOTE: this method actually chains onto parseContent (), if necessary,
+ * and parseContent () will take care of calling parseETag ().
+ */
+ private void parseElement (boolean maybeGetSubset)
+ throws Exception
+ {
+ String gi;
+ char c;
+ int oldElementContent = currentElementContent;
+ String oldElement = currentElement;
+ Object element [];
+
+ // This is the (global) counter for the
+ // array of specified attributes.
+ tagAttributePos = 0;
+
+ // Read the element type name.
+ gi = readNmtoken (true);
+
+ // If we saw no DTD, and this is the document root element,
+ // let the application modify the input stream by providing one.
+ if (maybeGetSubset) {
+ InputSource subset = handler.getExternalSubset (gi,
+ handler.getSystemId ());
+ if (subset != null) {
+ String publicId = subset.getPublicId ();
+ String systemId = subset.getSystemId ();
+
+ handler.warn ("modifying document by adding DTD");
+ handler.doctypeDecl (gi, publicId, systemId);
+ pushString (null, ">");
+
+ // NOTE: [dtd] is so we say what SAX2 expects,
+ // though it's misleading (subset, not entire dtd)
+ pushURL (true, "[dtd]",
+ new String [] { publicId, systemId, null },
+ subset.getCharacterStream (),
+ subset.getByteStream (),
+ subset.getEncoding (),
+ false);
+
+ // Loop until we end up back at '>'
+ while (true) {
+ doReport = expandPE = true;
+ skipWhitespace ();
+ doReport = expandPE = false;
+ if (tryRead ('>')) {
+ break;
+ } else {
+ expandPE = true;
+ parseMarkupdecl ();
+ expandPE = false;
+ }
+ }
+
+ // the ">" string isn't popped yet
+ if (inputStack.size () != 1)
+ error ("external subset has unmatched '>'");
+
+ handler.endDoctype ();
+ }
+ }
+
+ // Determine the current content type.
+ currentElement = gi;
+ element = (Object []) elementInfo.get (gi);
+ currentElementContent = getContentType (element, CONTENT_ANY);
+
+ // Read the attributes, if any.
+ // After this loop, "c" is the closing delimiter.
+ boolean white = tryWhitespace ();
+ c = readCh ();
+ while (c != '/' && c != '>') {
+ unread (c);
+ if (!white)
+ error ("need whitespace between attributes");
+ parseAttribute (gi);
+ white = tryWhitespace ();
+ c = readCh ();
+ }
+
+ // Supply any defaulted attributes.
+ Enumeration atts = declaredAttributes (element);
+ if (atts != null) {
+ String aname;
+loop:
+ while (atts.hasMoreElements ()) {
+ aname = (String) atts.nextElement ();
+ // See if it was specified.
+ for (int i = 0; i < tagAttributePos; i++) {
+ if (tagAttributes [i] == aname) {
+ continue loop;
+ }
+ }
+ // ... or has a default
+ String value = getAttributeDefaultValue (gi, aname);
+
+ if (value == null)
+ continue;
+ handler.attribute (aname, value, false);
+ }
+ }
+
+ // Figure out if this is a start tag
+ // or an empty element, and dispatch an
+ // event accordingly.
+ switch (c) {
+ case '>':
+ handler.startElement (gi);
+ parseContent ();
+ break;
+ case '/':
+ require ('>');
+ handler.startElement (gi);
+ handler.endElement (gi);
+ break;
+ }
+
+ // Restore the previous state.
+ currentElement = oldElement;
+ currentElementContent = oldElementContent;
+ }
+
+
+ /**
+ * Parse an attribute assignment.
+ * <pre>
+ * [41] Attribute ::= Name Eq AttValue
+ * </pre>
+ * @param name The name of the attribute's element.
+ * @see SAXDriver#attribute
+ */
+ private void parseAttribute (String name)
+ throws Exception
+ {
+ String aname;
+ String type;
+ String value;
+ int flags = LIT_ATTRIBUTE | LIT_ENTITY_REF;
+
+ // Read the attribute name.
+ aname = readNmtoken (true);
+ type = getAttributeType (name, aname);
+
+ // Parse '='
+ parseEq ();
+
+ // Read the value, normalizing whitespace
+ // unless it is CDATA.
+ if (handler.getFeature (SAXDriver.FEATURE + "string-interning")) {
+ if (type == "CDATA" || type == null) {
+ value = readLiteral (flags);
+ } else {
+ value = readLiteral (flags | LIT_NORMALIZE);
+ }
+ } else {
+ if (type.equals("CDATA") || type == null) {
+ value = readLiteral (flags);
+ } else {
+ value = readLiteral (flags | LIT_NORMALIZE);
+ }
+ }
+
+ // WFC: no duplicate attributes
+ for (int i = 0; i < tagAttributePos; i++)
+ if (aname.equals (tagAttributes [i]))
+ error ("duplicate attribute", aname, null);
+
+ // Inform the handler about the
+ // attribute.
+ handler.attribute (aname, value, true);
+ dataBufferPos = 0;
+
+ // Note that the attribute has been
+ // specified.
+ if (tagAttributePos == tagAttributes.length) {
+ String newAttrib[] = new String [tagAttributes.length * 2];
+ System.arraycopy (tagAttributes, 0, newAttrib, 0, tagAttributePos);
+ tagAttributes = newAttrib;
+ }
+ tagAttributes [tagAttributePos++] = aname;
+ }
+
+
+ /**
+ * Parse an equals sign surrounded by optional whitespace.
+ * <pre>
+ * [25] Eq ::= S? '=' S?
+ * </pre>
+ */
+ private void parseEq ()
+ throws SAXException, IOException
+ {
+ skipWhitespace ();
+ require ('=');
+ skipWhitespace ();
+ }
+
+
+ /**
+ * Parse an end tag.
+ * <pre>
+ * [42] ETag ::= '</' Name S? '>'
+ * </pre>
+ * <p>NOTE: parseContent () chains to here, we already read the
+ * "&lt;/".
+ */
+ private void parseETag ()
+ throws Exception
+ {
+ require (currentElement);
+ skipWhitespace ();
+ require ('>');
+ handler.endElement (currentElement);
+ // not re-reporting any SAXException re bogus end tags,
+ // even though that diagnostic might be clearer ...
+ }
+
+
+ /**
+ * Parse the content of an element.
+ * <pre>
+ * [43] content ::= (element | CharData | Reference
+ * | CDSect | PI | Comment)*
+ * [67] Reference ::= EntityRef | CharRef
+ * </pre>
+ * <p> NOTE: consumes ETtag.
+ */
+ private void parseContent ()
+ throws Exception
+ {
+ char c;
+
+ while (true) {
+ // consume characters (or ignorable whitspace) until delimiter
+ parseCharData ();
+
+ // Handle delimiters
+ c = readCh ();
+ switch (c) {
+
+ case '&': // Found "&"
+ c = readCh ();
+ if (c == '#') {
+ parseCharRef ();
+ } else {
+ unread (c);
+ parseEntityRef (true);
+ }
+ isDirtyCurrentElement = true;
+ break;
+
+ case '<': // Found "<"
+ dataBufferFlush ();
+ c = readCh ();
+ switch (c) {
+ case '!': // Found "<!"
+ c = readCh ();
+ switch (c) {
+ case '-': // Found "<!-"
+ require ('-');
+ isDirtyCurrentElement = false;
+ parseComment ();
+ break;
+ case '[': // Found "<!["
+ isDirtyCurrentElement = false;
+ require ("CDATA[");
+ handler.startCDATA ();
+ inCDATA = true;
+ parseCDSect ();
+ inCDATA = false;
+ handler.endCDATA ();
+ break;
+ default:
+ error ("expected comment or CDATA section", c, null);
+ break;
+ }
+ break;
+
+ case '?': // Found "<?"
+ isDirtyCurrentElement = false;
+ parsePI ();
+ break;
+
+ case '/': // Found "</"
+ isDirtyCurrentElement = false;
+ parseETag ();
+ return;
+
+ default: // Found "<" followed by something else
+ isDirtyCurrentElement = false;
+ unread (c);
+ parseElement (false);
+ break;
+ }
+ }
+ }
+
+ }
+
+
+ /**
+ * Parse an element type declaration.
+ * <pre>
+ * [45] elementdecl ::= '&lt;!ELEMENT' S Name S contentspec S? '&gt;'
+ * </pre>
+ * <p> NOTE: the '&lt;!ELEMENT' has already been read.
+ */
+ private void parseElementDecl ()
+ throws Exception
+ {
+ String name;
+
+ requireWhitespace ();
+ // Read the element type name.
+ name = readNmtoken (true);
+
+ requireWhitespace ();
+ // Read the content model.
+ parseContentspec (name);
+
+ skipWhitespace ();
+ require ('>');
+ }
+
+
+ /**
+ * Content specification.
+ * <pre>
+ * [46] contentspec ::= 'EMPTY' | 'ANY' | Mixed | elements
+ * </pre>
+ */
+ private void parseContentspec (String name)
+ throws Exception
+ {
+// FIXME: move elementDecl() into setElement(), pass EMTPY/ANY ...
+ if (tryRead ("EMPTY")) {
+ setElement (name, CONTENT_EMPTY, null, null);
+ if (!skippedPE)
+ handler.getDeclHandler ().elementDecl (name, "EMPTY");
+ return;
+ } else if (tryRead ("ANY")) {
+ setElement (name, CONTENT_ANY, null, null);
+ if (!skippedPE)
+ handler.getDeclHandler ().elementDecl (name, "ANY");
+ return;
+ } else {
+ String model;
+ char saved [];
+
+ require ('(');
+ saved = readBuffer;
+ dataBufferAppend ('(');
+ skipWhitespace ();
+ if (tryRead ("#PCDATA")) {
+ dataBufferAppend ("#PCDATA");
+ parseMixed (saved);
+ model = dataBufferToString ();
+ setElement (name, CONTENT_MIXED, model, null);
+ } else {
+ parseElements (saved);
+ model = dataBufferToString ();
+ setElement (name, CONTENT_ELEMENTS, model, null);
+ }
+ if (!skippedPE)
+ handler.getDeclHandler ().elementDecl (name, model);
+ }
+ }
+
+ /**
+ * Parse an element-content model.
+ * <pre>
+ * [47] elements ::= (choice | seq) ('?' | '*' | '+')?
+ * [49] choice ::= '(' S? cp (S? '|' S? cp)+ S? ')'
+ * [50] seq ::= '(' S? cp (S? ',' S? cp)* S? ')'
+ * </pre>
+ *
+ * <p> NOTE: the opening '(' and S have already been read.
+ *
+ * @param saved Buffer for entity that should have the terminal ')'
+ */
+ private void parseElements (char saved [])
+ throws Exception
+ {
+ char c;
+ char sep;
+
+ // Parse the first content particle
+ skipWhitespace ();
+ parseCp ();
+
+ // Check for end or for a separator.
+ skipWhitespace ();
+ c = readCh ();
+ switch (c) {
+ case ')':
+ // VC: Proper Group/PE Nesting
+ if (readBuffer != saved)
+ handler.verror ("Illegal Group/PE nesting");
+
+ dataBufferAppend (')');
+ c = readCh ();
+ switch (c) {
+ case '*':
+ case '+':
+ case '?':
+ dataBufferAppend (c);
+ break;
+ default:
+ unread (c);
+ }
+ return;
+ case ',': // Register the separator.
+ case '|':
+ sep = c;
+ dataBufferAppend (c);
+ break;
+ default:
+ error ("bad separator in content model", c, null);
+ return;
+ }
+
+ // Parse the rest of the content model.
+ while (true) {
+ skipWhitespace ();
+ parseCp ();
+ skipWhitespace ();
+ c = readCh ();
+ if (c == ')') {
+ // VC: Proper Group/PE Nesting
+ if (readBuffer != saved)
+ handler.verror ("Illegal Group/PE nesting");
+
+ dataBufferAppend (')');
+ break;
+ } else if (c != sep) {
+ error ("bad separator in content model", c, null);
+ return;
+ } else {
+ dataBufferAppend (c);
+ }
+ }
+
+ // Check for the occurrence indicator.
+ c = readCh ();
+ switch (c) {
+ case '?':
+ case '*':
+ case '+':
+ dataBufferAppend (c);
+ return;
+ default:
+ unread (c);
+ return;
+ }
+ }
+
+
+ /**
+ * Parse a content particle.
+ * <pre>
+ * [48] cp ::= (Name | choice | seq) ('?' | '*' | '+')?
+ * </pre>
+ */
+ private void parseCp ()
+ throws Exception
+ {
+ if (tryRead ('(')) {
+ dataBufferAppend ('(');
+ parseElements (readBuffer);
+ } else {
+ dataBufferAppend (readNmtoken (true));
+ char c = readCh ();
+ switch (c) {
+ case '?':
+ case '*':
+ case '+':
+ dataBufferAppend (c);
+ break;
+ default:
+ unread (c);
+ break;
+ }
+ }
+ }
+
+
+ /**
+ * Parse mixed content.
+ * <pre>
+ * [51] Mixed ::= '(' S? ( '#PCDATA' (S? '|' S? Name)*) S? ')*'
+ * | '(' S? ('#PCDATA') S? ')'
+ * </pre>
+ *
+ * @param saved Buffer for entity that should have the terminal ')'
+ */
+ private void parseMixed (char saved [])
+ throws Exception
+ {
+ // Check for PCDATA alone.
+ skipWhitespace ();
+ if (tryRead (')')) {
+ // VC: Proper Group/PE Nesting
+ if (readBuffer != saved)
+ handler.verror ("Illegal Group/PE nesting");
+
+ dataBufferAppend (")*");
+ tryRead ('*');
+ return;
+ }
+
+ // Parse mixed content.
+ skipWhitespace ();
+ while (!tryRead (")")) {
+ require ('|');
+ dataBufferAppend ('|');
+ skipWhitespace ();
+ dataBufferAppend (readNmtoken (true));
+ skipWhitespace ();
+ }
+
+ // VC: Proper Group/PE Nesting
+ if (readBuffer != saved)
+ handler.verror ("Illegal Group/PE nesting");
+
+ require ('*');
+ dataBufferAppend (")*");
+ }
+
+
+ /**
+ * Parse an attribute list declaration.
+ * <pre>
+ * [52] AttlistDecl ::= '&lt;!ATTLIST' S Name AttDef* S? '&gt;'
+ * </pre>
+ * <p>NOTE: the '&lt;!ATTLIST' has already been read.
+ */
+ private void parseAttlistDecl ()
+ throws Exception
+ {
+ String elementName;
+
+ requireWhitespace ();
+ elementName = readNmtoken (true);
+ boolean white = tryWhitespace ();
+ while (!tryRead ('>')) {
+ if (!white)
+ error ("whitespace required before attribute definition");
+ parseAttDef (elementName);
+ white = tryWhitespace ();
+ }
+ }
+
+
+ /**
+ * Parse a single attribute definition.
+ * <pre>
+ * [53] AttDef ::= S Name S AttType S DefaultDecl
+ * </pre>
+ */
+ private void parseAttDef (String elementName)
+ throws Exception
+ {
+ String name;
+ String type;
+ String enumer = null;
+
+ // Read the attribute name.
+ name = readNmtoken (true);
+
+ // Read the attribute type.
+ requireWhitespace ();
+ type = readAttType ();
+
+ // Get the string of enumerated values if necessary.
+ if (handler.getFeature (SAXDriver.FEATURE + "string-interning")) {
+ if ("ENUMERATION" == type || "NOTATION" == type)
+ enumer = dataBufferToString ();
+ } else {
+ if ("ENUMERATION".equals(type) || "NOTATION".equals(type))
+ enumer = dataBufferToString ();
+ }
+
+ // Read the default value.
+ requireWhitespace ();
+ parseDefault (elementName, name, type, enumer);
+ }
+
+
+ /**
+ * Parse the attribute type.
+ * <pre>
+ * [54] AttType ::= StringType | TokenizedType | EnumeratedType
+ * [55] StringType ::= 'CDATA'
+ * [56] TokenizedType ::= 'ID' | 'IDREF' | 'IDREFS' | 'ENTITY'
+ * | 'ENTITIES' | 'NMTOKEN' | 'NMTOKENS'
+ * [57] EnumeratedType ::= NotationType | Enumeration
+ * </pre>
+ */
+ private String readAttType ()
+ throws Exception
+ {
+ if (tryRead ('(')) {
+ parseEnumeration (false);
+ return "ENUMERATION";
+ } else {
+ String typeString = readNmtoken (true);
+ if (handler.getFeature (SAXDriver.FEATURE + "string-interning")) {
+ if ("NOTATION" == typeString) {
+ parseNotationType ();
+ return typeString;
+ } else if ("CDATA" == typeString
+ || "ID" == typeString
+ || "IDREF" == typeString
+ || "IDREFS" == typeString
+ || "ENTITY" == typeString
+ || "ENTITIES" == typeString
+ || "NMTOKEN" == typeString
+ || "NMTOKENS" == typeString)
+ return typeString;
+ } else {
+ if ("NOTATION".equals(typeString)) {
+ parseNotationType ();
+ return typeString;
+ } else if ("CDATA".equals(typeString)
+ || "ID".equals(typeString)
+ || "IDREF".equals(typeString)
+ || "IDREFS".equals(typeString)
+ || "ENTITY".equals(typeString)
+ || "ENTITIES".equals(typeString)
+ || "NMTOKEN".equals(typeString)
+ || "NMTOKENS".equals(typeString))
+ return typeString;
+ }
+ error ("illegal attribute type", typeString, null);
+ return null;
+ }
+ }
+
+
+ /**
+ * Parse an enumeration.
+ * <pre>
+ * [59] Enumeration ::= '(' S? Nmtoken (S? '|' S? Nmtoken)* S? ')'
+ * </pre>
+ * <p>NOTE: the '(' has already been read.
+ */
+ private void parseEnumeration (boolean isNames)
+ throws Exception
+ {
+ dataBufferAppend ('(');
+
+ // Read the first token.
+ skipWhitespace ();
+ dataBufferAppend (readNmtoken (isNames));
+ // Read the remaining tokens.
+ skipWhitespace ();
+ while (!tryRead (')')) {
+ require ('|');
+ dataBufferAppend ('|');
+ skipWhitespace ();
+ dataBufferAppend (readNmtoken (isNames));
+ skipWhitespace ();
+ }
+ dataBufferAppend (')');
+ }
+
+
+ /**
+ * Parse a notation type for an attribute.
+ * <pre>
+ * [58] NotationType ::= 'NOTATION' S '(' S? NameNtoks
+ * (S? '|' S? name)* S? ')'
+ * </pre>
+ * <p>NOTE: the 'NOTATION' has already been read
+ */
+ private void parseNotationType ()
+ throws Exception
+ {
+ requireWhitespace ();
+ require ('(');
+
+ parseEnumeration (true);
+ }
+
+
+ /**
+ * Parse the default value for an attribute.
+ * <pre>
+ * [60] DefaultDecl ::= '#REQUIRED' | '#IMPLIED'
+ * | (('#FIXED' S)? AttValue)
+ * </pre>
+ */
+ private void parseDefault (
+ String elementName,
+ String name,
+ String type,
+ String enumer
+ ) throws Exception
+ {
+ int valueType = ATTRIBUTE_DEFAULT_SPECIFIED;
+ String value = null;
+ int flags = LIT_ATTRIBUTE;
+ boolean saved = expandPE;
+ String defaultType = null;
+
+ // LIT_ATTRIBUTE forces '<' checks now (ASAP) and turns whitespace
+ // chars to spaces (doesn't matter when that's done if it doesn't
+ // interfere with char refs expanding to whitespace).
+
+ if (!skippedPE) {
+ flags |= LIT_ENTITY_REF;
+ if (handler.getFeature (SAXDriver.FEATURE + "string-interning")) {
+ if ("CDATA" != type)
+ flags |= LIT_NORMALIZE;
+ } else {
+ if (!"CDATA".equals(type))
+ flags |= LIT_NORMALIZE;
+ }
+ }
+
+ expandPE = false;
+ if (tryRead ('#')) {
+ if (tryRead ("FIXED")) {
+ defaultType = "#FIXED";
+ valueType = ATTRIBUTE_DEFAULT_FIXED;
+ requireWhitespace ();
+ value = readLiteral (flags);
+ } else if (tryRead ("REQUIRED")) {
+ defaultType = "#REQUIRED";
+ valueType = ATTRIBUTE_DEFAULT_REQUIRED;
+ } else if (tryRead ("IMPLIED")) {
+ defaultType = "#IMPLIED";
+ valueType = ATTRIBUTE_DEFAULT_IMPLIED;
+ } else {
+ error ("illegal keyword for attribute default value");
+ }
+ } else
+ value = readLiteral (flags);
+ expandPE = saved;
+ setAttribute (elementName, name, type, enumer, value, valueType);
+ if (handler.getFeature (SAXDriver.FEATURE + "string-interning")) {
+ if ("ENUMERATION" == type)
+ type = enumer;
+ else if ("NOTATION" == type)
+ type = "NOTATION " + enumer;
+ } else {
+ if ("ENUMERATION".equals(type))
+ type = enumer;
+ else if ("NOTATION".equals(type))
+ type = "NOTATION " + enumer;
+ }
+ if (!skippedPE) handler.getDeclHandler ()
+ .attributeDecl (elementName, name, type, defaultType, value);
+ }
+
+
+ /**
+ * Parse a conditional section.
+ * <pre>
+ * [61] conditionalSect ::= includeSect || ignoreSect
+ * [62] includeSect ::= '&lt;![' S? 'INCLUDE' S? '['
+ * extSubsetDecl ']]&gt;'
+ * [63] ignoreSect ::= '&lt;![' S? 'IGNORE' S? '['
+ * ignoreSectContents* ']]&gt;'
+ * [64] ignoreSectContents ::= Ignore
+ * ('&lt;![' ignoreSectContents* ']]&gt;' Ignore )*
+ * [65] Ignore ::= Char* - (Char* ( '&lt;![' | ']]&gt;') Char* )
+ * </pre>
+ * <p> NOTE: the '&gt;![' has already been read.
+ */
+ private void parseConditionalSect (char saved [])
+ throws Exception
+ {
+ skipWhitespace ();
+ if (tryRead ("INCLUDE")) {
+ skipWhitespace ();
+ require ('[');
+ // VC: Proper Conditional Section/PE Nesting
+ if (readBuffer != saved)
+ handler.verror ("Illegal Conditional Section/PE nesting");
+ skipWhitespace ();
+ while (!tryRead ("]]>")) {
+ parseMarkupdecl ();
+ skipWhitespace ();
+ }
+ } else if (tryRead ("IGNORE")) {
+ skipWhitespace ();
+ require ('[');
+ // VC: Proper Conditional Section/PE Nesting
+ if (readBuffer != saved)
+ handler.verror ("Illegal Conditional Section/PE nesting");
+ int nesting = 1;
+ char c;
+ expandPE = false;
+ for (int nest = 1; nest > 0;) {
+ c = readCh ();
+ switch (c) {
+ case '<':
+ if (tryRead ("![")) {
+ nest++;
+ }
+ case ']':
+ if (tryRead ("]>")) {
+ nest--;
+ }
+ }
+ }
+ expandPE = true;
+ } else {
+ error ("conditional section must begin with INCLUDE or IGNORE");
+ }
+ }
+
+ private void parseCharRef ()
+ throws SAXException, IOException
+ {
+ parseCharRef (true /* do flushDataBuffer by default */);
+ }
+
+ /**
+ * Try to read a character reference without consuming data from buffer.
+ * <pre>
+ * [66] CharRef ::= '&#' [0-9]+ ';' | '&#x' [0-9a-fA-F]+ ';'
+ * </pre>
+ * <p>NOTE: the '&#' has already been read.
+ */
+ private void tryReadCharRef ()
+ throws SAXException, IOException
+ {
+ int value = 0;
+ char c;
+
+ if (tryRead ('x')) {
+loop1:
+ while (true) {
+ c = readCh ();
+ int n;
+ switch (c) {
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ n = c - '0';
+ break;
+ case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
+ n = (c - 'a') + 10;
+ break;
+ case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
+ n = (c - 'A') + 10;
+ break;
+ case ';':
+ break loop1;
+ default:
+ error ("illegal character in character reference", c, null);
+ break loop1;
+ }
+ value *= 16;
+ value += n;
+ }
+ } else {
+loop2:
+ while (true) {
+ c = readCh ();
+ switch (c) {
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ value *= 10;
+ value += c - '0';
+ break;
+ case ';':
+ break loop2;
+ default:
+ error ("illegal character in character reference", c, null);
+ break loop2;
+ }
+ }
+ }
+
+ // check for character refs being legal XML
+ if ((value < 0x0020
+ && ! (value == '\n' || value == '\t' || value == '\r'))
+ || (value >= 0xD800 && value <= 0xDFFF)
+ || value == 0xFFFE || value == 0xFFFF
+ || value > 0x0010ffff)
+ error ("illegal XML character reference U+"
+ + Integer.toHexString (value));
+
+ // Check for surrogates: 00000000 0000xxxx yyyyyyyy zzzzzzzz
+ // (1101|10xx|xxyy|yyyy + 1101|11yy|zzzz|zzzz:
+ if (value > 0x0010ffff) {
+ // too big for surrogate
+ error ("character reference " + value + " is too large for UTF-16",
+ new Integer (value).toString (), null);
+ }
+
+ }
+
+ /**
+ * Read and interpret a character reference.
+ * <pre>
+ * [66] CharRef ::= '&#' [0-9]+ ';' | '&#x' [0-9a-fA-F]+ ';'
+ * </pre>
+ * <p>NOTE: the '&#' has already been read.
+ */
+ private void parseCharRef (boolean doFlush)
+ throws SAXException, IOException
+ {
+ int value = 0;
+ char c;
+
+ if (tryRead ('x')) {
+loop1:
+ while (true) {
+ c = readCh ();
+ int n;
+ switch (c) {
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ n = c - '0';
+ break;
+ case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
+ n = (c - 'a') + 10;
+ break;
+ case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
+ n = (c - 'A') + 10;
+ break;
+ case ';':
+ break loop1;
+ default:
+ error ("illegal character in character reference", c, null);
+ break loop1;
+ }
+ value *= 16;
+ value += n;
+ }
+ } else {
+loop2:
+ while (true) {
+ c = readCh ();
+ switch (c) {
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ value *= 10;
+ value += c - '0';
+ break;
+ case ';':
+ break loop2;
+ default:
+ error ("illegal character in character reference", c, null);
+ break loop2;
+ }
+ }
+ }
+
+ // check for character refs being legal XML
+ if ((value < 0x0020
+ && ! (value == '\n' || value == '\t' || value == '\r'))
+ || (value >= 0xD800 && value <= 0xDFFF)
+ || value == 0xFFFE || value == 0xFFFF
+ || value > 0x0010ffff)
+ error ("illegal XML character reference U+"
+ + Integer.toHexString (value));
+
+ // Check for surrogates: 00000000 0000xxxx yyyyyyyy zzzzzzzz
+ // (1101|10xx|xxyy|yyyy + 1101|11yy|zzzz|zzzz:
+ if (value <= 0x0000ffff) {
+ // no surrogates needed
+ dataBufferAppend ((char) value);
+ } else if (value <= 0x0010ffff) {
+ value -= 0x10000;
+ // > 16 bits, surrogate needed
+ dataBufferAppend ((char) (0xd800 | (value >> 10)));
+ dataBufferAppend ((char) (0xdc00 | (value & 0x0003ff)));
+ } else {
+ // too big for surrogate
+ error ("character reference " + value + " is too large for UTF-16",
+ new Integer (value).toString (), null);
+ }
+ if (doFlush) dataBufferFlush ();
+ }
+
+
+ /**
+ * Parse and expand an entity reference.
+ * <pre>
+ * [68] EntityRef ::= '&' Name ';'
+ * </pre>
+ * <p>NOTE: the '&amp;' has already been read.
+ * @param externalAllowed External entities are allowed here.
+ */
+ private void parseEntityRef (boolean externalAllowed)
+ throws SAXException, IOException
+ {
+ String name;
+
+ name = readNmtoken (true);
+ require (';');
+ switch (getEntityType (name)) {
+ case ENTITY_UNDECLARED:
+ // NOTE: XML REC describes amazingly convoluted handling for
+ // this case. Nothing as meaningful as being a WFness error
+ // unless the processor might _legitimately_ not have seen a
+ // declaration ... which is what this implements.
+ String message;
+
+ message = "reference to undeclared general entity " + name;
+ if (skippedPE && !docIsStandalone) {
+ handler.verror (message);
+ // we don't know this entity, and it might be external...
+ if (externalAllowed)
+ handler.skippedEntity (name);
+ } else
+ error (message);
+ break;
+ case ENTITY_INTERNAL:
+ pushString (name, getEntityValue (name));
+
+ //workaround for possible input pop before marking
+ //the buffer reading position
+ char t = readCh ();
+ unread (t);
+ int bufferPosMark = readBufferPos;
+
+ int end = readBufferPos + getEntityValue (name).length();
+ for(int k = readBufferPos; k < end; k++){
+ t = readCh ();
+ if (t == '&'){
+ t = readCh ();
+ if (t == '#'){
+ //try to match a character ref
+ tryReadCharRef ();
+
+ //everything has been read
+ if (readBufferPos >= end)
+ break;
+ k = readBufferPos;
+ continue;
+ }
+ else if (Character.isLetter(t)){
+ //looks like an entity ref
+ unread (t);
+ readNmtoken (true);
+ require (';');
+
+ //everything has been read
+ if (readBufferPos >= end)
+ break;
+ k = readBufferPos;
+ continue;
+ }
+ error(" malformed entity reference");
+ }
+
+ }
+ readBufferPos = bufferPosMark;
+ break;
+ case ENTITY_TEXT:
+ if (externalAllowed) {
+ pushURL (false, name, getEntityIds (name),
+ null, null, null, true);
+ } else {
+ error ("reference to external entity in attribute value.",
+ name, null);
+ }
+ break;
+ case ENTITY_NDATA:
+ if (externalAllowed) {
+ error ("unparsed entity reference in content", name, null);
+ } else {
+ error ("reference to external entity in attribute value.",
+ name, null);
+ }
+ break;
+ default:
+ throw new RuntimeException ();
+ }
+ }
+
+
+ /**
+ * Parse and expand a parameter entity reference.
+ * <pre>
+ * [69] PEReference ::= '%' Name ';'
+ * </pre>
+ * <p>NOTE: the '%' has already been read.
+ */
+ private void parsePEReference ()
+ throws SAXException, IOException
+ {
+ String name;
+
+ name = "%" + readNmtoken (true);
+ require (';');
+ switch (getEntityType (name)) {
+ case ENTITY_UNDECLARED:
+ // VC: Entity Declared
+ handler.verror ("reference to undeclared parameter entity " + name);
+
+ // we should disable handling of all subsequent declarations
+ // unless this is a standalone document (info discarded)
+ break;
+ case ENTITY_INTERNAL:
+ if (inLiteral)
+ pushString (name, getEntityValue (name));
+ else
+ pushString (name, ' ' + getEntityValue (name) + ' ');
+ break;
+ case ENTITY_TEXT:
+ if (!inLiteral)
+ pushString (null, " ");
+ pushURL (true, name, getEntityIds (name), null, null, null, true);
+ if (!inLiteral)
+ pushString (null, " ");
+ break;
+ }
+ }
+
+ /**
+ * Parse an entity declaration.
+ * <pre>
+ * [70] EntityDecl ::= GEDecl | PEDecl
+ * [71] GEDecl ::= '&lt;!ENTITY' S Name S EntityDef S? '&gt;'
+ * [72] PEDecl ::= '&lt;!ENTITY' S '%' S Name S PEDef S? '&gt;'
+ * [73] EntityDef ::= EntityValue | (ExternalID NDataDecl?)
+ * [74] PEDef ::= EntityValue | ExternalID
+ * [75] ExternalID ::= 'SYSTEM' S SystemLiteral
+ * | 'PUBLIC' S PubidLiteral S SystemLiteral
+ * [76] NDataDecl ::= S 'NDATA' S Name
+ * </pre>
+ * <p>NOTE: the '&lt;!ENTITY' has already been read.
+ */
+ private void parseEntityDecl ()
+ throws Exception
+ {
+ boolean peFlag = false;
+ int flags = 0;
+
+ // Check for a parameter entity.
+ expandPE = false;
+ requireWhitespace ();
+ if (tryRead ('%')) {
+ peFlag = true;
+ requireWhitespace ();
+ }
+ expandPE = true;
+
+ // Read the entity name, and prepend
+ // '%' if necessary.
+ String name = readNmtoken (true);
+ //NE08
+ if (name.indexOf(':') >= 0)
+ error ("Illegal character(':') in entity name ", name, null);
+ if (peFlag) {
+ name = "%" + name;
+ }
+
+ // Read the entity value.
+ requireWhitespace ();
+ char c = readCh ();
+ unread (c);
+ if (c == '"' || c == '\'') {
+ // Internal entity ... replacement text has expanded refs
+ // to characters and PEs, but not to general entities
+ String value = readLiteral (flags);
+ setInternalEntity (name, value);
+ } else {
+ // Read the external IDs
+ String ids [] = readExternalIds (false, false);
+
+ // Check for NDATA declaration.
+ boolean white = tryWhitespace ();
+ if (!peFlag && tryRead ("NDATA")) {
+ if (!white)
+ error ("whitespace required before NDATA");
+ requireWhitespace ();
+ String notationName = readNmtoken (true);
+ if (!skippedPE) {
+ setExternalEntity (name, ENTITY_NDATA, ids, notationName);
+ handler.unparsedEntityDecl (name, ids, notationName);
+ }
+ } else if (!skippedPE) {
+ setExternalEntity (name, ENTITY_TEXT, ids, null);
+ handler.getDeclHandler ()
+ .externalEntityDecl (name, ids [0],
+ handler.resolveURIs ()
+ // FIXME: ASSUMES not skipped
+ // "false" forces error on bad URI
+ ? handler.absolutize (ids [2], ids [1], false)
+ : ids [1]);
+ }
+ }
+
+ // Finish the declaration.
+ skipWhitespace ();
+ require ('>');
+ }
+
+
+ /**
+ * Parse a notation declaration.
+ * <pre>
+ * [82] NotationDecl ::= '&lt;!NOTATION' S Name S
+ * (ExternalID | PublicID) S? '&gt;'
+ * [83] PublicID ::= 'PUBLIC' S PubidLiteral
+ * </pre>
+ * <P>NOTE: the '&lt;!NOTATION' has already been read.
+ */
+ private void parseNotationDecl ()
+ throws Exception
+ {
+ String nname, ids[];
+
+
+ requireWhitespace ();
+ nname = readNmtoken (true);
+ //NE08
+ if (nname.indexOf(':') >= 0)
+ error ("Illegal character(':') in notation name ", nname, null);
+ requireWhitespace ();
+
+ // Read the external identifiers.
+ ids = readExternalIds (true, false);
+
+ // Register the notation.
+ setNotation (nname, ids);
+
+ skipWhitespace ();
+ require ('>');
+ }
+
+
+ /**
+ * Parse character data.
+ * <pre>
+ * [14] CharData ::= [^&lt;&amp;]* - ([^&lt;&amp;]* ']]&gt;' [^&lt;&amp;]*)
+ * </pre>
+ */
+ private void parseCharData ()
+ throws Exception
+ {
+ char c;
+ int state = 0;
+ boolean pureWhite = false;
+
+ // assert (dataBufferPos == 0);
+
+ // are we expecting pure whitespace? it might be dirty...
+ if ((currentElementContent == CONTENT_ELEMENTS) && !isDirtyCurrentElement)
+ pureWhite = true;
+
+ // always report right out of readBuffer
+ // to minimize (pointless) buffer copies
+ while (true) {
+ int lineAugment = 0;
+ int columnAugment = 0;
+ int i;
+
+loop:
+ for (i = readBufferPos; i < readBufferLength; i++) {
+ switch (c = readBuffer [i]) {
+ case '\n':
+ lineAugment++;
+ columnAugment = 0;
+ // pureWhite unmodified
+ break;
+ case '\r': // should not happen!!
+ case '\t':
+ case ' ':
+ // pureWhite unmodified
+ columnAugment++;
+ break;
+ case '&':
+ case '<':
+ columnAugment++;
+ // pureWhite unmodified
+ // CLEAN end of text sequence
+ state = 1;
+ break loop;
+ case ']':
+ // that's not a whitespace char, and
+ // can not terminate pure whitespace either
+ pureWhite = false;
+ if ((i + 2) < readBufferLength) {
+ if (readBuffer [i + 1] == ']'
+ && readBuffer [i + 2] == '>') {
+ // ERROR end of text sequence
+ state = 2;
+ break loop;
+ }
+ } else {
+ // FIXME missing two end-of-buffer cases
+ }
+ columnAugment++;
+ break;
+ default:
+ if ((c < 0x0020 || c > 0xFFFD)
+ || ((c >= 0x007f) && (c <= 0x009f) && (c != 0x0085)
+ && xmlVersion == XML_11))
+ error ("illegal XML character U+"
+ + Integer.toHexString (c));
+ // that's not a whitespace char
+ pureWhite = false;
+ columnAugment++;
+ }
+ }
+
+ // report text thus far
+ if (lineAugment > 0) {
+ line += lineAugment;
+ column = columnAugment;
+ } else {
+ column += columnAugment;
+ }
+
+ // report characters/whitspace
+ int length = i - readBufferPos;
+
+ if (length != 0) {
+ if (pureWhite)
+ handler.ignorableWhitespace (readBuffer,
+ readBufferPos, length);
+ else
+ handler.charData (readBuffer, readBufferPos, length);
+ readBufferPos = i;
+ }
+
+ if (state != 0)
+ break;
+
+ // fill next buffer from this entity, or
+ // pop stack and continue with previous entity
+ unread (readCh ());
+ }
+ if (!pureWhite)
+ isDirtyCurrentElement = true;
+ // finish, maybe with error
+ if (state != 1) // finish, no error
+ error ("character data may not contain ']]>'");
+ }
+
+
+ //////////////////////////////////////////////////////////////////////
+ // High-level reading and scanning methods.
+ //////////////////////////////////////////////////////////////////////
+
+ /**
+ * Require whitespace characters.
+ */
+ private void requireWhitespace ()
+ throws SAXException, IOException
+ {
+ char c = readCh ();
+ if (isWhitespace (c)) {
+ skipWhitespace ();
+ } else {
+ error ("whitespace required", c, null);
+ }
+ }
+
+
+ /**
+ * Skip whitespace characters.
+ * <pre>
+ * [3] S ::= (#x20 | #x9 | #xd | #xa)+
+ * </pre>
+ */
+ private void skipWhitespace ()
+ throws SAXException, IOException
+ {
+ // Start with a little cheat. Most of
+ // the time, the white space will fall
+ // within the current read buffer; if
+ // not, then fall through.
+ if (USE_CHEATS) {
+ int lineAugment = 0;
+ int columnAugment = 0;
+
+loop:
+ for (int i = readBufferPos; i < readBufferLength; i++) {
+ switch (readBuffer [i]) {
+ case ' ':
+ case '\t':
+ case '\r':
+ columnAugment++;
+ break;
+ case '\n':
+ lineAugment++;
+ columnAugment = 0;
+ break;
+ case '%':
+ if (expandPE)
+ break loop;
+ // else fall through...
+ default:
+ readBufferPos = i;
+ if (lineAugment > 0) {
+ line += lineAugment;
+ column = columnAugment;
+ } else {
+ column += columnAugment;
+ }
+ return;
+ }
+ }
+ }
+
+ // OK, do it the slow way.
+ char c = readCh ();
+ while (isWhitespace (c)) {
+ c = readCh ();
+ }
+ unread (c);
+ }
+
+
+ /**
+ * Read a name or (when parsing an enumeration) name token.
+ * <pre>
+ * [5] Name ::= (Letter | '_' | ':') (NameChar)*
+ * [7] Nmtoken ::= (NameChar)+
+ * </pre>
+ */
+ private String readNmtoken (boolean isName)
+ throws SAXException, IOException
+ {
+ char c;
+
+ if (USE_CHEATS) {
+loop:
+ for (int i = readBufferPos; i < readBufferLength; i++) {
+ c = readBuffer [i];
+ switch (c) {
+ case '%':
+ if (expandPE)
+ break loop;
+ // else fall through...
+
+ // What may legitimately come AFTER a name/nmtoken?
+ case '<': case '>': case '&':
+ case ',': case '|': case '*': case '+': case '?':
+ case ')':
+ case '=':
+ case '\'': case '"':
+ case '[':
+ case ' ': case '\t': case '\r': case '\n':
+ case ';':
+ case '/':
+ int start = readBufferPos;
+ if (i == start)
+ error ("name expected", readBuffer [i], null);
+ readBufferPos = i;
+ return intern (readBuffer, start, i - start);
+
+ default:
+// FIXME ... per IBM's OASIS test submission, these:
+// ? U+06dd
+// Combining U+309B
+ //these switches are kind of ugly but at least we won't
+ //have to go over the whole lits for each char
+ if (isName && i == readBufferPos){
+ char c2 = (char) (c & 0x00f0);
+ switch (c & 0xff00){
+ //starting with 01
+ case 0x0100:
+ switch (c2){
+ case 0x0030:
+ if (c == 0x0132 || c == 0x0133 || c == 0x013f)
+ error ("Not a name start character, U+"
+ + Integer.toHexString (c));
+ break;
+ case 0x0040:
+ if (c == 0x0140 || c == 0x0149)
+ error ("Not a name start character, U+"
+ + Integer.toHexString (c));
+ break;
+ case 0x00c0:
+ if (c == 0x01c4 || c == 0x01cc)
+ error ("Not a name start character, U+"
+ + Integer.toHexString (c));
+ break;
+ case 0x00f0:
+ if (c == 0x01f1 || c == 0x01f3)
+ error ("Not a name start character, U+"
+ + Integer.toHexString (c));
+ break;
+ case 0x00b0:
+ if (c == 0x01f1 || c == 0x01f3)
+ error ("Not a name start character, U+"
+ + Integer.toHexString (c));
+ break;
+ default:
+ if (c == 0x017f)
+ error ("Not a name start character, U+"
+ + Integer.toHexString (c));
+ }
+
+ break;
+ //starting with 11
+ case 0x1100:
+ switch (c2){
+ case 0x0000:
+ if (c == 0x1104 || c == 0x1108 ||
+ c == 0x110a || c == 0x110d)
+ error ("Not a name start character, U+"
+ + Integer.toHexString (c));
+ break;
+ case 0x0030:
+ if (c == 0x113b || c == 0x113f)
+ error ("Not a name start character, U+"
+ + Integer.toHexString (c));
+ break;
+ case 0x0040:
+ if (c == 0x1141 || c == 0x114d
+ || c == 0x114f )
+ error ("Not a name start character, U+"
+ + Integer.toHexString (c));
+ break;
+ case 0x0050:
+ if (c == 0x1151 || c == 0x1156)
+ error ("Not a name start character, U+"
+ + Integer.toHexString (c));
+ break;
+ case 0x0060:
+ if (c == 0x1162 || c == 0x1164
+ || c == 0x1166 || c == 0x116b
+ || c == 0x116f)
+ error ("Not a name start character, U+"
+ + Integer.toHexString (c));
+ break;
+ case 0x00b0:
+ if (c == 0x11b6 || c == 0x11b9
+ || c == 0x11bb || c == 0x116f)
+ error ("Not a name start character, U+"
+ + Integer.toHexString (c));
+ break;
+ default:
+ if (c == 0x1174 || c == 0x119f
+ || c == 0x11ac || c == 0x11c3
+ || c == 0x11f1)
+ error ("Not a name start character, U+"
+ + Integer.toHexString (c));
+ }
+ break;
+ default:
+ if (c == 0x0e46 || c == 0x1011
+ || c == 0x212f || c == 0x0587
+ || c == 0x0230 )
+ error ("Not a name start character, U+"
+ + Integer.toHexString (c));
+ }
+ }
+ // punt on exact tests from Appendix A; approximate
+ // them using the Unicode ID start/part rules
+ if (i == readBufferPos && isName) {
+ if (!Character.isUnicodeIdentifierStart (c)
+ && c != ':' && c != '_')
+ error ("Not a name start character, U+"
+ + Integer.toHexString (c));
+ } else if (!Character.isUnicodeIdentifierPart (c)
+ && c != '-' && c != ':' && c != '_' && c != '.'
+ && !isExtender (c))
+ error ("Not a name character, U+"
+ + Integer.toHexString (c));
+ }
+ }
+ }
+
+ nameBufferPos = 0;
+
+ // Read the first character.
+loop:
+ while (true) {
+ c = readCh ();
+ switch (c) {
+ case '%':
+ case '<': case '>': case '&':
+ case ',': case '|': case '*': case '+': case '?':
+ case ')':
+ case '=':
+ case '\'': case '"':
+ case '[':
+ case ' ': case '\t': case '\n': case '\r':
+ case ';':
+ case '/':
+ unread (c);
+ if (nameBufferPos == 0) {
+ error ("name expected");
+ }
+ // punt on exact tests from Appendix A, but approximate them
+ if (isName
+ && !Character.isUnicodeIdentifierStart (
+ nameBuffer [0])
+ && ":_".indexOf (nameBuffer [0]) == -1)
+ error ("Not a name start character, U+"
+ + Integer.toHexString (nameBuffer [0]));
+ String s = intern (nameBuffer, 0, nameBufferPos);
+ nameBufferPos = 0;
+ return s;
+ default:
+ // punt on exact tests from Appendix A, but approximate them
+
+ if ((nameBufferPos != 0 || !isName)
+ && !Character.isUnicodeIdentifierPart (c)
+ && ":-_.".indexOf (c) == -1
+ && !isExtender (c))
+ error ("Not a name character, U+"
+ + Integer.toHexString (c));
+ if (nameBufferPos >= nameBuffer.length)
+ nameBuffer =
+ (char[]) extendArray (nameBuffer,
+ nameBuffer.length, nameBufferPos);
+ nameBuffer [nameBufferPos++] = c;
+ }
+ }
+ }
+
+ private static boolean isExtender (char c)
+ {
+ // [88] Extender ::= ...
+ return c == 0x00b7 || c == 0x02d0 || c == 0x02d1 || c == 0x0387
+ || c == 0x0640 || c == 0x0e46 || c == 0x0ec6 || c == 0x3005
+ || (c >= 0x3031 && c <= 0x3035)
+ || (c >= 0x309d && c <= 0x309e)
+ || (c >= 0x30fc && c <= 0x30fe);
+ }
+
+
+ /**
+ * Read a literal. With matching single or double quotes as
+ * delimiters (and not embedded!) this is used to parse:
+ * <pre>
+ * [9] EntityValue ::= ... ([^%&amp;] | PEReference | Reference)* ...
+ * [10] AttValue ::= ... ([^<&] | Reference)* ...
+ * [11] SystemLiteral ::= ... (URLchar - "'")* ...
+ * [12] PubidLiteral ::= ... (PubidChar - "'")* ...
+ * </pre>
+ * as well as the quoted strings in XML and text declarations
+ * (for version, encoding, and standalone) which have their
+ * own constraints.
+ */
+ private String readLiteral (int flags)
+ throws SAXException, IOException
+ {
+ char delim, c;
+ int startLine = line;
+ boolean saved = expandPE;
+ boolean savedReport = doReport;
+
+ // Find the first delimiter.
+ delim = readCh ();
+ if (delim != '"' && delim != '\'') {
+ error ("expected '\"' or \"'\"", delim, null);
+ return null;
+ }
+ inLiteral = true;
+ if ((flags & LIT_DISABLE_PE) != 0)
+ expandPE = false;
+ doReport = false;
+
+ // Each level of input source has its own buffer; remember
+ // ours, so we won't read the ending delimiter from any
+ // other input source, regardless of entity processing.
+ char ourBuf [] = readBuffer;
+
+ // Read the literal.
+ try {
+ c = readCh ();
+ boolean ampRead = false;
+loop:
+ while (! (c == delim && readBuffer == ourBuf)) {
+ switch (c) {
+ // attributes and public ids are normalized
+ // in almost the same ways
+ case '\n':
+ case '\r':
+ if ((flags & (LIT_ATTRIBUTE | LIT_PUBID)) != 0)
+ c = ' ';
+ break;
+ case '\t':
+ if ((flags & LIT_ATTRIBUTE) != 0)
+ c = ' ';
+ break;
+ case '&':
+ c = readCh ();
+ // Char refs are expanded immediately, except for
+ // all the cases where it's deferred.
+ if (c == '#') {
+ if ((flags & LIT_DISABLE_CREF) != 0) {
+ dataBufferAppend ('&');
+ break;
+ }
+ parseCharRef (false /* Do not do flushDataBuffer */);
+
+ // exotic WFness risk: this is an entity literal,
+ // dataBuffer [dataBufferPos - 1] == '&', and
+ // following chars are a _partial_ entity/char ref
+
+ // It looks like an entity ref ...
+ } else {
+ unread (c);
+ // Expand it?
+ if ((flags & LIT_ENTITY_REF) > 0) {
+ parseEntityRef (false);
+ if (String.valueOf (readBuffer).equals("&#38;"))
+ ampRead = true;
+ //Is it just data?
+ } else if ((flags & LIT_DISABLE_EREF) != 0) {
+ dataBufferAppend ('&');
+
+ // OK, it will be an entity ref -- expanded later.
+ } else {
+ String name = readNmtoken (true);
+ require (';');
+ dataBufferAppend ('&');
+ dataBufferAppend (name);
+ dataBufferAppend (';');
+ }
+ }
+ c = readCh ();
+ continue loop;
+
+ case '<':
+ // and why? Perhaps so "&foo;" expands the same
+ // inside and outside an attribute?
+ if ((flags & LIT_ATTRIBUTE) != 0)
+ error ("attribute values may not contain '<'");
+ break;
+
+ // We don't worry about case '%' and PE refs, readCh does.
+
+ default:
+ break;
+ }
+ dataBufferAppend (c);
+ c = readCh ();
+ }
+ } catch (EOFException e) {
+ error ("end of input while looking for delimiter (started on line "
+ + startLine + ')', null, new Character (delim).toString ());
+ }
+ inLiteral = false;
+ expandPE = saved;
+ doReport = savedReport;
+
+ // Normalise whitespace if necessary.
+ if ((flags & LIT_NORMALIZE) > 0) {
+ dataBufferNormalize ();
+ }
+
+ // Return the value.
+ return dataBufferToString ();
+ }
+
+
+ /**
+ * Try reading external identifiers.
+ * A system identifier is not required for notations.
+ * @param inNotation Are we parsing a notation decl?
+ * @param isSubset Parsing external subset decl (may be omitted)?
+ * @return A three-member String array containing the identifiers,
+ * or nulls. Order: public, system, baseURI.
+ */
+ private String[] readExternalIds (boolean inNotation, boolean isSubset)
+ throws Exception
+ {
+ char c;
+ String ids[] = new String [3];
+ int flags = LIT_DISABLE_CREF | LIT_DISABLE_PE | LIT_DISABLE_EREF;
+
+ if (tryRead ("PUBLIC")) {
+ requireWhitespace ();
+ ids [0] = readLiteral (LIT_NORMALIZE | LIT_PUBID | flags);
+ if (inNotation) {
+ skipWhitespace ();
+ c = readCh ();
+ unread (c);
+ if (c == '"' || c == '\'') {
+ ids [1] = readLiteral (flags);
+ }
+ } else {
+ requireWhitespace ();
+ ids [1] = readLiteral (flags);
+ }
+
+ for (int i = 0; i < ids [0].length (); i++) {
+ c = ids [0].charAt (i);
+ if (c >= 'a' && c <= 'z')
+ continue;
+ if (c >= 'A' && c <= 'Z')
+ continue;
+ if (" \r\n0123456789-' ()+,./:=?;!*#@$_%".indexOf (c) != -1)
+ continue;
+ error ("illegal PUBLIC id character U+"
+ + Integer.toHexString (c));
+ }
+ } else if (tryRead ("SYSTEM")) {
+ requireWhitespace ();
+ ids [1] = readLiteral (flags);
+ } else if (!isSubset)
+ error ("missing SYSTEM or PUBLIC keyword");
+
+ if (ids [1] != null) {
+ if (ids [1].indexOf ('#') != -1)
+ handler.verror ("SYSTEM id has a URI fragment: " + ids [1]);
+ ids [2] = handler.getSystemId ();
+ if (ids [2] == null)
+ handler.warn ("No base URI; hope URI is absolute: "
+ + ids [1]);
+ }
+
+ return ids;
+ }
+
+
+ /**
+ * Test if a character is whitespace.
+ * <pre>
+ * [3] S ::= (#x20 | #x9 | #xd | #xa)+
+ * </pre>
+ * @param c The character to test.
+ * @return true if the character is whitespace.
+ */
+ private final boolean isWhitespace (char c)
+ {
+ if (c > 0x20)
+ return false;
+ if (c == 0x20 || c == 0x0a || c == 0x09 || c == 0x0d)
+ return true;
+ return false; // illegal ...
+ }
+
+
+ //////////////////////////////////////////////////////////////////////
+ // Utility routines.
+ //////////////////////////////////////////////////////////////////////
+
+
+ /**
+ * Add a character to the data buffer.
+ */
+ private void dataBufferAppend (char c)
+ {
+ // Expand buffer if necessary.
+ if (dataBufferPos >= dataBuffer.length)
+ dataBuffer =
+ (char[]) extendArray (dataBuffer,
+ dataBuffer.length, dataBufferPos);
+ dataBuffer [dataBufferPos++] = c;
+ }
+
+
+ /**
+ * Add a string to the data buffer.
+ */
+ private void dataBufferAppend (String s)
+ {
+ dataBufferAppend (s.toCharArray (), 0, s.length ());
+ }
+
+
+ /**
+ * Append (part of) a character array to the data buffer.
+ */
+ private void dataBufferAppend (char ch[], int start, int length)
+ {
+ dataBuffer = (char[])
+ extendArray (dataBuffer, dataBuffer.length,
+ dataBufferPos + length);
+
+ System.arraycopy (ch, start, dataBuffer, dataBufferPos, length);
+ dataBufferPos += length;
+ }
+
+
+ /**
+ * Normalise space characters in the data buffer.
+ */
+ private void dataBufferNormalize ()
+ {
+ int i = 0;
+ int j = 0;
+ int end = dataBufferPos;
+
+ // Skip spaces at the start.
+ while (j < end && dataBuffer [j] == ' ') {
+ j++;
+ }
+
+ // Skip whitespace at the end.
+ while (end > j && dataBuffer [end - 1] == ' ') {
+ end --;
+ }
+
+ // Start copying to the left.
+ while (j < end) {
+
+ char c = dataBuffer [j++];
+
+ // Normalise all other spaces to
+ // a single space.
+ if (c == ' ') {
+ while (j < end && dataBuffer [j++] == ' ')
+ continue;
+ dataBuffer [i++] = ' ';
+ dataBuffer [i++] = dataBuffer [j - 1];
+ } else {
+ dataBuffer [i++] = c;
+ }
+ }
+
+ // The new length is <= the old one.
+ dataBufferPos = i;
+ }
+
+
+ /**
+ * Convert the data buffer to a string.
+ */
+ private String dataBufferToString ()
+ {
+ String s = new String (dataBuffer, 0, dataBufferPos);
+ dataBufferPos = 0;
+ return s;
+ }
+
+
+ /**
+ * Flush the contents of the data buffer to the handler, as
+ * appropriate, and reset the buffer for new input.
+ */
+ private void dataBufferFlush ()
+ throws SAXException
+ {
+ if (currentElementContent == CONTENT_ELEMENTS
+ && dataBufferPos > 0
+ && !inCDATA
+ ) {
+ // We can't just trust the buffer to be whitespace, there
+ // are (error) cases when it isn't
+ for (int i = 0; i < dataBufferPos; i++) {
+ if (!isWhitespace (dataBuffer [i])) {
+ handler.charData (dataBuffer, 0, dataBufferPos);
+ dataBufferPos = 0;
+ }
+ }
+ if (dataBufferPos > 0) {
+ handler.ignorableWhitespace (dataBuffer, 0, dataBufferPos);
+ dataBufferPos = 0;
+ }
+ } else if (dataBufferPos > 0) {
+ handler.charData (dataBuffer, 0, dataBufferPos);
+ dataBufferPos = 0;
+ }
+ }
+
+
+ /**
+ * Require a string to appear, or throw an exception.
+ * <p><em>Precondition:</em> Entity expansion is not required.
+ * <p><em>Precondition:</em> data buffer has no characters that
+ * will get sent to the application.
+ */
+ private void require (String delim)
+ throws SAXException, IOException
+ {
+ int length = delim.length ();
+ char ch [];
+
+ if (length < dataBuffer.length) {
+ ch = dataBuffer;
+ delim.getChars (0, length, ch, 0);
+ } else
+ ch = delim.toCharArray ();
+
+ if (USE_CHEATS
+ && length <= (readBufferLength - readBufferPos)) {
+ int offset = readBufferPos;
+
+ for (int i = 0; i < length; i++, offset++)
+ if (ch [i] != readBuffer [offset])
+ error ("required string", null, delim);
+ readBufferPos = offset;
+
+ } else {
+ for (int i = 0; i < length; i++)
+ require (ch [i]);
+ }
+ }
+
+
+ /**
+ * Require a character to appear, or throw an exception.
+ */
+ private void require (char delim)
+ throws SAXException, IOException
+ {
+ char c = readCh ();
+
+ if (c != delim) {
+ error ("required character", c, new Character (delim).toString ());
+ }
+ }
+
+
+ /**
+ * Create an interned string from a character array.
+ * &AElig;lfred uses this method to create an interned version
+ * of all names and name tokens, so that it can test equality
+ * with <code>==</code> instead of <code>String.equals ()</code>.
+ *
+ * <p>This is much more efficient than constructing a non-interned
+ * string first, and then interning it.
+ *
+ * @param ch an array of characters for building the string.
+ * @param start the starting position in the array.
+ * @param length the number of characters to place in the string.
+ * @return an interned string.
+ * @see #intern (String)
+ * @see java.lang.String#intern
+ */
+ public String intern (char ch[], int start, int length)
+ {
+ int index = 0;
+ int hash = 0;
+ Object bucket [];
+
+ // Generate a hash code. This is a widely used string hash,
+ // often attributed to Brian Kernighan.
+ for (int i = start; i < start + length; i++)
+ hash = 31 * hash + ch [i];
+ hash = (hash & 0x7fffffff) % SYMBOL_TABLE_LENGTH;
+
+ // Get the bucket -- consists of {array,String} pairs
+ if ((bucket = symbolTable [hash]) == null) {
+ // first string in this bucket
+ bucket = new Object [8];
+
+ // Search for a matching tuple, and
+ // return the string if we find one.
+ } else {
+ while (index < bucket.length) {
+ char chFound [] = (char []) bucket [index];
+
+ // Stop when we hit an empty entry.
+ if (chFound == null)
+ break;
+
+ // If they're the same length, check for a match.
+ if (chFound.length == length) {
+ for (int i = 0; i < chFound.length; i++) {
+ // continue search on failure
+ if (ch [start + i] != chFound [i]) {
+ break;
+ } else if (i == length - 1) {
+ // That's it, we have a match!
+ return (String) bucket [index + 1];
+ }
+ }
+ }
+ index += 2;
+ }
+ // Not found -- we'll have to add it.
+
+ // Do we have to grow the bucket?
+ bucket = (Object []) extendArray (bucket, bucket.length, index);
+ }
+ symbolTable [hash] = bucket;
+
+ // OK, add it to the end of the bucket -- "local" interning.
+ // Intern "globally" to let applications share interning benefits.
+ // That is, "!=" and "==" work on our strings, not just equals().
+ String s = new String (ch, start, length).intern ();
+ bucket [index] = s.toCharArray ();
+ bucket [index + 1] = s;
+ return s;
+ }
+
+ /**
+ * Ensure the capacity of an array, allocating a new one if
+ * necessary. Usually extends only for name hash collisions.
+ */
+ private Object extendArray (Object array, int currentSize, int requiredSize)
+ {
+ if (requiredSize < currentSize) {
+ return array;
+ } else {
+ Object newArray = null;
+ int newSize = currentSize * 2;
+
+ if (newSize <= requiredSize)
+ newSize = requiredSize + 1;
+
+ if (array instanceof char[])
+ newArray = new char [newSize];
+ else if (array instanceof Object[])
+ newArray = new Object [newSize];
+ else
+ throw new RuntimeException ();
+
+ System.arraycopy (array, 0, newArray, 0, currentSize);
+ return newArray;
+ }
+ }
+
+
+ //////////////////////////////////////////////////////////////////////
+ // XML query routines.
+ //////////////////////////////////////////////////////////////////////
+
+
+ boolean isStandalone () { return docIsStandalone; }
+
+
+ //
+ // Elements
+ //
+
+ private int getContentType (Object element [], int defaultType)
+ {
+ int retval;
+
+ if (element == null)
+ return defaultType;
+ retval = ((Integer) element [0]).intValue ();
+ if (retval == CONTENT_UNDECLARED)
+ retval = defaultType;
+ return retval;
+ }
+
+
+ /**
+ * Look up the content type of an element.
+ * @param name The element type name.
+ * @return An integer constant representing the content type.
+ * @see #CONTENT_UNDECLARED
+ * @see #CONTENT_ANY
+ * @see #CONTENT_EMPTY
+ * @see #CONTENT_MIXED
+ * @see #CONTENT_ELEMENTS
+ */
+ public int getElementContentType (String name)
+ {
+ Object element [] = (Object []) elementInfo.get (name);
+ return getContentType (element, CONTENT_UNDECLARED);
+ }
+
+
+ /**
+ * Register an element.
+ * Array format:
+ * [0] element type name
+ * [1] content model (mixed, elements only)
+ * [2] attribute hash table
+ */
+ private void setElement (
+ String name,
+ int contentType,
+ String contentModel,
+ Hashtable attributes
+ ) throws SAXException
+ {
+ if (skippedPE)
+ return;
+
+ Object element [] = (Object []) elementInfo.get (name);
+
+ // first <!ELEMENT ...> or <!ATTLIST ...> for this type?
+ if (element == null) {
+ element = new Object [3];
+ element [0] = new Integer (contentType);
+ element [1] = contentModel;
+ element [2] = attributes;
+ elementInfo.put (name, element);
+ return;
+ }
+
+ // <!ELEMENT ...> declaration?
+ if (contentType != CONTENT_UNDECLARED) {
+ // ... following an associated <!ATTLIST ...>
+ if (((Integer) element [0]).intValue () == CONTENT_UNDECLARED) {
+ element [0] = new Integer (contentType);
+ element [1] = contentModel;
+ } else
+ // VC: Unique Element Type Declaration
+ handler.verror ("multiple declarations for element type: "
+ + name);
+ }
+
+ // first <!ATTLIST ...>, before <!ELEMENT ...> ?
+ else if (attributes != null)
+ element [2] = attributes;
+ }
+
+
+ /**
+ * Look up the attribute hash table for an element.
+ * The hash table is the second item in the element array.
+ */
+ private Hashtable getElementAttributes (String name)
+ {
+ Object element[] = (Object[]) elementInfo.get (name);
+ if (element == null)
+ return null;
+ else
+ return (Hashtable) element [2];
+ }
+
+
+
+ //
+ // Attributes
+ //
+
+ /**
+ * Get the declared attributes for an element type.
+ * @param elname The name of the element type.
+ * @return An Enumeration of all the attributes declared for
+ * a specific element type. The results will be valid only
+ * after the DTD (if any) has been parsed.
+ * @see #getAttributeType
+ * @see #getAttributeEnumeration
+ * @see #getAttributeDefaultValueType
+ * @see #getAttributeDefaultValue
+ * @see #getAttributeExpandedValue
+ */
+ private Enumeration declaredAttributes (Object element [])
+ {
+ Hashtable attlist;
+
+ if (element == null)
+ return null;
+ if ((attlist = (Hashtable) element [2]) == null)
+ return null;
+ return attlist.keys ();
+ }
+
+ /**
+ * Get the declared attributes for an element type.
+ * @param elname The name of the element type.
+ * @return An Enumeration of all the attributes declared for
+ * a specific element type. The results will be valid only
+ * after the DTD (if any) has been parsed.
+ * @see #getAttributeType
+ * @see #getAttributeEnumeration
+ * @see #getAttributeDefaultValueType
+ * @see #getAttributeDefaultValue
+ * @see #getAttributeExpandedValue
+ */
+ public Enumeration declaredAttributes (String elname)
+ {
+ return declaredAttributes ((Object []) elementInfo.get (elname));
+ }
+
+
+ /**
+ * Retrieve the declared type of an attribute.
+ * @param name The name of the associated element.
+ * @param aname The name of the attribute.
+ * @return An interend string denoting the type, or null
+ * indicating an undeclared attribute.
+ */
+ public String getAttributeType (String name, String aname)
+ {
+ Object attribute[] = getAttribute (name, aname);
+ if (attribute == null) {
+ return null;
+ } else {
+ return (String) attribute [0];
+ }
+ }
+
+
+ /**
+ * Retrieve the allowed values for an enumerated attribute type.
+ * @param name The name of the associated element.
+ * @param aname The name of the attribute.
+ * @return A string containing the token list.
+ */
+ public String getAttributeEnumeration (String name, String aname)
+ {
+ Object attribute[] = getAttribute (name, aname);
+ if (attribute == null) {
+ return null;
+ } else {
+ // assert: attribute [0] is "ENUMERATION" or "NOTATION"
+ return (String) attribute [3];
+ }
+ }
+
+
+ /**
+ * Retrieve the default value of a declared attribute.
+ * @param name The name of the associated element.
+ * @param aname The name of the attribute.
+ * @return The default value, or null if the attribute was
+ * #IMPLIED or simply undeclared and unspecified.
+ * @see #getAttributeExpandedValue
+ */
+ public String getAttributeDefaultValue (String name, String aname)
+ {
+ Object attribute[] = getAttribute (name, aname);
+ if (attribute == null) {
+ return null;
+ } else {
+ return (String) attribute [1];
+ }
+ }
+
+ /*
+
+// FIXME: Leaving this in, until W3C finally resolves the confusion
+// between parts of the XML 2nd REC about when entity declararations
+// are guaranteed to be known. Current code matches what section 5.1
+// (conformance) describes, but some readings of the self-contradicting
+// text in 4.1 (the "Entity Declared" WFC and VC) seem to expect that
+// attribute expansion/normalization must be deferred in some cases
+// (just TRY to identify them!).
+
+ * Retrieve the expanded value of a declared attribute.
+ * <p>General entities (and char refs) will be expanded (once).
+ * @param name The name of the associated element.
+ * @param aname The name of the attribute.
+ * @return The expanded default value, or null if the attribute was
+ * #IMPLIED or simply undeclared
+ * @see #getAttributeDefaultValue
+ public String getAttributeExpandedValue (String name, String aname)
+ throws Exception
+ {
+ Object attribute[] = getAttribute (name, aname);
+
+ if (attribute == null) {
+ return null;
+ } else if (attribute [4] == null && attribute [1] != null) {
+ // we MUST use the same buf for both quotes else the literal
+ // can't be properly terminated
+ char buf [] = new char [1];
+ int flags = LIT_ENTITY_REF | LIT_ATTRIBUTE;
+ String type = getAttributeType (name, aname);
+
+ if (type != "CDATA" && type != null)
+ flags |= LIT_NORMALIZE;
+ buf [0] = '"';
+ pushCharArray (null, buf, 0, 1);
+ pushString (null, (String) attribute [1]);
+ pushCharArray (null, buf, 0, 1);
+ attribute [4] = readLiteral (flags);
+ }
+ return (String) attribute [4];
+ }
+ */
+
+ /**
+ * Retrieve the default value mode of a declared attribute.
+ * @see #ATTRIBUTE_DEFAULT_SPECIFIED
+ * @see #ATTRIBUTE_DEFAULT_IMPLIED
+ * @see #ATTRIBUTE_DEFAULT_REQUIRED
+ * @see #ATTRIBUTE_DEFAULT_FIXED
+ */
+ public int getAttributeDefaultValueType (String name, String aname)
+ {
+ Object attribute[] = getAttribute (name, aname);
+ if (attribute == null) {
+ return ATTRIBUTE_DEFAULT_UNDECLARED;
+ } else {
+ return ((Integer) attribute [2]).intValue ();
+ }
+ }
+
+
+ /**
+ * Register an attribute declaration for later retrieval.
+ * Format:
+ * - String type
+ * - String default value
+ * - int value type
+ * - enumeration
+ * - processed default value
+ */
+ private void setAttribute (String elName, String name, String type,
+ String enumeration,
+ String value, int valueType)
+ throws Exception
+ {
+ Hashtable attlist;
+
+ if (skippedPE)
+ return;
+
+ // Create a new hashtable if necessary.
+ attlist = getElementAttributes (elName);
+ if (attlist == null)
+ attlist = new Hashtable ();
+
+ // ignore multiple attribute declarations!
+ if (attlist.get (name) != null) {
+ // warn ...
+ return;
+ } else {
+ Object attribute [] = new Object [5];
+ attribute [0] = type;
+ attribute [1] = value;
+ attribute [2] = new Integer (valueType);
+ attribute [3] = enumeration;
+ attribute [4] = null;
+ attlist.put (name, attribute);
+
+ // save; but don't overwrite any existing <!ELEMENT ...>
+ setElement (elName, CONTENT_UNDECLARED, null, attlist);
+ }
+ }
+
+
+ /**
+ * Retrieve the array representing an attribute declaration.
+ */
+ private Object[] getAttribute (String elName, String name)
+ {
+ Hashtable attlist;
+
+ attlist = getElementAttributes (elName);
+ if (attlist == null)
+ return null;
+ return (Object[]) attlist.get (name);
+ }
+
+
+ //
+ // Entities
+ //
+
+ /**
+ * Find the type of an entity.
+ * @returns An integer constant representing the entity type.
+ * @see #ENTITY_UNDECLARED
+ * @see #ENTITY_INTERNAL
+ * @see #ENTITY_NDATA
+ * @see #ENTITY_TEXT
+ */
+ public int getEntityType (String ename)
+ {
+ Object entity[] = (Object[]) entityInfo.get (ename);
+ if (entity == null) {
+ return ENTITY_UNDECLARED;
+ } else {
+ return ((Integer) entity [0]).intValue ();
+ }
+ }
+
+
+ /**
+ * Return an external entity's identifier array.
+ * @param ename The name of the external entity.
+ * @return Three element array containing (in order) the entity's
+ * public identifier, system identifier, and base URI. Null if
+ * the entity was not declared as an external entity.
+ * @see #getEntityType
+ */
+ public String [] getEntityIds (String ename)
+ {
+ Object entity[] = (Object[]) entityInfo.get (ename);
+ if (entity == null) {
+ return null;
+ } else {
+ return (String []) entity [1];
+ }
+ }
+
+
+ /**
+ * Return an internal entity's replacement text.
+ * @param ename The name of the internal entity.
+ * @return The entity's replacement text, or null if
+ * the entity was not declared as an internal entity.
+ * @see #getEntityType
+ */
+ public String getEntityValue (String ename)
+ {
+ Object entity[] = (Object[]) entityInfo.get (ename);
+ if (entity == null) {
+ return null;
+ } else {
+ return (String) entity [3];
+ }
+ }
+
+
+ /**
+ * Register an entity declaration for later retrieval.
+ */
+ private void setInternalEntity (String eName, String value)
+ throws SAXException
+ {
+ if (skippedPE)
+ return;
+
+ if (entityInfo.get (eName) == null) {
+ Object entity[] = new Object [5];
+ entity [0] = new Integer (ENTITY_INTERNAL);
+// FIXME: shrink!! [2] useless
+ entity [3] = value;
+ entityInfo.put (eName, entity);
+ }
+ if (handler.getFeature (SAXDriver.FEATURE + "string-interning")) {
+ if ("lt" == eName || "gt" == eName || "quot" == eName
+ || "apos" == eName || "amp" == eName)
+ return;
+ } else {
+ if ("lt".equals(eName) || "gt".equals(eName) || "quot".equals(eName)
+ || "apos".equals(eName) || "amp".equals(eName))
+ return;
+ }
+ handler.getDeclHandler ()
+ .internalEntityDecl (eName, value);
+ }
+
+
+ /**
+ * Register an external entity declaration for later retrieval.
+ */
+ private void setExternalEntity (String eName, int eClass,
+ String ids [], String nName)
+ {
+ if (entityInfo.get (eName) == null) {
+ Object entity[] = new Object [5];
+ entity [0] = new Integer (eClass);
+ entity [1] = ids;
+// FIXME: shrink!! [2] no longer used, [4] irrelevant given [0]
+ entity [4] = nName;
+ entityInfo.put (eName, entity);
+ }
+ }
+
+
+ //
+ // Notations.
+ //
+
+ /**
+ * Report a notation declaration, checking for duplicates.
+ */
+ private void setNotation (String nname, String ids [])
+ throws SAXException
+ {
+ if (skippedPE)
+ return;
+
+ handler.notationDecl (nname, ids);
+ if (notationInfo.get (nname) == null)
+ notationInfo.put (nname, nname);
+ else
+ // VC: Unique Notation Name
+ handler.verror ("Duplicate notation name decl: " + nname);
+ }
+
+
+ //
+ // Location.
+ //
+
+
+ /**
+ * Return the current line number.
+ */
+ public int getLineNumber ()
+ {
+ return line;
+ }
+
+
+ /**
+ * Return the current column number.
+ */
+ public int getColumnNumber ()
+ {
+ return column;
+ }
+
+
+ //////////////////////////////////////////////////////////////////////
+ // High-level I/O.
+ //////////////////////////////////////////////////////////////////////
+
+
+ /**
+ * Read a single character from the readBuffer.
+ * <p>The readDataChunk () method maintains the buffer.
+ * <p>If we hit the end of an entity, try to pop the stack and
+ * keep going.
+ * <p> (This approach doesn't really enforce XML's rules about
+ * entity boundaries, but this is not currently a validating
+ * parser).
+ * <p>This routine also attempts to keep track of the current
+ * position in external entities, but it's not entirely accurate.
+ * @return The next available input character.
+ * @see #unread (char)
+ * @see #readDataChunk
+ * @see #readBuffer
+ * @see #line
+ * @return The next character from the current input source.
+ */
+ private char readCh ()
+ throws SAXException, IOException
+ {
+ // As long as there's nothing in the
+ // read buffer, try reading more data
+ // (for an external entity) or popping
+ // the entity stack (for either).
+ while (readBufferPos >= readBufferLength) {
+ switch (sourceType) {
+ case INPUT_READER:
+ case INPUT_STREAM:
+ readDataChunk ();
+ while (readBufferLength < 1) {
+ popInput ();
+ if (readBufferLength < 1) {
+ readDataChunk ();
+ }
+ }
+ break;
+
+ default:
+
+ popInput ();
+ break;
+ }
+ }
+
+ char c = readBuffer [readBufferPos++];
+
+ if (c == '\n') {
+ line++;
+ column = 0;
+ } else {
+ if (c == '<') {
+ /* the most common return to parseContent () ... NOP */
+ } else if (((c < 0x0020 && (c != '\t') && (c != '\r')) || c > 0xFFFD)
+ || ((c >= 0x007f) && (c <= 0x009f) && (c != 0x0085)
+ && xmlVersion == XML_11))
+ error ("illegal XML character U+"
+ + Integer.toHexString (c));
+
+ // If we're in the DTD and in a context where PEs get expanded,
+ // do so ... 1/14/2000 errata identify those contexts. There
+ // are also spots in the internal subset where PE refs are fatal
+ // errors, hence yet another flag.
+ else if (c == '%' && expandPE) {
+ if (peIsError)
+ error ("PE reference within decl in internal subset.");
+ parsePEReference ();
+ return readCh ();
+ }
+ column++;
+ }
+
+ return c;
+ }
+
+
+ /**
+ * Push a single character back onto the current input stream.
+ * <p>This method usually pushes the character back onto
+ * the readBuffer.
+ * <p>I don't think that this would ever be called with
+ * readBufferPos = 0, because the methods always reads a character
+ * before unreading it, but just in case, I've added a boundary
+ * condition.
+ * @param c The character to push back.
+ * @see #readCh
+ * @see #unread (char[])
+ * @see #readBuffer
+ */
+ private void unread (char c)
+ throws SAXException
+ {
+ // Normal condition.
+ if (c == '\n') {
+ line--;
+ column = -1;
+ }
+ if (readBufferPos > 0) {
+ readBuffer [--readBufferPos] = c;
+ } else {
+ pushString (null, new Character (c).toString ());
+ }
+ }
+
+
+ /**
+ * Push a char array back onto the current input stream.
+ * <p>NOTE: you must <em>never</em> push back characters that you
+ * haven't actually read: use pushString () instead.
+ * @see #readCh
+ * @see #unread (char)
+ * @see #readBuffer
+ * @see #pushString
+ */
+ private void unread (char ch[], int length)
+ throws SAXException
+ {
+ for (int i = 0; i < length; i++) {
+ if (ch [i] == '\n') {
+ line--;
+ column = -1;
+ }
+ }
+ if (length < readBufferPos) {
+ readBufferPos -= length;
+ } else {
+ pushCharArray (null, ch, 0, length);
+ }
+ }
+
+
+ /**
+ * Push, or skip, a new external input source.
+ * The source will be some kind of parsed entity, such as a PE
+ * (including the external DTD subset) or content for the body.
+ *
+ * @param url The java.net.URL object for the entity.
+ * @see SAXDriver#resolveEntity
+ * @see #pushString
+ * @see #sourceType
+ * @see #pushInput
+ * @see #detectEncoding
+ * @see #sourceType
+ * @see #readBuffer
+ */
+ private void pushURL (
+ boolean isPE,
+ String ename,
+ String ids [], // public, system, baseURI
+ Reader reader,
+ InputStream stream,
+ String encoding,
+ boolean doResolve
+ ) throws SAXException, IOException
+ {
+ boolean ignoreEncoding;
+ String systemId;
+ InputSource source;
+
+ if (!isPE)
+ dataBufferFlush ();
+
+ scratch.setPublicId (ids [0]);
+ scratch.setSystemId (ids [1]);
+
+ // See if we should skip or substitute the entity.
+ // If we're not skipping, resolving reports startEntity()
+ // and updates the (handler's) stack of URIs.
+ if (doResolve) {
+ // assert (stream == null && reader == null && encoding == null)
+ source = handler.resolveEntity (isPE, ename, scratch, ids [2]);
+ if (source == null) {
+ handler.warn ("skipping entity: " + ename);
+ handler.skippedEntity (ename);
+ if (isPE)
+ skippedPE = true;
+ return;
+ }
+
+ // we might be using alternate IDs/encoding
+ systemId = source.getSystemId ();
+ // The following warning and setting systemId was deleted bcause
+ // the application has the option of not setting systemId
+ // provided that it has set the characte/byte stream.
+ /*
+ if (systemId == null) {
+ handler.warn ("missing system ID, using " + ids [1]);
+ systemId = ids [1];
+ }
+ */
+ } else {
+ // "[document]", or "[dtd]" via getExternalSubset()
+ scratch.setCharacterStream (reader);
+ scratch.setByteStream (stream);
+ scratch.setEncoding (encoding);
+ source = scratch;
+ systemId = ids [1];
+ if (handler.getFeature (SAXDriver.FEATURE + "string-interning")) {
+ handler.startExternalEntity (ename, systemId,
+ "[document]" == ename);
+ } else {
+ handler.startExternalEntity (ename, systemId,
+ "[document]".equals(ename));
+ }
+ }
+
+ // we may have been given I/O streams directly
+ if (source.getCharacterStream () != null) {
+ if (source.getByteStream () != null)
+ error ("InputSource has two streams!");
+ reader = source.getCharacterStream ();
+ } else if (source.getByteStream () != null) {
+ encoding = source.getEncoding ();
+ if (encoding == null)
+ stream = source.getByteStream ();
+ else try {
+ reader = new InputStreamReader (
+ source.getByteStream (),
+ encoding);
+ } catch (IOException e) {
+ stream = source.getByteStream ();
+ }
+ } else if (systemId == null)
+ error ("InputSource has no URI!");
+ scratch.setCharacterStream (null);
+ scratch.setByteStream (null);
+ scratch.setEncoding (null);
+
+ // Push the existing status.
+ pushInput (ename);
+
+ // Create a new read buffer.
+ // (Note the four-character margin)
+ readBuffer = new char [READ_BUFFER_MAX + 4];
+ readBufferPos = 0;
+ readBufferLength = 0;
+ readBufferOverflow = -1;
+ is = null;
+ line = 1;
+ column = 0;
+ currentByteCount = 0;
+
+ // If there's an explicit character stream, just
+ // ignore encoding declarations.
+ if (reader != null) {
+ sourceType = INPUT_READER;
+ this.reader = reader;
+ tryEncodingDecl (true);
+ return;
+ }
+
+ // Else we handle the conversion, and need to ensure
+ // it's done right.
+ sourceType = INPUT_STREAM;
+ if (stream != null) {
+ is = stream;
+ } else {
+ // We have to open our own stream to the URL.
+ URL url = new URL (systemId);
+
+ externalEntity = url.openConnection ();
+ externalEntity.connect ();
+ is = externalEntity.getInputStream ();
+ }
+
+ // If we get to here, there must be
+ // an InputStream available.
+ if (!is.markSupported ()) {
+ is = new BufferedInputStream (is);
+ }
+
+ // Get any external encoding label.
+ if (encoding == null && externalEntity != null) {
+ // External labels can be untrustworthy; filesystems in
+ // particular often have the wrong default for content
+ // that wasn't locally originated. Those we autodetect.
+ if (!"file".equals (externalEntity.getURL ().getProtocol ())) {
+ int temp;
+
+ // application/xml;charset=something;otherAttr=...
+ // ... with many variants on 'something'
+ encoding = externalEntity.getContentType ();
+
+ // MHK code (fix for Saxon 5.5.1/007):
+ // protect against encoding==null
+ if (encoding==null) {
+ temp = -1;
+ } else {
+ temp = encoding.indexOf ("charset");
+ }
+
+ // RFC 2376 sez MIME text defaults to ASCII, but since the
+ // JDK will create a MIME type out of thin air, we always
+ // autodetect when there's no explicit charset attribute.
+ if (temp < 0)
+ encoding = null; // autodetect
+ else {
+ // only this one attribute
+ if ((temp = encoding.indexOf (';')) > 0)
+ encoding = encoding.substring (0, temp);
+
+ if ((temp = encoding.indexOf ('=', temp + 7)) > 0) {
+ encoding = encoding.substring (temp + 1);
+
+ // attributes can have comment fields (RFC 822)
+ if ((temp = encoding.indexOf ('(')) > 0)
+ encoding = encoding.substring (0, temp);
+ // ... and values may be quoted
+ if ((temp = encoding.indexOf ('"')) > 0)
+ encoding = encoding.substring (temp + 1,
+ encoding.indexOf ('"', temp + 2));
+ encoding.trim ();
+ } else {
+ handler.warn ("ignoring illegal MIME attribute: "
+ + encoding);
+ encoding = null;
+ }
+ }
+ }
+ }
+
+ // if we got an external encoding label, use it ...
+ if (encoding != null) {
+ this.encoding = ENCODING_EXTERNAL;
+ setupDecoding (encoding);
+ ignoreEncoding = true;
+
+ // ... else autodetect from first bytes.
+ } else {
+ detectEncoding ();
+ ignoreEncoding = false;
+ }
+
+ // Read any XML or text declaration.
+ // If we autodetected, it may tell us the "real" encoding.
+ try {
+ tryEncodingDecl (ignoreEncoding);
+ } catch (UnsupportedEncodingException x) {
+ encoding = x.getMessage ();
+
+ // if we don't handle the declared encoding,
+ // try letting a JVM InputStreamReader do it
+ try {
+ if (sourceType != INPUT_STREAM)
+ throw x;
+
+ is.reset ();
+ readBufferPos = 0;
+ readBufferLength = 0;
+ readBufferOverflow = -1;
+ line = 1;
+ currentByteCount = column = 0;
+
+ sourceType = INPUT_READER;
+ this.reader = new InputStreamReader (is, encoding);
+ is = null;
+
+ tryEncodingDecl (true);
+
+ } catch (IOException e) {
+ error ("unsupported text encoding",
+ encoding,
+ null);
+ }
+ }
+ }
+
+
+ /**
+ * Check for an encoding declaration. This is the second part of the
+ * XML encoding autodetection algorithm, relying on detectEncoding to
+ * get to the point that this part can read any encoding declaration
+ * in the document (using only US-ASCII characters).
+ *
+ * <p> Because this part starts to fill parser buffers with this data,
+ * it's tricky to setup a reader so that Java's built-in decoders can be
+ * used for the character encodings that aren't built in to this parser
+ * (such as EUC-JP, KOI8-R, Big5, etc).
+ *
+ * @return any encoding in the declaration, uppercased; or null
+ * @see detectEncoding
+ */
+ private String tryEncodingDecl (boolean ignoreEncoding)
+ throws SAXException, IOException
+ {
+ // Read the XML/text declaration.
+ if (tryRead ("<?xml")) {
+ if (tryWhitespace ()) {
+ if (inputStack.size () > 0) {
+ return parseTextDecl (ignoreEncoding);
+ } else {
+ return parseXMLDecl (ignoreEncoding);
+ }
+ } else {
+ // <?xml-stylesheet ...?> or similar
+ unread ('l');
+ unread ('m');
+ unread ('x');
+ unread ('?');
+ unread ('<');
+ }
+ }
+ return null;
+ }
+
+
+ /**
+ * Attempt to detect the encoding of an entity.
+ * <p>The trick here (as suggested in the XML standard) is that
+ * any entity not in UTF-8, or in UCS-2 with a byte-order mark,
+ * <b>must</b> begin with an XML declaration or an encoding
+ * declaration; we simply have to look for "&lt;?xml" in various
+ * encodings.
+ * <p>This method has no way to distinguish among 8-bit encodings.
+ * Instead, it sets up for UTF-8, then (possibly) revises its assumption
+ * later in setupDecoding (). Any ASCII-derived 8-bit encoding
+ * should work, but most will be rejected later by setupDecoding ().
+ * @see #tryEncoding (byte[], byte, byte, byte, byte)
+ * @see #tryEncoding (byte[], byte, byte)
+ * @see #setupDecoding
+ */
+ private void detectEncoding ()
+ throws SAXException, IOException
+ {
+ byte signature[] = new byte [4];
+
+ // Read the first four bytes for
+ // autodetection.
+ is.mark (4);
+ is.read (signature);
+ is.reset ();
+
+ //
+ // FIRST: four byte encodings (who uses these?)
+ //
+ if (tryEncoding (signature, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x3c)) {
+ // UCS-4 must begin with "<?xml"
+ // 0x00 0x00 0x00 0x3c: UCS-4, big-endian (1234)
+ // "UTF-32BE"
+ encoding = ENCODING_UCS_4_1234;
+
+ } else if (tryEncoding (signature, (byte) 0x3c, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00)) {
+ // 0x3c 0x00 0x00 0x00: UCS-4, little-endian (4321)
+ // "UTF-32LE"
+ encoding = ENCODING_UCS_4_4321;
+
+ } else if (tryEncoding (signature, (byte) 0x00, (byte) 0x00,
+ (byte) 0x3c, (byte) 0x00)) {
+ // 0x00 0x00 0x3c 0x00: UCS-4, unusual (2143)
+ encoding = ENCODING_UCS_4_2143;
+
+ } else if (tryEncoding (signature, (byte) 0x00, (byte) 0x3c,
+ (byte) 0x00, (byte) 0x00)) {
+ // 0x00 0x3c 0x00 0x00: UCS-4, unusual (3421)
+ encoding = ENCODING_UCS_4_3412;
+
+ // 00 00 fe ff UCS_4_1234 (with BOM)
+ // ff fe 00 00 UCS_4_4321 (with BOM)
+ }
+
+ //
+ // SECOND: two byte encodings
+ // note ... with 1/14/2000 errata the XML spec identifies some
+ // more "broken UTF-16" autodetection cases, with no XML decl,
+ // which we don't handle here (that's legal too).
+ //
+ else if (tryEncoding (signature, (byte) 0xfe, (byte) 0xff)) {
+ // UCS-2 with a byte-order marker. (UTF-16)
+ // 0xfe 0xff: UCS-2, big-endian (12)
+ encoding = ENCODING_UCS_2_12;
+ is.read (); is.read ();
+
+ } else if (tryEncoding (signature, (byte) 0xff, (byte) 0xfe)) {
+ // UCS-2 with a byte-order marker. (UTF-16)
+ // 0xff 0xfe: UCS-2, little-endian (21)
+ encoding = ENCODING_UCS_2_21;
+ is.read (); is.read ();
+
+ } else if (tryEncoding (signature, (byte) 0x00, (byte) 0x3c,
+ (byte) 0x00, (byte) 0x3f)) {
+ // UTF-16BE (otherwise, malformed UTF-16)
+ // 0x00 0x3c 0x00 0x3f: UCS-2, big-endian, no byte-order mark
+ encoding = ENCODING_UCS_2_12;
+ error ("no byte-order mark for UCS-2 entity");
+
+ } else if (tryEncoding (signature, (byte) 0x3c, (byte) 0x00,
+ (byte) 0x3f, (byte) 0x00)) {
+ // UTF-16LE (otherwise, malformed UTF-16)
+ // 0x3c 0x00 0x3f 0x00: UCS-2, little-endian, no byte-order mark
+ encoding = ENCODING_UCS_2_21;
+ error ("no byte-order mark for UCS-2 entity");
+ }
+
+ //
+ // THIRD: ASCII-derived encodings, fixed and variable lengths
+ //
+ else if (tryEncoding (signature, (byte) 0x3c, (byte) 0x3f,
+ (byte) 0x78, (byte) 0x6d)) {
+ // ASCII derived
+ // 0x3c 0x3f 0x78 0x6d: UTF-8 or other 8-bit markup (read ENCODING)
+ encoding = ENCODING_UTF_8;
+ prefetchASCIIEncodingDecl ();
+
+ } else if (signature [0] == (byte) 0xef
+ && signature [1] == (byte) 0xbb
+ && signature [2] == (byte) 0xbf) {
+ // 0xef 0xbb 0xbf: UTF-8 BOM (not part of document text)
+ // this un-needed notion slipped into XML 2nd ed through a
+ // "non-normative" erratum; now required by MSFT and UDDI,
+ // and E22 made it normative.
+ encoding = ENCODING_UTF_8;
+ is.read (); is.read (); is.read ();
+
+ } else {
+ // 4c 6f a7 94 ... we don't understand EBCDIC flavors
+ // ... but we COULD at least kick in some fixed code page
+
+ // (default) UTF-8 without encoding/XML declaration
+ encoding = ENCODING_UTF_8;
+ }
+ }
+
+
+ /**
+ * Check for a four-byte signature.
+ * <p>Utility routine for detectEncoding ().
+ * <p>Always looks for some part of "<?XML" in a specific encoding.
+ * @param sig The first four bytes read.
+ * @param b1 The first byte of the signature
+ * @param b2 The second byte of the signature
+ * @param b3 The third byte of the signature
+ * @param b4 The fourth byte of the signature
+ * @see #detectEncoding
+ */
+ private static boolean tryEncoding (
+ byte sig[], byte b1, byte b2, byte b3, byte b4)
+ {
+ return (sig [0] == b1 && sig [1] == b2
+ && sig [2] == b3 && sig [3] == b4);
+ }
+
+
+ /**
+ * Check for a two-byte signature.
+ * <p>Looks for a UCS-2 byte-order mark.
+ * <p>Utility routine for detectEncoding ().
+ * @param sig The first four bytes read.
+ * @param b1 The first byte of the signature
+ * @param b2 The second byte of the signature
+ * @see #detectEncoding
+ */
+ private static boolean tryEncoding (byte sig[], byte b1, byte b2)
+ {
+ return ((sig [0] == b1) && (sig [1] == b2));
+ }
+
+
+ /**
+ * This method pushes a string back onto input.
+ * <p>It is useful either as the expansion of an internal entity,
+ * or for backtracking during the parse.
+ * <p>Call pushCharArray () to do the actual work.
+ * @param s The string to push back onto input.
+ * @see #pushCharArray
+ */
+ private void pushString (String ename, String s)
+ throws SAXException
+ {
+ char ch[] = s.toCharArray ();
+ pushCharArray (ename, ch, 0, ch.length);
+ }
+
+
+ /**
+ * Push a new internal input source.
+ * <p>This method is useful for expanding an internal entity,
+ * or for unreading a string of characters. It creates a new
+ * readBuffer containing the characters in the array, instead
+ * of characters converted from an input byte stream.
+ * @param ch The char array to push.
+ * @see #pushString
+ * @see #pushURL
+ * @see #readBuffer
+ * @see #sourceType
+ * @see #pushInput
+ */
+ private void pushCharArray (String ename, char ch[], int start, int length)
+ throws SAXException
+ {
+ // Push the existing status
+ pushInput (ename);
+ if (ename != null && doReport) {
+ dataBufferFlush ();
+ handler.startInternalEntity (ename);
+ }
+ sourceType = INPUT_INTERNAL;
+ readBuffer = ch;
+ readBufferPos = start;
+ readBufferLength = length;
+ readBufferOverflow = -1;
+ }
+
+
+ /**
+ * Save the current input source onto the stack.
+ * <p>This method saves all of the global variables associated with
+ * the current input source, so that they can be restored when a new
+ * input source has finished. It also tests for entity recursion.
+ * <p>The method saves the following global variables onto a stack
+ * using a fixed-length array:
+ * <ol>
+ * <li>sourceType
+ * <li>externalEntity
+ * <li>readBuffer
+ * <li>readBufferPos
+ * <li>readBufferLength
+ * <li>line
+ * <li>encoding
+ * </ol>
+ * @param ename The name of the entity (if any) causing the new input.
+ * @see #popInput
+ * @see #sourceType
+ * @see #externalEntity
+ * @see #readBuffer
+ * @see #readBufferPos
+ * @see #readBufferLength
+ * @see #line
+ * @see #encoding
+ */
+ private void pushInput (String ename)
+ throws SAXException
+ {
+ // Check for entity recursion.
+ if (ename != null) {
+ Enumeration entities = entityStack.elements ();
+ while (entities.hasMoreElements ()) {
+ String e = (String) entities.nextElement ();
+ if (e != null && e == ename) {
+ error ("recursive reference to entity", ename, null);
+ }
+ }
+ }
+ entityStack.push (ename);
+
+ // Don't bother if there is no current input.
+ if (sourceType == INPUT_NONE) {
+ return;
+ }
+
+ // Set up a snapshot of the current
+ // input source.
+ Object input[] = new Object [12];
+
+ input [0] = new Integer (sourceType);
+ input [1] = externalEntity;
+ input [2] = readBuffer;
+ input [3] = new Integer (readBufferPos);
+ input [4] = new Integer (readBufferLength);
+ input [5] = new Integer (line);
+ input [6] = new Integer (encoding);
+ input [7] = new Integer (readBufferOverflow);
+ input [8] = is;
+ input [9] = new Integer (currentByteCount);
+ input [10] = new Integer (column);
+ input [11] = reader;
+
+ // Push it onto the stack.
+ inputStack.push (input);
+ }
+
+
+ /**
+ * Restore a previous input source.
+ * <p>This method restores all of the global variables associated with
+ * the current input source.
+ * @exception java.io.EOFException
+ * If there are no more entries on the input stack.
+ * @see #pushInput
+ * @see #sourceType
+ * @see #externalEntity
+ * @see #readBuffer
+ * @see #readBufferPos
+ * @see #readBufferLength
+ * @see #line
+ * @see #encoding
+ */
+ private void popInput ()
+ throws SAXException, IOException
+ {
+ String ename = (String) entityStack.pop ();
+
+ if (ename != null && doReport)
+ dataBufferFlush ();
+ switch (sourceType) {
+ case INPUT_STREAM:
+ handler.endExternalEntity (ename);
+ is.close ();
+ break;
+ case INPUT_READER:
+ handler.endExternalEntity (ename);
+ reader.close ();
+ break;
+ case INPUT_INTERNAL:
+ if (ename != null && doReport)
+ handler.endInternalEntity (ename);
+ break;
+ }
+
+ // Throw an EOFException if there
+ // is nothing else to pop.
+ if (inputStack.isEmpty ()) {
+ throw new EOFException ("no more input");
+ }
+
+ Object input [] = (Object[]) inputStack.pop ();
+
+ sourceType = ((Integer) input [0]).intValue ();
+ externalEntity = (URLConnection) input [1];
+ readBuffer = (char[]) input [2];
+ readBufferPos = ((Integer) input [3]).intValue ();
+ readBufferLength = ((Integer) input [4]).intValue ();
+ line = ((Integer) input [5]).intValue ();
+ encoding = ((Integer) input [6]).intValue ();
+ readBufferOverflow = ((Integer) input [7]).intValue ();
+ is = (InputStream) input [8];
+ currentByteCount = ((Integer) input [9]).intValue ();
+ column = ((Integer) input [10]).intValue ();
+ reader = (Reader) input [11];
+ }
+
+
+ /**
+ * Return true if we can read the expected character.
+ * <p>Note that the character will be removed from the input stream
+ * on success, but will be put back on failure. Do not attempt to
+ * read the character again if the method succeeds.
+ * @param delim The character that should appear next. For a
+ * insensitive match, you must supply this in upper-case.
+ * @return true if the character was successfully read, or false if
+ * it was not.
+ * @see #tryRead (String)
+ */
+ private boolean tryRead (char delim)
+ throws SAXException, IOException
+ {
+ char c;
+
+ // Read the character
+ c = readCh ();
+
+ // Test for a match, and push the character
+ // back if the match fails.
+ if (c == delim) {
+ return true;
+ } else {
+ unread (c);
+ return false;
+ }
+ }
+
+
+ /**
+ * Return true if we can read the expected string.
+ * <p>This is simply a convenience method.
+ * <p>Note that the string will be removed from the input stream
+ * on success, but will be put back on failure. Do not attempt to
+ * read the string again if the method succeeds.
+ * <p>This method will push back a character rather than an
+ * array whenever possible (probably the majority of cases).
+ * @param delim The string that should appear next.
+ * @return true if the string was successfully read, or false if
+ * it was not.
+ * @see #tryRead (char)
+ */
+ private boolean tryRead (String delim)
+ throws SAXException, IOException
+ {
+ return tryRead (delim.toCharArray ());
+ }
+
+ private boolean tryRead (char ch [])
+ throws SAXException, IOException
+ {
+ char c;
+
+ // Compare the input, character-
+ // by character.
+
+ for (int i = 0; i < ch.length; i++) {
+ c = readCh ();
+ if (c != ch [i]) {
+ unread (c);
+ if (i != 0) {
+ unread (ch, i);
+ }
+ return false;
+ }
+ }
+ return true;
+ }
+
+
+
+ /**
+ * Return true if we can read some whitespace.
+ * <p>This is simply a convenience method.
+ * <p>This method will push back a character rather than an
+ * array whenever possible (probably the majority of cases).
+ * @return true if whitespace was found.
+ */
+ private boolean tryWhitespace ()
+ throws SAXException, IOException
+ {
+ char c;
+ c = readCh ();
+ if (isWhitespace (c)) {
+ skipWhitespace ();
+ return true;
+ } else {
+ unread (c);
+ return false;
+ }
+ }
+
+
+ /**
+ * Read all data until we find the specified string.
+ * This is useful for scanning CDATA sections and PIs.
+ * <p>This is inefficient right now, since it calls tryRead ()
+ * for every character.
+ * @param delim The string delimiter
+ * @see #tryRead (String, boolean)
+ * @see #readCh
+ */
+ private void parseUntil (String delim)
+ throws SAXException, IOException
+ {
+ parseUntil (delim.toCharArray ());
+ }
+
+ private void parseUntil (char delim [])
+ throws SAXException, IOException
+ {
+ char c;
+ int startLine = line;
+
+ try {
+ while (!tryRead (delim)) {
+ c = readCh ();
+ dataBufferAppend (c);
+ }
+ } catch (EOFException e) {
+ error ("end of input while looking for delimiter "
+ + "(started on line " + startLine
+ + ')', null, new String (delim));
+ }
+ }
+
+
+ //////////////////////////////////////////////////////////////////////
+ // Low-level I/O.
+ //////////////////////////////////////////////////////////////////////
+
+
+ /**
+ * Prefetch US-ASCII XML/text decl from input stream into read buffer.
+ * Doesn't buffer more than absolutely needed, so that when an encoding
+ * decl says we need to create an InputStreamReader, we can discard our
+ * buffer and reset(). Caller knows the first chars of the decl exist
+ * in the input stream.
+ */
+ private void prefetchASCIIEncodingDecl ()
+ throws SAXException, IOException
+ {
+ int ch;
+ readBufferPos = readBufferLength = 0;
+
+ is.mark (readBuffer.length);
+ while (true) {
+ ch = is.read ();
+ readBuffer [readBufferLength++] = (char) ch;
+ switch (ch) {
+ case (int) '>':
+ return;
+ case -1:
+ error ("file ends before end of XML or encoding declaration.",
+ null, "?>");
+ }
+ if (readBuffer.length == readBufferLength)
+ error ("unfinished XML or encoding declaration");
+ }
+ }
+
+ /**
+ * Read a chunk of data from an external input source.
+ * <p>This is simply a front-end that fills the rawReadBuffer
+ * with bytes, then calls the appropriate encoding handler.
+ * @see #encoding
+ * @see #rawReadBuffer
+ * @see #readBuffer
+ * @see #filterCR
+ * @see #copyUtf8ReadBuffer
+ * @see #copyIso8859_1ReadBuffer
+ * @see #copyUcs_2ReadBuffer
+ * @see #copyUcs_4ReadBuffer
+ */
+ private void readDataChunk ()
+ throws SAXException, IOException
+ {
+ int count;
+
+ // See if we have any overflow (filterCR sets for CR at end)
+ if (readBufferOverflow > -1) {
+ readBuffer [0] = (char) readBufferOverflow;
+ readBufferOverflow = -1;
+ readBufferPos = 1;
+ sawCR = true;
+ } else {
+ readBufferPos = 0;
+ sawCR = false;
+ }
+
+ // input from a character stream.
+ if (sourceType == INPUT_READER) {
+ count = reader.read (readBuffer,
+ readBufferPos, READ_BUFFER_MAX - readBufferPos);
+ if (count < 0)
+ readBufferLength = readBufferPos;
+ else
+ readBufferLength = readBufferPos + count;
+ if (readBufferLength > 0)
+ filterCR (count >= 0);
+ sawCR = false;
+ return;
+ }
+
+ // Read as many bytes as possible into the raw buffer.
+ count = is.read (rawReadBuffer, 0, READ_BUFFER_MAX);
+
+ // Dispatch to an encoding-specific reader method to populate
+ // the readBuffer. In most parser speed profiles, these routines
+ // show up at the top of the CPU usage chart.
+ if (count > 0) {
+ switch (encoding) {
+ // one byte builtins
+ case ENCODING_ASCII:
+ copyIso8859_1ReadBuffer (count, (char) 0x0080);
+ break;
+ case ENCODING_UTF_8:
+ copyUtf8ReadBuffer (count);
+ break;
+ case ENCODING_ISO_8859_1:
+ copyIso8859_1ReadBuffer (count, (char) 0);
+ break;
+
+ // two byte builtins
+ case ENCODING_UCS_2_12:
+ copyUcs2ReadBuffer (count, 8, 0);
+ break;
+ case ENCODING_UCS_2_21:
+ copyUcs2ReadBuffer (count, 0, 8);
+ break;
+
+ // four byte builtins
+ case ENCODING_UCS_4_1234:
+ copyUcs4ReadBuffer (count, 24, 16, 8, 0);
+ break;
+ case ENCODING_UCS_4_4321:
+ copyUcs4ReadBuffer (count, 0, 8, 16, 24);
+ break;
+ case ENCODING_UCS_4_2143:
+ copyUcs4ReadBuffer (count, 16, 24, 0, 8);
+ break;
+ case ENCODING_UCS_4_3412:
+ copyUcs4ReadBuffer (count, 8, 0, 24, 16);
+ break;
+ }
+ } else
+ readBufferLength = readBufferPos;
+
+ readBufferPos = 0;
+
+ // Filter out all carriage returns if we've seen any
+ // (including any saved from a previous read)
+ if (sawCR) {
+ filterCR (count >= 0);
+ sawCR = false;
+
+ // must actively report EOF, lest some CRs get lost.
+ if (readBufferLength == 0 && count >= 0)
+ readDataChunk ();
+ }
+
+ if (count > 0)
+ currentByteCount += count;
+ }
+
+
+ /**
+ * Filter carriage returns in the read buffer.
+ * CRLF becomes LF; CR becomes LF.
+ * @param moreData true iff more data might come from the same source
+ * @see #readDataChunk
+ * @see #readBuffer
+ * @see #readBufferOverflow
+ */
+ private void filterCR (boolean moreData)
+ {
+ int i, j;
+
+ readBufferOverflow = -1;
+
+loop:
+ for (i = j = readBufferPos; j < readBufferLength; i++, j++) {
+ switch (readBuffer [j]) {
+ case '\r':
+ if (j == readBufferLength - 1) {
+ if (moreData) {
+ readBufferOverflow = '\r';
+ readBufferLength--;
+ } else // CR at end of buffer
+ readBuffer [i++] = '\n';
+ break loop;
+ } else if (readBuffer [j + 1] == '\n') {
+ j++;
+ }
+ readBuffer [i] = '\n';
+ break;
+
+ case '\n':
+ default:
+ readBuffer [i] = readBuffer [j];
+ break;
+ }
+ }
+ readBufferLength = i;
+ }
+
+ /**
+ * Convert a buffer of UTF-8-encoded bytes into UTF-16 characters.
+ * <p>When readDataChunk () calls this method, the raw bytes are in
+ * rawReadBuffer, and the final characters will appear in
+ * readBuffer.
+ * <p>Note that as of Unicode 3.1, good practice became a requirement,
+ * so that each Unicode character has exactly one UTF-8 representation.
+ * @param count The number of bytes to convert.
+ * @see #readDataChunk
+ * @see #rawReadBuffer
+ * @see #readBuffer
+ * @see #getNextUtf8Byte
+ */
+ private void copyUtf8ReadBuffer (int count)
+ throws SAXException, IOException
+ {
+ int i = 0;
+ int j = readBufferPos;
+ int b1;
+ char c = 0;
+
+ /*
+ // check once, so the runtime won't (if it's smart enough)
+ if (count < 0 || count > rawReadBuffer.length)
+ throw new ArrayIndexOutOfBoundsException (Integer.toString (count));
+ */
+
+ while (i < count) {
+ b1 = rawReadBuffer [i++];
+
+ // Determine whether we are dealing
+ // with a one-, two-, three-, or four-
+ // byte sequence.
+ if (b1 < 0) {
+ if ((b1 & 0xe0) == 0xc0) {
+ // 2-byte sequence: 00000yyyyyxxxxxx = 110yyyyy 10xxxxxx
+ c = (char) (((b1 & 0x1f) << 6)
+ | getNextUtf8Byte (i++, count));
+ if (c < 0x0080)
+ encodingError ("Illegal two byte UTF-8 sequence",
+ c, 0);
+ //Sec 2.11
+ // [1] the two-character sequence #xD #xA
+ // [2] the two-character sequence #xD #x85
+ if ((c == 0x0085 || c == 0x000a) && sawCR)
+ continue;
+
+ // Sec 2.11
+ // [3] the single character #x85
+
+ if(c == 0x0085 && xmlVersion == XML_11)
+ readBuffer[j++] = '\r';
+ } else if ((b1 & 0xf0) == 0xe0) {
+ // 3-byte sequence:
+ // zzzzyyyyyyxxxxxx = 1110zzzz 10yyyyyy 10xxxxxx
+ // most CJKV characters
+ c = (char) (((b1 & 0x0f) << 12) |
+ (getNextUtf8Byte (i++, count) << 6) |
+ getNextUtf8Byte (i++, count));
+ //sec 2.11
+ //[4] the single character #x2028
+ if(c == 0x2028 && xmlVersion == XML_11){
+ readBuffer[j++] = '\r';
+ sawCR = true;
+ continue;
+ }
+ if (c < 0x0800 || (c >= 0xd800 && c <= 0xdfff))
+ encodingError ("Illegal three byte UTF-8 sequence",
+ c, 0);
+ } else if ((b1 & 0xf8) == 0xf0) {
+ // 4-byte sequence: 11101110wwwwzzzzyy + 110111yyyyxxxxxx
+ // = 11110uuu 10uuzzzz 10yyyyyy 10xxxxxx
+ // (uuuuu = wwww + 1)
+ // "Surrogate Pairs" ... from the "Astral Planes"
+ // Unicode 3.1 assigned the first characters there
+ int iso646 = b1 & 07;
+ iso646 = (iso646 << 6) + getNextUtf8Byte (i++, count);
+ iso646 = (iso646 << 6) + getNextUtf8Byte (i++, count);
+ iso646 = (iso646 << 6) + getNextUtf8Byte (i++, count);
+
+ if (iso646 <= 0xffff) {
+ encodingError ("Illegal four byte UTF-8 sequence",
+ iso646, 0);
+ } else {
+ if (iso646 > 0x0010ffff)
+ encodingError (
+ "UTF-8 value out of range for Unicode",
+ iso646, 0);
+ iso646 -= 0x010000;
+ readBuffer [j++] = (char) (0xd800 | (iso646 >> 10));
+ readBuffer [j++] = (char) (0xdc00 | (iso646 & 0x03ff));
+ continue;
+ }
+ } else {
+ // The five and six byte encodings aren't supported;
+ // they exceed the Unicode (and XML) range.
+ encodingError (
+ "unsupported five or six byte UTF-8 sequence",
+ 0xff & b1, i);
+ // NOTREACHED
+ c = 0;
+ }
+ } else {
+ // 1-byte sequence: 000000000xxxxxxx = 0xxxxxxx
+ // (US-ASCII character, "common" case, one branch to here)
+ c = (char) b1;
+ }
+ readBuffer [j++] = c;
+ if (c == '\r')
+ sawCR = true;
+ }
+ // How many characters have we read?
+ readBufferLength = j;
+ }
+
+
+ /**
+ * Return the next byte value in a UTF-8 sequence.
+ * If it is not possible to get a byte from the current
+ * entity, throw an exception.
+ * @param pos The current position in the rawReadBuffer.
+ * @param count The number of bytes in the rawReadBuffer
+ * @return The significant six bits of a non-initial byte in
+ * a UTF-8 sequence.
+ * @exception EOFException If the sequence is incomplete.
+ */
+ private int getNextUtf8Byte (int pos, int count)
+ throws SAXException, IOException
+ {
+ int val;
+
+ // Take a character from the buffer
+ // or from the actual input stream.
+ if (pos < count) {
+ val = rawReadBuffer [pos];
+ } else {
+ val = is.read ();
+ if (val == -1) {
+ encodingError ("unfinished multi-byte UTF-8 sequence at EOF",
+ -1, pos);
+ }
+ }
+
+ // Check for the correct bits at the start.
+ if ((val & 0xc0) != 0x80) {
+ encodingError ("bad continuation of multi-byte UTF-8 sequence",
+ val, pos + 1);
+ }
+
+ // Return the significant bits.
+ return (val & 0x3f);
+ }
+
+
+ /**
+ * Convert a buffer of US-ASCII or ISO-8859-1-encoded bytes into
+ * UTF-16 characters.
+ *
+ * <p>When readDataChunk () calls this method, the raw bytes are in
+ * rawReadBuffer, and the final characters will appear in
+ * readBuffer.
+ *
+ * @param count The number of bytes to convert.
+ * @param mask For ASCII conversion, 0x7f; else, 0xff.
+ * @see #readDataChunk
+ * @see #rawReadBuffer
+ * @see #readBuffer
+ */
+ private void copyIso8859_1ReadBuffer (int count, char mask)
+ throws IOException
+ {
+ int i, j;
+ for (i = 0, j = readBufferPos; i < count; i++, j++) {
+ char c = (char) (rawReadBuffer [i] & 0xff);
+ if ((c & mask) != 0)
+ throw new CharConversionException ("non-ASCII character U+"
+ + Integer.toHexString (c));
+ if (c == 0x0085 && xmlVersion == XML_11)
+ c = '\r';
+ readBuffer [j] = c;
+ if (c == '\r') {
+ sawCR = true;
+ }
+ }
+ readBufferLength = j;
+ }
+
+
+ /**
+ * Convert a buffer of UCS-2-encoded bytes into UTF-16 characters
+ * (as used in Java string manipulation).
+ *
+ * <p>When readDataChunk () calls this method, the raw bytes are in
+ * rawReadBuffer, and the final characters will appear in
+ * readBuffer.
+ * @param count The number of bytes to convert.
+ * @param shift1 The number of bits to shift byte 1.
+ * @param shift2 The number of bits to shift byte 2
+ * @see #readDataChunk
+ * @see #rawReadBuffer
+ * @see #readBuffer
+ */
+ private void copyUcs2ReadBuffer (int count, int shift1, int shift2)
+ throws SAXException
+ {
+ int j = readBufferPos;
+
+ if (count > 0 && (count % 2) != 0) {
+ encodingError ("odd number of bytes in UCS-2 encoding", -1, count);
+ }
+ // The loops are faster with less internal brancing; hence two
+ if (shift1 == 0) { // "UTF-16-LE"
+ for (int i = 0; i < count; i += 2) {
+ char c = (char) (rawReadBuffer [i + 1] << 8);
+ c |= 0xff & rawReadBuffer [i];
+ readBuffer [j++] = c;
+ if (c == '\r')
+ sawCR = true;
+ }
+ } else { // "UTF-16-BE"
+ for (int i = 0; i < count; i += 2) {
+ char c = (char) (rawReadBuffer [i] << 8);
+ c |= 0xff & rawReadBuffer [i + 1];
+ readBuffer [j++] = c;
+ if (c == '\r')
+ sawCR = true;
+ }
+ }
+ readBufferLength = j;
+ }
+
+
+ /**
+ * Convert a buffer of UCS-4-encoded bytes into UTF-16 characters.
+ *
+ * <p>When readDataChunk () calls this method, the raw bytes are in
+ * rawReadBuffer, and the final characters will appear in
+ * readBuffer.
+ * <p>Java has Unicode chars, and this routine uses surrogate pairs
+ * for ISO-10646 values between 0x00010000 and 0x000fffff. An
+ * exception is thrown if the ISO-10646 character has no Unicode
+ * representation.
+ *
+ * @param count The number of bytes to convert.
+ * @param shift1 The number of bits to shift byte 1.
+ * @param shift2 The number of bits to shift byte 2
+ * @param shift3 The number of bits to shift byte 2
+ * @param shift4 The number of bits to shift byte 2
+ * @see #readDataChunk
+ * @see #rawReadBuffer
+ * @see #readBuffer
+ */
+ private void copyUcs4ReadBuffer (int count, int shift1, int shift2,
+ int shift3, int shift4)
+ throws SAXException
+ {
+ int j = readBufferPos;
+
+ if (count > 0 && (count % 4) != 0) {
+ encodingError (
+ "number of bytes in UCS-4 encoding not divisible by 4",
+ -1, count);
+ }
+ for (int i = 0; i < count; i += 4) {
+ int value = (((rawReadBuffer [i] & 0xff) << shift1) |
+ ((rawReadBuffer [i + 1] & 0xff) << shift2) |
+ ((rawReadBuffer [i + 2] & 0xff) << shift3) |
+ ((rawReadBuffer [i + 3] & 0xff) << shift4));
+ if (value < 0x0000ffff) {
+ readBuffer [j++] = (char) value;
+ if (value == (int) '\r') {
+ sawCR = true;
+ }
+ } else if (value < 0x0010ffff) {
+ value -= 0x010000;
+ readBuffer [j++] = (char) (0xd8 | ((value >> 10) & 0x03ff));
+ readBuffer [j++] = (char) (0xdc | (value & 0x03ff));
+ } else {
+ encodingError ("UCS-4 value out of range for Unicode",
+ value, i);
+ }
+ }
+ readBufferLength = j;
+ }
+
+
+ /**
+ * Report a character encoding error.
+ */
+ private void encodingError (String message, int value, int offset)
+ throws SAXException
+ {
+ if (value != -1)
+ message = message + " (character code: 0x" +
+ Integer.toHexString (value) + ')';
+ error (message);
+ }
+
+
+ //////////////////////////////////////////////////////////////////////
+ // Local Variables.
+ //////////////////////////////////////////////////////////////////////
+
+ /**
+ * Re-initialize the variables for each parse.
+ */
+ private void initializeVariables ()
+ {
+ // First line
+ line = 1;
+ column = 0;
+
+ // Set up the buffers for data and names
+ dataBufferPos = 0;
+ dataBuffer = new char [DATA_BUFFER_INITIAL];
+ nameBufferPos = 0;
+ nameBuffer = new char [NAME_BUFFER_INITIAL];
+
+ // Set up the DTD hash tables
+ elementInfo = new Hashtable ();
+ entityInfo = new Hashtable ();
+ notationInfo = new Hashtable ();
+ skippedPE = false;
+
+ // Set up the variables for the current
+ // element context.
+ currentElement = null;
+ currentElementContent = CONTENT_UNDECLARED;
+
+ // Set up the input variables
+ sourceType = INPUT_NONE;
+ inputStack = new Stack ();
+ entityStack = new Stack ();
+ externalEntity = null;
+ tagAttributePos = 0;
+ tagAttributes = new String [100];
+ rawReadBuffer = new byte [READ_BUFFER_MAX];
+ readBufferOverflow = -1;
+
+ scratch = new InputSource ();
+
+ inLiteral = false;
+ expandPE = false;
+ peIsError = false;
+
+ doReport = false;
+
+ inCDATA = false;
+
+ symbolTable = new Object [SYMBOL_TABLE_LENGTH][];
+ }
+
+
+ //
+ // The current XML handler interface.
+ //
+ private SAXDriver handler;
+
+ //
+ // I/O information.
+ //
+ private Reader reader; // current reader
+ private InputStream is; // current input stream
+ private int line; // current line number
+ private int column; // current column number
+ private int sourceType; // type of input source
+ private Stack inputStack; // stack of input soruces
+ private URLConnection externalEntity; // current external entity
+ private int encoding; // current character encoding
+ private int currentByteCount; // bytes read from current source
+ private InputSource scratch; // temporary
+
+ //
+ // Buffers for decoded but unparsed character input.
+ //
+ private char readBuffer [];
+ private int readBufferPos;
+ private int readBufferLength;
+ private int readBufferOverflow; // overflow from last data chunk.
+
+
+ //
+ // Buffer for undecoded raw byte input.
+ //
+ private final static int READ_BUFFER_MAX = 16384;
+ private byte rawReadBuffer [];
+
+
+ //
+ // Buffer for attribute values, char refs, DTD stuff.
+ //
+ private static int DATA_BUFFER_INITIAL = 4096;
+ private char dataBuffer [];
+ private int dataBufferPos;
+
+ //
+ // Buffer for parsed names.
+ //
+ private static int NAME_BUFFER_INITIAL = 1024;
+ private char nameBuffer [];
+ private int nameBufferPos;
+
+ //
+ // Save any standalone flag
+ //
+ private boolean docIsStandalone;
+
+ //
+ // Hashtables for DTD information on elements, entities, and notations.
+ // Populated until we start ignoring decls (because of skipping a PE)
+ //
+ private Hashtable elementInfo;
+ private Hashtable entityInfo;
+ private Hashtable notationInfo;
+ private boolean skippedPE;
+
+
+ //
+ // Element type currently in force.
+ //
+ private String currentElement;
+ private int currentElementContent;
+
+ //
+ // Stack of entity names, to detect recursion.
+ //
+ private Stack entityStack;
+
+ //
+ // PE expansion is enabled in most chunks of the DTD, not all.
+ // When it's enabled, literals are treated differently.
+ //
+ private boolean inLiteral;
+ private boolean expandPE;
+ private boolean peIsError;
+
+ //
+ // can't report entity expansion inside two constructs:
+ // - attribute expansions (internal entities only)
+ // - markup declarations (parameter entities only)
+ //
+ private boolean doReport;
+
+ //
+ // Symbol table, for caching interned names.
+ //
+ // These show up wherever XML names or nmtokens are used: naming elements,
+ // attributes, PIs, notations, entities, and enumerated attribute values.
+ //
+ // NOTE: This hashtable doesn't grow. The default size is intended to be
+ // rather large for most documents. Example: one snapshot of the DocBook
+ // XML 4.1 DTD used only about 350 such names. As a rule, only pathological
+ // documents (ones that don't reuse names) should ever see much collision.
+ //
+ // Be sure that SYMBOL_TABLE_LENGTH always stays prime, for best hashing.
+ // "2039" keeps the hash table size at about two memory pages on typical
+ // 32 bit hardware.
+ //
+ private final static int SYMBOL_TABLE_LENGTH = 2039;
+
+ private Object symbolTable [][];
+
+ //
+ // Hash table of attributes found in current start tag.
+ //
+ private String tagAttributes [];
+ private int tagAttributePos;
+
+ //
+ // Utility flag: have we noticed a CR while reading the last
+ // data chunk? If so, we will have to go back and normalise
+ // CR or CR/LF line ends.
+ //
+ private boolean sawCR;
+
+ //
+ // Utility flag: are we in CDATA? If so, whitespace isn't ignorable.
+ //
+ private boolean inCDATA;
+
+ //
+ // Xml version.
+ //
+ private static final int XML_10 = 0;
+ private static final int XML_11 = 1;
+ private int xmlVersion = XML_10;
+}
diff --git a/libjava/gnu/xml/aelfred2/XmlReader.java b/libjava/gnu/xml/aelfred2/XmlReader.java
new file mode 100644
index 0000000..96c9c72
--- /dev/null
+++ b/libjava/gnu/xml/aelfred2/XmlReader.java
@@ -0,0 +1,315 @@
+/* XmlReader.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.aelfred2;
+
+import java.io.IOException;
+import java.util.Locale;
+
+import org.xml.sax.*;
+import org.xml.sax.ext.*;
+
+import gnu.xml.pipeline.EventFilter;
+import gnu.xml.pipeline.ValidationConsumer;
+
+
+/**
+ * This SAX2 parser optionally layers a validator over the &AElig;lfred2
+ * SAX2 parser. While this will not evaluate every XML validity constraint,
+ * it does support all the validity constraints that are of any real utility
+ * outside the strict SGML-compatible world. See the documentation for the
+ * SAXDriver class for information about the SAX2 features and properties
+ * that are supported, and documentation for the ValidationConsumer for
+ * information about what validity constraints may not be supported.
+ * (&AElig;lfred2 tests some of those, even in non-validating mode, to
+ * achieve better conformance.)
+ *
+ * <p> Note that due to its internal construction, you can't change most
+ * handlers until parse() returns. This diverges slightly from SAX, which
+ * expects later binding to be supported. Early binding involves less
+ * runtime overhead, which is an issue for event pipelines as used inside
+ * this parser. Rather than relying on the parser to handle late binding
+ * to your own handlers, do it yourself.
+ *
+ * @see SAXDriver
+ * @see gnu.xml.pipeline.ValidationConsumer
+ *
+ * @author David Brownell
+ */
+public final class XmlReader implements XMLReader
+{
+ private SAXDriver aelfred2 = new SAXDriver ();
+ private EventFilter filter = new EventFilter ();
+ private boolean isValidating;
+ private boolean active;
+
+
+ /** Constructs a SAX Parser. */
+ public XmlReader ()
+ { }
+
+ /**
+ * Constructs a SAX Parser, optionally treating validity errors
+ * as if they were fatal errors.
+ */
+ public XmlReader (boolean invalidIsFatal)
+ {
+ if (invalidIsFatal)
+ setErrorHandler (new DefaultHandler2 () {
+ public void error (SAXParseException e)
+ throws SAXException
+ { throw e; }
+ });
+ }
+
+ /**
+ * <b>SAX2</b>: Returns the object used to report the logical
+ * content of an XML document.
+ */
+ public ContentHandler getContentHandler ()
+ { return filter.getContentHandler (); }
+
+ /**
+ * <b>SAX2</b>: Assigns the object used to report the logical
+ * content of an XML document.
+ * @exception IllegalStateException if called mid-parse
+ */
+ public void setContentHandler (ContentHandler handler)
+ {
+ if (active)
+ throw new IllegalStateException ("already parsing");
+ filter.setContentHandler (handler);
+ }
+
+ /**
+ * <b>SAX2</b>: Returns the object used to process declarations related
+ * to notations and unparsed entities.
+ */
+ public DTDHandler getDTDHandler ()
+ { return filter.getDTDHandler (); }
+
+ /**
+ * <b>SAX1</b> Assigns DTD handler
+ * @exception IllegalStateException if called mid-parse
+ */
+ public void setDTDHandler (DTDHandler handler)
+ {
+ if (active)
+ throw new IllegalStateException ("already parsing");
+ filter.setDTDHandler (handler);
+ }
+
+ /**
+ * <b>SAX2</b>: Returns the object used when resolving external
+ * entities during parsing (both general and parameter entities).
+ */
+ public EntityResolver getEntityResolver ()
+ { return aelfred2.getEntityResolver (); }
+
+ /** <b>SAX1</b> Assigns parser's entity resolver */
+ public void setEntityResolver (EntityResolver handler)
+ { aelfred2.setEntityResolver (handler); }
+
+ /**
+ * <b>SAX2</b>: Returns the object used to receive callbacks for XML
+ * errors of all levels (fatal, nonfatal, warning); this is never null;
+ */
+ public ErrorHandler getErrorHandler ()
+ { return aelfred2.getErrorHandler (); }
+
+ /**
+ * <b>SAX1</b> Assigns error handler
+ * @exception IllegalStateException if called mid-parse
+ */
+ public void setErrorHandler (ErrorHandler handler)
+ {
+ if (active)
+ throw new IllegalStateException ("already parsing");
+ aelfred2.setErrorHandler (handler);
+ }
+
+ /**
+ * <b>SAX2</b>: Assigns the specified property.
+ * @exception IllegalStateException if called mid-parse
+ */
+ public void setProperty (String propertyId, Object value)
+ throws SAXNotRecognizedException, SAXNotSupportedException
+ {
+ if (active)
+ throw new IllegalStateException ("already parsing");
+ if (getProperty (propertyId) != value)
+ filter.setProperty (propertyId, value);
+ }
+
+ /**
+ * <b>SAX2</b>: Returns the specified property.
+ */
+ public Object getProperty (String propertyId)
+ throws SAXNotRecognizedException
+ {
+ if ((SAXDriver.PROPERTY + "declaration-handler")
+ .equals (propertyId)
+ || (SAXDriver.PROPERTY + "lexical-handler")
+ .equals (propertyId))
+ return filter.getProperty (propertyId);
+ throw new SAXNotRecognizedException (propertyId);
+ }
+
+ private void forceValidating ()
+ throws SAXNotRecognizedException, SAXNotSupportedException
+ {
+ aelfred2.setFeature (
+ SAXDriver.FEATURE + "namespace-prefixes",
+ true);
+ aelfred2.setFeature (
+ SAXDriver.FEATURE + "external-general-entities",
+ true);
+ aelfred2.setFeature (
+ SAXDriver.FEATURE + "external-parameter-entities",
+ true);
+ }
+
+ /**
+ * <b>SAX2</b>: Sets the state of features supported in this parser.
+ * Note that this parser requires reporting of namespace prefixes when
+ * validating.
+ */
+ public void setFeature (String featureId, boolean state)
+ throws SAXNotRecognizedException, SAXNotSupportedException
+ {
+ boolean value = getFeature (featureId);
+
+ if (state == value)
+ return;
+
+ if ((SAXDriver.FEATURE + "validation").equals (featureId)) {
+ if (active)
+ throw new SAXNotSupportedException ("already parsing");
+ if (state)
+ forceValidating ();
+ isValidating = state;
+ } else
+ aelfred2.setFeature (featureId, state);
+ }
+
+ /**
+ * <b>SAX2</b>: Tells whether this parser supports the specified feature.
+ * At this time, this directly parallels the underlying SAXDriver,
+ * except that validation is optionally supported.
+ *
+ * @see SAXDriver
+ */
+ public boolean getFeature (String featureId)
+ throws SAXNotRecognizedException, SAXNotSupportedException
+ {
+ if ((SAXDriver.FEATURE + "validation").equals (featureId))
+ return isValidating;
+
+ return aelfred2.getFeature (featureId);
+ }
+
+ /**
+ * <b>SAX1</b>: Sets the locale used for diagnostics; currently,
+ * only locales using the English language are supported.
+ * @param locale The locale for which diagnostics will be generated
+ */
+ public void setLocale (Locale locale)
+ throws SAXException
+ { aelfred2.setLocale (locale); }
+
+ /**
+ * <b>SAX1</b>: Preferred API to parse an XML document, using a
+ * system identifier (URI).
+ */
+ public void parse (String systemId)
+ throws SAXException, IOException
+ {
+ parse (new InputSource (systemId));
+ }
+
+ /**
+ * <b>SAX1</b>: Underlying API to parse an XML document, used
+ * directly when no URI is available. When this is invoked,
+ * and the parser is set to validate, some features will be
+ * automatically reset to appropriate values: for reporting
+ * namespace prefixes, and incorporating external entities.
+ *
+ * @param source The XML input source.
+ *
+ * @exception IllegalStateException if called mid-parse
+ * @exception SAXException The handlers may throw any SAXException,
+ * and the parser normally throws SAXParseException objects.
+ * @exception IOException IOExceptions are normally through through
+ * the parser if there are problems reading the source document.
+ */
+ public void parse (InputSource source)
+ throws SAXException, IOException
+ {
+ EventFilter next;
+ boolean nsdecls;
+
+ synchronized (aelfred2) {
+ if (active)
+ throw new IllegalStateException ("already parsing");
+ active = true;
+ }
+
+ // set up the output pipeline
+ if (isValidating) {
+ forceValidating ();
+ next = new ValidationConsumer (filter);
+ } else
+ next = filter;
+
+ // connect pipeline and error handler
+ // don't let _this_ call to bind() affect xmlns* attributes
+ nsdecls = aelfred2.getFeature (
+ SAXDriver.FEATURE + "namespace-prefixes");
+ EventFilter.bind (aelfred2, next);
+ if (!nsdecls)
+ aelfred2.setFeature (
+ SAXDriver.FEATURE + "namespace-prefixes",
+ false);
+
+ // parse, clean up
+ try {
+ aelfred2.parse (source);
+ } finally {
+ active = false;
+ }
+ }
+}
diff --git a/libjava/gnu/xml/aelfred2/package.html b/libjava/gnu/xml/aelfred2/package.html
new file mode 100644
index 0000000..e204258
--- /dev/null
+++ b/libjava/gnu/xml/aelfred2/package.html
@@ -0,0 +1,506 @@
+<!DOCTYPE html PUBLIC
+ '-//W3C//DTD XHTML 1.0 Transitional//EN'
+ 'http://www.w3.org/TR/xhtml1/DTD/transitional.dtd'>
+
+<html><head>
+ <title>package overview</title>
+<!--
+/*
+ * Copyright (C) 1999,2000,2001 The Free Software Foundation, Inc.
+ */
+-->
+</head><body>
+
+<p> This package contains &AElig;lfred2, which includes an
+enhanced SAX2-compatible version of the &AElig;lfred
+non-validating XML parser, a modular (and hence optional)
+DTD validating parser, and modular (and hence optional)
+JAXP glue to those.
+Use these like any other SAX2 parsers. </p>
+
+<ul>
+ <li><a href="#about">About &AElig;lfred</a><ul>
+ <li><a href="#principles">Design Principles</a></li>
+ <li><a href="#name">About the Name &AElig;lfred</a></li>
+ <li><a href="#encodings">Character Encodings</a></li>
+ <li><a href="#violations">Known Conformance Violations</a></li>
+ <li><a href="#copyright">Licensing</a></li>
+ </ul></li>
+
+ <li><a href="#changes">Changes Since the Last Microstar Release</a><ul>
+ <li><a href="#sax2">SAX2 Support</a></li>
+ <li><a href="#validation">Validation</a></li>
+ <li><a href="#smaller">You Want Smaller?</a></li>
+ <li><a href="#bugfixes">Bugs Fixed</a></li>
+ </ul></li>
+
+</ul>
+
+<h2><a name="about">About &AElig;lfred</a></h2>
+
+<p>&AElig;lfred is a XML parser written in the java programming language.
+
+<h3><a name="principles">Design Principles</a></h3>
+
+<p>In most Java applets and applications, XML should not be the central
+feature; instead, XML is the means to another end, such as loading
+configuration information, reading meta-data, or parsing transactions.</p>
+
+<p> When an XML parser is only a single component of a much larger
+program, it cannot be large, slow, or resource-intensive. With Java
+applets, in particular, code size is a significant issue. The standard
+modem is still not operating at 56 Kbaud, or sometimes even with data
+compression. Assuming an uncompressed 28.8 Kbaud modem, only about
+3 KBytes can be downloaded in one second; compression often doubles
+that speed, but a V.90 modem may not provide another doubling. When
+used with embedded processors, similar size concerns apply. </p>
+
+<p> &AElig;lfred is designed for easy and efficient use over the Internet,
+based on the following principles: </p> <ol>
+
+<li> &AElig;lfred must be as small as possible, so that it doesn't add too
+ much to an applet's download time. </li>
+
+<li> &AElig;lfred must use as few class files as possible, to minimize the
+ number of HTTP connections necessary. (The use of JAR files has made this
+ be less of a concern.) </li>
+
+<li> &AElig;lfred must be compatible with most or all Java implementations
+ and platforms. (Write once, run anywhere.) </li>
+
+<li> &AElig;lfred must use as little memory as possible, so that it does
+ not take away resources from the rest of your program. (It doesn't force
+ you to use DOM or a similar costly data structure API.)</li>
+
+<li> &AElig;lfred must run as fast as possible, so that it does not slow down
+ the rest of your program. </li>
+
+<li> &AElig;lfred must produce correct output for well-formed and valid
+ documents, but need not reject every document that is not valid or
+ not well-formed. (In &AElig;lfred2, correctness was a bigger concern
+ than in the original version; and a validation option is available.) </li>
+
+<li> &AElig;lfred must provide full internationalization from the first
+ release. (&AElig;lfred2 now automatically handles all encodings
+ supported by the underlying JVM; previous versions handled only
+ UTF-8, UTF_16, ASCII, and ISO-8859-1.)</li>
+
+</ol>
+
+<p>As you can see from this list, &AElig;lfred is designed for production
+use, but neither validation nor perfect conformance was a requirement.
+Good validating parsers exist, including one in this package,
+and you should use them as appropriate. (See conformance reviews
+available at <a href="http://www.xml.com/">http://www.xml.com</a>)
+</p>
+
+<p> One of the main goals of &AElig;lfred2 was to significantly improve
+conformance, while not significantly affecting the other goals stated above.
+Since the only use of this parser is with SAX, some classes could be
+removed, and so the overall size of &AElig;lfred was actually reduced.
+Subsequent performance work produced a notable speedup (over twenty
+percent on larger files). That is, the tradeoffs between speed, size, and
+conformance were re-targeted towards conformance and support of newer APIs
+(SAX2), with a a positive performance impact. </p>
+
+<p> The role anticipated for this version of &AElig;lfred is as a
+lightweight Free Software SAX parser that can be used in essentially every
+Java program where the handful of conformance violations (noted below)
+are acceptable.
+That certainly includes applets, and
+nowadays one must also mention embedded systems as being even more
+size-critical.
+At this writing, all parsers that are more conformant are
+significantly larger, even when counting the optional
+validation support in this version of &AElig;lfred. </p>
+
+
+<h3><a name="name">About the Name <em>&AElig;lfred</em></a></h3>
+
+<p>&AElig;lfred the Great (AElfred in ASCII) was King of Wessex, and
+some say of King of England, at the time of his death in 899 AD.
+&AElig;lfred introduced a wide-spread literacy program in the hope that
+his people would learn to read English, at least, if Latin was too
+difficult for them. This &AElig;lfred hopes to bring another sort of
+literacy to Java, using XML, at least, if full SGML is too difficult.</p>
+
+<p>The initial &AElig; ligature ("AE)" is also a reminder that XML is
+not limited to ASCII.</p>
+
+
+<h3><a name="encodings">Character Encodings</a></h3>
+
+<p> The &AElig;lfred parser currently builds in support for a handful
+of input encodings. Of course these include UTF-8 and UTF-16, which
+all XML parsers are required to support:</p> <ul>
+
+ <li> UTF-8 ... the standard eight bit encoding, used unless
+ you provide an encoding declaration or a MIME charset tag.</li>
+
+ <li> US-ASCII ... an extremely common seven bit encoding,
+ which happens to be a subset of UTF-8 and ISO-8859-1 as well
+ as many other encodings. XHTML web pages using US-ASCII
+ (without an encoding declaration) are probably more
+ widely interoperable than those in any other encoding. </li>
+
+ <li> ISO-8859-1 ... includes accented characters used in
+ much of western Europe (but excluding the Euro currency
+ symbol).</li>
+
+ <li> UTF-16 ... with several variants, this encodes each
+ sixteen bit Unicode character in sixteen bits of output.
+ Variants include UTF-16BE (big endian, no byte order mark),
+ UTF-16LE (little endian, no byte order mark), and
+ ISO-10646-UCS-2 (an older and less used encoding, using a
+ version of Unicode without surrogate pairs). This is
+ essentially the native encoding used by Java. </li>
+
+ <li> ISO-10646-UCS-4 ... a seldom-used four byte encoding,
+ also known as UTF-32BE. Four byte order variants are supported,
+ including one known as UTF-32LE. Some operating systems
+ standardized on UCS-4 despite its significant size penalty,
+ in anticipation that Unicode (even with surrogate pairs)
+ would eventually become limiting. UCS-4 permits encoding
+ of non-Unicode characters, which Java can't represent (and
+ XML doesn't allow).
+ </li>
+
+ </ul>
+
+<p> If you use any encoding other than UTF-8 or UTF-16 you should
+make sure to label your data appropriately: </p>
+
+<blockquote>
+&lt;?xml version="1.0" encoding="<b>ISO-8859-15</b>"?&gt;
+</blockquote>
+
+<p> Encodings accessed through <code>java.io.InputStreamReader</code>
+are now fully supported for both external labels (such as MIME types)
+and internal types (as shown above).
+There is one limitation in the support for internal labels:
+the encodings must be derived from the US-ASCII encoding,
+the EBCDIC family of encodings is not recognized.
+Note that Java defines its
+own encoding names, which don't always correspond to the standard
+Internet encoding names defined by the IETF/IANA, and that Java
+may even <em>require</em> use of nonstandard encoding names.
+Please report
+such problems; some of them can be worked around in this parser,
+and many can be worked around by using external labels.
+</p>
+
+<p>Note that if you are using the Euro symbol with an fixed length
+eight bit encoding, you should probably be using the encoding label
+<em>iso-8859-15</em> or, with a Microsoft OS, <em>cp-1252</em>.
+Of course, UTF-8 and UTF-16 handle the Euro symbol directly.
+</p>
+
+
+<h3><a name="violations">Known Conformance Violations</a></h3>
+
+<p>Known conformance issues should be of negligible importance for
+most applications, and include: </p><ul>
+
+ <li> Rather than following the voluminous "Appendix B" rules about
+ what characters may appear in names (and name tokens), the Unicode
+ rules embedded in <em>java.lang.Character</em> are used.
+ This means mostly that some names are inappropriately accepted,
+ though a few are inappropriately rejected. (It's much simpler
+ to avoid that much special case code. Recent OASIS/NIST test
+ cases may have these rules be realistically testable.) </li>
+
+ <li> Text containing "]]&gt;" is not rejected unless it fully resides
+ in an internal buffer ... which is, thankfully, the typical case. This
+ text is illegal, but sometimes appears in illegal attempts to
+ nest CDATA sections. (Not catching that boundary condition
+ substantially simplifies parsing text.) </li>
+
+ <li> Surrogate characters that aren't correctly paired are ignored
+ rather than rejected, unless they were encoded using UTF-8. (This
+ simplifies parsing text.) Unicode 3.1 assigned the first characters
+ to those character codes, in early 2001, so few documents (or tools)
+ use such characters in any case. </li>
+
+ <li> Declarations following references to an undefined parameter
+ entity reference are not ignored. (Not maintaining and using state
+ about this validity error simplifies declaration handling; few
+ XML parsers address this constraint in any case.) </li>
+
+ <li> Well formedness constraints for general entity references
+ are not enforced. (The code to handle the "content" production
+ is merged with the element parsing code, making it hard to reuse
+ for this additional situation.) </li>
+
+</ul>
+
+<p> When tested against the July 12, 1999 version of the OASIS
+XML Conformance test suite, an earlier version passed 1057 of 1067 tests.
+That contrasts with the original version, which passed 867. The
+current parser is top-ranked in terms of conformance, as is its
+validating sibling (which has some additional conformance violations
+imposed on it by SAX2 API deficiencies as well as some of the more
+curious SGML layering artifacts found in the XML specification). </p>
+
+<p> The XML 1.0 specification itself was not without problems,
+and after some delays the W3C has come out with a revised
+"second edition" specification. While that doesn't resolve all
+the problems identified the XML specification, many of the most
+egregious problems have been resolved. (You still need to drink
+magic Kool-Aid before some DTD-related issues make sense.)
+To the extent possible, this parser conforms to that second
+edition specification, and does well against corrected versions
+of the OASIS/NIST XML conformance test cases. See <a href=
+"http://xmlconf.sourceforge.net">http://xmlconf.sourceforge.net</a>
+for more information about SAX2/XML conformance testing. </p>
+
+
+<h3><a name="copyright">Copyright and distribution terms</a></h3>
+
+<p>
+The software in this package is distributed under the GNU General Public
+License (with a special exception described below).
+</p>
+
+<p>
+A copy of GNU General Public License (GPL) is included in this distribution,
+in the file COPYING. If you do not have the source code, it is available at:
+
+ <a href="http://www.gnu.org/software/classpath/">http://www.gnu.org/software/classpath/</a>
+</p>
+
+<pre>
+ 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.
+
+ Parts derived from code which carried the following notice:
+
+ Copyright (c) 1997, 1998 by Microstar Software Ltd.
+
+ AElfred is free for both commercial and non-commercial use and
+ redistribution, provided that Microstar's copyright and disclaimer are
+ retained intact. You are free to modify AElfred for your own use and
+ to redistribute AElfred with your modifications, provided that the
+ modifications are clearly documented.
+
+ This program 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. Please use it AT
+ YOUR OWN RISK.
+</pre>
+
+<p> Some of this documentation was modified from the original
+&AElig;lfred README.txt file. All of it has been updated. </p>
+
+</p>
+
+
+<h2><a name="changes">Changes Since the last Microstar Release</a></h2>
+
+<p> As noted above, Microstar has not updated this parser since
+the summer of 1998, when it released version 1.2a on its web site.
+This release is intended to benefit the developer community by
+refocusing the API on SAX2, and improving conformance to the extent
+that most developers should not need to use another XML parser. </p>
+
+<p> The code has been cleaned up (referring to the XML 1.0 spec in
+all the production numbers in
+comments, rather than some preliminary draft, for one example) and
+has been sped up a bit as well.
+JAXP support has been added, although developers are still
+strongly encouraged to use the SAX2 APIs directly. </p>
+
+
+<h3><a name="sax2">SAX2 Support</a></h3>
+
+<p> The original version of &AElig;lfred did not support the
+SAX2 APIs. </p>
+
+<p> This version supports the SAX2 APIs, exposing the standard
+boolean feature descriptors. It supports the "DeclHandler" property
+to provide access to all DTD declarations not already exposed
+through the SAX1 API. The "LexicalHandler" property is supported,
+exposing entity boundaries (including the unnamed external subset) and
+things like comments and CDATA boundaries. SAX1 compatibility is
+currently provided.</p>
+
+
+<h3><a name="validation">Validation</a></h3>
+
+<p> In the 'pipeline' package in this same software distribution is an
+<a href="../pipeline/ValidationConsumer.html">XML Validation component</a>
+using any full SAX2 event stream (including all document type declarations)
+to validate. There is now a <a href="XmlReader.html">XmlReader</a> class
+which combines that class and this enhanced &AElig;lfred parser, creating
+an optionally validating SAX2 parser. </p>
+
+<p> As noted in the documentation for that validating component, certain
+validity constraints can't reliably be tested by a layered validator.
+These include all constraints relying on
+layering violations (exposing XML at the level of tokens or below,
+required since XML isn't a context-free grammar), some that
+SAX2 doesn't support, and a few others. The resulting validating
+parser is conformant enough for most applications that aren't doing
+strange SGML tricks with DTDs.
+Moreover, that validating filter can be used without
+a parser ... any application component that emits SAX event streams
+can DTD-validate its output on demand. </p>
+
+<h3><a name="smaller">You want Smaller?</a></h3>
+
+<p> You'll have noticed that the original version of &AElig;lfred
+had small size as a top goal. &AElig;lfred2 normally includes a
+DTD validation layer, but you can package without that.
+Similarly, JAXP factory support is available but optional.
+Then the main added cost due to this revision are for
+supporting the SAX2 API itself; DTD validation is as
+cleanly layered as allowed by SAX2.</p>
+
+<h3><a name="bugfixes">Bugs Fixed</a></h3>
+
+<p> Bugs fixed in &AElig;lfred2 include: </p>
+
+<ol>
+ <li> Originally &AElig;lfred didn't close file descriptors, which
+ led to file descriptor leakage on programs which ran for any
+ length of time. </li>
+
+ <li> NOTATION declarations without system identifiers are
+ now handled correctly. </li>
+
+ <li> DTD events are now reported for all invocations of a
+ given parser, not just the first one. </li>
+
+ <li> More correct character handling: <ul>
+
+ <li> Rejects out-of-range characters, both in text and in
+ character references. </li>
+
+ <li> Correctly handles character references that expand to
+ surrogate pairs. </li>
+
+ <li> Correctly handles UTF-8 encodings of surrogate pairs. </li>
+
+ <li> Correctly handles Unicode 3.1 rules about illegal UTF-8
+ encodings: there is only one legal encoding per character. </li>
+
+ <li> PUBLIC identifiers are now rejected if they have illegal
+ characters. </li>
+
+ <li> The parser is more correct about what characters are allowed
+ in names and name tokens. Uses Unicode rules (built in to Java)
+ rather than the voluminous XML rules, although some extensions
+ have been made to match XML rules more closely.</li>
+
+ <li> Line ends are now normalized to newlines in all known
+ cases. </li>
+
+ </ul></li>
+
+ <li> Certain validity errors were previously treated as well
+ formedness violations. <ul>
+
+ <li> Repeated declarations of an element type are no
+ longer fatal errors. </li>
+
+ <li> Undeclared parameter entity references are no longer
+ fatal errors. </li>
+
+ </ul></li>
+
+ <li> Attribute handling is improved: <ul>
+
+ <li> Whitespace must exist between attributes. </li>
+
+ <li> Only one value for a given attribute is permitted. </li>
+
+ <li> ATTLIST declarations don't need to declare attributes. </li>
+
+ <li> Attribute values are normalized when required. </li>
+
+ <li> Tabs in attribute values are normalized to spaces. </li>
+
+ <li> Attribute values containing a literal "&lt;" are rejected. </li>
+
+ </ul></li>
+
+ <li> More correct entity handling: <ul>
+
+ <li> Whitespace must precede NDATA when declaring unparsed
+ entities.</li>
+
+ <li> Parameter entity declarations may not have NDATA annotations. </li>
+
+ <li> The XML specification has a bug in that it doesn't specify
+ that certain contexts exist within which parameter entity
+ expansion must not be performed. Lacking an offical erratum,
+ this parser now disables such expansion inside comments,
+ processing instructions, ignored sections, public identifiers,
+ and parts of entity declarations. </li>
+
+ <li> Entity expansions that include quote characters no longer
+ confuse parsing of strings using such expansions. </li>
+
+ <li> Whitespace in the values of internal entities is not mapped
+ to space characters. </li>
+
+ <li> General Entity references in attribute defaults within the
+ DTD now cause fatal errors when the entity is not defined at the
+ time it is referenced. </li>
+
+ <li> Malformed general entity references in entity declarations are
+ now detected. </li>
+
+ </ul></li>
+
+ <li> Neither conditional sections
+ nor parameter entity references within markup declarations
+ are permitted in the internal subset. </li>
+
+ <li> Processing instructions whose target names are "XML"
+ (ignoring case) are now rejected. </li>
+
+ <li> Comments may not include "--".</li>
+
+ <li> Most "]]&gt;" sequences in text are rejected. </li>
+
+ <li> Correct syntax for standalone declarations is enforced. </li>
+
+ <li> Setting a locale for diagnostics only produces an exception
+ if the language of that locale isn't English. </li>
+
+ <li> Some more encoding names are recognized. These include the
+ Unicode 3.0 variants of UTF-16 (UTF-16BE, UTF-16LE) as well as
+ US-ASCII and a few commonly seen synonyms. </li>
+
+ <li> Text (from character content, PIs, or comments) large enough
+ not to fit into internal buffers is now handled correctly even in
+ some cases which were originally handled incorrectly.</li>
+
+ <li> Content is now reported for element types for which attributes
+ have been declared, but no content model is known. (Such documents
+ are invalid, but may still be well formed.) </li>
+
+</ol>
+
+<p> Other bugs may also have been fixed. </p>
+
+<p> For better overall validation support, some of the validity
+constraints that can't be verified using the SAX2 event stream
+are now reported directly by &AElig;lfred2. </p>
+
+</body></html>
+
diff --git a/libjava/gnu/xml/dom/Consumer.java b/libjava/gnu/xml/dom/Consumer.java
new file mode 100644
index 0000000..1aad5be
--- /dev/null
+++ b/libjava/gnu/xml/dom/Consumer.java
@@ -0,0 +1,373 @@
+/* Consumer.java --
+ Copyright (C) 2001,2004 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.dom;
+
+import java.io.IOException;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.DocumentType;
+import org.w3c.dom.DOMImplementation;
+import org.w3c.dom.Node;
+import org.w3c.dom.Text;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.ErrorHandler;
+import org.xml.sax.SAXException;
+import org.xml.sax.ext.Attributes2;
+
+import gnu.xml.pipeline.DomConsumer;
+import gnu.xml.pipeline.EventConsumer;
+
+
+/**
+ * Event consumer which constructs DOM documents using the implementation
+ * in this package, using SAX2 events. This packages various backdoors
+ * into this DOM implementation, as needed to address DOM requirements
+ * that can't be met by strictly conforming implementations of DOM.
+ *
+ * <p> These requirements all relate to {@link DocumentType} nodes and
+ * features of that node type. These features are normally not used,
+ * because that interface only exposes a subset of the information found
+ * in DTDs. More, that subset does not include the most important typing
+ * information. For example, it excludes element content models and
+ * attribute typing. It does expose some entity management issues,
+ * although entity management doesn't relate to document typing.
+ *
+ * <p> Note that SAX2 does not expose the literal text of the DTD's
+ * internal subset, so it will not be present in DOM trees constructed
+ * using this API. (Though with a good SAX2 implementation, it could
+ * be partially recreated...)
+ *
+ * @author David Brownell
+ */
+public class Consumer extends DomConsumer
+{
+ /**
+ * Constructs an unconfigured event consumer,
+ * as a terminus in a SAX event pipeline.
+ */
+ // used by PipelineFactory [terminus]
+ public Consumer ()
+ throws SAXException
+ {
+ super (DomDocument.class);
+ setHandler (new Backdoor (this));
+ }
+
+ /**
+ * Constructs an unconfigured event consumer,
+ * as a stage in a SAX event pipeline.
+ */
+ // used by PipelineFactory [filter]
+ public Consumer (EventConsumer next)
+ throws SAXException
+ {
+ super (DomDocument.class, next);
+ setHandler (new Backdoor (this));
+ }
+
+ /**
+ * Implements the backdoors needed by DOM.
+ * All methods in this class use implementation-specific APIs that are
+ * implied by the DOM specification (needed to implement testable
+ * behavior) but which are excluded from the DOM specification.
+ */
+ public static class Backdoor extends DomConsumer.Handler
+ {
+ /**
+ * Constructor.
+ * @param consumer must have been initialized to use the
+ * {@link DomDocument} class (or a subclass) for
+ * constructing DOM trees
+ */
+ protected Backdoor (DomConsumer consumer)
+ throws SAXException
+ { super (consumer); }
+
+ // helper routine
+ private DomDoctype getDoctype ()
+ throws SAXException
+ {
+ DomDocument doc = (DomDocument) getDocument ();
+ DocumentType dt = doc.getDoctype ();
+
+ if (dt == null)
+ throw new SAXException ("doctype missing!");
+ return (DomDoctype) dt;
+ }
+
+ // SAX2 "lexical" event
+ public void startDTD (String name, String publicId, String systemId)
+ throws SAXException
+ {
+ DomDocument doc = (DomDocument) getDocument ();
+
+ super.startDTD (name, publicId, systemId);
+ // DOM L2 doctype creation model is bizarre
+ DomDoctype dt = new DomDoctype (doc, name, publicId, systemId);
+ doc.appendChild (dt);
+ }
+
+ // SAX2 "lexical" event
+ public void endDTD ()
+ throws SAXException
+ {
+ super.endDTD ();
+ // DOM L2 has no way to make things readonly
+ getDoctype ().makeReadonly ();
+ }
+
+ // SAX1 DTD event
+ public void notationDecl (
+ String name,
+ String publicId, String systemId
+ ) throws SAXException
+ {
+ // DOM L2 can't create/save notation nodes
+ getDoctype ().declareNotation (name, publicId, systemId);
+ }
+
+ // SAX1 DTD event
+ public void unparsedEntityDecl (
+ String name,
+ String publicId, String systemId,
+ String notationName
+ ) throws SAXException
+ {
+ // DOM L2 can't create/save entity nodes
+ getDoctype ().declareEntity (name, publicId, systemId,
+ notationName);
+ }
+
+ // SAX2 declaration event
+ public void internalEntityDecl (String name, String value)
+ throws SAXException
+ {
+ // DOM L2 can't create/save entity nodes
+ // NOTE: this doesn't save the value as a child of this
+ // node, though it could realistically do so.
+ getDoctype ().declareEntity (name, null, null, null);
+ }
+
+ // SAX2 declaration event
+ public void externalEntityDecl (
+ String name,
+ String publicId,
+ String systemId
+ ) throws SAXException
+ {
+ // DOM L2 can't create/save entity nodes
+ // NOTE: DOM allows for these to have children, if
+ // they don't have unbound namespace references.
+ getDoctype ().declareEntity (name, publicId, systemId, null);
+ }
+
+ // SAX2 element
+ public void startElement (
+ String uri,
+ String localName,
+ String qName,
+ Attributes atts
+ ) throws SAXException
+ {
+ Node top;
+
+ super.startElement (uri, localName, qName, atts);
+
+ // might there be more work?
+ top = getTop ();
+ if (!top.hasAttributes () || !(atts instanceof Attributes2))
+ return;
+
+ // remember any attributes that got defaulted
+ DomNamedNodeMap map = (DomNamedNodeMap) top.getAttributes ();
+ Attributes2 attrs = (Attributes2) atts;
+ int length = atts.getLength ();
+
+ //map.compact ();
+ for (int i = 0; i < length; i++) {
+ if (attrs.isSpecified (i))
+ continue;
+
+ // value was defaulted.
+ String temp = attrs.getQName (i);
+ DomAttr attr;
+
+ if ("".equals (temp))
+ attr = (DomAttr) map.getNamedItemNS (attrs.getURI (i),
+ atts.getLocalName (i));
+ else
+ attr = (DomAttr) map.getNamedItem (temp);
+
+ // DOM L2 can't write this flag, only read it
+ attr.setSpecified (false);
+ }
+ }
+
+ public void endElement (
+ String uri,
+ String localName,
+ String qName
+ ) throws SAXException
+ {
+ DomNode top = (DomNode) getTop ();
+ top.compact ();
+ super.endElement (uri, localName, qName);
+ }
+
+ protected Text createText (
+ boolean isCDATA,
+ char buf [],
+ int off,
+ int len
+ ) {
+ DomDocument doc = (DomDocument) getDocument ();
+
+ if (isCDATA)
+ return doc.createCDATASection (buf, off, len);
+ else
+ return doc.createTextNode (buf, off, len);
+ }
+
+ public void elementDecl(String name, String model)
+ throws SAXException
+ {
+ getDoctype().elementDecl(name, model);
+ }
+
+ public void attributeDecl (
+ String ename,
+ String aname,
+ String type,
+ String mode,
+ String value
+ ) throws SAXException
+ {
+ getDoctype().attributeDecl(ename, aname, type, mode, value);
+ /*
+ if (value == null && !"ID".equals (type))
+ return;
+
+ DomDoctype.ElementInfo info;
+
+ info = getDoctype ().getElementInfo (ename);
+ if (value != null)
+ info.setAttrDefault (aname, value);
+ if ("ID".equals (type))
+ info.setIdAttr (aname);
+ */
+
+ }
+
+ // force duplicate name checking off while we're
+ // using parser output (don't duplicate the work)
+ public void startDocument () throws SAXException
+ {
+ super.startDocument ();
+
+ DomDocument doc = (DomDocument) getDocument ();
+ doc.setStrictErrorChecking(false);
+ doc.setBuilding(true);
+ }
+
+ /**
+ * Required by DOM Level 3 to report document parameters
+ */
+ public void xmlDecl(String version,
+ String encoding,
+ boolean standalone,
+ String inputEncoding)
+ throws SAXException
+ {
+ super.xmlDecl(version, encoding, standalone, inputEncoding);
+
+ DomDocument doc = (DomDocument) getDocument();
+ doc.setXmlEncoding(encoding);
+ doc.setInputEncoding(inputEncoding);
+ }
+
+ public void endDocument ()
+ throws SAXException
+ {
+ DomDocument doc = (DomDocument) getDocument ();
+ doc.setStrictErrorChecking(true);
+ doc.setBuilding(false);
+ doc.compact ();
+ DomDoctype doctype = (DomDoctype) doc.getDoctype();
+ if (doctype != null)
+ {
+ doctype.makeReadonly();
+ }
+ super.endDocument ();
+ }
+
+ // these three methods collaborate to populate entity
+ // refs, marking contents readonly on end-of-entity
+
+ public boolean canPopulateEntityRefs ()
+ { return true; }
+
+ public void startEntity (String name)
+ throws SAXException
+ {
+ if (name.charAt (0) == '%' || "[dtd]".equals (name))
+ return;
+ super.startEntity (name);
+
+ DomNode top = (DomNode) getTop ();
+
+ if (top.getNodeType () == Node.ENTITY_REFERENCE_NODE)
+ top.readonly = false;
+ }
+
+ public void endEntity (String name)
+ throws SAXException
+ {
+ if (name.charAt (0) == '%' || "[dtd]".equals (name))
+ return;
+ DomNode top = (DomNode) getTop ();
+
+ if (top.getNodeType () == Node.ENTITY_REFERENCE_NODE) {
+ top.compact ();
+ top.makeReadonly ();
+ }
+ super.endEntity (name);
+ }
+ }
+}
diff --git a/libjava/gnu/xml/dom/DTDAttributeTypeInfo.java b/libjava/gnu/xml/dom/DTDAttributeTypeInfo.java
new file mode 100644
index 0000000..53e5fbd
--- /dev/null
+++ b/libjava/gnu/xml/dom/DTDAttributeTypeInfo.java
@@ -0,0 +1,84 @@
+/* DTDAttributeTypeInfo.java --
+ Copyright (C) 2004 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.dom;
+
+import org.w3c.dom.TypeInfo;
+
+/**
+ * Attribute type information supplied by a DTD attribute declaration.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+class DTDAttributeTypeInfo
+ implements TypeInfo
+{
+
+ final String elementName;
+ final String name;
+ final String type;
+ final String mode;
+ final String value;
+
+ DTDAttributeTypeInfo(String elementName, String name, String type,
+ String mode, String value)
+ {
+ this.elementName = elementName;
+ this.name = name;
+ this.type = type;
+ this.mode = mode;
+ this.value = value;
+ }
+
+ public final String getTypeName()
+ {
+ return type;
+ }
+
+ public final String getTypeNamespace()
+ {
+ return "http://www.w3.org/TR/REC-xml";
+ }
+
+ public final boolean isDerivedFrom(String typeNamespace, String typeName,
+ int derivationMethod)
+ {
+ return false;
+ }
+
+}
+
diff --git a/libjava/gnu/xml/dom/DTDElementTypeInfo.java b/libjava/gnu/xml/dom/DTDElementTypeInfo.java
new file mode 100644
index 0000000..535ea3c
--- /dev/null
+++ b/libjava/gnu/xml/dom/DTDElementTypeInfo.java
@@ -0,0 +1,112 @@
+/* DTDElementTypeInfo.java --
+ Copyright (C) 2004 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.dom;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import org.w3c.dom.TypeInfo;
+
+/**
+ * Element type information provided by a DTD element declaration.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+class DTDElementTypeInfo
+ implements TypeInfo
+{
+
+ final String name;
+ String model;
+ HashMap attributes;
+ String idAttrName;
+
+ DTDElementTypeInfo(String name, String model)
+ {
+ this.name = name;
+ this.model = model;
+ }
+
+ public final String getTypeName()
+ {
+ return null;
+ }
+
+ public final String getTypeNamespace()
+ {
+ return "http://www.w3.org/TR/REC-xml";
+ }
+
+ public final boolean isDerivedFrom(String typeNamespace, String typeName,
+ int derivationMethod)
+ {
+ return false;
+ }
+
+ DTDAttributeTypeInfo getAttributeTypeInfo(String name)
+ {
+ if (attributes == null)
+ {
+ return null;
+ }
+ return (DTDAttributeTypeInfo) attributes.get(name);
+ }
+
+ void setAttributeTypeInfo(String name, DTDAttributeTypeInfo info)
+ {
+ if (attributes == null)
+ {
+ attributes = new HashMap();
+ }
+ attributes.put(name, info);
+ if ("ID".equals(info.type))
+ {
+ idAttrName = name;
+ }
+ }
+
+ Iterator attributes()
+ {
+ if (attributes == null)
+ {
+ return null;
+ }
+ return attributes.values().iterator();
+ }
+
+}
+
diff --git a/libjava/gnu/xml/dom/DomAttr.java b/libjava/gnu/xml/dom/DomAttr.java
new file mode 100644
index 0000000..2890430
--- /dev/null
+++ b/libjava/gnu/xml/dom/DomAttr.java
@@ -0,0 +1,379 @@
+/* DomAttr.java --
+ Copyright (C) 1999,2000,2001,2004 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.dom;
+
+import org.w3c.dom.Attr;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.TypeInfo;
+import org.w3c.dom.events.MutationEvent;
+
+
+/**
+ * <p> "Attr" implementation. In DOM, attributes cost quite a lot of
+ * memory because their values are complex structures rather than just
+ * simple strings. To reduce your costs, avoid having more than one
+ * child of an attribute; stick to a single Text node child, and ignore
+ * even that by using the attribute's "nodeValue" property.</p>
+ *
+ * <p> As a bit of general advice, only look at attribute modification
+ * events through the DOMAttrModified event (sent to the associated
+ * element). Implementations are not guaranteed to report other events
+ * in the same order, so you're very likely to write nonportable code if
+ * you monitor events at the "children of Attr" level.</p>
+ *
+ * <p> At this writing, not all attribute modifications will cause the
+ * DOMAttrModified event to be triggered ... only the ones using the string
+ * methods (setNodeValue, setValue, and Element.setAttribute) to modify
+ * those values. That is, if you manipulate those children directly,
+ * elements won't get notified that attribute values have changed.
+ * The natural fix for that will report other modifications, but won't
+ * be able to expose "previous" attribute value; it'll need to be cached
+ * or something (at which point why bother using child nodes). </p>
+ *
+ * <p><em>You are strongly advised not to use "children" of any attribute
+ * nodes you work with.</em> </p>
+ *
+ * @author David Brownell
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+public class DomAttr
+ extends DomNsNode
+ implements Attr
+{
+
+ private boolean specified;
+ private String value; // string value cache
+
+ /**
+ * Constructs an Attr node associated with the specified document.
+ * The "specified" flag is initialized to true, since this DOM has
+ * no current "back door" mechanisms to manage default values so
+ * that every value must effectively be "specified".
+ *
+ * <p>This constructor should only be invoked by a Document as part of
+ * its createAttribute functionality, or through a subclass which is
+ * similarly used in a "Sub-DOM" style layer.
+ *
+ * @param owner The document with which this node is associated
+ * @param namespaceURI Combined with the local part of the name,
+ * this is used to uniquely identify a type of attribute
+ * @param name Name of this attribute, which may include a prefix
+ */
+ protected DomAttr(DomDocument owner, String namespaceURI, String name)
+ {
+ super(ATTRIBUTE_NODE, owner, namespaceURI, name);
+ specified = true;
+ length = 1;
+
+ // XXX register self to get insertion/removal events
+ // and character data change events and when they happen,
+ // report self-mutation
+ }
+
+ /**
+ * <b>DOM L1</b>
+ * Returns the attribute name (same as getNodeName)
+ */
+ public final String getName()
+ {
+ return getNodeName();
+ }
+
+ /**
+ * <b>DOM L1</b>
+ * Returns true if a parser reported this was in the source text.
+ */
+ public final boolean getSpecified()
+ {
+ return specified;
+ }
+
+ /**
+ * Records whether this attribute was in the source text.
+ */
+ public final void setSpecified(boolean value)
+ {
+ specified = value;
+ }
+
+ /**
+ * <b>DOM L1</b>
+ * Returns the attribute value, with character and entity
+ * references substituted.
+ * <em>NOTE: entity refs as children aren't currently handled.</em>
+ */
+ public String getNodeValue()
+ {
+ // If we have a simple node-value, use that
+ if (first == null)
+ {
+ return (value == null) ? "" : value;
+ }
+ // Otherwise collect child node-values
+ StringBuffer buf = new StringBuffer();
+ for (DomNode ctx = first; ctx != null; ctx = ctx.next)
+ {
+ switch (ctx.nodeType)
+ {
+ case Node.TEXT_NODE:
+ buf.append(ctx.getNodeValue());
+ break;
+ case Node.ENTITY_REFERENCE_NODE:
+ // TODO
+ break;
+ }
+ }
+ return buf.toString();
+ }
+
+ /**
+ * <b>DOM L1</b>
+ * Assigns the value of the attribute; it will have one child,
+ * which is a text node with the specified value (same as
+ * setNodeValue).
+ */
+ public final void setValue(String value)
+ {
+ setNodeValue(value);
+ }
+
+ /**
+ * <b>DOM L1</b>
+ * Returns the value of the attribute as a non-null string; same
+ * as getNodeValue.
+ * <em>NOTE: entity refs as children aren't currently handled.</em>
+ */
+ public final String getValue()
+ {
+ return getNodeValue();
+ }
+
+ /**
+ * <b>DOM L1</b>
+ * Assigns the attribute value; using this API, no entity or
+ * character references will exist.
+ * Causes a DOMAttrModified mutation event to be sent.
+ */
+ public void setNodeValue(String value)
+ {
+ if (readonly)
+ {
+ throw new DomEx(DomEx.NO_MODIFICATION_ALLOWED_ERR);
+ }
+ if (value == null)
+ {
+ value = "";
+ }
+ String oldValue = getNodeValue();
+ while (last != null)
+ {
+ removeChild(last);
+ }
+ // don't create a new node just for this...
+ /*
+ Node text = owner.createTextNode(value);
+ appendChild(text);
+ */
+ this.value = value;
+ length = 1;
+ specified = true;
+
+ mutating(oldValue, value, MutationEvent.MODIFICATION);
+ }
+
+ public final Node getFirstChild()
+ {
+ // Create a child text node if necessary
+ if (first == null)
+ {
+ length = 0;
+ Node text = owner.createTextNode((value == null) ? "" : value);
+ appendChild(text);
+ }
+ return first;
+ }
+
+ public final Node getLastChild()
+ {
+ // Create a child text node if necessary
+ if (last == null)
+ {
+ length = 0;
+ Node text = owner.createTextNode((value == null) ? "" : value);
+ appendChild(text);
+ }
+ return last;
+ }
+
+ public Node item(int index)
+ {
+ // Create a child text node if necessary
+ if (first == null)
+ {
+ length = 0;
+ Node text = owner.createTextNode((value == null) ? "" : value);
+ appendChild(text);
+ }
+ return super.item(index);
+ }
+
+ /**
+ * <b>DOM L2</b>
+ * Returns the element with which this attribute is associated.
+ */
+ public final Element getOwnerElement()
+ {
+ return (Element) parent;
+ }
+
+ public final Node getNextSibling()
+ {
+ return null;
+ }
+
+ public final Node getPreviousSibling()
+ {
+ return null;
+ }
+
+ public Node getParentNode()
+ {
+ return null;
+ }
+
+ /**
+ * Records the element with which this attribute is associated.
+ */
+ public final void setOwnerElement(Element e)
+ {
+ if (parent != null)
+ {
+ throw new DomEx (DomEx.HIERARCHY_REQUEST_ERR);
+ }
+ if (!(e instanceof DomElement))
+ {
+ throw new DomEx(DomEx.WRONG_DOCUMENT_ERR);
+ }
+ parent = (DomElement) e;
+ depth = parent.depth + 1;
+ }
+
+ /**
+ * The base URI of an Attr is always <code>null</code>.
+ */
+ public final String getBaseURI()
+ {
+ return null;
+ }
+
+ /**
+ * Shallow clone of the attribute, breaking all ties with any
+ * elements.
+ */
+ public Object clone()
+ {
+ DomAttr retval = (DomAttr) super.clone();
+ retval.specified = true;
+ return retval;
+ }
+
+ private void mutating(String oldValue, String newValue, short why)
+ {
+ if (!reportMutations || parent == null)
+ {
+ return;
+ }
+
+ // EVENT: DOMAttrModified, target = parent,
+ // prev/new values provided, also attr name
+ MutationEvent event;
+
+ event = (MutationEvent) createEvent ("MutationEvents");
+ event.initMutationEvent ("DOMAttrModified",
+ true /* bubbles */, false /* nocancel */,
+ null, oldValue, newValue, getNodeName (), why);
+ parent.dispatchEvent (event);
+ }
+
+ // DOM Level 3 methods
+
+ public TypeInfo getSchemaTypeInfo()
+ {
+ if (parent != null)
+ {
+ // DTD implementation
+ DomDoctype doctype = (DomDoctype) parent.owner.getDoctype();
+ if (doctype != null)
+ {
+ return doctype.getAttributeTypeInfo(parent.getNodeName(),
+ getNodeName());
+ }
+ // TODO XML Schema implementation
+ }
+ return null;
+ }
+
+ public boolean isId()
+ {
+ if (parent != null)
+ {
+ DomDoctype doctype = (DomDoctype) parent.owner.getDoctype();
+ if (doctype != null)
+ {
+ DTDAttributeTypeInfo info =
+ doctype.getAttributeTypeInfo(parent.getNodeName(),
+ getNodeName());
+ if (info != null && "ID".equals(info.type))
+ {
+ return true;
+ }
+ }
+ DomElement element = (DomElement) parent;
+ if (element.userIdAttrs != null &&
+ element.userIdAttrs.contains(this))
+ {
+ return true;
+ }
+ // TODO XML Schema implementation
+ }
+ return false;
+ }
+
+}
+
diff --git a/libjava/gnu/xml/dom/DomCDATA.java b/libjava/gnu/xml/dom/DomCDATA.java
new file mode 100644
index 0000000..f6f471c6
--- /dev/null
+++ b/libjava/gnu/xml/dom/DomCDATA.java
@@ -0,0 +1,91 @@
+/* DomCDATA.java --
+ Copyright (C) 1999,2000,2001,2004 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.dom;
+
+import org.w3c.dom.CDATASection;
+
+/**
+ * <p> "CDATASection" implementation.
+ * This is a non-core DOM class, supporting the "XML" feature.
+ * CDATA sections are just ways to represent text using different
+ * delimeters. </p>
+ *
+ * <p> <em>You are strongly advised not to use CDATASection nodes.</em>
+ * The advantage of having slightly prettier ways to print text that may
+ * have lots of embedded XML delimiters, such as "&amp;" and "&lt;",
+ * can be dwarfed by the cost of dealing with multiple kinds of text
+ * nodes in all your algorithms. </p>
+ *
+ * @author David Brownell
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+public class DomCDATA
+ extends DomText
+ implements CDATASection
+{
+
+ /**
+ * Constructs a CDATA section node associated with the specified
+ * document and holding the specified data.
+ *
+ * <p>This constructor should only be invoked by a Document as part of
+ * its createCDATASection functionality, or through a subclass which is
+ * similarly used in a "Sub-DOM" style layer.
+ *
+ */
+ protected DomCDATA(DomDocument owner, String value)
+ {
+ super(CDATA_SECTION_NODE, owner, value);
+ }
+
+ protected DomCDATA(DomDocument owner, char buf [], int off, int len)
+ {
+ super(CDATA_SECTION_NODE, owner, buf, off, len);
+ }
+
+ /**
+ * <b>DOM L1</b>
+ * Returns the string "#cdata-section".
+ */
+ final public String getNodeName()
+ {
+ return "#cdata-section";
+ }
+
+}
+
diff --git a/libjava/gnu/xml/dom/DomCharacterData.java b/libjava/gnu/xml/dom/DomCharacterData.java
new file mode 100644
index 0000000..48ccb82
--- /dev/null
+++ b/libjava/gnu/xml/dom/DomCharacterData.java
@@ -0,0 +1,311 @@
+/* DomCharacterData.java --
+ Copyright (C) 1999,2000,2001,2004 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.dom;
+
+import org.w3c.dom.CharacterData;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.w3c.dom.events.MutationEvent;
+
+
+/**
+ * <p> Abstract "CharacterData" implementation. This
+ * facilitates reusing code in classes implementing subtypes of that DOM
+ * interface (Text, Comment, CDATASection). </p>
+ *
+ * @author David Brownell
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+public abstract class DomCharacterData
+ extends DomNode
+ implements CharacterData
+{
+
+ private String text;
+
+ // package private
+ DomCharacterData(short nodeType, DomDocument doc, String value)
+ {
+ super(nodeType, doc);
+ text = (value == null) ? "" : value;
+ }
+
+ // package private
+ DomCharacterData(short nodeType, DomDocument doc,
+ char[] buf, int offset, int length)
+ {
+ super(nodeType, doc);
+ text = (buf == null) ? "" : new String(buf, offset, length);
+ }
+
+ /**
+ * <b>DOM L1</b>
+ * Appends the specified data to the value of this node.
+ * Causes a DOMCharacterDataModified mutation event to be reported.
+ */
+ public void appendData(String arg)
+ {
+ if (isReadonly())
+ {
+ throw new DomEx(DomEx.NO_MODIFICATION_ALLOWED_ERR);
+ }
+ String value = text + arg;
+ mutating(value);
+ text = value;
+ }
+
+ /**
+ * <b>DOM L1</b>
+ * Modifies the value of this node.
+ * Causes a DOMCharacterDataModified mutation event to be reported.
+ */
+ public void deleteData(int offset, int count)
+ {
+ if (isReadonly())
+ {
+ throw new DomEx(DomEx.NO_MODIFICATION_ALLOWED_ERR);
+ }
+ char[] raw = text.toCharArray();
+ if (offset < 0 || count < 0 || offset > raw.length)
+ {
+ throw new DomEx(DomEx.INDEX_SIZE_ERR);
+ }
+ if ((offset + count) > raw.length)
+ {
+ count = raw.length - offset;
+ }
+ if (count == 0)
+ {
+ return;
+ }
+ try
+ {
+ char[] buf = new char[raw.length - count];
+ System.arraycopy(raw, 0, buf, 0, offset);
+ System.arraycopy(raw, offset + count, buf, offset,
+ raw.length - (offset + count));
+ String value = new String(buf);
+ mutating(value);
+ text = value;
+ }
+ catch (IndexOutOfBoundsException x)
+ {
+ throw new DomEx(DomEx.INDEX_SIZE_ERR);
+ }
+ }
+
+ /**
+ * <b>DOM L1</b>
+ * Returns the value of this node.
+ */
+ public String getNodeValue()
+ {
+ return text;
+ }
+
+ /**
+ * <b>DOM L1</b>
+ * Returns the value of this node; same as getNodeValue.
+ */
+ public final String getData()
+ {
+ return text;
+ }
+
+ /**
+ * <b>DOM L1</b>
+ * Returns the length of the data.
+ */
+ public int getLength()
+ {
+ return text.length();
+ }
+
+ /**
+ * <b>DOM L1</b>
+ * Modifies the value of this node.
+ */
+ public void insertData(int offset, String arg)
+ {
+ if (isReadonly())
+ {
+ throw new DomEx(DomEx.NO_MODIFICATION_ALLOWED_ERR);
+ }
+ char[] raw = text.toCharArray();
+ char[] tmp = arg.toCharArray ();
+ char[] buf = new char[raw.length + tmp.length];
+
+ try
+ {
+ System.arraycopy(raw, 0, buf, 0, offset);
+ System.arraycopy(tmp, 0, buf, offset, tmp.length);
+ System.arraycopy(raw, offset, buf, offset + tmp.length,
+ raw.length - offset);
+ String value = new String(buf);
+ mutating(value);
+ text = value;
+ }
+ catch (IndexOutOfBoundsException x)
+ {
+ throw new DomEx(DomEx.INDEX_SIZE_ERR);
+ }
+ }
+
+ /**
+ * <b>DOM L1</b>
+ * Modifies the value of this node. Causes DOMCharacterDataModified
+ * mutation events to be reported (at least one).
+ */
+ public void replaceData(int offset, int count, String arg)
+ {
+ if (readonly)
+ {
+ throw new DomEx(DomEx.NO_MODIFICATION_ALLOWED_ERR);
+ }
+ char[] raw = text.toCharArray();
+
+ // deleteData
+ if (offset < 0 || count < 0 || offset > raw.length)
+ {
+ throw new DomEx(DomEx.INDEX_SIZE_ERR);
+ }
+ if ((offset + count) > raw.length)
+ {
+ count = raw.length - offset;
+ }
+ try
+ {
+ char[] buf = new char[raw.length - count];
+ System.arraycopy(raw, 0, buf, 0, offset);
+ System.arraycopy(raw, offset + count, buf, offset,
+ raw.length - (offset + count));
+
+ // insertData
+ char[] tmp = arg.toCharArray ();
+ char[] buf2 = new char[buf.length + tmp.length];
+ System.arraycopy(raw, 0, buf, 0, offset);
+ System.arraycopy(tmp, 0, buf, offset, tmp.length);
+ System.arraycopy(raw, offset, buf, offset + tmp.length,
+ raw.length - offset);
+ String value = new String(buf);
+ mutating(value);
+ text = value;
+ }
+ catch (IndexOutOfBoundsException x)
+ {
+ throw new DomEx(DomEx.INDEX_SIZE_ERR);
+ }
+ }
+
+ /**
+ * <b>DOM L1</b>
+ * Assigns the value of this node.
+ * Causes a DOMCharacterDataModified mutation event to be reported.
+ */
+ public void setNodeValue(String value)
+ {
+ if (isReadonly())
+ {
+ throw new DomEx (DomEx.NO_MODIFICATION_ALLOWED_ERR);
+ }
+ if (value == null)
+ {
+ value = "";
+ }
+ mutating(value);
+ text = value;
+ }
+
+ /**
+ * <b>DOM L1</b>
+ * Assigns the value of this node; same as setNodeValue.
+ */
+ final public void setData(String data)
+ {
+ setNodeValue(data);
+ }
+
+ /**
+ * <b>DOM L1</b>
+ * Returns the specified substring.
+ */
+ public String substringData(int offset, int count)
+ {
+ try
+ {
+ return text.substring(offset, count);
+ }
+ catch (StringIndexOutOfBoundsException e)
+ {
+ if (offset >= 0 && count >= 0 && offset < text.length())
+ {
+ return text.substring(offset);
+ }
+ throw new DomEx(DomEx.INDEX_SIZE_ERR);
+ }
+ }
+
+ /**
+ * The base URI for character data is <code>null</code>.
+ * @since DOM Level 3 Core
+ */
+ public final String getBaseURI()
+ {
+ return null;
+ }
+
+ private void mutating(String newValue)
+ {
+ if (!reportMutations)
+ {
+ return;
+ }
+
+ // EVENT: DOMCharacterDataModified, target = this,
+ // prev/new values provided
+ MutationEvent event;
+
+ event = (MutationEvent) createEvent("MutationEvents");
+ event.initMutationEvent("DOMCharacterDataModified",
+ true /* bubbles */, false /* nocancel */,
+ null, text, newValue, null, (short) 0);
+ dispatchEvent(event);
+ }
+
+}
+
diff --git a/libjava/gnu/xml/dom/DomComment.java b/libjava/gnu/xml/dom/DomComment.java
new file mode 100644
index 0000000..d1d84d1
--- /dev/null
+++ b/libjava/gnu/xml/dom/DomComment.java
@@ -0,0 +1,81 @@
+/* DomComment.java --
+ Copyright (C) 1999,2000,2001,2004 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.dom;
+
+import org.w3c.dom.Comment;
+
+/**
+ * <p> "Comment" implementation.
+ * Comments hold data intended for direct consumption by people;
+ * programs should only use ProcessingInstruction nodes. Note that
+ * since SAX makes comment reporting optional, XML systems that
+ * rely on comments (such as by using this class) will often lose
+ * those comments at some point in the processing pipeline. </p>
+ *
+ * @author David Brownell
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+public class DomComment
+ extends DomCharacterData
+ implements Comment
+{
+
+ /**
+ * Constructs a comment node associated with the specified
+ * document and holding the specified data.
+ *
+ * <p>This constructor should only be invoked by a Document as part of
+ * its createComment functionality, or through a subclass which is
+ * similarly used in a "Sub-DOM" style layer.
+ */
+ protected DomComment(DomDocument owner, String value)
+ {
+ super(COMMENT_NODE, owner, value);
+ }
+
+ /**
+ * <b>DOM L1</b>
+ * Returns the string "#comment".
+ */
+ final public String getNodeName()
+ {
+ return "#comment";
+ }
+
+}
+
diff --git a/libjava/gnu/xml/dom/DomDoctype.java b/libjava/gnu/xml/dom/DomDoctype.java
new file mode 100644
index 0000000..35fb613
--- /dev/null
+++ b/libjava/gnu/xml/dom/DomDoctype.java
@@ -0,0 +1,454 @@
+/* DomDoctype.java --
+ Copyright (C) 1999,2000,2001,2004 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.dom;
+
+import java.util.HashMap;
+import org.w3c.dom.DocumentType;
+import org.w3c.dom.DOMImplementation;
+import org.w3c.dom.Entity;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.Notation;
+
+/**
+ * <p> "DocumentType" implementation (with no extensions for supporting
+ * any document typing information). This is a non-core DOM class,
+ * supporting the "XML" feature. </p>
+ *
+ * <p> <em>Few XML applications will actually care about this partial
+ * DTD support</em>, since it doesn't expose any (!) of the data typing
+ * facilities which can motivate applications to use DTDs. It does not
+ * expose element content models, or information about attribute typing
+ * rules. Plus the information it exposes isn't very useful; as one example,
+ * DOM exposes information about unparsed ENTITY objects, which is only used
+ * with certain element attributes, but does not expose the information about
+ * those attributes which is needed to apply that data! </p>
+ *
+ * <p> Also, note that there are no nonportable ways to associate even the
+ * notation and entity information exposed by DOM with a DocumentType. While
+ * there is a DOM L2 method to construct a DocumentType, it only gives access
+ * to the textual content of the &lt;!DOCTYPE ...&gt; declaration. </p>
+ *
+ * <p> In short, <em>you are strongly advised not to rely on this incomplete
+ * DTD functionality</em> in your application code.</p>
+ *
+ * @see DomEntity
+ * @see DomEntityReference
+ * @see DomNotation
+ *
+ * @author David Brownell
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+public class DomDoctype
+ extends DomExtern
+ implements DocumentType
+{
+
+ private DomNamedNodeMap notations;
+ private DomNamedNodeMap entities;
+ private final DOMImplementation implementation;
+ private String subset;
+
+ private HashMap elements = new HashMap();
+ private boolean ids;
+
+ /**
+ * Constructs a DocumentType node associated with the specified
+ * implementation, with the specified name.
+ *
+ * <p>This constructor should only be invoked by a DOMImplementation as
+ * part of its createDocumentType functionality, or through a subclass
+ * which is similarly used in a "Sub-DOM" style layer.
+ *
+ * <p> Note that at this time there is no standard SAX API granting
+ * access to the internal subset text, so that relying on that value
+ * is not currently portable.
+ *
+ * @param impl The implementation with which this object is associated
+ * @param name Name of this root element
+ * @param publicId If non-null, provides the external subset's
+ * PUBLIC identifier
+ * @param systemId If non-null, provides the external subset's
+ * SYSTEM identifier
+ * @param internalSubset Provides the literal value (unparsed, no
+ * entities expanded) of the DTD's internal subset.
+ */
+ protected DomDoctype(DOMImplementation impl,
+ String name,
+ String publicId,
+ String systemId,
+ String internalSubset)
+ {
+ super(DOCUMENT_TYPE_NODE, null, name, publicId, systemId);
+ implementation = impl;
+ subset = internalSubset;
+ }
+
+ /**
+ * JAXP builder constructor.
+ * @param doc the document
+ * @param name the name of the document element
+ * @param publicId the public ID of the document type declaration
+ * @param systemId the system ID of the document type declaration
+ */
+ public DomDoctype(DomDocument doc,
+ String name,
+ String publicId,
+ String systemId)
+ {
+ super(DOCUMENT_TYPE_NODE, doc, name, publicId, systemId);
+ implementation = doc.getImplementation();
+ }
+
+ /**
+ * <b>DOM L1</b>
+ * Returns the root element's name (just like getNodeName).
+ */
+ final public String getName()
+ {
+ return getNodeName();
+ }
+
+ /**
+ * <b>DOM L1</b>
+ * Returns information about any general entities declared
+ * in the DTD.
+ *
+ * <p><em>Note: DOM L1 doesn't throw a DOMException here, but
+ * then it doesn't have the strange construction rules of L2.</em>
+ *
+ * @exception DOMException HIERARCHY_REQUEST_ERR if the DocumentType
+ * is not associated with a document.
+ */
+ public NamedNodeMap getEntities()
+ {
+ if (entities == null)
+ {
+ entities = new DomNamedNodeMap(this, Node.ENTITY_NODE);
+ }
+ return entities;
+ }
+
+ /**
+ * Records the declaration of a general entity in this DocumentType.
+ *
+ * @param name Name of the entity
+ * @param publicId If non-null, provides the entity's PUBLIC identifier
+ * @param systemId Provides the entity's SYSTEM identifier
+ * @param notation If non-null, provides the entity's notation
+ * (indicating an unparsed entity)
+ * @return The Entity that was declared, or null if the entity wasn't
+ * recorded (because it's a parameter entity or because an entity with
+ * this name was already declared).
+ *
+ * @exception DOMException NO_MODIFICATION_ALLOWED_ERR if the
+ * DocumentType is no longer writable.
+ * @exception DOMException HIERARCHY_REQUEST_ERR if the DocumentType
+ * is not associated with a document.
+ */
+ public Entity declareEntity(String name,
+ String publicId,
+ String systemId,
+ String notation)
+ {
+ DomEntity entity;
+
+ if (name.charAt(0) == '%' || "[dtd]".equals(name))
+ {
+ return null;
+ }
+ if (isReadonly())
+ {
+ throw new DomEx(DomEx.NO_MODIFICATION_ALLOWED_ERR);
+ }
+ getEntities();
+
+ DomDocument.checkName(name, (owner != null) ?
+ "1.1".equals(owner.getXmlVersion()) : false);
+ if (entities.getNamedItem(name) != null)
+ {
+ return null;
+ }
+
+ entity = new DomEntity(owner, name, publicId, systemId, notation);
+ entities.setNamedItem(entity);
+ return entity;
+ }
+
+ /**
+ * <b>DOM L1</b>
+ * Returns information about any notations declared in the DTD.
+ *
+ * <p><em>Note: DOM L1 doesn't throw a DOMException here, but
+ * then it doesn't have the strange construction rules of L2.</em>
+ *
+ * @exception DOMException HIERARCHY_REQUEST_ERR if the DocumentType
+ * is not associated with a document.
+ */
+ public NamedNodeMap getNotations()
+ {
+ if (notations == null)
+ {
+ notations = new DomNamedNodeMap(this, Node.NOTATION_NODE);
+ }
+ return notations;
+ }
+
+ /**
+ * Records the declaration of a notation in this DocumentType.
+ *
+ * @param name Name of the notation
+ * @param publicId If non-null, provides the notation's PUBLIC identifier
+ * @param systemId If non-null, provides the notation's SYSTEM identifier
+ * @return The notation that was declared.
+ *
+ * @exception DOMException NO_MODIFICATION_ALLOWED_ERR if the
+ * DocumentType is no longer writable.
+ * @exception DOMException HIERARCHY_REQUEST_ERR if the DocumentType
+ * is not associated with a document.
+ */
+ public Notation declareNotation(String name,
+ String publicId,
+ String systemId)
+ {
+ DomNotation notation;
+
+ if (isReadonly())
+ {
+ throw new DomEx(DomEx.NO_MODIFICATION_ALLOWED_ERR);
+ }
+ getNotations();
+
+ DomDocument.checkName(name, (owner != null) ?
+ "1.1".equals(owner.getXmlVersion()) : false);
+
+ notation = new DomNotation(owner, name, publicId, systemId);
+ notations.setNamedItem(notation);
+ return notation;
+ }
+
+ /**
+ * <b>DOM L2</b>
+ * Returns the internal subset of the document, as a string of unparsed
+ * XML declarations (and comments, PIs, whitespace); or returns null if
+ * there is no such subset. There is no vendor-independent expectation
+ * that this attribute be set, or that declarations found in it be
+ * reflected in the <em>entities</em> or <em>notations</em> attributes
+ * of this Document "Type" object.
+ *
+ * <p> Some application-specific XML profiles require that documents
+ * only use specific PUBLIC identifiers, without an internal subset
+ * to modify the interperetation of the declarations associated with
+ * that PUBLIC identifier through some standard.
+ */
+ public String getInternalSubset()
+ {
+ return subset;
+ }
+
+ /**
+ * The base URI of a DocumentType is always <code>null</code>.
+ * See the Infoset Mapping, appendix C.
+ */
+ public final String getBaseURI()
+ {
+ return null;
+ }
+
+ /**
+ * Sets the internal "readonly" flag so the node and its associated
+ * data (only lists of entities and notations, no type information
+ * at the moment) can't be changed.
+ */
+ public void makeReadonly()
+ {
+ super.makeReadonly();
+ if (entities != null)
+ {
+ entities.makeReadonly();
+ }
+ if (notations != null)
+ {
+ notations.makeReadonly();
+ }
+ }
+
+ void setOwner(DomDocument doc)
+ {
+ if (entities != null)
+ {
+ for (DomNode ctx = entities.first; ctx != null; ctx = ctx.next)
+ {
+ ctx.setOwner(doc);
+ }
+ }
+ if (notations != null)
+ {
+ for (DomNode ctx = notations.first; ctx != null; ctx = ctx.next)
+ {
+ ctx.setOwner(doc);
+ }
+ }
+ super.setOwner(doc);
+ }
+
+ /**
+ * <b>DOM L2</b>
+ * Consults the DOM implementation to determine if the requested
+ * feature is supported.
+ */
+ final public boolean supports(String feature, String version)
+ {
+ return implementation.hasFeature(feature, version);
+ }
+
+ /**
+ * Returns the implementation associated with this document type.
+ */
+ final public DOMImplementation getImplementation()
+ {
+ return implementation;
+ }
+
+ public void elementDecl(String name, String model)
+ {
+ DTDElementTypeInfo info = getElementTypeInfo(name);
+ if (info == null)
+ {
+ info = new DTDElementTypeInfo(name, model);
+ elements.put(name, info);
+ }
+ else
+ {
+ info.model = model;
+ }
+ }
+
+ DTDElementTypeInfo getElementTypeInfo(String name)
+ {
+ return (DTDElementTypeInfo) elements.get(name);
+ }
+
+ public void attributeDecl(String eName, String aName, String type,
+ String mode, String value)
+ {
+ DTDAttributeTypeInfo info = new DTDAttributeTypeInfo(eName, aName, type,
+ mode, value);
+ DTDElementTypeInfo elementInfo = getElementTypeInfo(eName);
+ if (elementInfo == null)
+ {
+ elementInfo = new DTDElementTypeInfo(eName, null);
+ elements.put(eName, elementInfo);
+ }
+ elementInfo.setAttributeTypeInfo(aName, info);
+ if ("ID".equals(type))
+ {
+ ids = true;
+ }
+ }
+
+ DTDAttributeTypeInfo getAttributeTypeInfo(String elementName, String name)
+ {
+ DTDElementTypeInfo elementInfo =
+ (DTDElementTypeInfo) elements.get(elementName);
+ return (elementInfo == null) ? null :
+ elementInfo.getAttributeTypeInfo(name);
+ }
+
+ boolean hasIds()
+ {
+ return ids;
+ }
+
+ public boolean isSameNode(Node arg)
+ {
+ if (equals(arg))
+ {
+ return true;
+ }
+ if (!(arg instanceof DocumentType))
+ {
+ return false;
+ }
+ DocumentType doctype = (DocumentType) arg;
+ if (!equal(getPublicId(), doctype.getPublicId()))
+ {
+ return false;
+ }
+ if (!equal(getSystemId(), doctype.getSystemId()))
+ {
+ return false;
+ }
+ if (!equal(getInternalSubset(), doctype.getInternalSubset()))
+ {
+ return false;
+ }
+ // TODO entities
+ // TODO notations
+ return true;
+ }
+
+ /**
+ * Shallow clone of the doctype, except that associated
+ * entities and notations are (deep) cloned.
+ */
+ public Object clone()
+ {
+ DomDoctype node = (DomDoctype) super.clone();
+
+ if (entities != null)
+ {
+ node.entities = new DomNamedNodeMap(node, Node.ENTITY_NODE);
+ for (DomNode ctx = entities.first; ctx != null; ctx = ctx.next)
+ {
+ node.entities.setNamedItem(ctx.cloneNode(true));
+ }
+ }
+ if (notations != null)
+ {
+ node.notations = new DomNamedNodeMap(node, Node.NOTATION_NODE);
+ for (DomNode ctx = notations.first; ctx != null; ctx = ctx.next)
+ {
+ node.notations.setNamedItem(ctx.cloneNode(true));
+ }
+ }
+ return node;
+ }
+
+
+}
diff --git a/libjava/gnu/xml/dom/DomDocument.java b/libjava/gnu/xml/dom/DomDocument.java
new file mode 100644
index 0000000..10693ae
--- /dev/null
+++ b/libjava/gnu/xml/dom/DomDocument.java
@@ -0,0 +1,1376 @@
+/* DomDocument.java --
+ Copyright (C) 1999,2000,2001,2004 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.dom;
+
+import java.util.Iterator;
+import javax.xml.XMLConstants;
+
+import org.w3c.dom.Attr;
+import org.w3c.dom.CDATASection;
+import org.w3c.dom.Comment;
+import org.w3c.dom.Document;
+import org.w3c.dom.DocumentFragment;
+import org.w3c.dom.DocumentType;
+import org.w3c.dom.DOMConfiguration;
+import org.w3c.dom.DOMImplementation;
+import org.w3c.dom.DOMException;
+import org.w3c.dom.Element;
+import org.w3c.dom.Entity;
+import org.w3c.dom.EntityReference;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.w3c.dom.Notation;
+import org.w3c.dom.ProcessingInstruction;
+import org.w3c.dom.Text;
+import org.w3c.dom.UserDataHandler;
+import org.w3c.dom.traversal.DocumentTraversal;
+import org.w3c.dom.traversal.NodeFilter;
+import org.w3c.dom.traversal.NodeIterator;
+import org.w3c.dom.traversal.TreeWalker;
+import org.w3c.dom.xpath.XPathEvaluator;
+import org.w3c.dom.xpath.XPathException;
+import org.w3c.dom.xpath.XPathExpression;
+import org.w3c.dom.xpath.XPathNSResolver;
+
+/**
+ * <p> "Document" and "DocumentTraversal" implementation.
+ *
+ * <p> Note that when this checks names for legality, it uses an
+ * approximation of the XML rules, not the real ones. Specifically,
+ * it uses Unicode rules, with sufficient tweaks to pass a majority
+ * of basic XML conformance tests. (The huge XML character tables are
+ * hairy to implement.)
+ *
+ * @author David Brownell
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+public class DomDocument
+ extends DomNode
+ implements Document, DocumentTraversal, XPathEvaluator
+{
+
+ private final DOMImplementation implementation;
+ private boolean checkingCharacters = true;
+ boolean checkingWellformedness = true;
+
+ boolean building; // if true, skip mutation events in the tree
+
+ DomDocumentConfiguration config;
+
+ String inputEncoding;
+ String encoding;
+ String version = "1.0";
+ boolean standalone;
+ String systemId;
+
+ /**
+ * Constructs a Document node, associating it with an instance
+ * of the DomImpl class.
+ *
+ * <p> Note that this constructor disables character checking.
+ * It is normally used when connecting a DOM to an XML parser,
+ * and duplicating such checks is undesirable. When used for
+ * purposes other than connecting to a parser, you should
+ * re-enable that checking.
+ *
+ * @see #setCheckingCharacters
+ */
+ public DomDocument()
+ {
+ this(new DomImpl());
+ }
+
+ /**
+ * Constructs a Document node, associating it with the specified
+ * implementation. This should only be used in conjunction with
+ * a specialized implementation; it will normally be called by
+ * that implementation.
+ *
+ * @see DomImpl
+ * @see #setCheckingCharacters
+ */
+ protected DomDocument(DOMImplementation impl)
+ {
+ super(DOCUMENT_NODE, null);
+ implementation = impl;
+ }
+
+ /**
+ * Sets the <code>building</code> flag.
+ * Mutation events in the document are not reported.
+ */
+ public void setBuilding(boolean flag)
+ {
+ building = flag;
+ }
+
+ /**
+ * Sets whether to check for document well-formedness.
+ * If true, an exception will be raised if a second doctype or root
+ * element node is added to the document.
+ */
+ public void setCheckWellformedness(boolean flag)
+ {
+ checkingWellformedness = flag;
+ }
+
+ /**
+ * <b>DOM L1</b>
+ * Returns the constant "#document".
+ */
+ final public String getNodeName()
+ {
+ return "#document";
+ }
+
+ /**
+ * <b>DOM L1</b>
+ * Returns the document's root element, or null.
+ */
+ final public Element getDocumentElement()
+ {
+ for (DomNode ctx = first; ctx != null; ctx = ctx.next)
+ {
+ if (ctx.nodeType == ELEMENT_NODE)
+ {
+ return (Element) ctx;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * <b>DOM L1</b>
+ * Returns the document's DocumentType, or null.
+ */
+ final public DocumentType getDoctype()
+ {
+ for (DomNode ctx = first; ctx != null; ctx = ctx.next)
+ {
+ if (ctx.nodeType == DOCUMENT_TYPE_NODE)
+ {
+ return (DocumentType) ctx;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * <b>DOM L1</b>
+ * Returns the document's DOMImplementation.
+ */
+ final public DOMImplementation getImplementation()
+ {
+ return implementation;
+ }
+
+ /**
+ * <b>DOM L1 (relocated in DOM L2)</b>
+ * Returns the element with the specified "ID" attribute, or null.
+ *
+ * <p>Returns null unless {@link Consumer} was used to populate internal
+ * DTD declaration information, using package-private APIs. If that
+ * internal DTD information is available, the document may be searched for
+ * the element with that ID.
+ */
+ public Element getElementById(String id)
+ {
+ DomDoctype doctype = (DomDoctype) getDoctype();
+
+ if (doctype == null || !doctype.hasIds()
+ || id == null || id.length() == 0)
+ {
+ return null;
+ }
+
+ // yes, this is linear in size of document.
+ // it'd be easy enough to maintain a hashtable.
+ Node current = getDocumentElement();
+ Node temp;
+
+ if (current == null)
+ {
+ return null;
+ }
+ while (current != this)
+ {
+ // done?
+ if (current.getNodeType() == ELEMENT_NODE)
+ {
+ DomElement element = (DomElement) current;
+ DTDElementTypeInfo info =
+ doctype.getElementTypeInfo(current.getNodeName());
+ if (info != null &&
+ id.equals(element.getAttribute(info.idAttrName)))
+ {
+ return element;
+ }
+ else if (element.userIdAttrs != null)
+ {
+ for (Iterator i = element.userIdAttrs.iterator();
+ i.hasNext(); )
+ {
+ Node idAttr = (Node) i.next();
+ if (id.equals(idAttr.getNodeValue()))
+ {
+ return element;
+ }
+ }
+ }
+ }
+
+ // descend?
+ if (current.hasChildNodes())
+ {
+ current = current.getFirstChild();
+ continue;
+ }
+
+ // lateral?
+ temp = current.getNextSibling();
+ if (temp != null)
+ {
+ current = temp;
+ continue;
+ }
+
+ // back up ...
+ do
+ {
+ temp = current.getParentNode();
+ if (temp == null)
+ {
+ return null;
+ }
+ current = temp;
+ temp = current.getNextSibling();
+ }
+ while (temp == null);
+ current = temp;
+ }
+ return null;
+ }
+
+ private void checkNewChild(Node newChild)
+ {
+ if (newChild.getNodeType() == ELEMENT_NODE
+ && getDocumentElement() != null)
+ {
+ throw new DomEx (DomEx.HIERARCHY_REQUEST_ERR,
+ "document element already present: " +
+ getDocumentElement(), newChild, 0);
+ }
+ if (newChild.getNodeType() == DOCUMENT_TYPE_NODE
+ && getDoctype() != null)
+ {
+ throw new DomEx (DomEx.HIERARCHY_REQUEST_ERR,
+ "document type already present: " +
+ getDoctype(), newChild, 0);
+ }
+ }
+
+ /**
+ * <b>DOM L1</b>
+ * Appends the specified node to this node's list of children,
+ * enforcing the constraints that there be only one root element
+ * and one document type child.
+ */
+ public Node appendChild(Node newChild)
+ {
+ if (checkingWellformedness)
+ {
+ checkNewChild(newChild);
+ }
+ return super.appendChild(newChild);
+ }
+
+ /**
+ * <b>DOM L1</b>
+ * Inserts the specified node in this node's list of children,
+ * enforcing the constraints that there be only one root element
+ * and one document type child.
+ */
+ public Node insertBefore(Node newChild, Node refChild)
+ {
+ if (checkingWellformedness)
+ {
+ checkNewChild(newChild);
+ }
+ return super.insertBefore(newChild, refChild);
+ }
+
+ /**
+ * <b>DOM L1</b>
+ * Replaces the specified node in this node's list of children,
+ * enforcing the constraints that there be only one root element
+ * and one document type child.
+ */
+ public Node replaceChild(Node newChild, Node refChild)
+ {
+ if (checkingWellformedness &&
+ ((newChild.getNodeType() == ELEMENT_NODE &&
+ refChild.getNodeType() != ELEMENT_NODE) ||
+ (newChild.getNodeType() == DOCUMENT_TYPE_NODE &&
+ refChild.getNodeType() != DOCUMENT_TYPE_NODE)))
+ {
+ checkNewChild(newChild);
+ }
+ return super.replaceChild(newChild, refChild);
+ }
+
+ // NOTE: DOM can't really tell when the name of an entity,
+ // notation, or PI must follow the namespace rules (excluding
+ // colons) instead of the XML rules (which allow them without
+ // much restriction). That's an API issue. verifyXmlName
+ // aims to enforce the XML rules, not the namespace rules.
+
+ /**
+ * Throws a DOM exception if the specified name is not a legal XML 1.0
+ * Name.
+ * @deprecated This method is deprecated and may be removed in future
+ * versions of GNU JAXP
+ */
+ public static void verifyXmlName(String name)
+ {
+ // XXX why is this public?
+ checkName(name, false);
+ }
+
+ static void checkName(String name, boolean xml11)
+ {
+ if (name == null)
+ {
+ throw new DomEx (DomEx.NAMESPACE_ERR, name, null, 0);
+ }
+ int len = name.length();
+ if (len == 0)
+ {
+ throw new DomEx (DomEx.NAMESPACE_ERR, name, null, 0);
+ }
+
+ // dog: rewritten to use the rules for XML 1.0 and 1.1
+
+ // Name start character
+ char c = name.charAt(0);
+ if (xml11)
+ {
+ // XML 1.1
+ if ((c < 0x0041 || c > 0x005a) &&
+ (c < 0x0061 || c > 0x007a) &&
+ c != ':' && c != '_' &&
+ (c < 0x00c0 || c > 0x00d6) &&
+ (c < 0x00d8 || c > 0x00f6) &&
+ (c < 0x00f8 || c > 0x02ff) &&
+ (c < 0x0370 || c > 0x037d) &&
+ (c < 0x037f || c > 0x1fff) &&
+ (c < 0x200c || c > 0x200d) &&
+ (c < 0x2070 || c > 0x218f) &&
+ (c < 0x2c00 || c > 0x2fef) &&
+ (c < 0x3001 || c > 0xd7ff) &&
+ (c < 0xf900 || c > 0xfdcf) &&
+ (c < 0xfdf0 || c > 0xfffd) &&
+ (c < 0x10000 || c > 0xeffff))
+ {
+ throw new DomEx(DomEx.INVALID_CHARACTER_ERR, name, null, c);
+ }
+ }
+ else
+ {
+ // XML 1.0
+ int type = Character.getType(c);
+ switch (type)
+ {
+ case Character.LOWERCASE_LETTER: // Ll
+ case Character.UPPERCASE_LETTER: // Lu
+ case Character.OTHER_LETTER: // Lo
+ case Character.TITLECASE_LETTER: // Lt
+ case Character.LETTER_NUMBER: // Nl
+ if ((c > 0xf900 && c < 0xfffe) ||
+ (c >= 0x20dd && c <= 0x20e0))
+ {
+ // Compatibility area and Unicode 2.0 exclusions
+ throw new DomEx(DomEx.INVALID_CHARACTER_ERR, name, null, c);
+ }
+ break;
+ default:
+ if (c != ':' && c != '_' && (c < 0x02bb || c > 0x02c1) &&
+ c != 0x0559 && c != 0x06e5 && c != 0x06e6)
+ {
+ throw new DomEx(DomEx.INVALID_CHARACTER_ERR, name, null, c);
+ }
+ }
+ }
+
+ // Subsequent characters
+ for (int i = 1; i < len; i++)
+ {
+ c = name.charAt(i);
+ if (xml11)
+ {
+ // XML 1.1
+ if ((c < 0x0041 || c > 0x005a) &&
+ (c < 0x0061 || c > 0x007a) &&
+ (c < 0x0030 || c > 0x0039) &&
+ c != ':' && c != '_' && c != '-' && c != '.' &&
+ (c < 0x00c0 || c > 0x00d6) &&
+ (c < 0x00d8 || c > 0x00f6) &&
+ (c < 0x00f8 || c > 0x02ff) &&
+ (c < 0x0370 || c > 0x037d) &&
+ (c < 0x037f || c > 0x1fff) &&
+ (c < 0x200c || c > 0x200d) &&
+ (c < 0x2070 || c > 0x218f) &&
+ (c < 0x2c00 || c > 0x2fef) &&
+ (c < 0x3001 || c > 0xd7ff) &&
+ (c < 0xf900 || c > 0xfdcf) &&
+ (c < 0xfdf0 || c > 0xfffd) &&
+ (c < 0x10000 || c > 0xeffff) &&
+ c != 0x00b7 &&
+ (c < 0x0300 || c > 0x036f) &&
+ (c < 0x203f || c > 0x2040))
+ {
+ throw new DomEx(DomEx.INVALID_CHARACTER_ERR, name, null, c);
+ }
+ }
+ else
+ {
+ // XML 1.0
+ int type = Character.getType(c);
+ switch (type)
+ {
+ case Character.LOWERCASE_LETTER: // Ll
+ case Character.UPPERCASE_LETTER: // Lu
+ case Character.DECIMAL_DIGIT_NUMBER: // Nd
+ case Character.OTHER_LETTER: // Lo
+ case Character.TITLECASE_LETTER: // Lt
+ case Character.LETTER_NUMBER: // Nl
+ case Character.COMBINING_SPACING_MARK: // Mc
+ case Character.ENCLOSING_MARK: // Me
+ case Character.NON_SPACING_MARK: // Mn
+ case Character.MODIFIER_LETTER: // Lm
+ if ((c > 0xf900 && c < 0xfffe) ||
+ (c >= 0x20dd && c <= 0x20e0))
+ {
+ // Compatibility area and Unicode 2.0 exclusions
+ throw new DomEx(DomEx.INVALID_CHARACTER_ERR, name, null, c);
+ }
+ break;
+ default:
+ if (c != '-' && c != '.' && c != ':' && c != '_' &&
+ c != 0x0387 && (c < 0x02bb || c > 0x02c1) &&
+ c != 0x0559 && c != 0x06e5 && c != 0x06e6 && c != 0x00b7)
+ {
+ throw new DomEx(DomEx.INVALID_CHARACTER_ERR, name, null, c);
+ }
+ }
+ }
+ }
+
+ // FIXME characters with a font or compatibility decomposition (i.e.
+ // those with a "compatibility formatting tag" in field 5 of the
+ // database -- marked by field 5 beginning with a "<") are not allowed.
+ }
+
+ // package private
+ static void checkNCName(String name, boolean xml11)
+ {
+ checkName(name, xml11);
+ int len = name.length();
+ int index = name.indexOf(':');
+ if (index != -1)
+ {
+ if (index == 0 || index == (len - 1) ||
+ name.lastIndexOf(':') != index)
+ {
+ throw new DomEx(DomEx.NAMESPACE_ERR, name, null, 0);
+ }
+ }
+ }
+
+ // package private
+ static void checkChar(String value, boolean xml11)
+ {
+ char[] chars = value.toCharArray();
+ checkChar(chars, 0, chars.length, xml11);
+ }
+
+ static void checkChar(char[] buf, int off, int len, boolean xml11)
+ {
+ for (int i = 0; i < len; i++)
+ {
+ char c = buf[i];
+
+ // assume surrogate pairing checks out OK, for simplicity
+ if ((c >= 0x0020 && c <= 0xd7ff) ||
+ (c == 0x000a || c == 0x000d || c == 0x0009) ||
+ (c >= 0xe000 && c <= 0xfffd) ||
+ (c >= 0x10000 && c <= 0x10ffff))
+ {
+ continue;
+ }
+ if (xml11)
+ {
+ if ((c >= 0x0001 && c <= 0x001f) ||
+ (c >= 0x007f && c <= 0x0084) ||
+ (c >= 0x0086 && c <= 0x009f))
+ {
+ continue;
+ }
+ }
+ throw new DomEx(DomEx.INVALID_CHARACTER_ERR,
+ new String(buf, off, len), null, c);
+ }
+ }
+
+ /**
+ * <b>DOM L1</b>
+ * Returns a newly created element with the specified name.
+ */
+ public Element createElement(String name)
+ {
+ Element element;
+
+ if (checkingCharacters)
+ {
+ checkName(name, "1.1".equals(version));
+ }
+ if (name.startsWith("xml:"))
+ {
+ element = createElementNS(null, name);
+ }
+ else
+ {
+ element = new DomElement(this, null, name);
+ }
+ defaultAttributes(element, name);
+ return element;
+ }
+
+ /**
+ * <b>DOM L2</b>
+ * Returns a newly created element with the specified name
+ * and namespace information.
+ */
+ public Element createElementNS(String namespaceURI, String name)
+ {
+ if (checkingCharacters)
+ {
+ checkNCName(name, "1.1".equals(version));
+ }
+
+ if ("".equals(namespaceURI))
+ {
+ namespaceURI = null;
+ }
+ if (name.startsWith("xml:"))
+ {
+ if (namespaceURI != null
+ && !XMLConstants.XML_NS_URI.equals(namespaceURI))
+ {
+ throw new DomEx(DomEx.NAMESPACE_ERR,
+ "xml namespace is always " +
+ XMLConstants.XML_NS_URI, this, 0);
+ }
+ namespaceURI = XMLConstants.XML_NS_URI;
+ }
+ else if (XMLConstants.XMLNS_ATTRIBUTE.equals(name) ||
+ name.startsWith("xmlns:"))
+ {
+ throw new DomEx(DomEx.NAMESPACE_ERR,
+ "xmlns is reserved", this, 0);
+ }
+ else if (namespaceURI == null && name.indexOf(':') != -1)
+ {
+ throw new DomEx(DomEx.NAMESPACE_ERR,
+ "prefixed name '" + name + "' needs a URI", this, 0);
+ }
+
+ Element element = new DomElement(this, namespaceURI, name);
+ defaultAttributes(element, name);
+ return element;
+ }
+
+ private void defaultAttributes(Element element, String name)
+ {
+ DomDoctype doctype = (DomDoctype) getDoctype();
+ if (doctype == null)
+ {
+ return;
+ }
+
+ // default any attributes that need it
+ DTDElementTypeInfo info = doctype.getElementTypeInfo(name);
+ if (info != null)
+ {
+ for (Iterator i = info.attributes(); i != null && i.hasNext(); )
+ {
+ DTDAttributeTypeInfo attr = (DTDAttributeTypeInfo) i.next();
+ DomAttr node = (DomAttr) createAttribute(attr.name);
+
+ String value = attr.value;
+ if (value == null)
+ {
+ value = "";
+ }
+ node.setValue(value);
+ node.setSpecified(false);
+ element.setAttributeNode(node);
+ }
+ }
+ }
+
+ /**
+ * <b>DOM L1</b>
+ * Returns a newly created document fragment.
+ */
+ public DocumentFragment createDocumentFragment()
+ {
+ return new DomFragment(this);
+ }
+
+ /**
+ * <b>DOM L1</b>
+ * Returns a newly created text node with the specified value.
+ */
+ public Text createTextNode(String value)
+ {
+ if (checkingCharacters)
+ {
+ checkChar(value, "1.1".equals(version));
+ }
+ return new DomText(this, value);
+ }
+
+ /**
+ * Returns a newly created text node with the specified value.
+ */
+ public Text createTextNode(char[] buf, int off, int len)
+ {
+ if (checkingCharacters)
+ {
+ checkChar(buf, off, len, "1.1".equals(version));
+ }
+ return new DomText(this, buf, off, len);
+ }
+
+ /**
+ * <b>DOM L1</b>
+ * Returns a newly created comment node with the specified value.
+ */
+ public Comment createComment(String value)
+ {
+ if (checkingCharacters)
+ {
+ checkChar(value, "1.1".equals(version));
+ }
+ return new DomComment(this, value);
+ }
+
+ /**
+ * <b>DOM L1</b>
+ * Returns a newly created CDATA section node with the specified value.
+ */
+ public CDATASection createCDATASection(String value)
+ {
+ if (checkingCharacters)
+ {
+ checkChar(value, "1.1".equals(version));
+ }
+ return new DomCDATA(this, value);
+ }
+
+ /**
+ * Returns a newly created CDATA section node with the specified value.
+ */
+ public CDATASection createCDATASection(char[] buf, int off, int len)
+ {
+ if (checkingCharacters)
+ {
+ checkChar(buf, off, len, "1.1".equals(version));
+ }
+ return new DomCDATA(this, buf, off, len);
+ }
+
+ /**
+ * <b>DOM L1</b>
+ * Returns a newly created processing instruction.
+ */
+ public ProcessingInstruction createProcessingInstruction(String target,
+ String data)
+ {
+ if (checkingCharacters)
+ {
+ boolean xml11 = "1.1".equals(version);
+ checkName(target, xml11);
+ if ("xml".equalsIgnoreCase(target))
+ {
+ throw new DomEx(DomEx.SYNTAX_ERR,
+ "illegal PI target name", this, 0);
+ }
+ checkChar(data, xml11);
+ }
+ return new DomPI(this, target, data);
+ }
+
+ /**
+ * <b>DOM L1</b>
+ * Returns a newly created attribute with the specified name.
+ */
+ public Attr createAttribute(String name)
+ {
+ if (checkingCharacters)
+ {
+ checkName(name, "1.1".equals(version));
+ }
+ if (name.startsWith("xml:"))
+ {
+ return createAttributeNS(XMLConstants.XML_NS_URI, name);
+ }
+ else if (XMLConstants.XMLNS_ATTRIBUTE.equals(name) ||
+ name.startsWith("xmlns:"))
+ {
+ return createAttributeNS(XMLConstants.XMLNS_ATTRIBUTE_NS_URI, name);
+ }
+ else
+ {
+ return new DomAttr(this, null, name);
+ }
+ }
+
+ /**
+ * <b>DOM L2</b>
+ * Returns a newly created attribute with the specified name
+ * and namespace information.
+ */
+ public Attr createAttributeNS(String namespaceURI, String name)
+ {
+ if (checkingCharacters)
+ {
+ checkNCName(name, "1.1".equals(version));
+ }
+
+ if ("".equals(namespaceURI))
+ {
+ namespaceURI = null;
+ }
+ if (name.startsWith ("xml:"))
+ {
+ if (namespaceURI == null)
+ {
+ namespaceURI = XMLConstants.XML_NS_URI;
+ }
+ else if (!XMLConstants.XML_NS_URI.equals(namespaceURI))
+ {
+ throw new DomEx(DomEx.NAMESPACE_ERR,
+ "xml namespace is always " +
+ XMLConstants.XML_NS_URI, this, 0);
+ }
+ }
+ else if (XMLConstants.XMLNS_ATTRIBUTE.equals(name) ||
+ name.startsWith("xmlns:"))
+ {
+ if (namespaceURI == null)
+ {
+ namespaceURI = XMLConstants.XMLNS_ATTRIBUTE_NS_URI;
+ }
+ else if (!XMLConstants.XMLNS_ATTRIBUTE_NS_URI.equals(namespaceURI))
+ {
+ throw new DomEx(DomEx.NAMESPACE_ERR,
+ "xmlns namespace must be " +
+ XMLConstants.XMLNS_ATTRIBUTE_NS_URI, this, 0);
+ }
+ }
+ else if (namespaceURI == null && name.indexOf(':') != -1)
+ {
+ throw new DomEx(DomEx.NAMESPACE_ERR,
+ "prefixed name needs a URI: " + name, this, 0);
+ }
+ return new DomAttr(this, namespaceURI, name);
+ }
+
+ /**
+ * <b>DOM L1</b>
+ * Returns a newly created reference to the specified entity.
+ * The caller should populate this with the appropriate children
+ * and then mark it as readonly.
+ *
+ * @see DomNode#makeReadonly
+ */
+ public EntityReference createEntityReference(String name)
+ {
+ DomEntityReference ret = new DomEntityReference(this, name);
+ DocumentType doctype = getDoctype();
+ if (doctype != null)
+ {
+ DomEntity ent = (DomEntity) doctype.getEntities().getNamedItem(name);
+ if (ent != null)
+ {
+ for (DomNode ctx = ent.first; ctx != null; ctx = ctx.next)
+ {
+ ret.appendChild(ctx.cloneNode(true));
+ }
+ }
+ }
+ ret.makeReadonly();
+ return ret;
+ }
+
+ /**
+ * <b>DOM L2</b>
+ * Makes a copy of the specified node, with all nodes "owned" by
+ * this document and with children optionally copied. This type
+ * of standard utility has become, well, a standard utility.
+ *
+ * <p> Note that EntityReference nodes created through this method (either
+ * directly, or recursively) never have children, and that there is no
+ * portable way to associate them with such children.
+ *
+ * <p> Note also that there is no requirement that the specified node
+ * be associated with a different document. This differs from the
+ * <em>cloneNode</em> operation in that the node itself is not given
+ * an opportunity to participate, so that any information managed
+ * by node subclasses will be lost.
+ */
+ public Node importNode(Node src, boolean deep)
+ {
+ Node dst = null;
+ switch (src.getNodeType())
+ {
+ case TEXT_NODE:
+ dst = createTextNode(src.getNodeValue());
+ break;
+ case CDATA_SECTION_NODE:
+ dst = createCDATASection(src.getNodeValue());
+ break;
+ case COMMENT_NODE:
+ dst = createComment(src.getNodeValue());
+ break;
+ case PROCESSING_INSTRUCTION_NODE:
+ dst = createProcessingInstruction(src.getNodeName(),
+ src.getNodeValue());
+ break;
+ case NOTATION_NODE:
+ // NOTE: There's no standard way to create
+ // these, or add them to a doctype. Useless.
+ Notation notation = (Notation) src;
+ dst = new DomNotation(this, notation.getNodeName(),
+ notation.getPublicId(),
+ notation.getSystemId());
+ break;
+ case ENTITY_NODE:
+ // NOTE: There's no standard way to create
+ // these, or add them to a doctype. Useless.
+ Entity entity = (Entity) src;
+ dst = new DomEntity(this, entity.getNodeName(),
+ entity.getPublicId(),
+ entity.getSystemId(),
+ entity.getNotationName());
+ if (deep)
+ {
+ for (Node ctx = src.getFirstChild(); ctx != null;
+ ctx = ctx.getNextSibling())
+ {
+ dst.appendChild(importNode(ctx, deep));
+ }
+ }
+ break;
+ case ENTITY_REFERENCE_NODE:
+ dst = createEntityReference(src.getNodeName());
+ break;
+ case DOCUMENT_FRAGMENT_NODE:
+ dst = new DomFragment(this);
+ if (deep)
+ {
+ for (Node ctx = src.getFirstChild(); ctx != null;
+ ctx = ctx.getNextSibling())
+ {
+ dst.appendChild(importNode(ctx, deep));
+ }
+ }
+ break;
+ case ATTRIBUTE_NODE:
+ String attr_nsuri = src.getNamespaceURI();
+ if (attr_nsuri != null)
+ {
+ dst = createAttributeNS(attr_nsuri, src.getNodeName());
+ }
+ else
+ {
+ dst = createAttribute(src.getNodeName());
+ }
+ // this is _always_ done regardless of "deep" setting
+ for (Node ctx = src.getFirstChild(); ctx != null;
+ ctx = ctx.getNextSibling())
+ {
+ dst.appendChild(importNode(ctx, false));
+ }
+ break;
+ case ELEMENT_NODE:
+ String elem_nsuri = src.getNamespaceURI();
+ if (elem_nsuri != null)
+ {
+ dst = createElementNS(elem_nsuri, src.getNodeName());
+ }
+ else
+ {
+ dst = createElement(src.getNodeName());
+ }
+ NamedNodeMap srcAttrs = src.getAttributes();
+ NamedNodeMap dstAttrs = dst.getAttributes();
+ int len = srcAttrs.getLength();
+ for (int i = 0; i < len; i++)
+ {
+ Attr a = (Attr) srcAttrs.item(i);
+ Attr dflt;
+
+ // maybe update defaulted attributes
+ dflt = (Attr) dstAttrs.getNamedItem(a.getNodeName());
+ if (dflt != null)
+ {
+ String newval = a.getNodeValue();
+ if (!dflt.getNodeValue().equals(newval)
+ || a.getSpecified () == true)
+ {
+ dflt.setNodeValue (newval);
+ }
+ continue;
+ }
+
+ dstAttrs.setNamedItem((Attr) importNode(a, false));
+ }
+ if (deep)
+ {
+ for (Node ctx = src.getFirstChild(); ctx != null;
+ ctx = ctx.getNextSibling())
+ {
+ dst.appendChild(importNode(ctx, true));
+ }
+ }
+ break;
+ // can't import document or doctype nodes
+ case DOCUMENT_NODE:
+ case DOCUMENT_TYPE_NODE:
+ // FALLTHROUGH
+ // can't import unrecognized or nonstandard nodes
+ default:
+ throw new DomEx(DomEx.NOT_SUPPORTED_ERR, null, src, 0);
+ }
+
+ // FIXME cleanup a bit -- for deep copies, copy those
+ // children in one place, here (code sharing is healthy)
+
+ if (src instanceof DomNode)
+ {
+ ((DomNode) src).notifyUserDataHandlers(UserDataHandler.NODE_IMPORTED,
+ src, dst);
+ }
+ return dst;
+ }
+
+ /**
+ * <b>DOM L2 (Traversal)</b>
+ * Returns a newly created node iterator. Don't forget to detach
+ * this iterator when you're done using it!
+ *
+ * @see DomIterator
+ */
+ public NodeIterator createNodeIterator(Node root,
+ int whatToShow,
+ NodeFilter filter,
+ boolean expandEntities)
+ {
+ return new DomNodeIterator(root, whatToShow, filter, expandEntities,
+ false);
+ }
+
+ public TreeWalker createTreeWalker(Node root,
+ int whatToShow,
+ NodeFilter filter,
+ boolean expandEntities)
+ {
+ return new DomNodeIterator(root, whatToShow, filter, expandEntities,
+ true);
+ }
+
+ // DOM Level 3 methods
+
+ /**
+ * DOM L3
+ */
+ public String getInputEncoding()
+ {
+ return inputEncoding;
+ }
+
+ public void setInputEncoding(String inputEncoding)
+ {
+ this.inputEncoding = inputEncoding;
+ }
+
+ /**
+ * DOM L3
+ */
+ public String getXmlEncoding()
+ {
+ return encoding;
+ }
+
+ public void setXmlEncoding(String encoding)
+ {
+ this.encoding = encoding;
+ }
+
+ public boolean getXmlStandalone()
+ {
+ return standalone;
+ }
+
+ public void setXmlStandalone(boolean xmlStandalone)
+ {
+ standalone = xmlStandalone;
+ }
+
+ public String getXmlVersion()
+ {
+ return version;
+ }
+
+ public void setXmlVersion(String xmlVersion)
+ {
+ if (xmlVersion == null)
+ {
+ xmlVersion = "1.0";
+ }
+ if ("1.0".equals(xmlVersion) ||
+ "1.1".equals(xmlVersion))
+ {
+ version = xmlVersion;
+ }
+ else
+ {
+ throw new DomEx(DomEx.NOT_SUPPORTED_ERR);
+ }
+ }
+
+ public boolean getStrictErrorChecking()
+ {
+ return checkingCharacters;
+ }
+
+ public void setStrictErrorChecking(boolean strictErrorChecking)
+ {
+ checkingCharacters = strictErrorChecking;
+ }
+
+ public String lookupPrefix(String namespaceURI)
+ {
+ Node root = getDocumentElement();
+ return (root == null) ? null : root.lookupPrefix(namespaceURI);
+ }
+
+ public boolean isDefaultNamespace(String namespaceURI)
+ {
+ Node root = getDocumentElement();
+ return (root == null) ? false : root.isDefaultNamespace(namespaceURI);
+ }
+
+ public String lookupNamespaceURI(String prefix)
+ {
+ Node root = getDocumentElement();
+ return (root == null) ? null : root.lookupNamespaceURI(prefix);
+ }
+
+ public String getBaseURI()
+ {
+ return getDocumentURI();
+ /*
+ Node root = getDocumentElement();
+ if (root != null)
+ {
+ NamedNodeMap attrs = root.getAttributes();
+ Node xmlBase = attrs.getNamedItemNS(XMLConstants.XML_NS_URI, "base");
+ if (xmlBase != null)
+ {
+ return xmlBase.getNodeValue();
+ }
+ }
+ return systemId;
+ */
+ }
+
+ public String getDocumentURI()
+ {
+ return systemId;
+ }
+
+ public void setDocumentURI(String documentURI)
+ {
+ systemId = documentURI;
+ }
+
+ public Node adoptNode(Node source)
+ {
+ switch (source.getNodeType())
+ {
+ case DOCUMENT_NODE:
+ case DOCUMENT_TYPE_NODE:
+ throw new DomEx(DomEx.NOT_SUPPORTED_ERR);
+ case ENTITY_NODE:
+ case NOTATION_NODE:
+ throw new DomEx(DomEx.NO_MODIFICATION_ALLOWED_ERR);
+ }
+ if (source instanceof DomNode)
+ {
+ DomNode src = (DomNode) source;
+ DomNode dst = src;
+ if (dst.parent != null)
+ {
+ dst = (DomNode) dst.cloneNode(true);
+ }
+ dst.setOwner(this);
+ src.notifyUserDataHandlers(UserDataHandler.NODE_ADOPTED, src, dst);
+ return dst;
+ }
+ return null;
+ }
+
+ public DOMConfiguration getDomConfig()
+ {
+ if (config == null)
+ {
+ config = new DomDocumentConfiguration();
+ }
+ return config;
+ }
+
+ public void normalizeDocument()
+ {
+ boolean save = building;
+ building = true;
+ normalizeNode(this);
+ building = save;
+ }
+
+ void normalizeNode(DomNode node)
+ {
+ node.normalize();
+ if (config != null)
+ {
+ switch (node.nodeType)
+ {
+ case CDATA_SECTION_NODE:
+ if (!config.cdataSections)
+ {
+ // replace CDATA section with text node
+ Text text = createTextNode(node.getNodeValue());
+ node.parent.insertBefore(text, node);
+ node.parent.removeChild(node);
+ // merge adjacent text nodes
+ String data = text.getWholeText();
+ node = (DomNode) text.replaceWholeText(data);
+ }
+ else if (config.splitCdataSections)
+ {
+ String value = node.getNodeValue();
+ int i = value.indexOf("]]>");
+ while (i != -1)
+ {
+ Node node2 = createCDATASection(value.substring(0, i));
+ node.parent.insertBefore(node2, node);
+ value = value.substring(i + 3);
+ node.setNodeValue(value);
+ i = value.indexOf("]]>");
+ }
+ }
+ break;
+ case COMMENT_NODE:
+ if (!config.comments)
+ {
+ node.parent.removeChild(node);
+ }
+ break;
+ case TEXT_NODE:
+ if (!config.elementContentWhitespace &&
+ ((Text) node).isElementContentWhitespace())
+ {
+ node.parent.removeChild(node);
+ }
+ break;
+ case ENTITY_REFERENCE_NODE:
+ if (!config.entities)
+ {
+ for (DomNode ctx = node.first; ctx != null; )
+ {
+ DomNode ctxNext = ctx.next;
+ node.parent.insertBefore(ctx, node);
+ ctx = ctxNext;
+ }
+ node.parent.removeChild(node);
+ }
+ break;
+ case ELEMENT_NODE:
+ if (!config.namespaceDeclarations)
+ {
+ DomNamedNodeMap attrs =
+ (DomNamedNodeMap) node.getAttributes();
+ boolean aro = attrs.readonly;
+ attrs.readonly = false; // Ensure we can delete if necessary
+ int len = attrs.getLength();
+ for (int i = 0; i < len; i++)
+ {
+ Node attr = attrs.item(i);
+ String namespace = attr.getNamespaceURI();
+ if (XMLConstants.XMLNS_ATTRIBUTE_NS_URI.equals(namespace))
+ {
+ attrs.removeNamedItemNS(namespace,
+ attr.getLocalName());
+ i--;
+ len--;
+ }
+ }
+ attrs.readonly = aro;
+ }
+ break;
+ }
+ }
+ for (DomNode ctx = node.first; ctx != null; )
+ {
+ DomNode ctxNext = ctx.next;
+ normalizeNode(ctx);
+ ctx = ctxNext;
+ }
+ }
+
+ public Node renameNode(Node n, String namespaceURI, String qualifiedName)
+ throws DOMException
+ {
+ if (n instanceof DomNsNode)
+ {
+ DomNsNode src = (DomNsNode) n;
+ if (src == null)
+ {
+ throw new DomEx(DomEx.NOT_FOUND_ERR);
+ }
+ if (src.owner != this)
+ {
+ throw new DomEx(DomEx.WRONG_DOCUMENT_ERR, null, src, 0);
+ }
+ boolean xml11 = "1.1".equals(version);
+ checkName(qualifiedName, xml11);
+ int ci = qualifiedName.indexOf(':');
+ if ("".equals(namespaceURI))
+ {
+ namespaceURI = null;
+ }
+ if (namespaceURI != null)
+ {
+ checkNCName(qualifiedName, xml11);
+ String prefix = (ci == -1) ? "" :
+ qualifiedName.substring(0, ci);
+ if (XMLConstants.XML_NS_PREFIX.equals(prefix) &&
+ !XMLConstants.XML_NS_URI.equals(namespaceURI))
+ {
+ throw new DomEx(DomEx.NAMESPACE_ERR,
+ "xml namespace must be " +
+ XMLConstants.XML_NS_URI, src, 0);
+ }
+ else if (src.nodeType == ATTRIBUTE_NODE &&
+ (XMLConstants.XMLNS_ATTRIBUTE.equals(prefix) ||
+ XMLConstants.XMLNS_ATTRIBUTE.equals(qualifiedName)) &&
+ !XMLConstants.XMLNS_ATTRIBUTE_NS_URI.equals(namespaceURI))
+ {
+ throw new DomEx(DomEx.NAMESPACE_ERR,
+ "xmlns namespace must be " +
+ XMLConstants.XMLNS_ATTRIBUTE_NS_URI, src, 0);
+ }
+ if (XMLConstants.XML_NS_URI.equals(namespaceURI) &&
+ !XMLConstants.XML_NS_PREFIX.equals(prefix))
+ {
+ throw new DomEx(DomEx.NAMESPACE_ERR,
+ "xml namespace must be " +
+ XMLConstants.XML_NS_URI, src, 0);
+ }
+ else if (src.nodeType == ATTRIBUTE_NODE &&
+ XMLConstants.XMLNS_ATTRIBUTE_NS_URI.equals(namespaceURI) &&
+ !(XMLConstants.XMLNS_ATTRIBUTE.equals(prefix) ||
+ XMLConstants.XMLNS_ATTRIBUTE.equals(qualifiedName)))
+ {
+ throw new DomEx(DomEx.NAMESPACE_ERR,
+ "xmlns namespace must be " +
+ XMLConstants.XMLNS_ATTRIBUTE_NS_URI, src, 0);
+ }
+
+ }
+ src.setNodeName(qualifiedName);
+ src.setNamespaceURI(namespaceURI);
+ src.notifyUserDataHandlers(UserDataHandler.NODE_RENAMED, src, src);
+ // TODO MutationNameEvents
+ // DOMElementNameChanged or DOMAttributeNameChanged
+ return src;
+ }
+ throw new DomEx(DomEx.NOT_SUPPORTED_ERR, null, n, 0);
+ }
+
+ // -- XPathEvaluator --
+
+ public XPathExpression createExpression(String expression,
+ XPathNSResolver resolver)
+ throws XPathException, DOMException
+ {
+ return new DomXPathExpression(this, expression, resolver);
+ }
+
+ public XPathNSResolver createNSResolver(Node nodeResolver)
+ {
+ return new DomXPathNSResolver(nodeResolver);
+ }
+
+ public Object evaluate(String expression,
+ Node contextNode,
+ XPathNSResolver resolver,
+ short type,
+ Object result)
+ throws XPathException, DOMException
+ {
+ XPathExpression xpe =
+ new DomXPathExpression(this, expression, resolver);
+ return xpe.evaluate(contextNode, type, result);
+ }
+
+}
+
diff --git a/libjava/gnu/xml/dom/DomDocumentBuilder.java b/libjava/gnu/xml/dom/DomDocumentBuilder.java
new file mode 100644
index 0000000..16ea2ae
--- /dev/null
+++ b/libjava/gnu/xml/dom/DomDocumentBuilder.java
@@ -0,0 +1,160 @@
+/* DomDocumentBuilder.java --
+ Copyright (C) 2004 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.dom;
+
+import java.io.InputStream;
+import java.io.IOException;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.ParserConfigurationException;
+import org.w3c.dom.Document;
+import org.w3c.dom.DOMConfiguration;
+import org.w3c.dom.DOMImplementation;
+import org.w3c.dom.ls.DOMImplementationLS;
+import org.w3c.dom.ls.LSInput;
+import org.w3c.dom.ls.LSParser;
+import org.xml.sax.EntityResolver;
+import org.xml.sax.ErrorHandler;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+
+/**
+ * Document builder using the GNU DOM Load &amp; Save implementation.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+class DomDocumentBuilder
+ extends DocumentBuilder
+{
+
+ final DOMImplementation impl;
+ final DOMImplementationLS ls;
+ final LSParser parser;
+
+ DomDocumentBuilder(DOMImplementation impl,
+ DOMImplementationLS ls,
+ LSParser parser)
+ {
+ this.impl = impl;
+ this.ls = ls;
+ this.parser = parser;
+ }
+
+ public boolean isNamespaceAware()
+ {
+ DOMConfiguration config = parser.getDomConfig();
+ return ((Boolean) config.getParameter("namespaces")).booleanValue();
+ }
+
+ public boolean isValidating()
+ {
+ DOMConfiguration config = parser.getDomConfig();
+ return ((Boolean) config.getParameter("validating")).booleanValue();
+ }
+
+ public boolean isXIncludeAware()
+ {
+ DOMConfiguration config = parser.getDomConfig();
+ return ((Boolean) config.getParameter("xinclude-aware")).booleanValue();
+ }
+
+ public void setEntityResolver(EntityResolver resolver)
+ {
+ DOMConfiguration config = parser.getDomConfig();
+ config.setParameter("entity-resolver", resolver);
+ }
+
+ public void setErrorHandler(ErrorHandler handler)
+ {
+ DOMConfiguration config = parser.getDomConfig();
+ config.setParameter("error-handler", handler);
+ }
+
+ public DOMImplementation getDOMImplementation()
+ {
+ return impl;
+ }
+
+ public Document newDocument()
+ {
+ return impl.createDocument(null, null, null);
+ }
+
+ public Document parse(InputStream in)
+ throws SAXException, IOException
+ {
+ LSInput input = ls.createLSInput();
+ input.setByteStream(in);
+ return parser.parse(input);
+ }
+
+ public Document parse(InputStream in, String systemId)
+ throws SAXException, IOException
+ {
+ LSInput input = ls.createLSInput();
+ input.setByteStream(in);
+ input.setSystemId(systemId);
+ return parser.parse(input);
+ }
+
+ public Document parse(String systemId)
+ throws SAXException, IOException
+ {
+ return parser.parseURI(systemId);
+ }
+
+ public Document parse(InputSource is)
+ throws SAXException, IOException
+ {
+ LSInput input = ls.createLSInput();
+ InputStream in = is.getByteStream();
+ if (in != null)
+ {
+ input.setByteStream(in);
+ }
+ else
+ {
+ input.setCharacterStream(is.getCharacterStream());
+ }
+ input.setPublicId(is.getPublicId());
+ input.setSystemId(is.getSystemId());
+ input.setEncoding(is.getEncoding());
+ return parser.parse(input);
+ }
+
+}
+
diff --git a/libjava/gnu/xml/dom/DomDocumentBuilderFactory.java b/libjava/gnu/xml/dom/DomDocumentBuilderFactory.java
new file mode 100644
index 0000000..2d049bd
--- /dev/null
+++ b/libjava/gnu/xml/dom/DomDocumentBuilderFactory.java
@@ -0,0 +1,128 @@
+/* DomDocumentBuilderFactory.java --
+ Copyright (C) 2004 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.dom;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.FactoryConfigurationError;
+import javax.xml.parsers.ParserConfigurationException;
+import org.w3c.dom.DOMConfiguration;
+import org.w3c.dom.DOMImplementation;
+import org.w3c.dom.bootstrap.DOMImplementationRegistry;
+import org.w3c.dom.ls.DOMImplementationLS;
+import org.w3c.dom.ls.LSParser;
+
+/**
+ * Document builder factory that uses a DOM Level 3 Load &amp; Save
+ * implementation.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+public class DomDocumentBuilderFactory
+ extends DocumentBuilderFactory
+{
+
+ final DOMImplementation impl;
+ final DOMImplementationLS ls;
+
+ public DomDocumentBuilderFactory()
+ {
+ try
+ {
+ DOMImplementationRegistry reg =
+ DOMImplementationRegistry.newInstance();
+ impl = reg.getDOMImplementation("LS 3.0");
+ if (impl == null)
+ {
+ throw new FactoryConfigurationError("no LS implementations found");
+ }
+ ls = (DOMImplementationLS) impl;
+ }
+ catch (Exception e)
+ {
+ throw new FactoryConfigurationError(e);
+ }
+ }
+
+ public DocumentBuilder newDocumentBuilder()
+ throws ParserConfigurationException
+ {
+ LSParser parser = ls.createLSParser(DOMImplementationLS.MODE_ASYNCHRONOUS,
+ "http://www.w3.org/TR/REC-xml");
+ DOMConfiguration config = parser.getDomConfig();
+ setParameter(config, "namespaces",
+ isNamespaceAware() ? Boolean.TRUE : Boolean.FALSE);
+ setParameter(config, "element-content-whitespace",
+ isIgnoringElementContentWhitespace() ? Boolean.FALSE :
+ Boolean.TRUE);
+ setParameter(config, "comments",
+ isIgnoringComments() ? Boolean.FALSE : Boolean.TRUE);
+ setParameter(config, "expand-entity-references",
+ isExpandEntityReferences() ? Boolean.TRUE : Boolean.FALSE);
+ setParameter(config, "coalescing",
+ isCoalescing() ? Boolean.TRUE : Boolean.FALSE);
+ setParameter(config, "validating",
+ isValidating() ? Boolean.TRUE : Boolean.FALSE);
+ setParameter(config, "xinclude-aware",
+ isXIncludeAware() ? Boolean.TRUE : Boolean.FALSE);
+ return new DomDocumentBuilder(impl, ls, parser);
+ }
+
+ void setParameter(DOMConfiguration config, String name, Object value)
+ throws ParserConfigurationException
+ {
+ if (!config.canSetParameter(name, value))
+ {
+ throw new ParserConfigurationException(name);
+ }
+ config.setParameter(name, value);
+ }
+
+ public Object getAttribute(String name)
+ {
+ // TODO
+ return null;
+ }
+
+ public void setAttribute(String name, Object value)
+ {
+ // TODO
+ }
+
+}
+
diff --git a/libjava/gnu/xml/dom/DomDocumentConfiguration.java b/libjava/gnu/xml/dom/DomDocumentConfiguration.java
new file mode 100644
index 0000000..eebee09
--- /dev/null
+++ b/libjava/gnu/xml/dom/DomDocumentConfiguration.java
@@ -0,0 +1,259 @@
+/* DomDocumentConfiguration.java --
+ Copyright (C) 2004 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.dom;
+
+import java.util.Arrays;
+import java.util.List;
+import org.w3c.dom.DOMConfiguration;
+import org.w3c.dom.DOMErrorHandler;
+import org.w3c.dom.DOMException;
+import org.w3c.dom.DOMStringList;
+
+/**
+ * Document configuration, used to store normalization and other parameters.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+class DomDocumentConfiguration
+ implements DOMConfiguration, DOMStringList
+{
+
+ private static final List SUPPORTED_PARAMETERS =
+ Arrays.asList(new String[] { "cdata-sections",
+ "comments",
+ "element-content-whitespace",
+ "entities",
+ "error-handler",
+ "namespace-declarations",
+ "split-cdata-sections",
+ "infoset"});
+
+ boolean cdataSections = true;
+ boolean comments = true;
+ boolean elementContentWhitespace = true;
+ boolean entities = true;
+ DOMErrorHandler errorHandler;
+ boolean namespaceDeclarations = true;
+ boolean splitCdataSections = true;
+
+ public void setParameter(String name, Object value)
+ throws DOMException
+ {
+ name = name.toLowerCase();
+ if ("cdata-sections".equals(name))
+ {
+ cdataSections = "true".equals(value.toString());
+ }
+ else if ("comments".equals(name))
+ {
+ comments = "true".equals(value.toString());
+ }
+ else if ("element-content-whitespace".equals(name))
+ {
+ elementContentWhitespace = "true".equals(value.toString());
+ }
+ else if ("entities".equals(name))
+ {
+ entities = "true".equals(value.toString());
+ }
+ else if ("error-handler".equals(name))
+ {
+ try
+ {
+ errorHandler = (DOMErrorHandler) value;
+ }
+ catch (ClassCastException e)
+ {
+ throw new DomEx(DomEx.TYPE_MISMATCH_ERR,
+ value.getClass().getName(), null, 0);
+ }
+ }
+ else if ("namespace-declarations".equals(name))
+ {
+ namespaceDeclarations = "true".equals(value.toString());
+ }
+ else if ("split-cdata-sections".equals(name))
+ {
+ comments = "true".equals(value.toString());
+ }
+ else if ("infoset".equals(name))
+ {
+ if ("true".equals(value.toString()))
+ {
+ entities = false;
+ cdataSections = false;
+ namespaceDeclarations = true;
+ elementContentWhitespace = true;
+ comments = true;
+ }
+ }
+ else if (("canonical-form".equals(name) ||
+ "check-character-normalization".equals(name) ||
+ "datatype-normalization".equals(name) ||
+ "normalize-characters".equals(name) ||
+ "validate".equals(name) ||
+ "validate-if-schema".equals(name)) &&
+ "false".equals(value.toString()))
+ {
+ // NOOP
+ }
+ else if (("namespaces".equals(name) ||
+ "well-formed".equals(name)) &&
+ "true".equals(value.toString()))
+ {
+ // NOOP
+ }
+ else
+ {
+ throw new DomEx(DomEx.NOT_SUPPORTED_ERR, name, null, 0);
+ }
+ }
+
+ public Object getParameter(String name)
+ throws DOMException
+ {
+ name = name.toLowerCase();
+ if ("cdata-sections".equals(name))
+ {
+ return cdataSections ? Boolean.TRUE : Boolean.FALSE;
+ }
+ else if ("comments".equals(name))
+ {
+ return comments ? Boolean.TRUE : Boolean.FALSE;
+ }
+ else if ("element-content-whitespace".equals(name))
+ {
+ return elementContentWhitespace ? Boolean.TRUE : Boolean.FALSE;
+ }
+ else if ("entities".equals(name))
+ {
+ return entities ? Boolean.TRUE : Boolean.FALSE;
+ }
+ else if ("error-handler".equals(name))
+ {
+ return errorHandler;
+ }
+ else if ("namespace-declarations".equals(name))
+ {
+ return namespaceDeclarations ? Boolean.TRUE : Boolean.FALSE;
+ }
+ else if ("split-cdata-sections".equals(name))
+ {
+ return comments ? Boolean.TRUE : Boolean.FALSE;
+ }
+ else if ("canonical-form".equals(name) ||
+ "check-character-normalization".equals(name) ||
+ "datatype-normalization".equals(name) ||
+ "normalize-characters".equals(name) ||
+ "validate".equals(name) ||
+ "validate-if-schema".equals(name))
+ {
+ return Boolean.FALSE;
+ }
+ else if ("namespaces".equals(name) ||
+ "well-formed".equals(name))
+ {
+ return Boolean.TRUE;
+ }
+ else if ("infoset".equals(name))
+ {
+ return (entities == false &&
+ cdataSections == false &&
+ namespaceDeclarations == true &&
+ comments == true) ? Boolean.TRUE : Boolean.FALSE;
+ }
+ throw new DomEx(DomEx.NOT_SUPPORTED_ERR, name, null, 0);
+ }
+
+ public boolean canSetParameter(String name, Object value)
+ {
+ name = name.toLowerCase();
+ if ("error-handler".equals(name))
+ {
+ return (value == null || value instanceof DOMErrorHandler);
+ }
+ else if (contains(name))
+ {
+ return true;
+ }
+ else if ("canonical-form".equals(name) ||
+ "check-character-normalization".equals(name) ||
+ "datatype-normalization".equals(name) ||
+ "normalize-characters".equals(name) ||
+ "validate".equals(name) ||
+ "validate-if-schema".equals(name))
+ {
+ return "false".equals(value.toString());
+ }
+ else if ("namespaces".equals(name) ||
+ "well-formed".equals(name))
+ {
+ return "true".equals(value.toString());
+ }
+ return false;
+ }
+
+ public DOMStringList getParameterNames()
+ {
+ return this;
+ }
+
+ public String item(int i)
+ {
+ try
+ {
+ return (String) SUPPORTED_PARAMETERS.get(i);
+ }
+ catch (IndexOutOfBoundsException e)
+ {
+ return null;
+ }
+ }
+
+ public int getLength()
+ {
+ return SUPPORTED_PARAMETERS.size();
+ }
+
+ public boolean contains(String str)
+ {
+ str = str.toLowerCase();
+ return SUPPORTED_PARAMETERS.contains(str);
+ }
+
+}
diff --git a/libjava/gnu/xml/dom/DomElement.java b/libjava/gnu/xml/dom/DomElement.java
new file mode 100644
index 0000000..fb04638
--- /dev/null
+++ b/libjava/gnu/xml/dom/DomElement.java
@@ -0,0 +1,522 @@
+/* DomElement.java --
+ Copyright (C) 1999,2000,2001,2004 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.dom;
+
+import java.util.HashSet;
+import java.util.Set;
+import javax.xml.XMLConstants;
+
+import org.w3c.dom.Attr;
+import org.w3c.dom.Element;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.TypeInfo;
+
+/**
+ * <p> "Element" implementation.
+ *
+ * @author David Brownell
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+public class DomElement
+ extends DomNsNode
+ implements Element
+{
+
+ /**
+ * User-defined ID attributes.
+ * Used by DomAttr.isId and DomDocument.getElementById
+ */
+ Set userIdAttrs;
+
+ // Attributes are VERY expensive in DOM, and not just for
+ // this implementation. Avoid creating them.
+ private DomNamedNodeMap attributes;
+
+ // xml:space cache
+ String xmlSpace = "";
+
+ /**
+ * Constructs an Element node associated with the specified document.
+ *
+ * <p>This constructor should only be invoked by a Document as part
+ * of its createElement functionality, or through a subclass which is
+ * similarly used in a "Sub-DOM" style layer.
+ *
+ * @param owner The document with which this node is associated
+ * @param namespaceURI Combined with the local part of the name,
+ * this is used to uniquely identify a type of element
+ * @param name Name of this element, which may include a prefix
+ */
+ protected DomElement(DomDocument owner, String namespaceURI, String name)
+ {
+ super(ELEMENT_NODE, owner, namespaceURI, name);
+ }
+
+ /**
+ * <b>DOM L1</b>
+ * Returns the element's attributes
+ */
+ public NamedNodeMap getAttributes()
+ {
+ if (attributes == null)
+ {
+ attributes = new DomNamedNodeMap(this, Node.ATTRIBUTE_NODE);
+ }
+ return attributes;
+ }
+
+ /**
+ * <b>DOM L2></b>
+ * Returns true iff this is an element node with attributes.
+ */
+ public boolean hasAttributes()
+ {
+ return attributes != null && attributes.length != 0;
+ }
+
+ /**
+ * Shallow clone of the element, except that associated
+ * attributes are (deep) cloned.
+ */
+ public Object clone()
+ {
+ DomElement node = (DomElement) super.clone();
+
+ if (attributes != null)
+ {
+ node.attributes = new DomNamedNodeMap(node, Node.ATTRIBUTE_NODE);
+ for (DomNode ctx = attributes.first; ctx != null; ctx = ctx.next)
+ {
+ node.attributes.setNamedItemNS(ctx.cloneNode(true));
+ }
+ }
+ return node;
+ }
+
+ void setOwner(DomDocument doc)
+ {
+ if (attributes != null)
+ {
+ for (DomNode ctx = attributes.first; ctx != null; ctx = ctx.next)
+ {
+ ctx.setOwner(doc);
+ }
+ }
+ super.setOwner(doc);
+ }
+
+ /**
+ * Marks this element, its children, and its associated attributes as
+ * readonly.
+ */
+ public void makeReadonly()
+ {
+ super.makeReadonly();
+ if (attributes != null)
+ {
+ attributes.makeReadonly();
+ }
+ }
+
+ /**
+ * <b>DOM L1</b>
+ * Returns the element name (same as getNodeName).
+ */
+ final public String getTagName()
+ {
+ return getNodeName();
+ }
+
+ /**
+ * <b>DOM L1</b>
+ * Returns the value of the specified attribute, or an
+ * empty string.
+ */
+ public String getAttribute(String name)
+ {
+ if ("xml:space" == name) // NB only works on interned string
+ {
+ // Use cached value
+ return xmlSpace;
+ }
+ Attr attr = getAttributeNode(name);
+ return (attr == null) ? "" : attr.getValue();
+ }
+
+ /**
+ * <b>DOM L2</b>
+ * Returns true if the element has an attribute with the
+ * specified name (specified or DTD defaulted).
+ */
+ public boolean hasAttribute(String name)
+ {
+ return getAttributeNode(name) != null;
+ }
+
+ /**
+ * <b>DOM L2</b>
+ * Returns true if the element has an attribute with the
+ * specified name (specified or DTD defaulted).
+ */
+ public boolean hasAttributeNS(String namespaceURI, String local)
+ {
+ return getAttributeNodeNS(namespaceURI, local) != null;
+ }
+
+ /**
+ * <b>DOM L2</b>
+ * Returns the value of the specified attribute, or an
+ * empty string.
+ */
+ public String getAttributeNS(String namespaceURI, String local)
+ {
+ Attr attr = getAttributeNodeNS(namespaceURI, local);
+ return (attr == null) ? "" : attr.getValue();
+ }
+
+ /**
+ * <b>DOM L1</b>
+ * Returns the appropriate attribute node; the name is the
+ * nodeName property of the attribute.
+ */
+ public Attr getAttributeNode(String name)
+ {
+ return (attributes == null) ? null :
+ (Attr) attributes.getNamedItem(name);
+ }
+
+ /**
+ * <b>DOM L2</b>
+ * Returns the appropriate attribute node; the name combines
+ * the namespace name and the local part.
+ */
+ public Attr getAttributeNodeNS(String namespace, String localPart)
+ {
+ return (attributes == null) ? null :
+ (Attr) attributes.getNamedItemNS(namespace, localPart);
+ }
+
+ /**
+ * <b>DOM L1</b>
+ * Modifies an existing attribute to have the specified value,
+ * or creates a new one with that value. The name used is the
+ * nodeName value.
+ */
+ public void setAttribute(String name, String value)
+ {
+ Attr attr = getAttributeNode(name);
+ if (attr != null)
+ {
+ attr.setNodeValue(value);
+ ((DomAttr) attr).setSpecified(true);
+ return;
+ }
+ attr = owner.createAttribute(name);
+ attr.setNodeValue(value);
+ setAttributeNode(attr);
+ }
+
+ /**
+ * <b>DOM L2</b>
+ * Modifies an existing attribute to have the specified value,
+ * or creates a new one with that value.
+ */
+ public void setAttributeNS(String uri, String aname, String value)
+ {
+ if (("xmlns".equals (aname) || aname.startsWith ("xmlns:"))
+ && !XMLConstants.XMLNS_ATTRIBUTE_NS_URI.equals (uri))
+ {
+ throw new DomEx(DomEx.NAMESPACE_ERR,
+ "setting xmlns attribute to illegal value", this, 0);
+ }
+
+ Attr attr = getAttributeNodeNS(uri, aname);
+ if (attr != null)
+ {
+ attr.setNodeValue(value);
+ return;
+ }
+ attr = owner.createAttributeNS(uri, aname);
+ attr.setNodeValue(value);
+ setAttributeNodeNS(attr);
+ }
+
+ /**
+ * <b>DOM L1</b>
+ * Stores the specified attribute, optionally overwriting any
+ * existing one with that name.
+ */
+ public Attr setAttributeNode(Attr attr)
+ {
+ return (Attr) getAttributes().setNamedItem(attr);
+ }
+
+ /**
+ * <b>DOM L2</b>
+ * Stores the specified attribute, optionally overwriting any
+ * existing one with that name.
+ */
+ public Attr setAttributeNodeNS(Attr attr)
+ {
+ return (Attr) getAttributes().setNamedItemNS(attr);
+ }
+
+ /**
+ * <b>DOM L1</b>
+ * Removes the appropriate attribute node.
+ * If there is no such node, this is (bizarrely enough) a NOP so you
+ * won't see exceptions if your code deletes non-existent attributes.
+ *
+ * <p>Note that since there is no portable way for DOM to record
+ * DTD information, default values for attributes will never be
+ * provided automatically.
+ */
+ public void removeAttribute(String name)
+ {
+ if (attributes == null)
+ {
+ return;
+ }
+
+ try
+ {
+ attributes.removeNamedItem(name);
+ }
+ catch (DomEx e)
+ {
+ if (e.code != DomEx.NOT_FOUND_ERR)
+ {
+ throw e;
+ }
+ }
+ }
+
+ /**
+ * <b>DOM L1</b>
+ * Removes the appropriate attribute node; the name is the
+ * nodeName property of the attribute.
+ *
+ * <p>Note that since there is no portable way for DOM to record
+ * DTD information, default values for attributes will never be
+ * provided automatically.
+ */
+ public Attr removeAttributeNode(Attr node)
+ {
+ if (attributes == null)
+ {
+ throw new DomEx(DomEx.NOT_FOUND_ERR, null, node, 0);
+ }
+ return (Attr) attributes.removeNamedItem(node.getNodeName());
+ }
+
+ /**
+ * <b>DOM L2</b>
+ * Removes the appropriate attribute node; the name combines
+ * the namespace name and the local part.
+ *
+ * <p>Note that since there is no portable way for DOM to record
+ * DTD information, default values for attributes will never be
+ * provided automatically.
+ */
+ public void removeAttributeNS(String namespace, String localPart)
+ {
+ if (attributes == null)
+ {
+ throw new DomEx(DomEx.NOT_FOUND_ERR, localPart, null, 0);
+ }
+ attributes.removeNamedItemNS (namespace, localPart);
+ }
+
+ // DOM Level 3 methods
+
+ public String lookupPrefix(String namespaceURI)
+ {
+ if (namespaceURI == null)
+ {
+ return null;
+ }
+ String namespace = getNamespaceURI();
+ if (namespace != null && namespace.equals(namespaceURI))
+ {
+ return getPrefix();
+ }
+ if (attributes != null)
+ {
+ for (DomNode ctx = attributes.first; ctx != null; ctx = ctx.next)
+ {
+ if (XMLConstants.XMLNS_ATTRIBUTE_NS_URI
+ .equals(ctx.getNamespaceURI()))
+ {
+ String value = ctx.getNodeValue();
+ if (value.equals(namespaceURI))
+ {
+ return ctx.getLocalName();
+ }
+ }
+ }
+ }
+ return super.lookupPrefix(namespaceURI);
+ }
+
+ public boolean isDefaultNamespace(String namespaceURI)
+ {
+ String namespace = getNamespaceURI();
+ if (namespace != null && namespace.equals(namespaceURI))
+ {
+ return getPrefix() == null;
+ }
+ if (attributes != null)
+ {
+ for (DomNode ctx = attributes.first; ctx != null; ctx = ctx.next)
+ {
+ if (XMLConstants.XMLNS_ATTRIBUTE_NS_URI
+ .equals(ctx.getNamespaceURI()))
+ {
+ String qName = ctx.getNodeName();
+ return (XMLConstants.XMLNS_ATTRIBUTE.equals(qName));
+ }
+ }
+ }
+ return super.isDefaultNamespace(namespaceURI);
+ }
+
+ public String lookupNamespaceURI(String prefix)
+ {
+ String namespace = getNamespaceURI();
+ if (namespace != null && equal(prefix, getPrefix()))
+ {
+ return namespace;
+ }
+ if (attributes != null)
+ {
+ for (DomNode ctx = attributes.first; ctx != null; ctx = ctx.next)
+ {
+ if (XMLConstants.XMLNS_ATTRIBUTE_NS_URI
+ .equals(ctx.getNamespaceURI()))
+ {
+ if (prefix == null)
+ {
+ if (XMLConstants.XMLNS_ATTRIBUTE.equals(ctx.getNodeName()))
+ {
+ return ctx.getNodeValue();
+ }
+ }
+ else
+ {
+ if (prefix.equals(ctx.getLocalName()))
+ {
+ return ctx.getNodeValue();
+ }
+ }
+ }
+ }
+ }
+ return super.lookupNamespaceURI(prefix);
+ }
+
+ public String getBaseURI()
+ {
+ if (attributes != null)
+ {
+ Node xmlBase =
+ attributes.getNamedItemNS(XMLConstants.XML_NS_URI, "base");
+ if (xmlBase != null)
+ {
+ return xmlBase.getNodeValue();
+ }
+ }
+ return super.getBaseURI();
+ }
+
+ public TypeInfo getSchemaTypeInfo()
+ {
+ // DTD implementation
+ DomDoctype doctype = (DomDoctype) owner.getDoctype();
+ if (doctype != null)
+ {
+ return doctype.getElementTypeInfo(getNodeName());
+ }
+ // TODO XML Schema implementation
+ return null;
+ }
+
+ public void setIdAttribute(String name, boolean isId)
+ {
+ NamedNodeMap attrs = getAttributes();
+ Attr attr = (Attr) attrs.getNamedItem(name);
+ setIdAttributeNode(attr, isId);
+ }
+
+ public void setIdAttributeNode(Attr attr, boolean isId)
+ {
+ if (readonly)
+ {
+ throw new DomEx(DomEx.NO_MODIFICATION_ALLOWED_ERR);
+ }
+ if (attr == null || attr.getOwnerElement() != this)
+ {
+ throw new DomEx(DomEx.NOT_FOUND_ERR);
+ }
+ if (isId)
+ {
+ if (userIdAttrs == null)
+ {
+ userIdAttrs = new HashSet();
+ }
+ userIdAttrs.add(attr);
+ }
+ else if (userIdAttrs != null)
+ {
+ userIdAttrs.remove(attr);
+ if (userIdAttrs.isEmpty())
+ {
+ userIdAttrs = null;
+ }
+ }
+ }
+
+ public void setIdAttributeNS(String namespaceURI, String localName,
+ boolean isId)
+ {
+ NamedNodeMap attrs = getAttributes();
+ Attr attr = (Attr) attrs.getNamedItemNS(namespaceURI, localName);
+ setIdAttributeNode(attr, isId);
+ }
+
+}
diff --git a/libjava/gnu/xml/dom/DomEntity.java b/libjava/gnu/xml/dom/DomEntity.java
new file mode 100644
index 0000000..5432bbc
--- /dev/null
+++ b/libjava/gnu/xml/dom/DomEntity.java
@@ -0,0 +1,147 @@
+/* DomEntity.java --
+ Copyright (C) 1999,2000,2004 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.dom;
+
+import org.w3c.dom.Entity;
+
+/**
+ * <p> "Entity" implementation. This is a non-core DOM class, supporting the
+ * "XML" feature. There are two types of entities, neither of which works
+ * particularly well in this API:</p><dl>
+ *
+ * <dt><em>Unparsed Entities</em></dt>
+ * <dd>Since ENTITY/ENTITIES attributes, the only legal use of unparsed
+ * entities in XML, can't be detected with DOM, there isn't much point in
+ * trying to use unparsed entities in DOM applications. (XML Linking is
+ * working to provide a better version of this functionality.) </dd>
+ *
+ * <dt><em>Parsed Entities</em></dt>
+ * <dd> While the DOM specification permits nodes for parsed entities
+ * to have a readonly set of children, this is not required and there
+ * is no portable way to provide such children. <em>This implementation
+ * currently does not permit children to be added to Entities.</em>
+ * There are related issues with the use of EntityReference nodes. </dd>
+ *
+ * </dl>
+ *
+ * <p> In short, <em>avoid using this DOM functionality</em>.
+ *
+ * @see DomDoctype
+ * @see DomEntityReference
+ * @see DomNotation
+ *
+ * @author David Brownell
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+public class DomEntity
+ extends DomExtern
+ implements Entity
+{
+
+ private String notation;
+
+ /**
+ * Constructs an Entity node associated with the specified document,
+ * with the specified descriptive data.
+ *
+ * <p>This constructor should only be invoked by a DomDoctype as part
+ * of its declareEntity functionality, or through a subclass which is
+ * similarly used in a "Sub-DOM" style layer.
+ *
+ * @param owner The document with which this entity is associated
+ * @param name Name of this entity
+ * @param publicId If non-null, provides the entity's PUBLIC identifier
+ * @param systemId Provides the entity's SYSTEM identifier (URI)
+ * @param notation If non-null, provides the unparsed entity's notation.
+ */
+ protected DomEntity(DomDocument owner,
+ String name,
+ String publicId,
+ String systemId,
+ String notation)
+ {
+ super(ENTITY_NODE, owner, name, publicId, systemId);
+ this.notation = notation;
+
+ // NOTE: if notation == null, this is a parsed entity
+ // which could reasonably be given child nodes ...
+ makeReadonly();
+ }
+
+ /**
+ * <b>DOM L1</b>
+ * Returns the NOTATION identifier associated with this entity, if any.
+ */
+ final public String getNotationName()
+ {
+ return notation;
+ }
+
+ // DOM Level 3 methods
+
+ public String getInputEncoding()
+ {
+ // TODO
+ return null;
+ }
+
+ public String getXmlEncoding()
+ {
+ // TODO
+ return null;
+ }
+
+ public String getXmlVersion()
+ {
+ // TODO
+ return null;
+ }
+
+ /**
+ * The base URI of an external entity is its system ID.
+ * The base URI of an internal entity is the parent document's base URI.
+ * @since DOM Level 3 Core
+ */
+ public String getBaseURI()
+ {
+ String systemId = getSystemId();
+ return (systemId == null) ? owner.getBaseURI() : systemId;
+ }
+
+}
+
diff --git a/libjava/gnu/xml/dom/DomEntityReference.java b/libjava/gnu/xml/dom/DomEntityReference.java
new file mode 100644
index 0000000..65efcfc
--- /dev/null
+++ b/libjava/gnu/xml/dom/DomEntityReference.java
@@ -0,0 +1,130 @@
+/* DomEntityReference.java --
+ Copyright (C) 1999,2000,2001,2004 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.dom;
+
+import org.w3c.dom.DocumentType;
+import org.w3c.dom.Entity;
+import org.w3c.dom.EntityReference;
+
+/**
+ * <p> "EntityReference" implementation (reference to parsed entity).
+ * This is a non-core DOM class, supporting the "XML" feature.
+ * It does not represent builtin entities (such as "&amp;amp;")
+ * or character references, which are always directly expanded in
+ * DOM trees.</p>
+ *
+ * <p> Note that while the DOM specification permits these nodes to have
+ * a readonly set of children, this is not required. Similarly, it does
+ * not require a DOM to couple EntityReference nodes with any Entity nodes
+ * that have the same entity name (and equivalent children). It also
+ * effectively guarantees that references created directly or indirectly
+ * through the <em>Document.ImportNode</em> method will not have children.
+ * The level of functionality you may get is extremely variable.
+ *
+ * <p> Also significant is that even at their most functional level, the fact
+ * that EntityReference children must be readonly has caused significant
+ * problems when modifying work products held in DOM trees. Other problems
+ * include issues related to undeclared namespace prefixes (and references
+ * to the current default namespace) that may be found in the text of such
+ * parsed entities nodes. These must be contextually bound as part of DOM
+ * tree construction. When such nodes are moved, the namespace associated
+ * with a given prefix (or default) may change to be in conflict with the
+ * namespace bound to the node at creation time.
+ *
+ * <p> In short, <em>avoid using this DOM functionality</em>.
+ *
+ * @see DomDoctype
+ * @see DomEntity
+ *
+ * @author David Brownell
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+public class DomEntityReference
+ extends DomNode
+ implements EntityReference
+{
+
+ private String name;
+
+ /**
+ * Constructs an EntityReference node associated with the specified
+ * document. The creator should populate this with whatever contents
+ * are appropriate, and then mark it as readonly.
+ *
+ * <p>This constructor should only be invoked by a Document as part of
+ * its createEntityReference functionality, or through a subclass which
+ * is similarly used in a "Sub-DOM" style layer.
+ *
+ * @see DomNode#makeReadonly
+ */
+ protected DomEntityReference(DomDocument owner, String name)
+ {
+ super(ENTITY_REFERENCE_NODE, owner);
+ this.name = name;
+ }
+
+ /**
+ * Returns the name of the referenced entity.
+ * @since DOM Level 1 Core
+ */
+ public final String getNodeName()
+ {
+ return name;
+ }
+
+ /**
+ * The base URI of an entity reference is the base URI where the entity
+ * declaration occurs.
+ * @since DOM Level 3 Core
+ */
+ public final String getBaseURI()
+ {
+ DocumentType doctype = owner.getDoctype();
+ if (doctype == null)
+ {
+ return null;
+ }
+ Entity entity = (Entity) doctype.getEntities().getNamedItem(name);
+ if (entity == null)
+ {
+ return null;
+ }
+ return entity.getBaseURI();
+ }
+
+}
diff --git a/libjava/gnu/xml/dom/DomEvent.java b/libjava/gnu/xml/dom/DomEvent.java
new file mode 100644
index 0000000..4a52492
--- /dev/null
+++ b/libjava/gnu/xml/dom/DomEvent.java
@@ -0,0 +1,345 @@
+/* DomEvent.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.dom;
+
+import org.w3c.dom.*;
+import org.w3c.dom.events.*;
+import org.w3c.dom.views.AbstractView; // used by UIEvent
+
+/**
+ * "Event" implementation. Events are
+ * created (through DocumentEvent interface methods on the document object),
+ * and are sent to any target node in the document.
+ *
+ * <p> Applications may define application specific event subclasses, but
+ * should otherwise use the <em>DocumentTraversal</em> interface to acquire
+ * event objects.
+ *
+ * @author David Brownell
+ */
+public class DomEvent
+ implements Event
+{
+
+ String type; // init
+ EventTarget target;
+ EventTarget currentNode;
+ short eventPhase;
+ boolean bubbles; // init
+ boolean cancelable; // init
+ long timeStamp; // ?
+
+ /** Returns the event's type (name) as initialized */
+ public final String getType()
+ {
+ return type;
+ }
+
+ /**
+ * Returns event's target; delivery of an event is initiated
+ * by a <em>target.dispatchEvent(event)</em> invocation.
+ */
+ public final EventTarget getTarget()
+ {
+ return target;
+ }
+
+ /**
+ * Returns the target to which events are currently being
+ * delivered. When capturing or bubbling, this will not
+ * be what <em>getTarget</em> returns.
+ */
+ public final EventTarget getCurrentTarget()
+ {
+ return currentNode;
+ }
+
+ /**
+ * Returns CAPTURING_PHASE, AT_TARGET, or BUBBLING;
+ * only meaningful within EventListener.handleEvent
+ */
+ public final short getEventPhase()
+ {
+ return eventPhase;
+ }
+
+ /**
+ * Returns true if the news of the event bubbles to tree tops
+ * (as specified during initialization).
+ */
+ public final boolean getBubbles()
+ {
+ return bubbles;
+ }
+
+ /**
+ * Returns true if the default handling may be canceled
+ * (as specified during initialization).
+ */
+ public final boolean getCancelable()
+ {
+ return cancelable;
+ }
+
+ /**
+ * Returns the event's timestamp.
+ */
+ public final long getTimeStamp()
+ {
+ return timeStamp;
+ }
+
+ boolean stop;
+ boolean doDefault;
+
+ /**
+ * Requests the event no longer be captured or bubbled; only
+ * listeners on the event target will see the event, if they
+ * haven't yet been notified.
+ *
+ * <p> <em> Avoid using this </em> except for application-specific
+ * events, for which you the protocol explicitly "blesses" the use
+ * of this with some event types. Otherwise, you are likely to break
+ * algorithms which depend on event notification either directly or
+ * through bubbling or capturing. </p>
+ *
+ * <p> Note that this method is not final, specifically to enable
+ * enforcing of policies about events always propagating. </p>
+ */
+ public void stopPropagation()
+ {
+ stop = true;
+ }
+
+ /**
+ * Requests that whoever dispatched the event not perform their
+ * default processing when event delivery completes. Initializes
+ * event timestamp.
+ */
+ public final void preventDefault()
+ {
+ doDefault = false;
+ }
+
+ /** Initializes basic event state. */
+ public void initEvent(String typeArg,
+ boolean canBubbleArg,
+ boolean cancelableArg)
+ {
+ eventPhase = 0;
+ type = typeArg;
+ bubbles = canBubbleArg;
+ cancelable = cancelableArg;
+ timeStamp = System.currentTimeMillis();
+ }
+
+ /** Constructs, but does not initialize, an event. */
+ public DomEvent(String type)
+ {
+ this.type = type;
+ }
+
+ /**
+ * Returns a basic printable description of the event's type,
+ * state, and delivery conditions
+ */
+ public String toString()
+ {
+ StringBuffer buf = new StringBuffer("[Event ");
+ buf.append(type);
+ switch (eventPhase)
+ {
+ case CAPTURING_PHASE:
+ buf.append(", CAPTURING");
+ break;
+ case AT_TARGET:
+ buf.append(", AT TARGET");
+ break;
+ case BUBBLING_PHASE:
+ buf.append(", BUBBLING");
+ break;
+ default:
+ buf.append(", (inactive)");
+ break;
+ }
+ if (bubbles && eventPhase != BUBBLING_PHASE)
+ {
+ buf.append(", bubbles");
+ }
+ if (cancelable)
+ {
+ buf.append(", can cancel");
+ }
+ // were we to provide subclass info, this's where it'd live
+ buf.append("]");
+ return buf.toString();
+ }
+
+ /**
+ * "MutationEvent" implementation.
+ */
+ public static final class DomMutationEvent
+ extends DomEvent
+ implements MutationEvent
+ {
+
+ // package private
+ Node relatedNode; // init
+
+ private String prevValue; // init
+ private String newValue; // init
+
+ private String attrName; // init
+ private short attrChange; // init
+
+ /** Returns any "related" node provided by this type of event */
+ public final Node getRelatedNode()
+ {
+ return relatedNode;
+ }
+
+ /** Returns any "previous value" provided by this type of event */
+ public final String getPrevValue()
+ {
+ return prevValue;
+ }
+
+ /** Returns any "new value" provided by this type of event */
+ public final String getNewValue()
+ {
+ return newValue;
+ }
+
+ /** For attribute change events, returns the attribute's name */
+ public final String getAttrName()
+ {
+ return attrName;
+ }
+
+ /** For attribute change events, returns how the attribuet changed */
+ public final short getAttrChange()
+ {
+ return attrChange;
+ }
+
+ /** Initializes a mutation event */
+ public final void initMutationEvent(String typeArg,
+ boolean canBubbleArg,
+ boolean cancelableArg,
+ Node relatedNodeArg,
+ String prevValueArg,
+ String newValueArg,
+ String attrNameArg,
+ short attrChangeArg)
+ {
+ // super.initEvent is inlined here for speed
+ // (mutation events are issued on all DOM changes)
+ eventPhase = 0;
+ type = typeArg;
+ bubbles = canBubbleArg;
+ cancelable = cancelableArg;
+ timeStamp = System.currentTimeMillis();
+
+ relatedNode = relatedNodeArg;
+ prevValue = prevValueArg;
+ newValue = newValueArg;
+ attrName = attrNameArg;
+ attrChange = attrChangeArg;
+ }
+
+ // clear everything that should be GC-able
+ void clear()
+ {
+ type = null;
+ target = null;
+ relatedNode = null;
+ currentNode = null;
+ prevValue = newValue = attrName = null;
+ }
+
+ /** Constructs an uninitialized mutation event. */
+ public DomMutationEvent(String type)
+ {
+ super(type);
+ }
+
+ }
+
+ /**
+ * "UIEvent" implementation.
+ */
+ public static class DomUIEvent
+ extends DomEvent
+ implements UIEvent
+ {
+
+ private AbstractView view; // init
+ private int detail; // init
+
+ /** Constructs an uninitialized User Interface (UI) event */
+ public DomUIEvent (String type) { super (type); }
+
+ public final AbstractView getView () { return view; }
+ public final int getDetail () { return detail; }
+
+ /** Initializes a UI event */
+ public final void initUIEvent(String typeArg,
+ boolean canBubbleArg,
+ boolean cancelableArg,
+ AbstractView viewArg,
+ int detailArg)
+ {
+ super.initEvent(typeArg, canBubbleArg, cancelableArg);
+ view = viewArg;
+ detail = detailArg;
+ }
+
+ }
+
+ /*
+
+ static final class DomMouseEvent extends DomUIEvent
+ implements MouseEvent
+ {
+ // another half dozen state variables/accessors
+ }
+
+ */
+
+}
+
diff --git a/libjava/gnu/xml/dom/DomEx.java b/libjava/gnu/xml/dom/DomEx.java
new file mode 100644
index 0000000..aacac96
--- /dev/null
+++ b/libjava/gnu/xml/dom/DomEx.java
@@ -0,0 +1,175 @@
+/* DomEx.java --
+ Copyright (C) 1999,2000,2001,2004 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.dom;
+
+import org.w3c.dom.DOMException;
+import org.w3c.dom.Node;
+
+/**
+ * <p> DOMException implementation. The version that
+ * is provided by the W3C is abstract, so it can't be instantiated.
+ *
+ * <p> This also provides a bit more information about the error
+ * that is being reported, in terms of the relevant DOM structures
+ * and data.
+ *
+ * @author David Brownell
+ */
+public class DomEx
+ extends DOMException
+{
+
+ /** @serial Data that caused an error to be reported */
+ private String data;
+
+ /** @serial Node associated with the error. */
+ private Node node;
+
+ /** @serial Data associated with the error. */
+ private int value;
+
+ /**
+ * Constructs an exception, with the diagnostic message
+ * corresponding to the specified code.
+ */
+ public DomEx(short code)
+ {
+ super(code, diagnostic(code));
+ }
+
+ /**
+ * Constructs an exception, with the diagnostic message
+ * corresponding to the specified code and additional
+ * information as provided.
+ */
+ public DomEx(short code, String data, Node node, int value)
+ {
+ super(code, diagnostic(code));
+ this.data = data;
+ this.node = node;
+ this.value = value;
+ }
+
+ /** Returns the node to which the diagnotic applies, or null. */
+ final public Node getNode()
+ {
+ return node;
+ }
+
+ /** Returns data to which the diagnotic applies, or null. */
+ final public String getData()
+ {
+ return data;
+ }
+
+ /** Returns data to which the diagnotic applies, or null. */
+ final public int getValue()
+ {
+ return value;
+ }
+
+ /**
+ * Returns a diagnostic message that may be slightly more useful
+ * than the generic one, where possible.
+ */
+ public String getMessage()
+ {
+ String retval = super.getMessage();
+
+ if (data != null)
+ {
+ retval += "\nMore Information: " + data;
+ }
+ if (value != 0)
+ {
+ retval += "\nNumber: " + value;
+ }
+ if (node != null)
+ {
+ retval += "\nNode Name: " + node.getNodeName();
+ }
+ return retval;
+ }
+
+ // these strings should be localizable.
+
+ private static String diagnostic(short code)
+ {
+ switch (code)
+ {
+ // DOM L1:
+ case INDEX_SIZE_ERR:
+ return "An index or size is out of range.";
+ case DOMSTRING_SIZE_ERR:
+ return "A string is too big.";
+ case HIERARCHY_REQUEST_ERR:
+ return "The node doesn't belong here.";
+ case WRONG_DOCUMENT_ERR:
+ return "The node belongs in another document.";
+ case INVALID_CHARACTER_ERR:
+ return "That character is not permitted.";
+ case NO_DATA_ALLOWED_ERR:
+ return "This node does not permit data.";
+ case NO_MODIFICATION_ALLOWED_ERR:
+ return "No changes are allowed.";
+ case NOT_FOUND_ERR:
+ return "The node was not found in that context.";
+ case NOT_SUPPORTED_ERR:
+ return "That object is not supported.";
+ case INUSE_ATTRIBUTE_ERR:
+ return "The attribute belongs to a different element.";
+
+ // DOM L2:
+ case INVALID_STATE_ERR:
+ return "The object is not usable.";
+ case SYNTAX_ERR:
+ return "An illegal string was provided.";
+ case INVALID_MODIFICATION_ERR:
+ return "An object's type may not be changed.";
+ case NAMESPACE_ERR:
+ return "The operation violates XML Namespaces.";
+ case INVALID_ACCESS_ERR:
+ return "Parameter or operation isn't supported by this node.";
+ case TYPE_MISMATCH_ERR:
+ return "The type of the argument is incompatible with the expected type.";
+ }
+ return "Reserved exception number: " + code;
+ }
+
+}
+
diff --git a/libjava/gnu/xml/dom/DomExtern.java b/libjava/gnu/xml/dom/DomExtern.java
new file mode 100644
index 0000000..afd5301
--- /dev/null
+++ b/libjava/gnu/xml/dom/DomExtern.java
@@ -0,0 +1,117 @@
+/* DomExtern.java --
+ Copyright (C) 1999,2000,2001,2004 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.dom;
+
+/**
+ * <p> Abstract implemention of nodes describing external DTD-related
+ * objects. This facilitates reusing code for Entity, Notation, and
+ * DocumentType (really, external subset) nodes. Such support is not
+ * part of the core DOM; it's for the "XML" feature. </p>
+ *
+ * <p> Note that you are strongly advised to avoid using the DOM
+ * features that take advantage of this class, since (as of L2) none
+ * of them is defined fully enough to permit full use of the
+ * XML feature they partially expose. </p>
+ *
+ * @author David Brownell
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+public abstract class DomExtern
+ extends DomNode
+{
+
+ private final String name;
+ private final String publicId;
+ private final String systemId;
+
+ /**
+ * Constructs a node associated with the specified document,
+ * with the specified descriptive data.
+ *
+ * @param owner The document with which this object is associated
+ * @param name Name of this object
+ * @param publicId If non-null, provides the entity's PUBLIC identifier
+ * @param systemId If non-null, provides the entity's SYSTEM identifier
+ */
+ // package private
+ DomExtern(short nodeType,
+ DomDocument owner,
+ String name,
+ String publicId,
+ String systemId)
+ {
+ super(nodeType, owner);
+ this.name = name;
+ this.publicId = publicId;
+ this.systemId = systemId;
+ }
+
+ /**
+ * <b>DOM L1</b>
+ * Returns the SYSTEM identifier associated with this object, if any.
+ */
+ public final String getSystemId()
+ {
+ return systemId;
+ }
+
+ /**
+ * <b>DOM L1</b>
+ * Returns the PUBLIC identifier associated with this object, if any.
+ */
+ public final String getPublicId()
+ {
+ return publicId;
+ }
+
+ /**
+ * <b>DOM L1</b>
+ * Returns the object's name.
+ */
+ public final String getNodeName()
+ {
+ return name;
+ }
+
+ public final String getLocalName()
+ {
+ return name;
+ }
+
+}
+
diff --git a/libjava/gnu/xml/dom/DomFragment.java b/libjava/gnu/xml/dom/DomFragment.java
new file mode 100644
index 0000000..44763a9
--- /dev/null
+++ b/libjava/gnu/xml/dom/DomFragment.java
@@ -0,0 +1,76 @@
+/* DomFragment.java --
+ Copyright (C) 1999,2000,2001,2004 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.dom;
+
+import org.w3c.dom.DocumentFragment;
+
+/**
+ * <p> "DocumentFragment" implementation. </p>
+ *
+ * @author David Brownell
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+public class DomFragment
+ extends DomNode
+ implements DocumentFragment
+{
+
+ /**
+ * Constructs a DocumentFragment node associated with the
+ * specified document.
+ *
+ * <p>This constructor should only be invoked by a Document as part of
+ * its createDocumentFragment functionality, or through a subclass which
+ * is similarly used in a "Sub-DOM" style layer.
+ */
+ protected DomFragment(DomDocument owner)
+ {
+ super(DOCUMENT_FRAGMENT_NODE, owner);
+ }
+
+ /**
+ * <b>DOM L1</b>
+ * Returns the string "#document-fragment".
+ */
+ final public String getNodeName()
+ {
+ return "#document-fragment";
+ }
+
+}
+
diff --git a/libjava/gnu/xml/dom/DomImpl.java b/libjava/gnu/xml/dom/DomImpl.java
new file mode 100644
index 0000000..1a16de3
--- /dev/null
+++ b/libjava/gnu/xml/dom/DomImpl.java
@@ -0,0 +1,260 @@
+/* DomImpl.java --
+ Copyright (C) 1999,2000,2001,2004 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.dom;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.DocumentType;
+import org.w3c.dom.DOMException;
+import org.w3c.dom.DOMImplementation;
+import org.w3c.dom.Element;
+import org.w3c.dom.ls.DOMImplementationLS;
+import org.w3c.dom.ls.LSInput;
+import org.w3c.dom.ls.LSOutput;
+import org.w3c.dom.ls.LSParser;
+import org.w3c.dom.ls.LSSerializer;
+import gnu.xml.dom.ls.DomLSInput;
+import gnu.xml.dom.ls.DomLSOutput;
+import gnu.xml.dom.ls.DomLSParser;
+import gnu.xml.dom.ls.DomLSSerializer;
+
+/**
+ * <p> "DOMImplementation" implementation. </p>
+ *
+ * <p> At this writing, the following features are supported:
+ * "XML" (L1, L2, L3),
+ * "Events" (L2), "MutationEvents" (L2), "USER-Events" (a conformant extension),
+ * "HTMLEvents" (L2), "UIEvents" (L2), "Traversal" (L2), "XPath" (L3),
+ * "LS" (L3) "LS-Async" (L3).
+ * It is possible to compile the package so it doesn't support some of these
+ * features (notably, Traversal).
+ *
+ * @author David Brownell
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+public class DomImpl
+ implements DOMImplementation, DOMImplementationLS
+{
+
+ /**
+ * Constructs a DOMImplementation object which supports
+ * "XML" and other DOM Level 2 features.
+ */
+ public DomImpl()
+ {
+ }
+
+ /**
+ * <b>DOM L1</b>
+ * Returns true if the specified feature and version are
+ * supported. Note that the case of the feature name is ignored.
+ */
+ public boolean hasFeature(String name, String version)
+ {
+ if (name.length() == 0)
+ {
+ return false;
+ }
+ name = name.toLowerCase();
+ if (name.charAt(0) == '+')
+ {
+ name = name.substring(1);
+ }
+
+ if ("xml".equals(name) || "core".equals(name))
+ {
+ return (version == null ||
+ "".equals(version) ||
+ "1.0".equals(version) ||
+ "2.0".equals(version) ||
+ "3.0".equals(version));
+
+ }
+ else if ("ls".equals(name) || "ls-async".equals(name))
+ {
+ return (version == null ||
+ "".equals(version) ||
+ "3.0".equals(version));
+ }
+ else if ("events".equals(name)
+ || "mutationevents".equals(name)
+ || "uievents".equals(name)
+ // || "mouseevents".equals(name)
+ || "htmlevents".equals(name))
+ {
+ return (version == null ||
+ "".equals(version) ||
+ "2.0".equals(version));
+
+ // Extension: "USER-" prefix event types can
+ // be created and passed through the DOM.
+
+ }
+ else if ("user-events".equals(name))
+ {
+ return (version == null ||
+ "".equals(version) ||
+ "0.1".equals(version));
+
+ // NOTE: "hasFeature" for events is here interpreted to
+ // mean the DOM can manufacture those sorts of events,
+ // since actually choosing to report the events is more
+ // often part of the environment or application. It's
+ // only really an issue for mutation events.
+
+ }
+ else if (DomNode.reportMutations
+ && "traversal".equals(name))
+ {
+ return (version == null ||
+ "".equals(version) ||
+ "2.0".equals(version));
+ }
+ else if ("xpath".equals(name))
+ {
+ return (version == null ||
+ "".equals(version) ||
+ "3.0".equals(version));
+ }
+
+ // views
+ // stylesheets
+ // css, css2
+ // range
+
+ return false;
+ }
+
+ /**
+ * <b>DOM L2</b>
+ * Creates and returns a DocumentType, associated with this
+ * implementation. This DocumentType can have no associated
+ * objects(notations, entities) until the DocumentType is
+ * first associated with a document.
+ *
+ * <p> Note that there is no implication that this DTD will
+ * be parsed by the DOM, or ever have contents. Moreover, the
+ * DocumentType created here can only be added to a document by
+ * the createDocument method(below). <em>That means that the only
+ * portable way to create a Document object is to start parsing,
+ * queue comment and processing instruction (PI) nodes, and then only
+ * create a DOM Document after <b>(a)</b> it's known if a DocumentType
+ * object is needed, and <b>(b) the name and namespace of the root
+ * element is known. Queued comment and PI nodes would then be
+ * inserted appropriately in the document prologue, both before and
+ * after the DTD node, and additional attributes assigned to the
+ * root element.</em>
+ *(One hopes that the final DOM REC fixes this serious botch.)
+ */
+ public DocumentType createDocumentType(String rootName,
+ String publicId,
+ String systemId)
+ // CR2 deleted internal subset, ensuring DocumentType
+ // is 100% useless instead of just 90% so.
+ {
+ DomDocument.checkNCName(rootName, false);
+ return new DomDoctype(this, rootName, publicId, systemId, null);
+ }
+
+ /**
+ * <b>DOM L2</b>
+ * Creates and returns a Document, populated only with a root element and
+ * optionally a document type(if that was provided).
+ */
+ public Document createDocument(String namespaceURI,
+ String rootName,
+ DocumentType doctype)
+ {
+ Document doc = new DomDocument(this);
+ Element root = null;
+
+ if (rootName != null)
+ {
+ root = doc.createElementNS(namespaceURI, rootName);
+ if (rootName.startsWith("xmlns:"))
+ {
+ throw new DomEx(DomEx.NAMESPACE_ERR, "xmlns is reserved", null, 0);
+ }
+ }
+ // Bleech -- L2 seemingly _requires_ omission of xmlns attributes.
+ if (doctype != null)
+ {
+ doc.appendChild(doctype); // handles WRONG_DOCUMENT error
+ }
+ if (root != null)
+ {
+ doc.appendChild(root);
+ }
+ return doc;
+ }
+
+ // DOM Level 3
+
+ public Object getFeature(String feature, String version)
+ {
+ if (hasFeature(feature, version))
+ {
+ return this;
+ }
+ return null;
+ }
+
+ // -- DOMImplementationLS --
+
+ public LSParser createLSParser(short mode, String schemaType)
+ throws DOMException
+ {
+ return new DomLSParser(mode, schemaType);
+ }
+
+ public LSSerializer createLSSerializer()
+ {
+ return new DomLSSerializer();
+ }
+
+ public LSInput createLSInput()
+ {
+ return new DomLSInput();
+ }
+
+ public LSOutput createLSOutput()
+ {
+ return new DomLSOutput();
+ }
+
+}
+
diff --git a/libjava/gnu/xml/dom/DomIterator.java b/libjava/gnu/xml/dom/DomIterator.java
new file mode 100644
index 0000000..3dfd964
--- /dev/null
+++ b/libjava/gnu/xml/dom/DomIterator.java
@@ -0,0 +1,374 @@
+/* DomIterator.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.dom;
+
+import java.util.Vector;
+
+import org.w3c.dom.Node;
+import org.w3c.dom.events.Event;
+import org.w3c.dom.events.EventListener;
+import org.w3c.dom.events.EventTarget;
+import org.w3c.dom.events.MutationEvent;
+import org.w3c.dom.traversal.NodeFilter;
+import org.w3c.dom.traversal.NodeIterator;
+
+/**
+ * <p> "NodeIterator" implementation, usable with any L2 DOM which
+ * supports MutationEvents. </p>
+ *
+ * @author David Brownell
+ */
+public final class DomIterator
+ implements NodeIterator, EventListener
+{
+
+ private Node reference;
+ private boolean right;
+ private boolean done;
+
+ private final Node root;
+ private final int whatToShow;
+ private final NodeFilter filter;
+ private final boolean expandEntityReferences;
+
+ /**
+ * Constructs and initializes an iterator.
+ */
+ protected DomIterator(Node root,
+ int whatToShow,
+ NodeFilter filter,
+ boolean entityReferenceExpansion)
+ {
+ if (!root.isSupported("MutationEvents", "2.0"))
+ {
+ throw new DomEx(DomEx.NOT_SUPPORTED_ERR,
+ "Iterator needs mutation events", root, 0);
+ }
+
+ this.root = root;
+ this.whatToShow = whatToShow;
+ this.filter = filter;
+ this.expandEntityReferences = entityReferenceExpansion;
+
+ // start condition: going right, seen nothing yet.
+ reference = null;
+ right = true;
+
+ EventTarget target = (EventTarget) root;
+ target.addEventListener("DOMNodeRemoved", this, false);
+ }
+
+ /**
+ * <b>DOM L2</b>
+ * Flags the iterator as done, unregistering its event listener so
+ * that the iterator can be garbage collected without relying on weak
+ * references (a "Java 2" feature) in the event subsystem.
+ */
+ public void detach()
+ {
+ EventTarget target = (EventTarget) root;
+ target.removeEventListener("DOMNodeRemoved", this, false);
+ done = true;
+ }
+
+ /**
+ * <b>DOM L2</b>
+ * Returns the flag controlling whether iteration descends
+ * through entity references.
+ */
+ public boolean getExpandEntityReferences()
+ {
+ return expandEntityReferences;
+ }
+
+ /**
+ * <b>DOM L2</b>
+ * Returns the filter provided during construction.
+ */
+ public NodeFilter getFilter()
+ {
+ return filter;
+ }
+
+ /**
+ * <b>DOM L2</b>
+ * Returns the root of the tree this is iterating through.
+ */
+ public Node getRoot()
+ {
+ return root;
+ }
+
+ /**
+ * <b>DOM L2</b>
+ * Returns the mask of flags provided during construction.
+ */
+ public int getWhatToShow()
+ {
+ return whatToShow;
+ }
+
+ /**
+ * <b>DOM L2</b>
+ * Returns the next node in a forward iteration, masked and filtered.
+ * Note that the node may be read-only due to entity expansions.
+ * A null return indicates the iteration is complete, but may still
+ * be processed backwards.
+ */
+ public Node nextNode()
+ {
+ if (done)
+ {
+ throw new DomEx(DomEx.INVALID_STATE_ERR);
+ }
+ right = true;
+ return walk(true);
+ }
+
+ /**
+ * <b>DOM L2</b>
+ * Returns the next node in a backward iteration, masked and filtered.
+ * Note that the node may be read-only due to entity expansions.
+ * A null return indicates the iteration is complete, but may still
+ * be processed forwards.
+ */
+ public Node previousNode()
+ {
+ if (done)
+ {
+ throw new DomEx(DomEx.INVALID_STATE_ERR);
+ }
+ Node previous = reference;
+ right = false;
+ walk(false);
+ return previous;
+ }
+
+ private boolean shouldShow(Node node)
+ // raises Runtime exceptions indirectly, via acceptNode()
+ {
+ if ((whatToShow & (1 << (node.getNodeType() - 1))) == 0)
+ {
+ return false;
+ }
+ if (filter == null)
+ {
+ return true;
+ }
+ return filter.acceptNode(node) == NodeFilter.FILTER_ACCEPT;
+ }
+
+ //
+ // scenario: root = 1, sequence = 1 2 ... 3 4
+ // forward walk: 1 2 ... 3 4 null
+ // then backward: 4 3 ... 2 1 null
+ //
+ // At the leftmost end, "previous" == null
+ // At the rightmost end, "previous" == 4
+ //
+ // The current draft spec really seem to make no sense re the
+ // role of the reference node, so what it says is ignored here.
+ //
+ private Node walk(boolean forward)
+ {
+ Node here = reference;
+
+ while ((here = successor(here, forward)) != null
+ && !shouldShow(here))
+ {
+ continue;
+ }
+ if (here != null || !forward)
+ {
+ reference = here;
+ }
+ return here;
+ }
+
+ private boolean isLeaf(Node here)
+ {
+ boolean leaf = !here.hasChildNodes();
+ if (!leaf && !expandEntityReferences)
+ {
+ leaf = (here.getNodeType() == Node.ENTITY_REFERENCE_NODE);
+ }
+ return leaf;
+ }
+
+ //
+ // Returns the immediate successor in a forward (or backward)
+ // document order walk, sans filtering ... except that it knows
+ // how to stop, returning null when done. This is a depth first
+ // preorder traversal when run in the forward direction.
+ //
+ private Node successor(Node here, boolean forward)
+ {
+ Node next;
+
+ // the "leftmost" end is funky
+ if (here == null)
+ {
+ return forward ? root : null;
+ }
+
+ //
+ // Forward, this is preorder: children before siblings.
+ // Backward, it's postorder: we saw the children already.
+ //
+ if (forward && !isLeaf(here))
+ {
+ return here.getFirstChild();
+ }
+
+ //
+ // Siblings ... if forward, we visit them, if backwards
+ // we visit their children first.
+ //
+ if (forward)
+ {
+ if ((next = here.getNextSibling()) != null)
+ {
+ return next;
+ }
+ }
+ else if ((next = here.getPreviousSibling()) != null)
+ {
+ if (isLeaf(next))
+ {
+ return next;
+ }
+ next = next.getLastChild();
+ while (!isLeaf(next))
+ {
+ next = next.getLastChild();
+ }
+ return next;
+ }
+
+ //
+ // We can't go down or lateral -- it's up, then. The logic is
+ // the converse of what's above: backwards is easy (the parent
+ // is next), forwards isn't.
+ //
+ next = here.getParentNode();
+ if (!forward)
+ {
+ return next;
+ }
+
+ Node temp = null;
+ while (next != null
+ && next != root
+ && (temp = next.getNextSibling()) == null)
+ {
+ next = next.getParentNode();
+ }
+ if (next == root)
+ {
+ return null;
+ }
+ return temp;
+ }
+
+ /**
+ * Not for public use. This lets the iterator know when its
+ * reference node will be removed from the tree, so that a new
+ * one may be selected.
+ *
+ * <p> This version works by watching removal events as they
+ * bubble up. So, don't prevent them from bubbling.
+ */
+ public void handleEvent(Event e)
+ {
+ MutationEvent event;
+ Node ancestor, removed;
+
+ if (reference == null
+ || !"DOMNodeRemoved".equals(e.getType())
+ || e.getEventPhase() != Event.BUBBLING_PHASE)
+ {
+ return;
+ }
+
+ event = (MutationEvent) e;
+ removed = (Node) event.getTarget();
+
+ // See if the removal will cause trouble for this iterator
+ // by being the reference node or an ancestor of it.
+ for (ancestor = reference;
+ ancestor != null && ancestor != root;
+ ancestor = ancestor.getParentNode())
+ {
+ if (ancestor == removed)
+ {
+ break;
+ }
+ }
+ if (ancestor != removed)
+ {
+ return;
+ }
+
+ // OK, it'll cause trouble. We want to make the "next"
+ // node in our current traversal direction seem right.
+ // So we pick the nearest node that's not getting removed,
+ // but go in the _opposite_ direction from our current
+ // traversal ... so the "next" doesn't skip anything.
+ Node candidate;
+
+search:
+ while ((candidate = walk(!right)) != null)
+ {
+ for (ancestor = candidate;
+ ancestor != null && ancestor != root;
+ ancestor = ancestor.getParentNode())
+ {
+ if (ancestor == removed)
+ {
+ continue search;
+ }
+ }
+ return;
+ }
+
+ // The current DOM WD talks about a special case here;
+ // I've not yet seen it.
+ }
+
+}
+
diff --git a/libjava/gnu/xml/dom/DomNSResolverContext.java b/libjava/gnu/xml/dom/DomNSResolverContext.java
new file mode 100644
index 0000000..cf75ec1
--- /dev/null
+++ b/libjava/gnu/xml/dom/DomNSResolverContext.java
@@ -0,0 +1,90 @@
+/* DomNSResolverContext.java --
+ Copyright (C) 2004 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.dom;
+
+import java.util.Iterator;
+import javax.xml.namespace.NamespaceContext;
+import org.w3c.dom.xpath.XPathNSResolver;
+
+/**
+ * Namespace content wrapper for an XPathNSResolver.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+class DomNSResolverContext
+ implements NamespaceContext, Iterator
+{
+
+ final XPathNSResolver resolver;
+
+ DomNSResolverContext(XPathNSResolver resolver)
+ {
+ this.resolver = resolver;
+ }
+
+ public String getNamespaceURI(String prefix)
+ {
+ return resolver.lookupNamespaceURI(prefix);
+ }
+
+ public String getPrefix(String namespaceURI)
+ {
+ return null;
+ }
+
+ public Iterator getPrefixes(String namespaceURI)
+ {
+ return this;
+ }
+
+ public boolean hasNext()
+ {
+ return false;
+ }
+
+ public Object next()
+ {
+ return null;
+ }
+
+ public void remove()
+ {
+ throw new UnsupportedOperationException();
+ }
+
+}
diff --git a/libjava/gnu/xml/dom/DomNamedNodeMap.java b/libjava/gnu/xml/dom/DomNamedNodeMap.java
new file mode 100644
index 0000000..af75942
--- /dev/null
+++ b/libjava/gnu/xml/dom/DomNamedNodeMap.java
@@ -0,0 +1,416 @@
+/* DomNamedNodeMap.java --
+ Copyright (C) 1999,2000,2001,2004 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.dom;
+
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+
+/**
+ * <p> "NamedNodeMap" implementation. </p>
+ * Used mostly to hold element attributes, but sometimes also
+ * to list notations or entities.
+ *
+ * @author David Brownell
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+public class DomNamedNodeMap
+ implements NamedNodeMap
+{
+
+ final DomNode owner;
+ final short type;
+
+ DomNode first;
+ int length;
+ boolean readonly;
+
+ // package private
+ DomNamedNodeMap(DomNode owner, short type)
+ {
+ this.owner = owner;
+ this.type = type;
+ }
+
+ /**
+ * Exposes the internal "readonly" flag. In DOM, all NamedNodeMap
+ * objects found in a DocumentType object are read-only (after
+ * they are fully constructed), and those holding attributes of
+ * a readonly element will also be readonly.
+ */
+ public final boolean isReadonly()
+ {
+ return readonly;
+ }
+
+ /**
+ * Sets the internal "readonly" flag so the node and its
+ * children can't be changed.
+ */
+ public void makeReadonly()
+ {
+ readonly = true;
+ for (DomNode ctx = first; ctx != null; ctx = ctx.next)
+ {
+ ctx.makeReadonly();
+ }
+ }
+
+ /**
+ * <b>DOM L1</b>
+ * Returns the named item from the map, or null; names are just
+ * the nodeName property.
+ */
+ public Node getNamedItem(String name)
+ {
+ for (DomNode ctx = first; ctx != null; ctx = ctx.next)
+ {
+ if (ctx.getNodeName().equals(name))
+ {
+ return ctx;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * <b>DOM L2</b>
+ * Returns the named item from the map, or null; names are the
+ * localName and namespaceURI properties, ignoring any prefix.
+ */
+ public Node getNamedItemNS(String namespaceURI, String localName)
+ {
+ if ("".equals(namespaceURI))
+ {
+ namespaceURI = null;
+ }
+ for (DomNode ctx = first; ctx != null; ctx = ctx.next)
+ {
+ String name = ctx.getLocalName();
+ if ((localName == null && name == null) ||
+ (localName != null && localName.equals(name)))
+ {
+ String uri = ctx.getNamespaceURI();
+ if ("".equals(uri))
+ {
+ uri = null;
+ }
+ if ((namespaceURI == null && uri == null) ||
+ (namespaceURI != null && namespaceURI.equals(uri)))
+ {
+ return ctx;
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * <b>DOM L1</b>
+ * Stores the named item into the map, optionally overwriting
+ * any existing node with that name. The name used is just
+ * the nodeName attribute.
+ */
+ public Node setNamedItem(Node arg)
+ {
+ return setNamedItem(arg, false);
+ }
+
+ /**
+ * <b>DOM L2</b>
+ * Stores the named item into the map, optionally overwriting
+ * any existing node with that fully qualified name. The name
+ * used incorporates the localName and namespaceURI properties,
+ * and ignores any prefix.
+ */
+ public Node setNamedItemNS(Node arg)
+ {
+ return setNamedItem(arg, true);
+ }
+
+ Node setNamedItem(Node arg, boolean ns)
+ {
+ if (readonly)
+ {
+ throw new DomEx(DomEx.NO_MODIFICATION_ALLOWED_ERR);
+ }
+
+ DomNode node = (DomNode) arg;
+ if (node.owner != owner.owner)
+ {
+ throw new DomEx(DomEx.WRONG_DOCUMENT_ERR);
+ }
+ if (node.nodeType != type)
+ {
+ throw new DomEx(DomEx.HIERARCHY_REQUEST_ERR);
+ }
+ if (node.nodeType == Node.ATTRIBUTE_NODE)
+ {
+ DomNode element = node.parent;
+ if (element != null && element != owner)
+ {
+ throw new DomEx(DomEx.INUSE_ATTRIBUTE_ERR);
+ }
+ node.parent = owner;
+ node.depth = owner.depth + 1;
+ }
+
+ String nodeName = node.getNodeName();
+ String localName = ns ? node.getLocalName() : null;
+ String namespaceURI = ns ? node.getNamespaceURI() : null;
+ if ("".equals(namespaceURI))
+ {
+ namespaceURI = null;
+ }
+
+ // maybe attribute ADDITION events (?)
+ DomNode last = null;
+ for (DomNode ctx = first; ctx != null; ctx = ctx.next)
+ {
+ boolean test = false;
+ if (ns)
+ {
+ String tln = ctx.getLocalName();
+ if (tln.equals(localName))
+ {
+ String tu = ctx.getNamespaceURI();
+ if ((tu == null && namespaceURI == null) ||
+ (tu != null && tu.equals(namespaceURI)))
+ {
+ test = true;
+ }
+ }
+ }
+ else
+ {
+ test = ctx.getNodeName().equals(nodeName);
+ }
+ if (test)
+ {
+ // replace
+ node.previous = ctx.previous;
+ node.next = ctx.next;
+ if (ctx.previous != null)
+ {
+ ctx.previous.next = node;
+ }
+ if (ctx.next != null)
+ {
+ ctx.next.previous = node;
+ }
+ if (first == ctx)
+ {
+ first = node;
+ }
+ reparent(node, nodeName, ctx.index);
+ ctx.parent = null;
+ ctx.next = null;
+ ctx.previous = null;
+ ctx.setDepth(0);
+ ctx.index = 0;
+ return ctx;
+ }
+ last = ctx;
+ }
+ // append
+ if (last != null)
+ {
+ last.next = node;
+ node.previous = last;
+ }
+ else
+ {
+ first = node;
+ }
+ length++;
+ reparent(node, nodeName, 0);
+ return null;
+ }
+
+ void reparent(DomNode node, String nodeName, int i)
+ {
+ node.parent = owner;
+ node.setDepth(owner.depth + 1);
+ // index renumbering
+ for (DomNode ctx = node; ctx != null; ctx = ctx.next)
+ {
+ ctx.index = i++;
+ }
+ // cache xml:space
+ boolean xmlSpace = "xml:space".equals(nodeName);
+ if (xmlSpace && owner instanceof DomElement)
+ {
+ ((DomElement) owner).xmlSpace = node.getNodeValue();
+ }
+ }
+
+ /**
+ * <b>DOM L1</b>
+ * Removes the named item from the map, or reports an exception;
+ * names are just the nodeName property.
+ */
+ public Node removeNamedItem(String name)
+ {
+ return removeNamedItem(null, name, false);
+ }
+
+ /**
+ * <b>DOM L2</b>
+ * Removes the named item from the map, or reports an exception;
+ * names are the localName and namespaceURI properties.
+ */
+ public Node removeNamedItemNS(String namespaceURI, String localName)
+ {
+ return removeNamedItem(namespaceURI, localName, true);
+ }
+
+ Node removeNamedItem(String uri, String name, boolean ns)
+ {
+ if (readonly)
+ {
+ throw new DomEx(DomEx.NO_MODIFICATION_ALLOWED_ERR);
+ }
+
+ // report attribute REMOVAL event?
+
+ for (DomNode ctx = first; ctx != null; ctx = ctx.next)
+ {
+ boolean test = false;
+ String nodeName = ctx.getNodeName();
+ if (ns)
+ {
+ String tln = ctx.getLocalName();
+ if (tln.equals(name))
+ {
+ String tu = ctx.getNamespaceURI();
+ if ((tu == null && uri == null) ||
+ (tu != null && tu.equals(uri)))
+ {
+ test = true;
+ }
+ }
+ }
+ else
+ {
+ test = nodeName.equals(name);
+ }
+ if (test)
+ {
+ // uncache xml:space
+ boolean xmlSpace = "xml:space".equals(nodeName);
+ if (xmlSpace && owner instanceof DomElement)
+ {
+ ((DomElement) owner).xmlSpace = "";
+ }
+ // is this a default attribute?
+ if (ctx.nodeType == Node.ATTRIBUTE_NODE)
+ {
+ String def = getDefaultValue(ctx.getNodeName());
+ if (def != null)
+ {
+ ctx.setNodeValue(def);
+ ((DomAttr) ctx).setSpecified(false);
+ return null;
+ }
+ }
+ // remove
+ if (ctx == first)
+ {
+ first = ctx.next;
+ }
+ if (ctx.previous != null)
+ {
+ ctx.previous.next = ctx.next;
+ }
+ if (ctx.next != null)
+ {
+ ctx.next.previous = ctx.previous;
+ }
+ length--;
+ ctx.previous = null;
+ ctx.next = null;
+ ctx.parent = null;
+ ctx.setDepth(0);
+ ctx.index = 0;
+ return ctx;
+ }
+ }
+ throw new DomEx(DomEx.NOT_FOUND_ERR);
+ }
+
+ String getDefaultValue(String name)
+ {
+ DomDoctype doctype = (DomDoctype) owner.owner.getDoctype();
+ if (doctype == null)
+ {
+ return null;
+ }
+ DTDAttributeTypeInfo info =
+ doctype.getAttributeTypeInfo(owner.getNodeName(), name);
+ if (info == null)
+ {
+ return null;
+ }
+ return info.value;
+ }
+
+ /**
+ * <b>DOM L1</b>
+ * Returns the indexed item from the map, or null.
+ */
+ public Node item(int index)
+ {
+ DomNode ctx = first;
+ int count = 0;
+ while (ctx != null && count < index)
+ {
+ ctx = ctx.next;
+ count++;
+ }
+ return ctx;
+ }
+
+ /**
+ * <b>DOM L1</b>
+ * Returns the length of the map.
+ */
+ public int getLength()
+ {
+ return length;
+ }
+
+}
diff --git a/libjava/gnu/xml/dom/DomNode.java b/libjava/gnu/xml/dom/DomNode.java
new file mode 100644
index 0000000..3858fe8
--- /dev/null
+++ b/libjava/gnu/xml/dom/DomNode.java
@@ -0,0 +1,2180 @@
+/* DomNode.java --
+ Copyright (C) 1999,2000,2001,2004 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.dom;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import javax.xml.XMLConstants;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.DOMException;
+import org.w3c.dom.DOMImplementation;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.w3c.dom.Text;
+import org.w3c.dom.UserDataHandler;
+import org.w3c.dom.events.DocumentEvent;
+import org.w3c.dom.events.Event;
+import org.w3c.dom.events.EventException;
+import org.w3c.dom.events.EventListener;
+import org.w3c.dom.events.EventTarget;
+import org.w3c.dom.events.MutationEvent;
+import org.w3c.dom.traversal.NodeFilter;
+import org.w3c.dom.traversal.NodeIterator;
+import org.w3c.dom.traversal.TreeWalker;
+
+/**
+ * <p> "Node", "EventTarget", and "DocumentEvent" implementation.
+ * This provides most of the core DOM functionality; only more
+ * specialized features are provided by subclasses. Those subclasses may
+ * have some particular constraints they must implement, by overriding
+ * methods defined here. Such constraints are noted here in the method
+ * documentation. </p>
+ *
+ * <p> Note that you can create events with type names prefixed with "USER-",
+ * and pass them through this DOM. This lets you use the DOM event scheme
+ * for application specific purposes, although you must use a predefined event
+ * structure (such as MutationEvent) to pass data along with those events.
+ * Test for existence of this feature with the "USER-Events" DOM feature
+ * name.</p>
+ *
+ * <p> Other kinds of events you can send include the "html" events,
+ * like "load", "unload", "abort", "error", and "blur"; and the mutation
+ * events. If this DOM has been compiled with mutation event support
+ * enabled, it will send mutation events when you change parts of the
+ * tree; otherwise you may create and send such events yourself, but
+ * they won't be generated by the DOM itself. </p>
+ *
+ * <p> Note that there is a namespace-aware name comparison method,
+ * <em>nameAndTypeEquals</em>, which compares the names (and types) of
+ * two nodes in conformance with the "Namespaces in XML" specification.
+ * While mostly intended for use with elements and attributes, this should
+ * also be helpful for ProcessingInstruction nodes and some others which
+ * do not have namespace URIs.
+ *
+ * @author David Brownell
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+public abstract class DomNode
+ implements Node, NodeList, EventTarget, DocumentEvent, Cloneable, Comparable
+{
+
+ // package private
+ //final static String xmlNamespace = "http://www.w3.org/XML/1998/namespace";
+ //final static String xmlnsURI = "http://www.w3.org/2000/xmlns/";
+
+ // tunable
+ // NKIDS_* affects arrays of children (which grow)
+ // (currently) fixed size:
+ // ANCESTORS_* is for event capture/bubbling, # ancestors
+ // NOTIFICATIONS_* is for per-node event delivery, # events
+ private static final int NKIDS_DELTA = 8;
+ private static final int ANCESTORS_INIT = 20;
+ private static final int NOTIFICATIONS_INIT = 10;
+
+ // tunable: enable mutation events or not? Enabling it costs about
+ // 10-15% in DOM construction time, last time it was measured.
+
+ // package private !!!
+ static final boolean reportMutations = true;
+
+ // locking protocol changeable only within this class
+ private static final Object lockNode = new Object();
+
+ // NON-FINAL class data
+
+ // Optimize event dispatch by not allocating memory each time
+ private static boolean dispatchDataLock;
+ private static DomNode[] ancestors = new DomNode[ANCESTORS_INIT];
+ private static ListenerRecord[] notificationSet
+ = new ListenerRecord[NOTIFICATIONS_INIT];
+
+ // Ditto for the (most common) event object itself!
+ private static boolean eventDataLock;
+ private static DomEvent.DomMutationEvent mutationEvent
+ = new DomEvent.DomMutationEvent(null);
+
+ //
+ // PER-INSTANCE DATA
+ //
+
+ DomDocument owner;
+ DomNode parent; // parent node;
+ DomNode previous; // previous sibling node
+ DomNode next; // next sibling node
+ DomNode first; // first child node
+ DomNode last; // last child node
+ int index; // index of this node in its parent's children
+ int depth; // depth of the node in the document
+ int length; // number of children
+ final short nodeType;
+
+ // Bleech ... "package private" so a builder can populate entity refs.
+ // writable during construction. DOM spec is nasty.
+ boolean readonly;
+
+ // event registrations
+ private ListenerRecord[] listeners;
+ private int nListeners;
+
+ // DOM Level 3 userData dictionary.
+ private HashMap userData;
+ private HashMap userDataHandlers;
+
+ //
+ // Some of the methods here are declared 'final' because
+ // knowledge about their implementation is built into this
+ // class -- for both integrity and performance.
+ //
+
+ /**
+ * Reduces space utilization for this node.
+ */
+ public void compact()
+ {
+ if (listeners != null && listeners.length != nListeners)
+ {
+ if (nListeners == 0)
+ {
+ listeners = null;
+ }
+ else
+ {
+ ListenerRecord[] l = new ListenerRecord[nListeners];
+ System.arraycopy(listeners, 0, l, 0, nListeners);
+ listeners = l;
+ }
+ }
+ }
+
+ /**
+ * Constructs a node and associates it with its owner. Only
+ * Document and DocumentType nodes may be created with no owner,
+ * and DocumentType nodes get an owner as soon as they are
+ * associated with a document.
+ */
+ protected DomNode(short nodeType, DomDocument owner)
+ {
+ this.nodeType = nodeType;
+
+ if (owner == null)
+ {
+ // DOM calls never go down this path
+ if (nodeType != DOCUMENT_NODE && nodeType != DOCUMENT_TYPE_NODE)
+ {
+ throw new IllegalArgumentException ("no owner!");
+ }
+ }
+ this.owner = owner;
+ }
+
+
+ /**
+ * <b>DOM L1</b>
+ * Returns null; Element subclasses must override this method.
+ */
+ public NamedNodeMap getAttributes()
+ {
+ return null;
+ }
+
+ /**
+ * <b>DOM L2></b>
+ * Returns true iff this is an element node with attributes.
+ */
+ public boolean hasAttributes()
+ {
+ return false;
+ }
+
+ /**
+ * <b>DOM L1</b>
+ * Returns a list, possibly empty, of the children of this node.
+ * In this implementation, to conserve memory, nodes are the same
+ * as their list of children. This can have ramifications for
+ * subclasses, which may need to provide their own getLength method
+ * for reasons unrelated to the NodeList method of the same name.
+ */
+ public NodeList getChildNodes()
+ {
+ return this;
+ }
+
+ /**
+ * <b>DOM L1</b>
+ * Returns the first child of this node, or null if there are none.
+ */
+ public Node getFirstChild()
+ {
+ return first;
+ }
+
+ /**
+ * <b>DOM L1</b>
+ * Returns the last child of this node, or null if there are none.
+ */
+ public Node getLastChild()
+ {
+ return last;
+ }
+
+ /**
+ * <b>DOM L1</b>
+ * Returns true if this node has children.
+ */
+ public boolean hasChildNodes()
+ {
+ return length != 0;
+ }
+
+
+ /**
+ * Exposes the internal "readonly" flag. In DOM, children of
+ * entities and entity references are readonly, as are the
+ * objects associated with DocumentType objets.
+ */
+ public final boolean isReadonly()
+ {
+ return readonly;
+ }
+
+ /**
+ * Sets the internal "readonly" flag so this subtree can't be changed.
+ * Subclasses need to override this method for any associated content
+ * that's not a child node, such as an element's attributes or the
+ * (few) declarations associated with a DocumentType.
+ */
+ public void makeReadonly()
+ {
+ readonly = true;
+ for (DomNode child = first; child != null; child = child.next)
+ {
+ child.makeReadonly();
+ }
+ }
+
+ /**
+ * Used to adopt a node to a new document.
+ */
+ void setOwner(DomDocument doc)
+ {
+ this.owner = doc;
+ for (DomNode ctx = first; ctx != null; ctx = ctx.next)
+ {
+ ctx.setOwner(doc);
+ }
+ }
+
+ // just checks the node for inclusion -- may be called many
+ // times (docfrag) before anything is allowed to change
+ private void checkMisc(DomNode child)
+ {
+ if (readonly && !owner.building)
+ {
+ throw new DomEx(DomEx.NO_MODIFICATION_ALLOWED_ERR,
+ null, this, 0);
+ }
+ for (DomNode ctx = this; ctx != null; ctx = ctx.parent)
+ {
+ if (child == ctx)
+ {
+ throw new DomEx(DomEx.HIERARCHY_REQUEST_ERR,
+ "can't make ancestor into a child", this, 0);
+ }
+ }
+
+ DomDocument owner = (nodeType == DOCUMENT_NODE) ? (DomDocument) this :
+ this.owner;
+ DomDocument childOwner = child.owner;
+ short childNodeType = child.nodeType;
+
+ if (childOwner != owner)
+ {
+ // new in DOM L2, this case -- patch it up later, in reparent()
+ if (!(childNodeType == DOCUMENT_TYPE_NODE && childOwner == null))
+ {
+ throw new DomEx(DomEx.WRONG_DOCUMENT_ERR,
+ null, child, 0);
+ }
+ }
+
+ // enforce various structural constraints
+ switch (nodeType)
+ {
+ case DOCUMENT_NODE:
+ switch (childNodeType)
+ {
+ case ELEMENT_NODE:
+ case PROCESSING_INSTRUCTION_NODE:
+ case COMMENT_NODE:
+ case DOCUMENT_TYPE_NODE:
+ return;
+ }
+ break;
+
+ case ATTRIBUTE_NODE:
+ switch (childNodeType)
+ {
+ case TEXT_NODE:
+ case ENTITY_REFERENCE_NODE:
+ return;
+ }
+ break;
+
+ case DOCUMENT_FRAGMENT_NODE:
+ case ENTITY_REFERENCE_NODE:
+ case ELEMENT_NODE:
+ case ENTITY_NODE:
+ switch (childNodeType)
+ {
+ case ELEMENT_NODE:
+ case TEXT_NODE:
+ case COMMENT_NODE:
+ case PROCESSING_INSTRUCTION_NODE:
+ case CDATA_SECTION_NODE:
+ case ENTITY_REFERENCE_NODE:
+ return;
+ }
+ break;
+ }
+ if (owner.checkingWellformedness)
+ {
+ throw new DomEx(DomEx.HIERARCHY_REQUEST_ERR,
+ "can't append " + nodeTypeToString(childNodeType) +
+ " to node of type " + nodeTypeToString(nodeType),
+ this, 0);
+ }
+ }
+
+ // Here's hoping a good optimizer will detect the case when the
+ // next several methods are never called, and won't allocate
+ // object code space of any kind. (Case: not reporting any
+ // mutation events. We can also remove some static variables
+ // listed above.)
+
+ private void insertionEvent(DomEvent.DomMutationEvent event,
+ DomNode target)
+ {
+ if (owner == null || owner.building)
+ {
+ return;
+ }
+ boolean doFree = false;
+
+ if (event == null)
+ {
+ event = getMutationEvent();
+ }
+ if (event != null)
+ {
+ doFree = true;
+ }
+ else
+ {
+ event = new DomEvent.DomMutationEvent(null);
+ }
+ event.initMutationEvent("DOMNodeInserted",
+ true /* bubbles */, false /* nocancel */,
+ this /* related */, null, null, null, (short) 0);
+ target.dispatchEvent(event);
+
+ // XXX should really visit every descendant of 'target'
+ // and sent a DOMNodeInsertedIntoDocument event to it...
+ // bleech, there's no way to keep that acceptably fast.
+
+ if (doFree)
+ {
+ event.target = null;
+ event.relatedNode = null;
+ event.currentNode = null;
+ eventDataLock = false;
+ } // else we created work for the GC
+ }
+
+ private void removalEvent(DomEvent.DomMutationEvent event,
+ DomNode target)
+ {
+ if (owner == null || owner.building)
+ {
+ return;
+ }
+ boolean doFree = false;
+
+ if (event == null)
+ {
+ event = getMutationEvent();
+ }
+ if (event != null)
+ {
+ doFree = true;
+ }
+ else
+ {
+ event = new DomEvent.DomMutationEvent(null);
+ }
+ event.initMutationEvent("DOMNodeRemoved",
+ true /* bubbles */, false /* nocancel */,
+ this /* related */, null, null, null, (short) 0);
+ target.dispatchEvent(event);
+
+ // XXX should really visit every descendant of 'target'
+ // and sent a DOMNodeRemovedFromDocument event to it...
+ // bleech, there's no way to keep that acceptably fast.
+
+ event.target = null;
+ event.relatedNode = null;
+ event.currentNode = null;
+ if (doFree)
+ {
+ eventDataLock = false;
+ }
+ // else we created more work for the GC
+ }
+
+ //
+ // Avoid creating lots of memory management work, by using a simple
+ // allocation strategy for the mutation event objects that get used
+ // at least once per tree modification. We can't use stack allocation,
+ // so we do the next simplest thing -- more or less, static allocation.
+ // Concurrent notifications should be rare, anyway.
+ //
+ // Returns the preallocated object, which needs to be carefully freed,
+ // or null to indicate the caller needs to allocate their own.
+ //
+ static private DomEvent.DomMutationEvent getMutationEvent()
+ {
+ synchronized (lockNode)
+ {
+ if (eventDataLock)
+ {
+ return null;
+ }
+ eventDataLock = true;
+ return mutationEvent;
+ }
+ }
+
+ // NOTE: this is manually inlined in the insertion
+ // and removal event methods above; change in sync.
+ static private void freeMutationEvent()
+ {
+ // clear fields to enable GC
+ mutationEvent.clear();
+ eventDataLock = false;
+ }
+
+ void setDepth(int depth)
+ {
+ this.depth = depth;
+ for (DomNode ctx = first; ctx != null; ctx = ctx.next)
+ {
+ ctx.setDepth(depth + 1);
+ }
+ }
+
+ /**
+ * <b>DOM L1</b>
+ * Appends the specified node to this node's list of children.
+ * Document subclasses must override this to enforce the restrictions
+ * that there be only one element and document type child.
+ *
+ * <p> Causes a DOMNodeInserted mutation event to be reported.
+ * Will first cause a DOMNodeRemoved event to be reported if the
+ * parameter already has a parent. If the new child is a document
+ * fragment node, both events will be reported for each child of
+ * the fragment; the order in which children are removed and
+ * inserted is implementation-specific.
+ *
+ * <p> If this DOM has been compiled without mutation event support,
+ * these events will not be reported.
+ */
+ public Node appendChild(Node newChild)
+ {
+ try
+ {
+ DomNode child = (DomNode) newChild;
+
+ if (child.nodeType == DOCUMENT_FRAGMENT_NODE)
+ {
+ // Append all nodes in the fragment to this node
+ for (DomNode ctx = child.first; ctx != null; ctx = ctx.next)
+ {
+ checkMisc(ctx);
+ }
+ for (DomNode ctx = child.first; ctx != null; )
+ {
+ DomNode ctxNext = ctx.next;
+ appendChild(ctx);
+ ctx = ctxNext;
+ }
+ }
+ else
+ {
+ checkMisc(child);
+ if (child.parent != null)
+ {
+ child.parent.removeChild(child);
+ }
+ child.parent = this;
+ child.index = length++;
+ child.setDepth(depth + 1);
+ child.next = null;
+ if (last == null)
+ {
+ first = child;
+ child.previous = null;
+ }
+ else
+ {
+ last.next = child;
+ child.previous = last;
+ }
+ last = child;
+
+ if (reportMutations)
+ {
+ insertionEvent(null, child);
+ }
+ }
+
+ return child;
+ }
+ catch (ClassCastException e)
+ {
+ throw new DomEx(DomEx.WRONG_DOCUMENT_ERR,
+ null, newChild, 0);
+ }
+ }
+
+ /**
+ * <b>DOM L1</b>
+ * Inserts the specified node in this node's list of children.
+ * Document subclasses must override this to enforce the restrictions
+ * that there be only one element and document type child.
+ *
+ * <p> Causes a DOMNodeInserted mutation event to be reported. Will
+ * first cause a DOMNodeRemoved event to be reported if the newChild
+ * parameter already has a parent. If the new child is a document
+ * fragment node, both events will be reported for each child of
+ * the fragment; the order in which children are removed and inserted
+ * is implementation-specific.
+ *
+ * <p> If this DOM has been compiled without mutation event support,
+ * these events will not be reported.
+ */
+ public Node insertBefore(Node newChild, Node refChild)
+ {
+ if (refChild == null)
+ {
+ return appendChild(newChild);
+ }
+
+ try
+ {
+ DomNode child = (DomNode) newChild;
+ DomNode ref = (DomNode) refChild;
+
+ if (child.nodeType == DOCUMENT_FRAGMENT_NODE)
+ {
+ // Append all nodes in the fragment to this node
+ for (DomNode ctx = child.first; ctx != null; ctx = ctx.next)
+ {
+ checkMisc(ctx);
+ }
+ for (DomNode ctx = child.first; ctx != null; )
+ {
+ DomNode ctxNext = ctx.next;
+ insertBefore(ctx, ref);
+ ctx = ctxNext;
+ }
+ }
+ else
+ {
+ checkMisc(child);
+ if (ref == null || ref.parent != this)
+ {
+ throw new DomEx(DomEx.NOT_FOUND_ERR, null, ref, 0);
+ }
+ if (ref == child)
+ {
+ throw new DomEx(DomEx.HIERARCHY_REQUEST_ERR,
+ "can't insert node before itself", ref, 0);
+ }
+
+ if (child.parent != null)
+ {
+ child.parent.removeChild(child);
+ }
+ child.parent = this;
+ int i = ref.index;
+ child.setDepth(depth + 1);
+ child.next = ref;
+ if (ref.previous != null)
+ {
+ ref.previous.next = child;
+ }
+ child.previous = ref.previous;
+ ref.previous = child;
+ if (first == ref)
+ {
+ first = child;
+ }
+ // index renumbering
+ for (DomNode ctx = child; ctx != null; ctx = ctx.next)
+ {
+ ctx.index = i++;
+ }
+
+ if (reportMutations)
+ {
+ insertionEvent(null, child);
+ }
+ }
+
+ return child;
+ }
+ catch (ClassCastException e)
+ {
+ throw new DomEx(DomEx.WRONG_DOCUMENT_ERR,
+ null, newChild, 0);
+ }
+ }
+
+ /**
+ * <b>DOM L1</b>
+ * Replaces the specified node in this node's list of children.
+ * Document subclasses must override this to test the restrictions
+ * that there be only one element and document type child.
+ *
+ * <p> Causes DOMNodeRemoved and DOMNodeInserted mutation event to be
+ * reported. Will cause another DOMNodeRemoved event to be reported if
+ * the newChild parameter already has a parent. These events may be
+ * delivered in any order, except that the event reporting removal
+ * from such an existing parent will always be delivered before the
+ * event reporting its re-insertion as a child of some other node.
+ * The order in which children are removed and inserted is implementation
+ * specific.
+ *
+ * <p> If your application needs to depend on the in which those removal
+ * and insertion events are delivered, don't use this API. Instead,
+ * invoke the removeChild and insertBefore methods directly, to guarantee
+ * a specific delivery order. Similarly, don't use document fragments,
+ * Otherwise your application code may not work on a DOM which implements
+ * this method differently.
+ *
+ * <p> If this DOM has been compiled without mutation event support,
+ * these events will not be reported.
+ */
+ public Node replaceChild(Node newChild, Node refChild)
+ {
+ try
+ {
+ DomNode child = (DomNode) newChild;
+ DomNode ref = (DomNode) refChild;
+
+ DomEvent.DomMutationEvent event = getMutationEvent();
+ boolean doFree = (event != null);
+
+ if (child.nodeType == DOCUMENT_FRAGMENT_NODE)
+ {
+ // Append all nodes in the fragment to this node
+ for (DomNode ctx = child.first; ctx != null; ctx = ctx.next)
+ {
+ checkMisc(ctx);
+ }
+ if (ref == null || ref.parent != this)
+ {
+ throw new DomEx(DomEx.NOT_FOUND_ERR, null, ref, 0);
+ }
+
+ if (reportMutations)
+ {
+ removalEvent(event, ref);
+ }
+ length--;
+ length += child.length;
+
+ if (child.length == 0)
+ {
+ // Removal
+ if (ref.previous != null)
+ {
+ ref.previous.next = ref.next;
+ }
+ if (ref.next != null)
+ {
+ ref.next.previous = ref.previous;
+ }
+ if (first == ref)
+ {
+ first = ref.next;
+ }
+ if (last == ref)
+ {
+ last = ref.previous;
+ }
+ }
+ else
+ {
+ int i = ref.index;
+ for (DomNode ctx = child.first; ctx != null; ctx = ctx.next)
+ {
+ // Insertion
+ ctx.parent = this;
+ ctx.index = i++;
+ ctx.setDepth(ref.depth);
+ if (ctx == child.first)
+ {
+ ctx.previous = ref.previous;
+ }
+ if (ctx == child.last)
+ {
+ ctx.next = ref.next;
+ }
+ }
+ if (first == ref)
+ {
+ first = child.first;
+ }
+ if (last == ref)
+ {
+ last = child.last;
+ }
+ }
+ }
+ else
+ {
+ checkMisc(child);
+ if (ref == null || ref.parent != this)
+ {
+ throw new DomEx(DomEx.NOT_FOUND_ERR, null, ref, 0);
+ }
+
+ if (reportMutations)
+ {
+ removalEvent(event, ref);
+ }
+
+ if (child.parent != null)
+ {
+ child.parent.removeChild(child);
+ }
+ child.parent = this;
+ child.index = ref.index;
+ child.setDepth(ref.depth);
+ if (ref.previous != null)
+ {
+ ref.previous.next = child;
+ }
+ child.previous = ref.previous;
+ if (ref.next != null)
+ {
+ ref.next.previous = child;
+ }
+ child.next = ref.next;
+ if (first == ref)
+ {
+ first = child;
+ }
+ if (last == ref)
+ {
+ last = child;
+ }
+
+ if (reportMutations)
+ {
+ insertionEvent(event, child);
+ }
+ if (doFree)
+ {
+ freeMutationEvent();
+ }
+ }
+ ref.parent = null;
+ ref.index = 0;
+ ref.setDepth(0);
+ ref.previous = null;
+ ref.next = null;
+
+ return ref;
+ }
+ catch (ClassCastException e)
+ {
+ throw new DomEx(DomEx.WRONG_DOCUMENT_ERR,
+ null, newChild, 0);
+ }
+ }
+
+ /**
+ * <b>DOM L1</b>
+ * Removes the specified child from this node's list of children,
+ * or else reports an exception.
+ *
+ * <p> Causes a DOMNodeRemoved mutation event to be reported.
+ *
+ * <p> If this DOM has been compiled without mutation event support,
+ * these events will not be reported.
+ */
+ public Node removeChild(Node refChild)
+ {
+ try
+ {
+ DomNode ref = (DomNode) refChild;
+
+ if (ref == null || ref.parent != this)
+ {
+ throw new DomEx(DomEx.NOT_FOUND_ERR, null, ref, 0);
+ }
+ if (readonly && !owner.building)
+ {
+ throw new DomEx(DomEx.NO_MODIFICATION_ALLOWED_ERR,
+ null, this, 0);
+ }
+
+ for (DomNode child = first; child != null; child = child.next)
+ {
+ if (child == ref)
+ {
+ if (reportMutations)
+ {
+ removalEvent(null, child);
+ }
+
+ length--;
+ if (ref.previous != null)
+ {
+ ref.previous.next = ref.next;
+ }
+ if (ref.next != null)
+ {
+ ref.next.previous = ref.previous;
+ }
+ if (first == ref)
+ {
+ first = ref.next;
+ }
+ if (last == ref)
+ {
+ last = ref.previous;
+ }
+ // renumber indices
+ int i = 0;
+ for (DomNode ctx = first; ctx != null; ctx = ctx.next)
+ {
+ ctx.index = i++;
+ }
+ ref.parent = null;
+ ref.setDepth(0);
+ ref.index = 0;
+ ref.previous = null;
+ ref.next = null;
+
+ return ref;
+ }
+ }
+ throw new DomEx(DomEx.NOT_FOUND_ERR,
+ "that's no child of mine", refChild, 0);
+ }
+ catch (ClassCastException e)
+ {
+ throw new DomEx(DomEx.WRONG_DOCUMENT_ERR,
+ null, refChild, 0);
+ }
+ }
+
+ /**
+ * <b>DOM L1 (NodeList)</b>
+ * Returns the item with the specified index in this NodeList,
+ * else null.
+ */
+ public Node item(int index)
+ {
+ DomNode child = first;
+ int count = 0;
+ while (child != null && count < index)
+ {
+ child = child.next;
+ count++;
+ }
+ return child;
+ }
+
+ /**
+ * <b>DOM L1 (NodeList)</b>
+ * Returns the number of elements in this NodeList.
+ * (Note that many interfaces have a "Length" property, not just
+ * NodeList, and if a node subtype must implement one of those,
+ * it will also need to override getChildNodes.)
+ */
+ public int getLength()
+ {
+ return length;
+ }
+
+ /**
+ * Minimize extra space consumed by this node to hold children and event
+ * listeners.
+ */
+ public void trimToSize()
+ {
+ if (listeners != null && listeners.length != nListeners)
+ {
+ ListenerRecord[] newKids = new ListenerRecord[length];
+ System.arraycopy(listeners, 0, newKids, 0, nListeners);
+ listeners = newKids;
+ }
+ }
+
+ /**
+ * <b>DOM L1</b>
+ * Returns the previous sibling, if one is known.
+ */
+ public Node getNextSibling()
+ {
+ return next;
+ }
+
+ /**
+ * <b>DOM L1</b>
+ * Returns the previous sibling, if one is known.
+ */
+ public Node getPreviousSibling()
+ {
+ return previous;
+ }
+
+ /**
+ * <b>DOM L1</b>
+ * Returns the parent node, if one is known.
+ */
+ public Node getParentNode()
+ {
+ return parent;
+ }
+
+ /**
+ * <b>DOM L2</b>
+ * Consults the DOM implementation to determine if the requested
+ * feature is supported. DocumentType subclasses must override
+ * this method, and associate themselves directly with the
+ * DOMImplementation node used. (This method relies on being able
+ * to access the DOMImplementation from the owner document, but
+ * DocumentType nodes can be created without an owner.)
+ */
+ public boolean isSupported(String feature, String version)
+ {
+ Document doc = owner;
+ DOMImplementation impl = null;
+
+ if (doc == null && nodeType == DOCUMENT_NODE)
+ {
+ doc = (Document) this;
+ }
+
+ if (doc == null)
+ {
+ // possible for DocumentType
+ throw new IllegalStateException ("unbound ownerDocument");
+ }
+
+ impl = doc.getImplementation();
+ return impl.hasFeature(feature, version);
+ }
+
+ /**
+ * <b>DOM L1 (modified in L2)</b>
+ * Returns the owner document. This is only null for Document nodes,
+ * and (new in L2) for DocumentType nodes which have not yet been
+ * associated with the rest of their document.
+ */
+ final public Document getOwnerDocument()
+ {
+ return owner;
+ }
+
+ /**
+ * <b>DOM L1</b>
+ * Does nothing; this must be overridden (along with the
+ * getNodeValue method) for nodes with a non-null defined value.
+ */
+ public void setNodeValue(String value)
+ {
+ }
+
+ /**
+ * <b>DOM L1</b>
+ * Returns null; this must be overridden for nodes types with
+ * a defined value, along with the setNodeValue method.
+ */
+ public String getNodeValue()
+ {
+ return null;
+ }
+
+ /** This forces GCJ compatibility.
+ * Without this method GCJ is unable to compile to byte code.
+ */
+ public final short getNodeType()
+ {
+ return nodeType;
+ }
+
+ /** This forces GCJ compatibility.
+ * Without this method GCJ seems unable to natively compile GNUJAXP.
+ */
+ public abstract String getNodeName();
+
+ /**
+ * <b>DOM L2</b>
+ * Does nothing; this must be overridden (along with the
+ * getPrefix method) for element and attribute nodes.
+ */
+ public void setPrefix(String prefix)
+ {
+ }
+
+ /**
+ * <b>DOM L2</b>
+ * Returns null; this must be overridden for element and
+ * attribute nodes.
+ */
+ public String getPrefix()
+ {
+ return null;
+ }
+
+ /**
+ * <b>DOM L2</b>
+ * Returns null; this must be overridden for element and
+ * attribute nodes.
+ */
+ public String getNamespaceURI()
+ {
+ return null;
+ }
+
+ /**
+ * <b>DOM L2</b>
+ * Returns the node name; this must be overridden for element and
+ * attribute nodes.
+ */
+ public String getLocalName()
+ {
+ return null;
+ }
+
+ /**
+ * <b>DOM L1</b>
+ * Returns a clone of this node which optionally includes cloned
+ * versions of child nodes. Clones are always mutable, except for
+ * entity reference nodes.
+ */
+ public Node cloneNode(boolean deep)
+ {
+ DomNode node = (DomNode) clone();
+
+ if (deep)
+ {
+ DomDocument doc = (nodeType == DOCUMENT_NODE) ?
+ (DomDocument) node : node.owner;
+ for (DomNode ctx = first; ctx != null; ctx = ctx.next)
+ {
+ DomNode newChild = (DomNode) ctx.cloneNode(deep);
+ newChild.setOwner(doc);
+ node.appendChild(newChild);
+ }
+ }
+
+ if (nodeType == ENTITY_REFERENCE_NODE)
+ {
+ node.makeReadonly();
+ }
+ notifyUserDataHandlers(UserDataHandler.NODE_CLONED, this, node);
+ return node;
+ }
+
+ void notifyUserDataHandlers(short op, Node src, Node dst)
+ {
+ if (userDataHandlers != null)
+ {
+ for (Iterator i = userDataHandlers.entrySet().iterator(); i.hasNext(); )
+ {
+ Map.Entry entry = (Map.Entry) i.next();
+ String key = (String) entry.getKey();
+ UserDataHandler handler = (UserDataHandler) entry.getValue();
+ Object data = userData.get(key);
+ handler.handle(op, key, data, src, dst);
+ }
+ }
+ }
+
+ /**
+ * Clones this node; roughly equivalent to cloneNode(false).
+ * Element subclasses must provide a new implementation which
+ * invokes this method to handle the basics, and then arranges
+ * to clone any element attributes directly. Attribute subclasses
+ * must make similar arrangements, ensuring that existing ties to
+ * elements are broken by cloning.
+ */
+ public Object clone()
+ {
+ try
+ {
+ DomNode node = (DomNode) super.clone();
+
+ node.parent = null;
+ node.depth = 0;
+ node.index = 0;
+ node.length = 0;
+ node.first = null;
+ node.last = null;
+ node.previous = null;
+ node.next = null;
+
+ node.readonly = false;
+ node.listeners = null;
+ node.nListeners = 0;
+ return node;
+
+ }
+ catch (CloneNotSupportedException x)
+ {
+ throw new Error("clone didn't work");
+ }
+ }
+
+ // the elements-by-tagname stuff is needed for both
+ // elements and documents ... this is in lieu of a
+ // common base class between Node and NodeNS.
+
+ /**
+ * <b>DOM L1</b>
+ * Creates a NodeList giving array-style access to elements with
+ * the specified name. Access is fastest if indices change by
+ * small values, and the DOM is not modified.
+ */
+ public NodeList getElementsByTagName(String tag)
+ {
+ return new ShadowList(null, tag);
+ }
+
+ /**
+ * <b>DOM L2</b>
+ * Creates a NodeList giving array-style access to elements with
+ * the specified namespace and local name. Access is fastest if
+ * indices change by small values, and the DOM is not modified.
+ */
+ public NodeList getElementsByTagNameNS(String namespace, String local)
+ {
+ return new ShadowList(namespace, local);
+ }
+
+
+ //
+ // This shadow class is GC-able even when the live list it shadows
+ // can't be, because of event registration hookups. Its finalizer
+ // makes that live list become GC-able.
+ //
+ final class ShadowList
+ implements NodeList
+ {
+
+ private LiveNodeList liveList;
+
+ ShadowList(String ns, String local)
+ {
+ liveList = new LiveNodeList(ns, local);
+ }
+
+ public void finalize()
+ {
+ liveList.detach();
+ liveList = null;
+ }
+
+ public Node item(int index)
+ {
+ return liveList.item(index);
+ }
+
+ public int getLength()
+ {
+ return liveList.getLength();
+ }
+ }
+
+ final class LiveNodeList
+ implements NodeList, EventListener, NodeFilter
+ {
+
+ private final boolean matchAnyURI;
+ private final boolean matchAnyName;
+ private final String elementURI;
+ private final String elementName;
+
+ private DomIterator current;
+ private int lastIndex;
+
+ LiveNodeList(String uri, String name)
+ {
+ elementURI = uri;
+ elementName = name;
+ matchAnyURI = "*".equals(uri);
+ matchAnyName = "*".equals(name);
+
+ DomNode.this.addEventListener("DOMNodeInserted", this, true);
+ DomNode.this.addEventListener("DOMNodeRemoved", this, true);
+ }
+
+ void detach()
+ {
+ current.detach();
+ current = null;
+
+ DomNode.this.removeEventListener("DOMNodeInserted", this, true);
+ DomNode.this.removeEventListener("DOMNodeRemoved", this, true);
+ }
+
+ public short acceptNode(Node element)
+ {
+ if (element == DomNode.this)
+ {
+ return FILTER_SKIP;
+ }
+
+ // use namespace-aware matching ...
+ if (elementURI != null)
+ {
+ if (!(matchAnyURI
+ || elementURI.equals(element.getNamespaceURI())))
+ {
+ return FILTER_SKIP;
+ }
+ if (!(matchAnyName
+ || elementName.equals(element.getLocalName())))
+ {
+ return FILTER_SKIP;
+ }
+
+ // ... or qName-based kind.
+ }
+ else
+ {
+ if (!(matchAnyName
+ || elementName.equals(element.getNodeName())))
+ {
+ return FILTER_SKIP;
+ }
+ }
+ return FILTER_ACCEPT;
+ }
+
+ private DomIterator createIterator()
+ {
+ return new DomIterator(DomNode.this,
+ NodeFilter.SHOW_ELEMENT,
+ this, /* filter */
+ true /* expand entity refs */
+ );
+ }
+
+ public void handleEvent(Event e)
+ {
+ MutationEvent mutation = (MutationEvent) e;
+ Node related = mutation.getRelatedNode();
+
+ // XXX if it's got children ... check all kids too, they
+ // will invalidate our saved index
+
+ if (related.getNodeType() != Node.ELEMENT_NODE ||
+ related.getNodeName() != elementName ||
+ related.getNamespaceURI() != elementURI)
+ {
+ return;
+ }
+
+ current = null;
+ }
+
+ public Node item(int index)
+ {
+ if (current == null)
+ {
+ current = createIterator();
+ lastIndex = -1;
+ }
+
+ // last node or before? go backwards
+ if (index <= lastIndex) {
+ while (index != lastIndex) {
+ current.previousNode ();
+ lastIndex--;
+ }
+ Node ret = current.previousNode ();
+ current = null;
+ return ret;
+ }
+
+ // somewhere after last node
+ while (++lastIndex != index)
+ current.nextNode ();
+ Node ret = current.nextNode ();
+ current = null;
+ return ret;
+ }
+
+ public int getLength()
+ {
+ int retval = 0;
+ NodeIterator iter = createIterator();
+
+ while (iter.nextNode() != null)
+ {
+ retval++;
+ }
+ current = null;
+ return retval;
+ }
+
+ }
+
+ //
+ // EventTarget support
+ //
+ static final class ListenerRecord
+ {
+
+ String type;
+ EventListener listener;
+ boolean useCapture;
+
+ // XXX use JDK 1.2 java.lang.ref.WeakReference to listener,
+ // and we can both get rid of "shadow" classes and remove
+ // the need for applications to apply similar trix ... but
+ // JDK 1.2 support isn't generally available yet
+
+ ListenerRecord(String type, EventListener listener, boolean useCapture)
+ {
+ this.type = type.intern();
+ this.listener = listener;
+ this.useCapture = useCapture;
+ }
+
+ boolean equals(ListenerRecord rec)
+ {
+ return listener == rec.listener
+ && useCapture == rec.useCapture
+ && type == rec.type;
+ }
+
+ }
+
+ /**
+ * <b>DOM L2 (Events)</b>
+ * Returns an instance of the specified type of event object.
+ * Understands about DOM Mutation, HTML, and UI events.
+ *
+ * <p>If the name of the event type begins with "USER-", then an object
+ * implementing the "Event" class will be returned; this provides a
+ * limited facility for application-defined events to use the DOM event
+ * infrastructure. Alternatively, use one of the standard DOM event
+ * classes and initialize it using use such a "USER-" event type name;
+ * or defin, instantiate, and initialize an application-specific subclass
+ * of DomEvent and pass that to dispatchEvent().
+ *
+ * @param eventType Identifies the particular DOM feature module
+ * defining the type of event, such as "MutationEvents".
+ * <em>The event "name" is a different kind of "type".</em>
+ */
+ public Event createEvent(String eventType)
+ {
+ eventType = eventType.toLowerCase();
+
+ if ("mutationevents".equals(eventType))
+ {
+ return new DomEvent.DomMutationEvent(null);
+ }
+
+ if ("htmlevents".equals(eventType)
+ || "events".equals(eventType)
+ || "user-events".equals(eventType))
+ {
+ return new DomEvent(null);
+ }
+
+ if ("uievents".equals(eventType))
+ {
+ return new DomEvent.DomUIEvent(null);
+ }
+
+ // mouse events
+
+ throw new DomEx(DomEx.NOT_SUPPORTED_ERR,
+ eventType, null, 0);
+ }
+
+ /**
+ * <b>DOM L2 (Events)</b>
+ * Registers an event listener's interest in a class of events.
+ */
+ public final void addEventListener(String type,
+ EventListener listener,
+ boolean useCapture)
+ {
+ if (listeners == null)
+ {
+ listeners = new ListenerRecord[1];
+ }
+ else if (nListeners == listeners.length)
+ {
+ ListenerRecord[] newListeners =
+ new ListenerRecord[listeners.length + NKIDS_DELTA];
+ System.arraycopy(listeners, 0, newListeners, 0, nListeners);
+ listeners = newListeners;
+ }
+
+ // prune duplicates
+ ListenerRecord record;
+
+ record = new ListenerRecord(type, listener, useCapture);
+ for (int i = 0; i < nListeners; i++)
+ {
+ if (record.equals(listeners[i]))
+ {
+ return;
+ }
+ }
+ listeners [nListeners++] = record;
+ }
+
+ // XXX this exception should be discarded from DOM
+
+ // this class can be instantiated, unlike the one in the spec
+ static final class DomEventException
+ extends EventException
+ {
+
+ DomEventException()
+ {
+ super(UNSPECIFIED_EVENT_TYPE_ERR, "unspecified event type");
+ }
+
+ }
+
+ /**
+ * <b>DOM L2 (Events)</b>
+ * Delivers an event to all relevant listeners, returning true if the
+ * caller should perform their default action. Note that the event
+ * must have been provided by the createEvent() method on this
+ * class, else it can't be dispatched.
+ *
+ * @see #createEvent
+ *
+ * @exception NullPointerException When a null event is passed.
+ * @exception ClassCastException When the event wasn't provided by
+ * the createEvent method, or otherwise isn't a DomEvent.
+ * @exception EventException If the event type wasn't specified
+ */
+ public final boolean dispatchEvent(Event event)
+ throws EventException
+ {
+ DomEvent e = (DomEvent) event;
+ DomNode[] ancestors = null;
+ int ancestorMax = 0;
+ boolean haveDispatchDataLock = false;
+
+ if (e.type == null)
+ {
+ throw new DomEventException();
+ }
+
+ e.doDefault = true;
+ e.target = this;
+
+ //
+ // Typical case: one nonrecursive dispatchEvent call at a time
+ // for this class. If that's our case, we can avoid allocating
+ // garbage, which is overall a big win. Even with advanced GCs
+ // that deal well with short-lived garbage, and wayfast allocators,
+ // it still helps.
+ //
+ // Remember -- EVERY mutation goes though here at least once.
+ //
+ // When populating a DOM tree, trying to send mutation events is
+ // the primary cost; this dominates the critical path.
+ //
+ try
+ {
+ DomNode current;
+ int index;
+ boolean haveAncestorRegistrations = false;
+ ListenerRecord[] notificationSet;
+ int ancestorLen;
+
+ synchronized (lockNode)
+ {
+ if (!dispatchDataLock)
+ {
+ haveDispatchDataLock = dispatchDataLock = true;
+ notificationSet = DomNode.notificationSet;
+ ancestors = DomNode.ancestors;
+ }
+ else
+ {
+ notificationSet = new ListenerRecord[NOTIFICATIONS_INIT];
+ ancestors = new DomNode[ANCESTORS_INIT];
+ }
+ ancestorLen = ancestors.length;
+ }
+
+ // XXX autogrow ancestors ... based on statistics
+
+ // Climb to the top of this subtree and handle capture, letting
+ // each node (from the top down) capture until one stops it or
+ // until we get to this one.
+
+ for (index = 0, current = parent;
+ current != null && index < ancestorLen;
+ index++, current = current.parent)
+ {
+ if (current.nListeners != 0)
+ {
+ haveAncestorRegistrations = true;
+ }
+ ancestors [index] = current;
+ }
+ if (current != null)
+ {
+ throw new RuntimeException("dispatchEvent capture stack size");
+ }
+
+ ancestorMax = index;
+ e.stop = false;
+
+ if (haveAncestorRegistrations)
+ {
+ e.eventPhase = Event.CAPTURING_PHASE;
+ while (!e.stop && index-- > 0)
+ {
+ current = ancestors [index];
+ if (current.nListeners != 0)
+ {
+ notifyNode(e, current, true, notificationSet);
+ }
+ }
+ }
+
+ // Always deliver events to the target node (this)
+ // unless stopPropagation was called. If we saw
+ // no registrations yet (typical!), we never will.
+ if (!e.stop && nListeners != 0)
+ {
+ e.eventPhase = Event.AT_TARGET;
+ notifyNode (e, this, false, notificationSet);
+ }
+ else if (!haveAncestorRegistrations)
+ {
+ e.stop = true;
+ }
+
+ // If the event bubbles and propagation wasn't halted,
+ // walk back up the ancestor list. Stop bubbling when
+ // any bubbled event handler stops it.
+
+ if (!e.stop && e.bubbles)
+ {
+ e.eventPhase = Event.BUBBLING_PHASE;
+ for (index = 0;
+ !e.stop
+ && index < ancestorMax
+ && (current = ancestors[index]) != null;
+ index++)
+ {
+ if (current.nListeners != 0)
+ {
+ notifyNode(e, current, false, notificationSet);
+ }
+ }
+ }
+ e.eventPhase = 0;
+
+ // Caller chooses whether to perform the default
+ // action based on return from this method.
+ return e.doDefault;
+
+ }
+ finally
+ {
+ if (haveDispatchDataLock)
+ {
+ // synchronize to force write ordering
+ synchronized (lockNode)
+ {
+ // null out refs to ensure they'll be GC'd
+ for (int i = 0; i < ancestorMax; i++)
+ {
+ ancestors [i] = null;
+ }
+ // notificationSet handled by notifyNode
+
+ dispatchDataLock = false;
+ }
+ }
+ }
+ }
+
+ private void notifyNode(DomEvent e,
+ DomNode current,
+ boolean capture,
+ ListenerRecord[] notificationSet)
+ {
+ int count = 0;
+
+ // do any of this set of listeners get notified?
+ for (int i = 0; i < current.nListeners; i++)
+ {
+ ListenerRecord rec = current.listeners[i];
+
+ if (rec.useCapture != capture)
+ {
+ continue;
+ }
+ if (!e.type.equals (rec.type))
+ {
+ continue;
+ }
+ if (count < notificationSet.length)
+ {
+ notificationSet[count++] = rec;
+ }
+ else
+ // XXX fire up some cheap growth algorithm
+ throw new RuntimeException("Event notification set size exceeded");
+ }
+
+ // Notify just those listeners
+ e.currentNode = current;
+ for (int i = 0; i < count; i++)
+ {
+ try
+ {
+ // Late in the DOM CR process (3rd or 4th CR?) the
+ // removeEventListener spec became asymmetric with respect
+ // to addEventListener ... effect is now immediate.
+ for (int j = 0; j < current.nListeners; j++)
+ {
+ if (current.listeners[j].equals(notificationSet[i]))
+ {
+ notificationSet[i].listener.handleEvent(e);
+ break;
+ }
+ }
+
+ }
+ catch (Exception x)
+ {
+ // ignore all exceptions
+ }
+ notificationSet[i] = null; // free for GC
+ }
+ }
+
+ /**
+ * <b>DOM L2 (Events)</b>
+ * Unregisters an event listener.
+ */
+ public final void removeEventListener(String type,
+ EventListener listener,
+ boolean useCapture)
+ {
+ for (int i = 0; i < nListeners; i++)
+ {
+ if (listeners[i].listener != listener)
+ {
+ continue;
+ }
+ if (listeners[i].useCapture != useCapture)
+ {
+ continue;
+ }
+ if (!listeners[i].type.equals(type))
+ {
+ continue;
+ }
+
+ if (nListeners == 1)
+ {
+ listeners = null;
+ nListeners = 0;
+ }
+ else
+ {
+ for (int j = i + 1; j < nListeners; j++)
+ {
+ listeners[i++] = listeners[j++];
+ }
+ listeners[--nListeners] = null;
+ }
+ break;
+ }
+ // no exceptions reported
+ }
+
+ /**
+ * <b>DOM L1 (relocated in DOM L2)</b>
+ * In this node and all contained nodes (including attributes if
+ * relevant) merge adjacent text nodes. This is done while ignoring
+ * text which happens to use CDATA delimiters).
+ */
+ public final void normalize()
+ {
+ // Suspend readonly status
+ boolean saved = readonly;
+ readonly = false;
+ for (DomNode ctx = first; ctx != null; ctx = ctx.next)
+ {
+ switch (ctx.nodeType)
+ {
+ case TEXT_NODE:
+ while (ctx.next != null && ctx.next.nodeType == TEXT_NODE)
+ {
+ Text text = (Text) ctx;
+ text.appendData(ctx.next.getNodeValue());
+ removeChild(ctx.next);
+ }
+ break;
+ case ELEMENT_NODE:
+ NamedNodeMap attrs = ctx.getAttributes();
+ int len = attrs.getLength();
+ for (int i = 0; i < len; i++)
+ {
+ attrs.item(i).normalize();
+ }
+ // Fall through
+ case DOCUMENT_NODE:
+ case DOCUMENT_FRAGMENT_NODE:
+ case ATTRIBUTE_NODE:
+ case ENTITY_REFERENCE_NODE:
+ ctx.normalize();
+ break;
+ }
+ }
+ readonly = saved;
+ }
+
+ /**
+ * Returns true iff node types match, and either (a) both nodes have no
+ * namespace and their getNodeName() values are the same, or (b) both
+ * nodes have the same getNamespaceURI() and same getLocalName() values.
+ *
+ * <p>Note that notion of a "Per-Element-Type" attribute name scope, as
+ * found in a non-normative appendix of the XML Namespaces specification,
+ * is not supported here. Your application must implement that notion,
+ * typically by not bothering to check nameAndTypeEquals for attributes
+ * without namespace URIs unless you already know their elements are
+ * nameAndTypeEquals.
+ */
+ public boolean nameAndTypeEquals(Node other)
+ {
+ if (other == this)
+ {
+ return true;
+ }
+ // node types must match
+ if (nodeType != other.getNodeType())
+ {
+ return false;
+ }
+
+ // if both have namespaces, do a "full" comparision
+ // this is a "global" partition
+ String ns1 = this.getNamespaceURI();
+ String ns2 = other.getNamespaceURI();
+
+ if (ns1 != null && ns2 != null)
+ {
+ return ns1.equals(ns2) &&
+ getLocalName().equals(other.getLocalName());
+ }
+
+ // if neither has a namespace, this is a "no-namespace" name.
+ if (ns1 == null && ns2 == null)
+ {
+ if (!getNodeName().equals(other.getNodeName()))
+ {
+ return false;
+ }
+ // can test the non-normative "per-element-type" scope here.
+ // if this is an attribute node and both nodes have been bound
+ // to elements (!!), then return the nameAndTypeEquals()
+ // comparison of those elements.
+ return true;
+ }
+
+ // otherwise they're unequal: one scoped, one not.
+ return false;
+ }
+
+ // DOM Level 3 methods
+
+ public String getBaseURI()
+ {
+ return (parent != null) ? parent.getBaseURI() : null;
+ }
+
+ public short compareDocumentPosition(Node other)
+ throws DOMException
+ {
+ return (short) compareTo(other);
+ }
+
+ /**
+ * DOM nodes have a natural ordering: document order.
+ */
+ public final int compareTo(Object other)
+ {
+ if (other instanceof DomNode)
+ {
+ DomNode n1 = this;
+ DomNode n2 = (DomNode) other;
+ if (n1.owner != n2.owner)
+ {
+ return 0;
+ }
+ int d1 = n1.depth, d2 = n2.depth;
+ int delta = d1 - d2;
+ while (d1 > d2)
+ {
+ n1 = n1.parent;
+ d1--;
+ }
+ while (d2 > d1)
+ {
+ n2 = n2.parent;
+ d2--;
+ }
+ int c = compareTo2(n1, n2);
+ return (c != 0) ? c : delta;
+ }
+ return 0;
+ }
+
+ /**
+ * Compare two nodes at the same depth.
+ */
+ final int compareTo2(DomNode n1, DomNode n2)
+ {
+ if (n1 == n2 || n1.depth == 0 || n2.depth == 0)
+ {
+ return 0;
+ }
+ int c = compareTo2(n1.parent, n2.parent);
+ return (c != 0) ? c : n1.index - n2.index;
+ }
+
+ public final String getTextContent()
+ throws DOMException
+ {
+ return getTextContent(true);
+ }
+
+ final String getTextContent(boolean topLevel)
+ throws DOMException
+ {
+ switch (nodeType)
+ {
+ case ELEMENT_NODE:
+ case ENTITY_NODE:
+ case ENTITY_REFERENCE_NODE:
+ case DOCUMENT_FRAGMENT_NODE:
+ StringBuffer buffer = new StringBuffer();
+ for (DomNode ctx = first; ctx != null; ctx = ctx.next)
+ {
+ String textContent = ctx.getTextContent(false);
+ if (textContent != null)
+ {
+ buffer.append(textContent);
+ }
+ }
+ return buffer.toString();
+ case TEXT_NODE:
+ case CDATA_SECTION_NODE:
+ if (((Text) this).isElementContentWhitespace())
+ {
+ return "";
+ }
+ return getNodeValue();
+ case ATTRIBUTE_NODE:
+ return getNodeValue();
+ case COMMENT_NODE:
+ case PROCESSING_INSTRUCTION_NODE:
+ return topLevel ? getNodeValue() : "";
+ default:
+ return null;
+ }
+ }
+
+ public void setTextContent(String textContent)
+ throws DOMException
+ {
+ switch (nodeType)
+ {
+ case ELEMENT_NODE:
+ case ATTRIBUTE_NODE:
+ case ENTITY_NODE:
+ case ENTITY_REFERENCE_NODE:
+ case DOCUMENT_FRAGMENT_NODE:
+ for (DomNode ctx = first; ctx != null; )
+ {
+ DomNode n = ctx.next;
+ removeChild(ctx);
+ ctx = n;
+ }
+ if (textContent != null)
+ {
+ Text text = owner.createTextNode(textContent);
+ appendChild(text);
+ }
+ break;
+ case TEXT_NODE:
+ case CDATA_SECTION_NODE:
+ case COMMENT_NODE:
+ case PROCESSING_INSTRUCTION_NODE:
+ setNodeValue(textContent);
+ break;
+ }
+ }
+
+ public boolean isSameNode(Node other)
+ {
+ return this == other;
+ }
+
+ public String lookupPrefix(String namespaceURI)
+ {
+ return (parent == null || parent == owner) ? null :
+ parent.lookupPrefix(namespaceURI);
+ }
+
+ public boolean isDefaultNamespace(String namespaceURI)
+ {
+ return (parent == null || parent == owner) ? false :
+ parent.isDefaultNamespace(namespaceURI);
+ }
+
+ public String lookupNamespaceURI(String prefix)
+ {
+ return (parent == null || parent == owner) ? null :
+ parent.lookupNamespaceURI(prefix);
+ }
+
+ public boolean isEqualNode(Node arg)
+ {
+ if (this == arg)
+ {
+ return true;
+ }
+ if (arg == null)
+ {
+ return false;
+ }
+ if (nodeType != arg.getNodeType() ||
+ !equal(getNodeName(), arg.getNodeName()) ||
+ !equal(getLocalName(), arg.getLocalName()) ||
+ !equal(getNamespaceURI(), arg.getNamespaceURI()) ||
+ !equal(getPrefix(), arg.getPrefix()) ||
+ !equal(getNodeValue(), arg.getNodeValue()))
+ {
+ return false;
+ }
+ // Children
+ Node argCtx = arg.getFirstChild();
+ getFirstChild(); // because of DomAttr lazy children
+ for (DomNode ctx = first; ctx != null; ctx = ctx.next)
+ {
+ if (!ctx.isEqualNode(argCtx))
+ {
+ return false;
+ }
+ argCtx = argCtx.getNextSibling();
+ }
+ if (argCtx != null)
+ {
+ return false;
+ }
+
+ // TODO Attr NamedNodeMap
+ // TODO DocumentType
+ return true;
+ }
+
+ boolean equal(String arg1, String arg2)
+ {
+ return ((arg1 == null && arg2 == null) ||
+ (arg1 != null && arg1.equals(arg2)));
+ }
+
+ public Object getFeature(String feature, String version)
+ {
+ DOMImplementation impl = (nodeType == DOCUMENT_NODE) ?
+ ((Document) this).getImplementation() : owner.getImplementation();
+ if (impl.hasFeature(feature, version))
+ {
+ return this;
+ }
+ return null;
+ }
+
+ public Object setUserData(String key, Object data, UserDataHandler handler)
+ {
+ if (userData == null)
+ {
+ userData = new HashMap();
+ }
+ if (handler != null)
+ {
+ if (userDataHandlers == null)
+ {
+ userDataHandlers = new HashMap();
+ }
+ userDataHandlers.put(key, handler);
+ }
+ return userData.put(key, data);
+ }
+
+ public Object getUserData(String key)
+ {
+ if (userData == null)
+ {
+ return null;
+ }
+ return userData.get(key);
+ }
+
+ public String toString()
+ {
+ String nodeName = getNodeName();
+ String nodeValue = getNodeValue();
+ StringBuffer buf = new StringBuffer(getClass().getName());
+ buf.append('[');
+ if (nodeName != null)
+ {
+ buf.append(nodeName);
+ }
+ if (nodeValue != null)
+ {
+ if (nodeName != null)
+ {
+ buf.append('=');
+ }
+ buf.append('\'');
+ buf.append(encode(nodeValue));
+ buf.append('\'');
+ }
+ buf.append(']');
+ return buf.toString();
+ }
+
+ String encode(String value)
+ {
+ StringBuffer buf = null;
+ int len = value.length();
+ for (int i = 0; i < len; i++)
+ {
+ char c = value.charAt(i);
+ if (c == '\n')
+ {
+ if (buf == null)
+ {
+ buf = new StringBuffer(value.substring(0, i));
+ }
+ buf.append("\\n");
+ }
+ else if (c == '\r')
+ {
+ if (buf == null)
+ {
+ buf = new StringBuffer(value.substring(0, i));
+ }
+ buf.append("\\r");
+ }
+ else if (buf != null)
+ {
+ buf.append(c);
+ }
+ }
+ return (buf != null) ? buf.toString() : value;
+ }
+
+ String nodeTypeToString(short nodeType)
+ {
+ switch (nodeType)
+ {
+ case ELEMENT_NODE:
+ return "ELEMENT_NODE";
+ case ATTRIBUTE_NODE:
+ return "ATTRIBUTE_NODE";
+ case TEXT_NODE:
+ return "TEXT_NODE";
+ case CDATA_SECTION_NODE:
+ return "CDATA_SECTION_NODE";
+ case DOCUMENT_NODE:
+ return "DOCUMENT_NODE";
+ case DOCUMENT_TYPE_NODE:
+ return "DOCUMENT_TYPE_NODE";
+ case COMMENT_NODE:
+ return "COMMENT_NODE";
+ case PROCESSING_INSTRUCTION_NODE:
+ return "PROCESSING_INSTRUCTION_NODE";
+ case DOCUMENT_FRAGMENT_NODE:
+ return "DOCUMENT_FRAGMENT_NODE";
+ case ENTITY_NODE:
+ return "ENTITY_NODE";
+ case ENTITY_REFERENCE_NODE:
+ return "ENTITY_REFERENCE_NODE";
+ case NOTATION_NODE:
+ return "NOTATION_NODE";
+ default:
+ return "UNKNOWN";
+ }
+ }
+
+}
+
diff --git a/libjava/gnu/xml/dom/DomNodeIterator.java b/libjava/gnu/xml/dom/DomNodeIterator.java
new file mode 100644
index 0000000..c08e90a
--- /dev/null
+++ b/libjava/gnu/xml/dom/DomNodeIterator.java
@@ -0,0 +1,327 @@
+/* DomNodeIterator.java --
+ Copyright (C) 2004 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.dom;
+
+import org.w3c.dom.DOMException;
+import org.w3c.dom.Node;
+import org.w3c.dom.traversal.NodeFilter;
+import org.w3c.dom.traversal.NodeIterator;
+import org.w3c.dom.traversal.TreeWalker;
+
+/**
+ * Node iterator and tree walker.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+public class DomNodeIterator
+ implements NodeIterator, TreeWalker
+{
+
+ Node root;
+ final int whatToShow;
+ final NodeFilter filter;
+ final boolean entityReferenceExpansion;
+ final boolean walk;
+ Node current;
+
+ public DomNodeIterator(Node root, int whatToShow, NodeFilter filter,
+ boolean entityReferenceExpansion, boolean walk)
+ {
+ if (root == null)
+ {
+ throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "null root");
+ }
+ this.root = root;
+ this.whatToShow = whatToShow;
+ this.filter = filter;
+ this.entityReferenceExpansion = entityReferenceExpansion;
+ this.walk = walk;
+ current = root;
+ }
+
+ public Node getRoot()
+ {
+ return root;
+ }
+
+ public int getWhatToShow()
+ {
+ return whatToShow;
+ }
+
+ public NodeFilter getFilter()
+ {
+ return filter;
+ }
+
+ public boolean getExpandEntityReferences()
+ {
+ return entityReferenceExpansion;
+ }
+
+ public Node nextNode()
+ throws DOMException
+ {
+ if (root == null)
+ {
+ throw new DOMException(DOMException.INVALID_STATE_ERR, "null root");
+ }
+ Node ret;
+ do
+ {
+ if (current.equals(root))
+ {
+ ret = root.getFirstChild();
+ }
+ else if (walk)
+ {
+ ret = current.getFirstChild();
+ if (ret == null)
+ {
+ ret = current.getNextSibling();
+ }
+ if (ret == null)
+ {
+ Node tmp = current;
+ ret = tmp.getParentNode();
+ while (!ret.equals(root) && tmp.equals(ret.getLastChild()))
+ {
+ tmp = ret;
+ ret = tmp.getParentNode();
+ }
+ if (ret.equals(root))
+ {
+ ret = null;
+ }
+ else
+ {
+ ret = ret.getNextSibling();
+ }
+ }
+ }
+ else
+ {
+ ret = current.getNextSibling();
+ }
+ }
+ while (!accept(ret));
+ current = (ret == null) ? current : ret;
+ return ret;
+ }
+
+ public Node previousNode()
+ throws DOMException
+ {
+ if (root == null)
+ {
+ throw new DOMException(DOMException.INVALID_STATE_ERR, "null root");
+ }
+ Node ret;
+ do
+ {
+ if (current.equals(root))
+ {
+ ret = current.getLastChild();
+ }
+ else if (walk)
+ {
+ ret = current.getLastChild();
+ if (ret == null)
+ {
+ ret = current.getPreviousSibling();
+ }
+ if (ret == null)
+ {
+ Node tmp = current;
+ ret = tmp.getParentNode();
+ while (!ret.equals(root) && tmp.equals(ret.getFirstChild()))
+ {
+ tmp = ret;
+ ret = tmp.getParentNode();
+ }
+ if (ret.equals(root))
+ {
+ ret = null;
+ }
+ else
+ {
+ ret = ret.getPreviousSibling();
+ }
+ }
+ }
+ else
+ {
+ ret = current.getPreviousSibling();
+ }
+ }
+ while (!accept(ret));
+ current = (ret == null) ? current : ret;
+ return ret;
+ }
+
+ public Node getCurrentNode()
+ {
+ return current;
+ }
+
+ public void setCurrentNode(Node current)
+ throws DOMException
+ {
+ if (current == null)
+ {
+ throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "null root");
+ }
+ this.current = current;
+ }
+
+ public Node parentNode()
+ {
+ Node ret = current.getParentNode();
+ if (!accept (ret))
+ {
+ ret = null;
+ }
+ current = (ret == null) ? current : ret;
+ return ret;
+ }
+
+ public Node firstChild ()
+ {
+ Node ret = current.getFirstChild();
+ while (!accept(ret))
+ {
+ ret = ret.getNextSibling();
+ }
+ current = (ret == null) ? current : ret;
+ return ret;
+ }
+
+ public Node lastChild()
+ {
+ Node ret = current.getLastChild();
+ while (!accept(ret))
+ {
+ ret = ret.getPreviousSibling();
+ }
+ current = (ret == null) ? current : ret;
+ return ret;
+ }
+
+ public Node previousSibling()
+ {
+ Node ret = current.getPreviousSibling();
+ while (!accept(ret))
+ {
+ ret = ret.getPreviousSibling();
+ }
+ current = (ret == null) ? current : ret;
+ return ret;
+ }
+
+ public Node nextSibling()
+ {
+ Node ret = current.getNextSibling();
+ while (!accept(ret))
+ {
+ ret = ret.getNextSibling();
+ }
+ current = (ret == null) ? current : ret;
+ return ret;
+ }
+
+ public void detach()
+ {
+ root = null;
+ }
+
+ boolean accept(Node node)
+ {
+ if (node == null)
+ {
+ return true;
+ }
+ boolean ret;
+ switch (node.getNodeType())
+ {
+ case Node.ATTRIBUTE_NODE:
+ ret = (whatToShow & NodeFilter.SHOW_ATTRIBUTE) != 0;
+ break;
+ case Node.CDATA_SECTION_NODE:
+ ret = (whatToShow & NodeFilter.SHOW_CDATA_SECTION) != 0;
+ break;
+ case Node.COMMENT_NODE:
+ ret = (whatToShow & NodeFilter.SHOW_COMMENT) != 0;
+ break;
+ case Node.DOCUMENT_NODE:
+ ret = (whatToShow & NodeFilter.SHOW_DOCUMENT) != 0;
+ break;
+ case Node.DOCUMENT_FRAGMENT_NODE:
+ ret = (whatToShow & NodeFilter.SHOW_DOCUMENT_FRAGMENT) != 0;
+ break;
+ case Node.DOCUMENT_TYPE_NODE:
+ ret = (whatToShow & NodeFilter.SHOW_DOCUMENT_TYPE) != 0;
+ break;
+ case Node.ELEMENT_NODE:
+ ret = (whatToShow & NodeFilter.SHOW_ELEMENT) != 0;
+ break;
+ case Node.ENTITY_NODE:
+ ret = (whatToShow & NodeFilter.SHOW_ENTITY) != 0;
+ break;
+ case Node.ENTITY_REFERENCE_NODE:
+ ret = (whatToShow & NodeFilter.SHOW_ENTITY_REFERENCE) != 0;
+ ret = ret && entityReferenceExpansion;
+ break;
+ case Node.NOTATION_NODE:
+ ret = (whatToShow & NodeFilter.SHOW_NOTATION) != 0;
+ break;
+ case Node.PROCESSING_INSTRUCTION_NODE:
+ ret = (whatToShow & NodeFilter.SHOW_PROCESSING_INSTRUCTION) != 0;
+ break;
+ case Node.TEXT_NODE:
+ ret = (whatToShow & NodeFilter.SHOW_TEXT) != 0;
+ break;
+ default:
+ ret = true;
+ }
+ if (ret && filter != null)
+ {
+ ret = (filter.acceptNode(node) == NodeFilter.FILTER_ACCEPT);
+ }
+ return ret;
+ }
+
+}
diff --git a/libjava/gnu/xml/dom/DomNotation.java b/libjava/gnu/xml/dom/DomNotation.java
new file mode 100644
index 0000000..d666699
--- /dev/null
+++ b/libjava/gnu/xml/dom/DomNotation.java
@@ -0,0 +1,103 @@
+/* DomNotation.java --
+ Copyright (C) 1999,2000,2001,2004 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.dom;
+
+import org.w3c.dom.Notation;
+
+/**
+ * <p> "Notation" implementation. This is a non-core DOM class, supporting
+ * the "XML" feature. </p>
+ *
+ * <p> Although unparsed entities using this notation can be detected using
+ * DOM, neither NOTATIONS nor ENTITY/ENTITIES attributes can be so detected.
+ * More, there is no portable way to construct a Notation node, so there's
+ * no way that vendor-neutral DOM construction APIs could even report a
+ * NOTATION used to identify the intended meaning of a ProcessingInstruction.
+ * </p>
+ *
+ * <p> In short, <em>avoid using this DOM functionality</em>.
+ *
+ * @see DomDoctype
+ * @see DomEntity
+ * @see DomPI
+ *
+ * @author David Brownell
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+public class DomNotation
+ extends DomExtern
+ implements Notation
+{
+
+ /**
+ * Constructs a Notation node associated with the specified document,
+ * with the specified descriptive data. Note that at least one of
+ * the PUBLIC and SYSTEM identifiers must be provided; unlike other
+ * external objects in XML, notations may have only a PUBLIC identifier.
+ *
+ * <p>This constructor should only be invoked by a DomDoctype object
+ * as part of its declareNotation functionality, or through a subclass
+ * which is similarly used in a "Sub-DOM" style layer.
+ *
+ * @param owner The document with which this notation is associated
+ * @param name Name of this notation
+ * @param publicId If non-null, provides the notation's PUBLIC identifier
+ * @param systemId If non-null, rovides the notation's SYSTEM identifier
+ */
+ protected DomNotation(DomDocument owner,
+ String name,
+ String publicId,
+ String systemId)
+ {
+ super(NOTATION_NODE, owner, name, publicId, systemId);
+ makeReadonly();
+ }
+
+ /**
+ * The base URI of an external entity is its system ID.
+ * The base URI of an internal entity is the parent document's base URI.
+ * @since DOM Level 3 Core
+ */
+ public String getBaseURI()
+ {
+ String systemId = getSystemId();
+ return (systemId == null) ? owner.getBaseURI() : systemId;
+ }
+
+}
+
diff --git a/libjava/gnu/xml/dom/DomNsNode.java b/libjava/gnu/xml/dom/DomNsNode.java
new file mode 100644
index 0000000..ea41fff
--- /dev/null
+++ b/libjava/gnu/xml/dom/DomNsNode.java
@@ -0,0 +1,197 @@
+/* DomNsNode.java --
+ Copyright (C) 1999,2000,2001,2004 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.dom;
+
+import javax.xml.XMLConstants;
+
+/**
+ * <p> Abstract implemention of namespace support. This facilitates
+ * sharing code for attribute and element nodes.
+ *
+ * @author David Brownell
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+public abstract class DomNsNode
+ extends DomNode
+{
+
+ private String name;
+ private String namespace;
+ private String prefix;
+ private String localName;
+
+ /**
+ * Constructs a node associated with the specified document, and
+ * with the specified namespace information.
+ *
+ * @param owner The document with which this entity is associated
+ * @param namespaceURI Combined with the local part of the name,
+ * this identifies a type of element or attribute; may be null.
+ * If this is the empty string, it is reassigned as null so that
+ * applications only need to test that case.
+ * @param name Name of this node, which may include a prefix
+ */
+ // package private
+ DomNsNode(short nodeType, DomDocument owner, String namespaceURI, String name)
+ {
+ super(nodeType, owner);
+ setNodeName(name);
+ setNamespaceURI(namespaceURI);
+ }
+
+ /**
+ * <b>DOM L1</b>
+ * Returns the node's name, including any namespace prefix.
+ */
+ public final String getNodeName()
+ {
+ return name;
+ }
+
+ final void setNodeName(String name)
+ {
+ this.name = name.intern();
+ int index = name.indexOf(':');
+ if (index == -1)
+ {
+ prefix = null;
+ localName = this.name;
+ }
+ else
+ {
+ prefix = name.substring(0, index).intern();
+ localName = name.substring(index + 1).intern();
+ }
+ }
+
+ /**
+ * <b>DOM L2</b>
+ * Returns the node's namespace URI
+ * <em>or null</em> if the node name is not namespace scoped.
+ */
+ public final String getNamespaceURI()
+ {
+ return namespace;
+ }
+
+ final void setNamespaceURI(String namespaceURI)
+ {
+ if ("".equals(namespaceURI))
+ {
+ namespaceURI = null;
+ }
+ namespace = (namespaceURI == null) ? null : namespaceURI.intern();
+ }
+
+ /**
+ * <b>DOM L2</b>
+ * Returns any prefix part of the node's name (before any colon).
+ */
+ public final String getPrefix()
+ {
+ return prefix;
+ }
+
+ /**
+ * <b>DOM L2</b>
+ * Assigns the prefix part of the node's name (before any colon).
+ */
+ public final void setPrefix(String prefix)
+ {
+ if (readonly)
+ {
+ throw new DomEx(DomEx.NO_MODIFICATION_ALLOWED_ERR);
+ }
+
+ if (prefix == null)
+ {
+ name = localName;
+ return;
+ }
+ else if (namespace == null)
+ {
+ throw new DomEx(DomEx.NAMESPACE_ERR,
+ "can't set prefix, node has no namespace URI",
+ this, 0);
+ }
+
+ DomDocument.checkName(prefix, "1.1".equals(owner.getXmlVersion()));
+ if (prefix.indexOf (':') != -1)
+ {
+ throw new DomEx(DomEx.NAMESPACE_ERR,
+ "illegal prefix " + prefix, this, 0);
+ }
+
+ if (XMLConstants.XML_NS_PREFIX.equals(prefix)
+ && !XMLConstants.XML_NS_URI.equals(namespace))
+ {
+ throw new DomEx(DomEx.NAMESPACE_ERR,
+ "xml namespace is always " +
+ XMLConstants.XML_NS_URI, this, 0);
+ }
+
+ if (XMLConstants.XMLNS_ATTRIBUTE.equals(prefix))
+ {
+ if (namespace != null || getNodeType() != ATTRIBUTE_NODE)
+ {
+ throw new DomEx(DomEx.NAMESPACE_ERR,
+ "xmlns attribute prefix is reserved", this, 0);
+ }
+ }
+ else if (getNodeType () == ATTRIBUTE_NODE
+ && (XMLConstants.XMLNS_ATTRIBUTE.equals(name) ||
+ name.startsWith("xmlns:")))
+ {
+ throw new DomEx(DomEx.NAMESPACE_ERR,
+ "namespace declarations can't change names", this, 0);
+ }
+
+ this.prefix = prefix.intern();
+ }
+
+ /**
+ * <b>DOM L2</b>
+ * Returns the local part of the node's name (after any colon).
+ */
+ public final String getLocalName()
+ {
+ return localName;
+ }
+
+}
+
diff --git a/libjava/gnu/xml/dom/DomPI.java b/libjava/gnu/xml/dom/DomPI.java
new file mode 100644
index 0000000..dedf375
--- /dev/null
+++ b/libjava/gnu/xml/dom/DomPI.java
@@ -0,0 +1,145 @@
+/* DomPI.java --
+ Copyright (C) 1999,2000,2001,2004 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.dom;
+
+import org.w3c.dom.ProcessingInstruction;
+
+/**
+ * <p> "ProcessingInstruction" (PI) implementation.
+ * This is a non-core DOM class, supporting the "XML" feature. </p>
+ *
+ * <p> Unlike other DOM APIs in the "XML" feature, this one fully
+ * exposes the functionality it describes. So there is no reason
+ * inherent in DOM to avoid using this API, unless you want to rely
+ * on NOTATION declarations to associate meaning with your PIs;
+ * there is no vendor-neutal way to record those notations in DOM.</p>
+ *
+ * <p> Also of note is that PI support is part of SAX, so that XML
+ * systems using PIs can choose among multiple APIs. </p>
+ *
+ * @see DomNotation
+ *
+ * @author David Brownell
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+public class DomPI
+ extends DomNode
+ implements ProcessingInstruction
+{
+
+ private String target;
+ private String data;
+
+ /**
+ * Constructs a ProcessingInstruction node associated with the
+ * specified document, with the specified data.
+ *
+ * <p>This constructor should only be invoked by a Document object as
+ * part of its createProcessingInstruction functionality, or through
+ * a subclass which is similarly used in a "Sub-DOM" style layer.
+ */
+ protected DomPI(DomDocument owner, String target, String data)
+ {
+ super(PROCESSING_INSTRUCTION_NODE, owner);
+ this.target = target;
+ this.data = data;
+ }
+
+ /**
+ * <b>DOM L1</b>
+ * Returns the target of the processing instruction.
+ */
+ public final String getTarget()
+ {
+ return target;
+ }
+
+ /**
+ * <b>DOM L1</b>
+ * Returns the target of the processing instruction
+ * (same as getTarget).
+ */
+ public final String getNodeName()
+ {
+ return target;
+ }
+
+ /**
+ * <b>DOM L1</b>
+ * Returns the data associated with the processing instruction.
+ */
+ public final String getData()
+ {
+ return data;
+ }
+
+ /**
+ * <b>DOM L1</b>
+ * Returns the data associated with the processing instruction
+ * (same as getData).
+ */
+ public final String getNodeValue()
+ {
+ return data;
+ }
+
+ /**
+ * <b>DOM L1</b>
+ * Assigns the data associated with the processing instruction;
+ * same as setNodeValue.
+ */
+ public final void setData(String data)
+ {
+ setNodeValue(data);
+ }
+
+ /**
+ * <b>DOM L1</b>
+ * Assigns the data associated with the processing instruction.
+ */
+ public final void setNodeValue(String data)
+ {
+ if (isReadonly())
+ {
+ throw new DomEx(DomEx.NO_MODIFICATION_ALLOWED_ERR);
+ }
+ this.data = data;
+ }
+
+}
+
diff --git a/libjava/gnu/xml/dom/DomText.java b/libjava/gnu/xml/dom/DomText.java
new file mode 100644
index 0000000..d714655
--- /dev/null
+++ b/libjava/gnu/xml/dom/DomText.java
@@ -0,0 +1,220 @@
+/* DomText.java --
+ Copyright (C) 1999, 2000, 2001, 2004 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.dom;
+
+import org.w3c.dom.DOMException;
+import org.w3c.dom.Text;
+
+/**
+ * <p> "Text" implementation. </p>
+ *
+ * @author David Brownell
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+public class DomText
+ extends DomCharacterData
+ implements Text
+{
+
+ // NOTE: deleted unused per-instance "isIgnorable"
+ // support to reclaim its space.
+
+ /**
+ * Constructs a text node associated with the specified
+ * document and holding the specified data.
+ *
+ * <p>This constructor should only be invoked by a Document object
+ * as part of its createTextNode functionality, or through a subclass
+ * which is similarly used in a "Sub-DOM" style layer.
+ */
+ protected DomText(DomDocument owner, String value)
+ {
+ super(TEXT_NODE, owner, value);
+ }
+
+ protected DomText(DomDocument owner, char[] buf, int off, int len)
+ {
+ super(TEXT_NODE, owner, buf, off, len);
+ }
+
+ // Used by DomCDATA
+ DomText(short nodeType, DomDocument owner, String value)
+ {
+ super(nodeType, owner, value);
+ }
+
+ DomText(short nodeType, DomDocument owner, char[] buf, int off, int len)
+ {
+ super(nodeType, owner, buf, off, len);
+ }
+
+ /**
+ * <b>DOM L1</b>
+ * Returns the string "#text".
+ */
+ // can't be 'final' with CDATA subclassing
+ public String getNodeName()
+ {
+ return "#text";
+ }
+
+ /**
+ * <b>DOM L1</b>
+ * Splits this text node in two parts at the offset, returning
+ * the new text node (the sibling with the second part).
+ */
+ public Text splitText(int offset)
+ {
+ if (isReadonly())
+ {
+ throw new DomEx(DomEx.NO_MODIFICATION_ALLOWED_ERR);
+ }
+ try
+ {
+ String text = getNodeValue();
+ String before = text.substring(0, offset);
+ String after = text.substring(offset);
+ Text next;
+
+ if (getNodeType() == TEXT_NODE)
+ {
+ next = owner.createTextNode(after);
+ }
+ else // CDATA_SECTION_NODE
+ {
+ next = owner.createCDATASection(after);
+ }
+
+ if (this.next != null)
+ {
+ parent.insertBefore(next, this.next);
+ }
+ else
+ {
+ parent.appendChild(next);
+ }
+ setNodeValue(before);
+ return next;
+
+ }
+ catch (IndexOutOfBoundsException x)
+ {
+ throw new DomEx(DomEx.INDEX_SIZE_ERR);
+ }
+ }
+
+ // DOM Level 3
+
+ public boolean isElementContentWhitespace()
+ {
+ if (parent != null)
+ {
+ DomDoctype doctype = (DomDoctype) owner.getDoctype();
+ if (doctype != null)
+ {
+ DTDElementTypeInfo info =
+ doctype.getElementTypeInfo(parent.getNodeName());
+ if (info != null)
+ {
+ if (info.model == null && info.model.indexOf("#PCDATA") != -1)
+ {
+ return false;
+ }
+ return getNodeValue().trim().length() == 0;
+ }
+ }
+ }
+ return false;
+ }
+
+ public String getWholeText()
+ {
+ DomNode ref = this;
+ DomNode ctx;
+ for (ctx = previous; ctx != null &&
+ (ctx.nodeType == TEXT_NODE || ctx.nodeType == CDATA_SECTION_NODE);
+ ctx = ctx.previous)
+ {
+ ref = ctx;
+ }
+ StringBuffer buf = new StringBuffer(ref.getNodeValue());
+ for (ctx = ref.next; ctx != null &&
+ (ctx.nodeType == TEXT_NODE || ctx.nodeType == CDATA_SECTION_NODE);
+ ctx = ctx.next)
+ {
+ buf.append(ctx.getNodeValue());
+ }
+ return buf.toString ();
+ }
+
+ public Text replaceWholeText(String content)
+ throws DOMException
+ {
+ boolean isEmpty = (content == null || content.length () == 0);
+ if (!isEmpty)
+ {
+ setNodeValue(content);
+ }
+
+ DomNode ref = this;
+ DomNode ctx;
+ for (ctx = previous; ctx != null &&
+ (ctx.nodeType == TEXT_NODE || ctx.nodeType == CDATA_SECTION_NODE);
+ ctx = ctx.previous)
+ {
+ ref = ctx;
+ }
+ ctx = ref.next;
+ if ((isEmpty || ref != this) && parent != null)
+ {
+ parent.removeChild(ref);
+ }
+ for (; ctx != null &&
+ (ctx.nodeType == TEXT_NODE || ctx.nodeType == CDATA_SECTION_NODE);
+ ctx = ref)
+ {
+ ref = ctx.next;
+ if ((isEmpty || ctx != this) && parent != null)
+ {
+ parent.removeChild(ctx);
+ }
+ }
+ return (isEmpty) ? null : this;
+ }
+
+}
diff --git a/libjava/gnu/xml/dom/DomXPathExpression.java b/libjava/gnu/xml/dom/DomXPathExpression.java
new file mode 100644
index 0000000..d92670d
--- /dev/null
+++ b/libjava/gnu/xml/dom/DomXPathExpression.java
@@ -0,0 +1,148 @@
+/* DomXPathExpression.java --
+ Copyright (C) 2004 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.dom;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import javax.xml.namespace.QName;
+import javax.xml.xpath.XPath;
+import javax.xml.xpath.XPathConstants;
+import javax.xml.xpath.XPathExpression;
+import javax.xml.xpath.XPathExpressionException;
+import javax.xml.xpath.XPathFactory;
+import org.w3c.dom.DOMException;
+import org.w3c.dom.Node;
+import org.w3c.dom.xpath.XPathException;
+import org.w3c.dom.xpath.XPathNSResolver;
+import org.w3c.dom.xpath.XPathResult;
+import gnu.xml.xpath.DocumentOrderComparator;
+
+/**
+ * An XPath expression.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+class DomXPathExpression
+implements org.w3c.dom.xpath.XPathExpression
+{
+
+ final DomDocument doc;
+ final XPathExpression expression;
+ final XPathNSResolver resolver;
+
+ DomXPathExpression(DomDocument doc, String expression,
+ XPathNSResolver resolver)
+ throws XPathException
+ {
+ this.doc = doc;
+ this.resolver = resolver;
+
+ XPathFactory factory = XPathFactory.newInstance();
+ XPath xpath = factory.newXPath();
+ if (resolver != null)
+ {
+ xpath.setNamespaceContext(new DomNSResolverContext(resolver));
+ }
+ try
+ {
+ this.expression = xpath.compile(expression);
+ }
+ catch (XPathExpressionException e)
+ {
+ throw new XPathException(XPathException.INVALID_EXPRESSION_ERR,
+ e.getMessage ());
+ }
+ }
+
+ public Object evaluate(Node contextNode, short type, Object result)
+ throws XPathException, DOMException
+ {
+ try
+ {
+ QName typeName = null;
+ switch (type)
+ {
+ case XPathResult.BOOLEAN_TYPE:
+ typeName = XPathConstants.BOOLEAN;
+ break;
+ case XPathResult.NUMBER_TYPE:
+ typeName = XPathConstants.NUMBER;
+ break;
+ case XPathResult.STRING_TYPE:
+ typeName = XPathConstants.STRING;
+ break;
+ case XPathResult.ANY_UNORDERED_NODE_TYPE:
+ case XPathResult.FIRST_ORDERED_NODE_TYPE:
+ typeName = XPathConstants.NODE;
+ break;
+ case XPathResult.UNORDERED_NODE_ITERATOR_TYPE:
+ case XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE:
+ case XPathResult.ORDERED_NODE_ITERATOR_TYPE:
+ case XPathResult.ORDERED_NODE_SNAPSHOT_TYPE:
+ typeName = XPathConstants.NODESET;
+ break;
+ default:
+ throw new XPathException(XPathException.TYPE_ERR, null);
+ }
+ Object val = expression.evaluate(contextNode, typeName);
+ switch (type)
+ {
+ case XPathResult.ORDERED_NODE_ITERATOR_TYPE:
+ case XPathResult.ORDERED_NODE_SNAPSHOT_TYPE:
+ // Sort the nodes
+ List ns = new ArrayList((Collection) val);
+ Collections.sort(ns, new DocumentOrderComparator());
+ val = ns;
+ }
+ return new DomXPathResult(val, type);
+ }
+ catch (javax.xml.xpath.XPathException e)
+ {
+ throw new XPathException(XPathException.TYPE_ERR, e.getMessage());
+ }
+ }
+
+ public String toString ()
+ {
+ return getClass ().getName () + "[expression=" + expression + "]";
+ }
+
+}
diff --git a/libjava/gnu/xml/dom/DomXPathNSResolver.java b/libjava/gnu/xml/dom/DomXPathNSResolver.java
new file mode 100644
index 0000000..467327f
--- /dev/null
+++ b/libjava/gnu/xml/dom/DomXPathNSResolver.java
@@ -0,0 +1,64 @@
+/* DomXPathNSResolver.java --
+ Copyright (C) 2004 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.dom;
+
+import org.w3c.dom.Node;
+import org.w3c.dom.xpath.XPathNSResolver;
+
+/**
+ * Generic XPath namespace resolver using a DOM Node.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+class DomXPathNSResolver
+implements XPathNSResolver
+{
+
+ Node node;
+
+ DomXPathNSResolver (Node node)
+ {
+ this.node = node;
+ }
+
+ public String lookupNamespaceURI (String prefix)
+ {
+ return node.lookupNamespaceURI (prefix);
+ }
+
+}
diff --git a/libjava/gnu/xml/dom/DomXPathResult.java b/libjava/gnu/xml/dom/DomXPathResult.java
new file mode 100644
index 0000000..1cc49d8
--- /dev/null
+++ b/libjava/gnu/xml/dom/DomXPathResult.java
@@ -0,0 +1,235 @@
+/* DomXPathResult.java --
+ Copyright (C) 2004 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.dom;
+
+import java.util.Collection;
+import java.util.Iterator;
+import org.w3c.dom.DOMException;
+import org.w3c.dom.Node;
+import org.w3c.dom.xpath.XPathException;
+import org.w3c.dom.xpath.XPathNSResolver;
+import org.w3c.dom.xpath.XPathResult;
+
+/**
+ * An XPath result object.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+class DomXPathResult
+implements XPathResult
+{
+
+ final Object value;
+ final short type;
+ Iterator iterator;
+
+ DomXPathResult (Object value, short requestedType)
+ {
+ this.value = value;
+ if (value instanceof Boolean)
+ {
+ type = XPathResult.BOOLEAN_TYPE;
+ }
+ else if (value instanceof Double)
+ {
+ type = XPathResult.NUMBER_TYPE;
+ }
+ else if (value instanceof String)
+ {
+ type = XPathResult.STRING_TYPE;
+ }
+ else if (value instanceof Collection)
+ {
+ Collection ns = (Collection) value;
+ switch (requestedType)
+ {
+ case XPathResult.ANY_TYPE:
+ case XPathResult.ANY_UNORDERED_NODE_TYPE:
+ type = (ns.size () == 1) ? XPathResult.FIRST_ORDERED_NODE_TYPE :
+ XPathResult.ORDERED_NODE_ITERATOR_TYPE;
+ break;
+ default:
+ type = requestedType;
+ }
+ iterator = ns.iterator ();
+ }
+ else
+ {
+ throw new IllegalArgumentException ();
+ }
+ }
+
+ public boolean getBooleanValue()
+ {
+ if (type == XPathResult.BOOLEAN_TYPE)
+ {
+ return ((Boolean) value).booleanValue ();
+ }
+ throw new XPathException (XPathException.TYPE_ERR, value.toString ());
+ }
+
+ public boolean getInvalidIteratorState()
+ {
+ return iterator == null;
+ }
+
+ public double getNumberValue()
+ {
+ if (type == XPathResult.NUMBER_TYPE)
+ {
+ return ((Double) value).doubleValue ();
+ }
+ throw new XPathException (XPathException.TYPE_ERR, value.toString ());
+ }
+
+ public short getResultType()
+ {
+ return type;
+ }
+
+ public Node getSingleNodeValue()
+ {
+ switch (type)
+ {
+ case XPathResult.FIRST_ORDERED_NODE_TYPE:
+ case XPathResult.ORDERED_NODE_ITERATOR_TYPE:
+ case XPathResult.ORDERED_NODE_SNAPSHOT_TYPE:
+ case XPathResult.UNORDERED_NODE_ITERATOR_TYPE:
+ case XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE:
+ Collection ns = (Collection) value;
+ if (ns.isEmpty ())
+ {
+ return null;
+ }
+ else
+ {
+ return (Node) ns.iterator ().next ();
+ }
+ }
+ throw new XPathException (XPathException.TYPE_ERR, value.toString ());
+ }
+
+ public int getSnapshotLength()
+ {
+ switch (type)
+ {
+ case XPathResult.FIRST_ORDERED_NODE_TYPE:
+ case XPathResult.ORDERED_NODE_ITERATOR_TYPE:
+ case XPathResult.ORDERED_NODE_SNAPSHOT_TYPE:
+ case XPathResult.UNORDERED_NODE_ITERATOR_TYPE:
+ case XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE:
+ return ((Collection) value).size ();
+ }
+ throw new XPathException (XPathException.TYPE_ERR, value.toString ());
+ }
+
+ public String getStringValue()
+ {
+ if (type == XPathResult.STRING_TYPE)
+ {
+ return (String) value;
+ }
+ throw new XPathException (XPathException.TYPE_ERR, value.toString ());
+ }
+
+ public Node iterateNext()
+ {
+ if (iterator != null)
+ {
+ if (iterator.hasNext ())
+ {
+ return (Node) iterator.next ();
+ }
+ else
+ {
+ iterator = null;
+ return null;
+ }
+ }
+ throw new XPathException (XPathException.TYPE_ERR, value.toString ());
+ }
+
+ public Node snapshotItem(int index)
+ {
+ switch (type)
+ {
+ case XPathResult.FIRST_ORDERED_NODE_TYPE:
+ case XPathResult.ORDERED_NODE_ITERATOR_TYPE:
+ case XPathResult.ORDERED_NODE_SNAPSHOT_TYPE:
+ case XPathResult.UNORDERED_NODE_ITERATOR_TYPE:
+ case XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE:
+ Collection ns = (Collection) value;
+ Node[] nodes = new Node[ns.size ()];
+ ns.toArray (nodes);
+ return nodes[index];
+ }
+ throw new XPathException (XPathException.TYPE_ERR, value.toString ());
+ }
+
+ public String toString ()
+ {
+ return getClass ().getName () + "[type=" + typeName (type) + ",value=" +
+ value + ']';
+ }
+
+ private String typeName (short type)
+ {
+ switch (type)
+ {
+ case XPathResult.BOOLEAN_TYPE:
+ return "BOOLEAN_TYPE";
+ case XPathResult.NUMBER_TYPE:
+ return "NUMBER_TYPE";
+ case XPathResult.STRING_TYPE:
+ return "STRING_TYPE";
+ case XPathResult.FIRST_ORDERED_NODE_TYPE:
+ return "FIRST_ORDERED_NODE_TYPE";
+ case XPathResult.ORDERED_NODE_ITERATOR_TYPE:
+ return "ORDERED_NODE_ITERATOR_TYPE";
+ case XPathResult.ORDERED_NODE_SNAPSHOT_TYPE:
+ return "ORDERED_NODE_SNAPSHOT_TYPE";
+ case XPathResult.UNORDERED_NODE_ITERATOR_TYPE:
+ return "UNORDERED_NODE_ITERATOR_TYPE";
+ case XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE:
+ return "UNORDERED_NODE_SNAPSHOT_TYPE";
+ default:
+ return "(unknown)";
+ }
+ }
+
+}
diff --git a/libjava/gnu/xml/dom/ImplementationList.java b/libjava/gnu/xml/dom/ImplementationList.java
new file mode 100644
index 0000000..0f996ba
--- /dev/null
+++ b/libjava/gnu/xml/dom/ImplementationList.java
@@ -0,0 +1,70 @@
+/* ImplementationList.java --
+ Copyright (C) 2004 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.dom;
+
+import java.util.List;
+
+import org.w3c.dom.DOMImplementation;
+import org.w3c.dom.DOMImplementationList;
+
+/**
+ * Implementation list for GNU JAXP.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+public class ImplementationList
+ implements DOMImplementationList
+{
+
+ private List list;
+
+ ImplementationList(List list)
+ {
+ this.list = list;
+ }
+
+ public int getLength()
+ {
+ return list.size();
+ }
+
+ public DOMImplementation item(int index)
+ {
+ return (DOMImplementation) list.get(index);
+ }
+
+}
diff --git a/libjava/gnu/xml/dom/ImplementationSource.java b/libjava/gnu/xml/dom/ImplementationSource.java
new file mode 100644
index 0000000..f0c4bb5
--- /dev/null
+++ b/libjava/gnu/xml/dom/ImplementationSource.java
@@ -0,0 +1,167 @@
+/* ImplementationSource.java --
+ Copyright (C) 2004 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.dom;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+
+import org.w3c.dom.DOMImplementation;
+import org.w3c.dom.DOMImplementationList;
+import org.w3c.dom.DOMImplementationSource;
+
+/**
+ * Implementation source for GNU JAXP.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+public class ImplementationSource
+ implements DOMImplementationSource
+{
+
+ private static final String DIGITS = "1234567890";
+
+ /*
+ * GNU DOM implementations.
+ */
+ private static final DOMImplementation[] implementations;
+
+ static
+ {
+ List acc = new ArrayList();
+ acc.add(new gnu.xml.dom.DomImpl());
+ try
+ {
+ Class t = Class.forName("gnu.xml.libxmlj.dom.GnomeDocumentBuilder");
+ acc.add(t.newInstance());
+ }
+ catch (Exception e)
+ {
+ // libxmlj not available
+ }
+ catch (UnsatisfiedLinkError e)
+ {
+ // libxmlj not available
+ }
+ implementations = new DOMImplementation[acc.size()];
+ acc.toArray(implementations);
+ }
+
+ public DOMImplementation getDOMImplementation(String features)
+ {
+ List available = getImplementations(features);
+ if (available.isEmpty())
+ {
+ return null;
+ }
+ return (DOMImplementation) available.get(0);
+ }
+
+ public DOMImplementationList getDOMImplementationList(String features)
+ {
+ List available = getImplementations(features);
+ return new ImplementationList(available);
+ }
+
+ /**
+ * Returns a list of the implementations that support the specified
+ * features.
+ */
+ private final List getImplementations(String features)
+ {
+ List available = new ArrayList(Arrays.asList(implementations));
+ for (Iterator i = parseFeatures(features).iterator(); i.hasNext(); )
+ {
+ String feature = (String) i.next();
+ String version = null;
+ int si = feature.indexOf(' ');
+ if (si != -1)
+ {
+ version = feature.substring(si + 1);
+ feature = feature.substring(0, si);
+ }
+ for (Iterator j = available.iterator(); j.hasNext(); )
+ {
+ DOMImplementation impl = (DOMImplementation) j.next();
+ if (!impl.hasFeature(feature, version))
+ {
+ j.remove();
+ }
+ }
+ }
+ return available;
+ }
+
+ /**
+ * Parses the feature list into feature tokens.
+ */
+ final List parseFeatures(String features)
+ {
+ List list = new ArrayList();
+ int pos = 0, start = 0;
+ int len = features.length();
+ for (; pos < len; pos++)
+ {
+ char c = features.charAt(pos);
+ if (c == ' ')
+ {
+ if (pos + 1 < len &&
+ DIGITS.indexOf(features.charAt(pos + 1)) == -1)
+ {
+ list.add(getFeature(features, start, pos));
+ start = pos + 1;
+ }
+ }
+ }
+ if (pos > start)
+ {
+ list.add(getFeature(features, start, len));
+ }
+ return list;
+ }
+
+ final String getFeature(String features, int start, int end)
+ {
+ if (features.length() > 0 && features.charAt(start) == '+')
+ {
+ return features.substring(start + 1, end);
+ }
+ return features.substring(start, end);
+ }
+
+}
diff --git a/libjava/gnu/xml/dom/JAXPFactory.java b/libjava/gnu/xml/dom/JAXPFactory.java
new file mode 100644
index 0000000..4271208
--- /dev/null
+++ b/libjava/gnu/xml/dom/JAXPFactory.java
@@ -0,0 +1,287 @@
+/* JAXPFactory.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.dom;
+
+import java.io.IOException;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.DocumentType;
+import org.w3c.dom.DOMImplementation;
+
+import org.xml.sax.EntityResolver;
+import org.xml.sax.ErrorHandler;
+import org.xml.sax.InputSource;
+import org.xml.sax.XMLReader;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParserFactory;
+
+
+/**
+ * DOM bootstrapping API, for use with JAXP.
+ *
+ * @see Consumer
+ *
+ * @author David Brownell
+ */
+public final class JAXPFactory
+ extends DocumentBuilderFactory
+{
+
+ private static final String PROPERTY = "http://xml.org/sax/properties/";
+ private static final String FEATURE = "http://xml.org/sax/features/";
+
+ private SAXParserFactory pf;
+
+ /**
+ * Default constructor.
+ */
+ public JAXPFactory()
+ {
+ }
+
+ /**
+ * Constructs a JAXP document builder which uses the default
+ * JAXP SAX2 parser and the DOM implementation in this package.
+ */
+ public DocumentBuilder newDocumentBuilder()
+ throws ParserConfigurationException
+ {
+ if (pf == null)
+ {
+ // Force use of AElfred2 since not all JAXP parsers
+ // conform very well to the SAX2 API spec ...
+ pf = new gnu.xml.aelfred2.JAXPFactory();
+ // pf = SAXParserFactory.newInstance ();
+ }
+
+ // JAXP default: false
+ pf.setValidating(isValidating());
+
+ // FIXME: this namespace setup may cause errors in some
+ // conformant SAX2 parsers, which we CAN patch up by
+ // splicing a "NSFilter" stage up front ...
+
+ // JAXP default: false
+ pf.setNamespaceAware(isNamespaceAware());
+
+ try
+ {
+ // undo rude "namespace-prefixes=false" default
+ pf.setFeature(FEATURE + "namespace-prefixes", true);
+
+ return new JAXPBuilder(pf.newSAXParser().getXMLReader(), this);
+ }
+ catch (SAXException e)
+ {
+ String msg = "can't create JAXP DocumentBuilder: " + e.getMessage();
+ throw new ParserConfigurationException(msg);
+ }
+ }
+
+ /** There seems to be no useful specification for attribute names */
+ public void setAttribute(String name, Object value)
+ throws IllegalArgumentException
+ {
+ if ("http://java.sun.com/xml/jaxp/properties/schemaLanguage".equals(name))
+ {
+ // TODO
+ }
+ else
+ {
+ throw new IllegalArgumentException(name);
+ }
+ }
+
+ /** There seems to be no useful specification for attribute names */
+ public Object getAttribute(String name)
+ throws IllegalArgumentException
+ {
+ throw new IllegalArgumentException(name);
+ }
+
+ static final class JAXPBuilder
+ extends DocumentBuilder
+ implements ErrorHandler
+ {
+
+ private Consumer consumer;
+ private XMLReader producer;
+ private DomImpl impl;
+
+ JAXPBuilder(XMLReader parser, JAXPFactory factory)
+ throws ParserConfigurationException
+ {
+ impl = new DomImpl();
+
+ // set up consumer side
+ try
+ {
+ consumer = new Consumer();
+ }
+ catch (SAXException e)
+ {
+ throw new ParserConfigurationException(e.getMessage());
+ }
+
+ // JAXP defaults: true, noise nodes are good (bleech)
+ consumer.setHidingReferences(factory.isExpandEntityReferences());
+ consumer.setHidingComments(factory.isIgnoringComments());
+ consumer.setHidingWhitespace(factory.isIgnoringElementContentWhitespace());
+ consumer.setHidingCDATA(factory.isCoalescing());
+
+ // set up producer side
+ producer = parser;
+ producer.setContentHandler(consumer.getContentHandler());
+ producer.setDTDHandler(consumer.getDTDHandler());
+
+ try
+ {
+ String id;
+
+ // if validating, report validity errors, and default
+ // to treating them as fatal
+ if (factory.isValidating ())
+ {
+ producer.setFeature(FEATURE + "validation", true);
+ producer.setErrorHandler(this);
+ }
+
+ // always save prefix info, maybe do namespace processing
+ producer.setFeature(FEATURE + "namespace-prefixes", true);
+ producer.setFeature(FEATURE + "namespaces",
+ factory.isNamespaceAware());
+
+ // set important handlers
+ id = PROPERTY + "lexical-handler";
+ producer.setProperty(id, consumer.getProperty(id));
+
+ id = PROPERTY + "declaration-handler";
+ producer.setProperty(id, consumer.getProperty(id));
+
+ }
+ catch (SAXException e)
+ {
+ throw new ParserConfigurationException(e.getMessage());
+ }
+ }
+
+ public Document parse(InputSource source)
+ throws SAXException, IOException
+ {
+ producer.parse(source);
+ Document doc = consumer.getDocument();
+ // TODO inputEncoding
+ doc.setDocumentURI(source.getSystemId());
+ return doc;
+ }
+
+ public boolean isNamespaceAware()
+ {
+ try
+ {
+ return producer.getFeature(FEATURE + "namespaces");
+ }
+ catch (SAXException e)
+ {
+ // "can't happen"
+ throw new RuntimeException(e.getMessage());
+ }
+ }
+
+ public boolean isValidating()
+ {
+ try
+ {
+ return producer.getFeature(FEATURE + "validation");
+ }
+ catch (SAXException e)
+ {
+ // "can't happen"
+ throw new RuntimeException(e.getMessage());
+ }
+ }
+
+ public void setEntityResolver(EntityResolver resolver)
+ {
+ producer.setEntityResolver(resolver);
+ }
+
+ public void setErrorHandler(ErrorHandler handler)
+ {
+ producer.setErrorHandler(handler);
+ consumer.setErrorHandler(handler);
+ }
+
+ public DOMImplementation getDOMImplementation()
+ {
+ return impl;
+ }
+
+ public Document newDocument()
+ {
+ return new DomDocument();
+ }
+
+ // implementation of error handler that's used when validating
+ public void fatalError(SAXParseException e)
+ throws SAXException
+ {
+ throw e;
+ }
+
+ public void error(SAXParseException e)
+ throws SAXException
+ {
+ throw e;
+ }
+
+ public void warning(SAXParseException e)
+ throws SAXException
+ {
+ /* ignore */
+ }
+
+ }
+
+}
+
diff --git a/libjava/gnu/xml/dom/ls/DomLSEx.java b/libjava/gnu/xml/dom/ls/DomLSEx.java
new file mode 100644
index 0000000..5bf1595
--- /dev/null
+++ b/libjava/gnu/xml/dom/ls/DomLSEx.java
@@ -0,0 +1,75 @@
+/* DomLSEx.java --
+ Copyright (C) 2004 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.dom.ls;
+
+import java.io.PrintStream;
+import java.io.PrintWriter;
+import org.w3c.dom.ls.LSException;
+
+/**
+ * A DOM LS exception incorporating a cause.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+public class DomLSEx
+ extends LSException
+{
+
+ private final Exception cause2;
+
+ public DomLSEx(short type, Exception cause)
+ {
+ super(type, cause.getMessage());
+ cause2 = cause;
+ }
+
+ public void printStackTrace(PrintStream out)
+ {
+ super.printStackTrace(out);
+ out.print("caused by ");
+ cause2.printStackTrace(out);
+ }
+
+ public void printStackTrace(PrintWriter out)
+ {
+ super.printStackTrace(out);
+ out.print("caused by ");
+ cause2.printStackTrace(out);
+ }
+
+}
diff --git a/libjava/gnu/xml/dom/ls/DomLSInput.java b/libjava/gnu/xml/dom/ls/DomLSInput.java
new file mode 100644
index 0000000..9b30b4f
--- /dev/null
+++ b/libjava/gnu/xml/dom/ls/DomLSInput.java
@@ -0,0 +1,158 @@
+/* DomLSInput.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.dom.ls;
+
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.IOException;
+import java.io.Reader;
+import java.io.StringReader;
+import org.w3c.dom.ls.LSInput;
+
+/**
+ * Specification of XML input to parse.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+public class DomLSInput
+ implements LSInput
+{
+
+ private InputStream in;
+ private String systemId;
+ private String publicId;
+ private String baseURI;
+ private String encoding;
+ private boolean certifiedText;
+
+ public Reader getCharacterStream()
+ {
+ return new InputStreamReader(in);
+ }
+
+ public void setCharacterStream(Reader characterStream)
+ {
+ in = new ReaderInputStream(characterStream);
+ }
+
+ public InputStream getByteStream()
+ {
+ return in;
+ }
+
+ public void setByteStream(InputStream byteStream)
+ {
+ in = byteStream;
+ }
+
+ public String getStringData()
+ {
+ StringBuffer acc = new StringBuffer();
+ Reader reader = getCharacterStream();
+ try
+ {
+ char[] buf = new char[4096];
+ for (int len = reader.read(buf); len != -1; len = reader.read(buf))
+ {
+ acc.append(buf, 0, len);
+ }
+ }
+ catch (IOException e)
+ {
+ return null; // ?
+ }
+ return acc.toString();
+ }
+
+ public void setStringData(String stringData)
+ {
+ in = new ReaderInputStream(new StringReader(stringData));
+ }
+
+ public String getSystemId()
+ {
+ return systemId;
+ }
+
+ public void setSystemId(String systemId)
+ {
+ this.systemId = systemId;
+ }
+
+ public String getPublicId()
+ {
+ return publicId;
+ }
+
+ public void setPublicId(String publicId)
+ {
+ this.publicId = publicId;
+ }
+
+ public String getBaseURI()
+ {
+ return baseURI;
+ }
+
+ public void setBaseURI(String baseURI)
+ {
+ this.baseURI = baseURI;
+ }
+
+ public String getEncoding()
+ {
+ return encoding;
+ }
+
+ public void setEncoding(String encoding)
+ {
+ this.encoding = encoding;
+ }
+
+ public boolean getCertifiedText()
+ {
+ return certifiedText;
+ }
+
+ public void setCertifiedText(boolean certifiedText)
+ {
+ this.certifiedText = certifiedText;
+ }
+
+}
+
diff --git a/libjava/gnu/xml/dom/ls/DomLSOutput.java b/libjava/gnu/xml/dom/ls/DomLSOutput.java
new file mode 100644
index 0000000..643ae7d
--- /dev/null
+++ b/libjava/gnu/xml/dom/ls/DomLSOutput.java
@@ -0,0 +1,98 @@
+/* DomLSOutput.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.dom.ls;
+
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+import org.w3c.dom.ls.LSOutput;
+
+/**
+ * Specification of XML output to produce.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+public class DomLSOutput
+ implements LSOutput
+{
+
+ private OutputStream out;
+ private String systemId;
+ private String encoding;
+
+ public Writer getCharacterStream()
+ {
+ return new OutputStreamWriter(out);
+ }
+
+ public void setCharacterStream(Writer characterStream)
+ {
+ out = new WriterOutputStream(characterStream);
+ }
+
+ public OutputStream getByteStream()
+ {
+ return out;
+ }
+
+ public void setByteStream(OutputStream out)
+ {
+ this.out = out;
+ }
+
+ public String getSystemId()
+ {
+ return systemId;
+ }
+
+ public void setSystemId(String systemId)
+ {
+ this.systemId = systemId;
+ }
+
+ public String getEncoding()
+ {
+ return encoding;
+ }
+
+ public void setEncoding(String encoding)
+ {
+ this.encoding = encoding;
+ }
+
+}
diff --git a/libjava/gnu/xml/dom/ls/DomLSParser.java b/libjava/gnu/xml/dom/ls/DomLSParser.java
new file mode 100644
index 0000000..b050b99
--- /dev/null
+++ b/libjava/gnu/xml/dom/ls/DomLSParser.java
@@ -0,0 +1,574 @@
+/* DomLSParser.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.dom.ls;
+
+import java.io.File;
+import java.io.InputStream;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Arrays;
+import java.util.List;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+import org.w3c.dom.Document;
+import org.w3c.dom.DOMConfiguration;
+import org.w3c.dom.DOMException;
+import org.w3c.dom.DOMStringList;
+import org.w3c.dom.Node;
+import org.w3c.dom.ls.DOMImplementationLS;
+import org.w3c.dom.ls.LSException;
+import org.w3c.dom.ls.LSInput;
+import org.w3c.dom.ls.LSParser;
+import org.w3c.dom.ls.LSParserFilter;
+import org.xml.sax.EntityResolver;
+import org.xml.sax.ErrorHandler;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXNotRecognizedException;
+import org.xml.sax.SAXParseException;
+import org.xml.sax.XMLReader;
+import gnu.xml.dom.DomDocument;
+import gnu.xml.dom.DomEx;
+
+/**
+ * Parser implementation for GNU DOM.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+public class DomLSParser
+ implements LSParser, DOMConfiguration, DOMStringList, ErrorHandler
+{
+
+ private static final List SUPPORTED_PARAMETERS
+ = Arrays.asList(new String[] { "cdata-sections",
+ "comments",
+ "element-content-whitespace",
+ "namespaces",
+ "expand-entity-references",
+ "coalescing",
+ "validating",
+ "xinclude-aware",
+ "entity-resolver",
+ "error-handler" });
+
+ private LSParserFilter filter;
+ private final boolean async;
+ private String schemaType;
+ private SAXEventSink eventSink;
+ private SAXParserFactory factory;
+ private XMLReader reader;
+
+ private boolean namespaceAware = true;
+ private boolean ignoreWhitespace;
+ private boolean expandEntityReferences;
+ private boolean ignoreComments;
+ private boolean coalescing;
+ private boolean validating;
+ private boolean xIncludeAware;
+ private EntityResolver entityResolver;
+ private ErrorHandler errorHandler;
+
+ public DomLSParser(short mode, String schemaType)
+ throws DOMException
+ {
+ switch (mode)
+ {
+ case DOMImplementationLS.MODE_ASYNCHRONOUS:
+ async = true;
+ break;
+ case DOMImplementationLS.MODE_SYNCHRONOUS:
+ async = false;
+ break;
+ default:
+ throw new DomEx(DOMException.NOT_SUPPORTED_ERR);
+ }
+ // TODO schemaType
+ this.schemaType = schemaType;
+ factory = SAXParserFactory.newInstance();
+ }
+
+ // -- LSParser --
+
+ public DOMConfiguration getDomConfig()
+ {
+ return this;
+ }
+
+ public LSParserFilter getFilter()
+ {
+ return filter;
+ }
+
+ public void setFilter(LSParserFilter filter)
+ {
+ this.filter = filter;
+ }
+
+ public boolean getAsync()
+ {
+ return async;
+ }
+
+ public boolean getBusy()
+ {
+ return eventSink != null;
+ }
+
+ public Document parse(LSInput input)
+ throws DOMException, LSException
+ {
+ if (async)
+ {
+ return doParse(input);
+ }
+ else
+ {
+ synchronized (this)
+ {
+ return doParse(input);
+ }
+ }
+ }
+
+ public Document parseURI(String uri)
+ throws DOMException, LSException
+ {
+ LSInput input = new DomLSInput();
+ input.setSystemId(uri);
+ return parse(input);
+ }
+
+ public Node parseWithContext(LSInput input, Node context, short action)
+ throws DOMException, LSException
+ {
+ Document doc = (context.getNodeType() == Node.DOCUMENT_NODE) ?
+ (Document) context : context.getOwnerDocument();
+ input.setBaseURI(doc.getDocumentURI());
+ // TODO use namespaces defined on context node
+ Document ret = parse(input);
+ Node root = ret.getDocumentElement();
+ root = doc.adoptNode(root);
+ switch (action)
+ {
+ case ACTION_APPEND_AS_CHILDREN:
+ context.appendChild(root);
+ break;
+ case ACTION_REPLACE_CHILDREN:
+ Node c1 = context.getFirstChild();
+ while (c1 != null)
+ {
+ Node next = c1.getNextSibling();
+ context.removeChild(c1);
+ c1 = next;
+ }
+ context.appendChild(root);
+ break;
+ case ACTION_INSERT_BEFORE:
+ Node p1 = context.getParentNode();
+ p1.insertBefore(root, context);
+ break;
+ case ACTION_INSERT_AFTER:
+ Node p2 = context.getParentNode();
+ Node r1 = context.getNextSibling();
+ if (r1 == null)
+ {
+ p2.appendChild(root);
+ }
+ else
+ {
+ p2.insertBefore(root, r1);
+ }
+ break;
+ case ACTION_REPLACE:
+ Node p3 = context.getParentNode();
+ Node r2 = context.getNextSibling();
+ p3.removeChild(context);
+ if (r2 == null)
+ {
+ p3.appendChild(root);
+ }
+ else
+ {
+ p3.insertBefore(root, r2);
+ }
+ break;
+ }
+ return root;
+ }
+
+ public void abort()
+ {
+ if (eventSink != null)
+ {
+ eventSink.interrupt();
+ }
+ }
+
+ private Document doParse(LSInput input)
+ throws DOMException, LSException
+ {
+ // create event sink
+ if (eventSink != null)
+ {
+ throw new LSException(LSException.PARSE_ERR, "parse in progress");
+ }
+ InputSource source = getInputSource(input);
+ eventSink = (filter == null) ? new SAXEventSink() :
+ new FilteredSAXEventSink(filter);
+ // configure sink
+ eventSink.namespaceAware = namespaceAware;
+ eventSink.ignoreWhitespace = ignoreWhitespace;
+ eventSink.expandEntityReferences = expandEntityReferences;
+ eventSink.ignoreComments = ignoreComments;
+ eventSink.coalescing = coalescing;
+ // get and configure reader
+ XMLReader reader = getXMLReader();
+ try
+ {
+ reader.setContentHandler(eventSink);
+ reader.setDTDHandler(eventSink);
+ reader.setProperty("http://xml.org/sax/properties/lexical-handler",
+ eventSink);
+ reader.setProperty("http://xml.org/sax/properties/declaration-handler",
+ eventSink);
+ reader.setFeature("http://xml.org/sax/features/namespaces",
+ namespaceAware);
+ reader.setFeature("http://xml.org/sax/features/namespace-prefixes",
+ true);
+ reader.setFeature("http://xml.org/sax/features/validation",
+ validating);
+ try
+ {
+ reader.setFeature("http://xml.org/sax/features/use-attributes2",
+ true);
+ }
+ catch (SAXNotRecognizedException e)
+ {
+ // ignore
+ }
+ try
+ {
+ reader.setFeature("http://xml.org/sax/features/external-general-entities",
+ true);
+ }
+ catch (SAXNotRecognizedException e)
+ {
+ // ignore
+ }
+ reader.setEntityResolver(entityResolver);
+ reader.setErrorHandler(errorHandler);
+ // parse
+ reader.parse(source);
+ }
+ catch (DOMException e)
+ {
+ reader = null;
+ eventSink = null;
+ throw e;
+ }
+ catch (SAXException e)
+ {
+ reader = null;
+ eventSink = null;
+ throw new DomLSEx(LSException.PARSE_ERR, e);
+ }
+ catch (IOException e)
+ {
+ reader = null;
+ eventSink = null;
+ throw new DomLSEx(LSException.PARSE_ERR, e);
+ }
+ // return document
+ Document ret = eventSink.doc;
+ String systemId = input.getSystemId();
+ if (systemId != null && ret instanceof DomDocument)
+ {
+ ((DomDocument) ret).setDocumentURI(systemId);
+ }
+ eventSink = null;
+ return ret;
+ }
+
+ private XMLReader getXMLReader()
+ throws LSException
+ {
+ if (reader == null)
+ {
+ factory.setNamespaceAware(namespaceAware);
+ factory.setValidating(validating);
+ factory.setXIncludeAware(xIncludeAware);
+ try
+ {
+ SAXParser parser = factory.newSAXParser();
+ reader = parser.getXMLReader();
+ }
+ catch (ParserConfigurationException e)
+ {
+ throw new DomLSEx(LSException.PARSE_ERR, e);
+ }
+ catch (SAXException e)
+ {
+ throw new DomLSEx(LSException.PARSE_ERR, e);
+ }
+ }
+ return reader;
+ }
+
+ private InputSource getInputSource(LSInput input)
+ throws LSException
+ {
+ InputSource source = null;
+ String systemId = input.getSystemId();
+ InputStream in = input.getByteStream();
+ if (in != null)
+ {
+ source = new InputSource(in);
+ source.setSystemId(systemId);
+ }
+ if (source == null && entityResolver != null)
+ {
+ String publicId = input.getPublicId();
+ try
+ {
+ source = entityResolver.resolveEntity(publicId, systemId);
+ }
+ catch (SAXException e)
+ {
+ throw new DomLSEx(LSException.PARSE_ERR, e);
+ }
+ catch (IOException e)
+ {
+ throw new DomLSEx(LSException.PARSE_ERR, e);
+ }
+ }
+ if (source == null)
+ {
+ URL url = null;
+ String base = input.getBaseURI();
+ try
+ {
+ try
+ {
+ URL baseURL = (base == null) ? null : new URL(base);
+ url = (baseURL == null) ? new URL(systemId) :
+ new URL(baseURL, systemId);
+ }
+ catch (MalformedURLException e)
+ {
+ File baseFile = (base == null) ? null : new File(base);
+ url = (baseFile == null) ? new File(systemId).toURL() :
+ new File(baseFile, systemId).toURL();
+ }
+ in = url.openStream();
+ systemId = url.toString();
+ source = new InputSource(in);
+ source.setSystemId(systemId);
+ }
+ catch (IOException e)
+ {
+ throw new DomLSEx(LSException.PARSE_ERR, e);
+ }
+ }
+ return source;
+ }
+
+ // -- DOMConfiguration --
+
+ public void setParameter(String name, Object value)
+ throws DOMException
+ {
+ name = name.toLowerCase();
+ if ("cdata-sections".equals(name))
+ {
+ coalescing = !((Boolean) value).booleanValue();
+ }
+ else if ("comments".equals(name))
+ {
+ ignoreComments = !((Boolean) value).booleanValue();
+ }
+ else if ("element-content-whitespace".equals(name))
+ {
+ ignoreWhitespace = !((Boolean) value).booleanValue();
+ }
+ else if ("namespaces".equals(name))
+ {
+ namespaceAware = ((Boolean) value).booleanValue();
+ }
+ else if ("expand-entity-references".equals(name))
+ {
+ expandEntityReferences = ((Boolean) value).booleanValue();
+ }
+ else if ("coalescing".equals(name))
+ {
+ coalescing = ((Boolean) value).booleanValue();
+ }
+ else if ("validating".equals(name))
+ {
+ validating = ((Boolean) value).booleanValue();
+ }
+ else if ("xinclude-aware".equals(name))
+ {
+ xIncludeAware = ((Boolean) value).booleanValue();
+ }
+ else if ("entity-resolver".equals(name))
+ {
+ entityResolver = (EntityResolver) value;
+ }
+ else if ("error-handler".equals(name))
+ {
+ errorHandler = (ErrorHandler) value;
+ }
+ else
+ {
+ throw new DomEx(DomEx.NOT_SUPPORTED_ERR);
+ }
+ // invalidate reader, a new one will be created
+ reader = null;
+ }
+
+ public Object getParameter(String name)
+ throws DOMException
+ {
+ name = name.toLowerCase();
+ if ("cdata-sections".equals(name))
+ {
+ return coalescing ? Boolean.FALSE : Boolean.TRUE;
+ }
+ else if ("comments".equals(name))
+ {
+ return ignoreComments ? Boolean.FALSE : Boolean.TRUE;
+ }
+ else if ("element-content-whitespace".equals(name))
+ {
+ return ignoreWhitespace ? Boolean.FALSE : Boolean.TRUE;
+ }
+ else if ("namespaces".equals(name))
+ {
+ return namespaceAware ? Boolean.TRUE : Boolean.FALSE;
+ }
+ else if ("expand-entity-references".equals(name))
+ {
+ return expandEntityReferences ? Boolean.TRUE : Boolean.FALSE;
+ }
+ else if ("coalescing".equals(name))
+ {
+ return coalescing ? Boolean.TRUE : Boolean.FALSE;
+ }
+ else if ("validating".equals(name))
+ {
+ return validating ? Boolean.TRUE : Boolean.FALSE;
+ }
+ else if ("xinclude-aware".equals(name))
+ {
+ return xIncludeAware ? Boolean.TRUE : Boolean.FALSE;
+ }
+ else if ("entity-resolver".equals(name))
+ {
+ return entityResolver;
+ }
+ else if ("error-handler".equals(name))
+ {
+ return errorHandler;
+ }
+ else
+ {
+ throw new DomEx(DomEx.NOT_SUPPORTED_ERR);
+ }
+ }
+
+ public boolean canSetParameter(String name, Object value)
+ {
+ return contains(name);
+ }
+
+ public DOMStringList getParameterNames()
+ {
+ return this;
+ }
+
+ // -- DOMStringList --
+
+ public String item(int i)
+ {
+ return (String) SUPPORTED_PARAMETERS.get(i);
+ }
+
+ public int getLength()
+ {
+ return SUPPORTED_PARAMETERS.size();
+ }
+
+ public boolean contains(String str)
+ {
+ return SUPPORTED_PARAMETERS.contains(str);
+ }
+
+ // -- ErrorHandler --
+
+ public void warning(SAXParseException e)
+ throws SAXException
+ {
+ if (errorHandler != null)
+ {
+ errorHandler.warning(e);
+ }
+ }
+
+ public void error(SAXParseException e)
+ throws SAXException
+ {
+ if (errorHandler != null)
+ {
+ errorHandler.error(e);
+ }
+ }
+
+ public void fatalError(SAXParseException e)
+ throws SAXException
+ {
+ if (errorHandler != null)
+ {
+ errorHandler.fatalError(e);
+ }
+ abort();
+ }
+
+}
+
diff --git a/libjava/gnu/xml/dom/ls/DomLSSerializer.java b/libjava/gnu/xml/dom/ls/DomLSSerializer.java
new file mode 100644
index 0000000..91a01c9
--- /dev/null
+++ b/libjava/gnu/xml/dom/ls/DomLSSerializer.java
@@ -0,0 +1,354 @@
+/* DomLSSerializer.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.dom.ls;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.Arrays;
+import java.util.List;
+import org.w3c.dom.DOMConfiguration;
+import org.w3c.dom.DOMException;
+import org.w3c.dom.DOMStringList;
+import org.w3c.dom.Node;
+import org.w3c.dom.ls.LSException;
+import org.w3c.dom.ls.LSOutput;
+import org.w3c.dom.ls.LSSerializer;
+import org.w3c.dom.ls.LSSerializerFilter;
+import org.w3c.dom.traversal.NodeFilter;
+import gnu.xml.dom.DomEx;
+import gnu.xml.transform.StreamSerializer;
+
+/**
+ * Serialize a DOM node to a stream.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+public class DomLSSerializer
+ extends StreamSerializer
+ implements LSSerializer, DOMConfiguration, DOMStringList
+{
+
+ private static final List SUPPORTED_PARAMETERS =
+ Arrays.asList(new String[] {"discard-default-content",
+ "xml-declaration"});
+
+ private LSSerializerFilter filter;
+ private StreamSerializer serializer;
+
+ public DomLSSerializer()
+ {
+ super();
+ discardDefaultContent = true;
+ }
+
+ // -- LSSerializer --
+
+ public DOMConfiguration getDomConfig()
+ {
+ return this;
+ }
+
+ public String getNewLine()
+ {
+ return eol;
+ }
+
+ public void setNewLine(String newLine)
+ {
+ if (newLine == null)
+ {
+ newLine = System.getProperty("line.separator");
+ }
+ eol = newLine;
+ }
+
+ public LSSerializerFilter getFilter()
+ {
+ return filter;
+ }
+
+ public void setFilter(LSSerializerFilter filter)
+ {
+ this.filter = filter;
+ }
+
+ public boolean write(Node node, LSOutput output)
+ throws LSException
+ {
+ OutputStream out = output.getByteStream();
+ try
+ {
+ if (out == null)
+ {
+ String systemId = output.getSystemId();
+ try
+ {
+ URL url = new URL(systemId);
+ URLConnection connection = url.openConnection();
+ connection.setDoOutput(true);
+ if (connection instanceof HttpURLConnection)
+ {
+ ((HttpURLConnection) connection).setRequestMethod("PUT");
+ }
+ out = connection.getOutputStream();
+ }
+ catch (MalformedURLException e)
+ {
+ File file = new File(systemId);
+ out = new FileOutputStream(file);
+ }
+ }
+ serialize(node, out);
+ out.flush();
+ return true;
+ }
+ catch (IOException e)
+ {
+ throw new DomLSEx(LSException.SERIALIZE_ERR, e);
+ }
+ }
+
+ public boolean writeToURI(Node node, String uri)
+ throws LSException
+ {
+ LSOutput output = new DomLSOutput();
+ output.setSystemId(uri);
+ return write(node, output);
+ }
+
+ public String writeToString(Node node)
+ throws DOMException, LSException
+ {
+ Writer writer = new StringWriter();
+ LSOutput output = new DomLSOutput();
+ output.setCharacterStream(writer);
+ write(node, output);
+ return writer.toString();
+ }
+
+ public void serialize(Node node, OutputStream out)
+ throws IOException
+ {
+ if (filter == null)
+ {
+ super.serialize(node, out);
+ }
+ else
+ {
+ int wts = filter.getWhatToShow();
+ if (wts != NodeFilter.SHOW_ALL)
+ {
+ switch (node.getNodeType())
+ {
+ case Node.ATTRIBUTE_NODE:
+ if ((wts & NodeFilter.SHOW_ATTRIBUTE) == 0)
+ {
+ super.serialize(node, out);
+ return;
+ }
+ break;
+ case Node.TEXT_NODE:
+ if ((wts & NodeFilter.SHOW_TEXT) == 0)
+ {
+ super.serialize(node, out);
+ return;
+ }
+ break;
+ case Node.ELEMENT_NODE:
+ if ((wts & NodeFilter.SHOW_ELEMENT) == 0)
+ {
+ super.serialize(node, out);
+ return;
+ }
+ break;
+ case Node.CDATA_SECTION_NODE:
+ if ((wts & NodeFilter.SHOW_CDATA_SECTION) == 0)
+ {
+ super.serialize(node, out);
+ return;
+ }
+ break;
+ case Node.COMMENT_NODE:
+ if ((wts & NodeFilter.SHOW_COMMENT) == 0)
+ {
+ super.serialize(node, out);
+ return;
+ }
+ break;
+ case Node.DOCUMENT_NODE:
+ if ((wts & NodeFilter.SHOW_DOCUMENT) == 0)
+ {
+ super.serialize(node, out);
+ return;
+ }
+ break;
+ case Node.DOCUMENT_TYPE_NODE:
+ if ((wts & NodeFilter.SHOW_DOCUMENT_TYPE) == 0)
+ {
+ super.serialize(node, out);
+ return;
+ }
+ break;
+ case Node.PROCESSING_INSTRUCTION_NODE:
+ if ((wts & NodeFilter.SHOW_PROCESSING_INSTRUCTION) == 0)
+ {
+ super.serialize(node, out);
+ return;
+ }
+ break;
+ case Node.DOCUMENT_FRAGMENT_NODE:
+ if ((wts & NodeFilter.SHOW_DOCUMENT_FRAGMENT) == 0)
+ {
+ super.serialize(node, out);
+ return;
+ }
+ break;
+ case Node.ENTITY_NODE:
+ if ((wts & NodeFilter.SHOW_ENTITY) == 0)
+ {
+ super.serialize(node, out);
+ return;
+ }
+ break;
+ case Node.ENTITY_REFERENCE_NODE:
+ if ((wts & NodeFilter.SHOW_ENTITY_REFERENCE) == 0)
+ {
+ super.serialize(node, out);
+ return;
+ }
+ break;
+ case Node.NOTATION_NODE:
+ if ((wts & NodeFilter.SHOW_NOTATION) == 0)
+ {
+ super.serialize(node, out);
+ return;
+ }
+ break;
+ }
+ }
+ switch (filter.acceptNode(node))
+ {
+ case NodeFilter.FILTER_ACCEPT:
+ super.serialize(node, out);
+ break;
+ case NodeFilter.FILTER_REJECT:
+ break;
+ case NodeFilter.FILTER_SKIP:
+ Node first = node.getFirstChild();
+ if (first != null)
+ {
+ serialize(first, out);
+ }
+ break;
+ }
+ }
+ }
+
+ // -- DOMConfiguration --
+
+ public void setParameter(String name, Object value)
+ throws DOMException
+ {
+ if ("discard-default-content".equals(name))
+ {
+ discardDefaultContent = "true".equals(value.toString());
+ }
+ else if ("xml-declaration".equals(name))
+ {
+ xmlDeclaration = "false".equals(value.toString());
+ }
+ else
+ {
+ throw new DomEx(DomEx.NOT_SUPPORTED_ERR);
+ }
+ }
+
+ public Object getParameter(String name)
+ throws DOMException
+ {
+ if ("discard-default-content".equals(name))
+ {
+ return discardDefaultContent ? "true" : "false";
+ }
+ else if ("xml-declaration".equals(name))
+ {
+ return xmlDeclaration ? "true" : "false";
+ }
+ else
+ {
+ throw new DomEx(DomEx.NOT_SUPPORTED_ERR);
+ }
+ }
+
+ public boolean canSetParameter(String name, Object value)
+ {
+ return contains(name);
+ }
+
+ public DOMStringList getParameterNames()
+ {
+ return this;
+ }
+
+ // -- DOMStringList --
+
+ public String item(int i)
+ {
+ return (String) SUPPORTED_PARAMETERS.get(i);
+ }
+
+ public int getLength()
+ {
+ return SUPPORTED_PARAMETERS.size();
+ }
+
+ public boolean contains(String str)
+ {
+ return SUPPORTED_PARAMETERS.contains(str);
+ }
+
+}
+
diff --git a/libjava/gnu/xml/dom/ls/FilteredSAXEventSink.java b/libjava/gnu/xml/dom/ls/FilteredSAXEventSink.java
new file mode 100644
index 0000000..63f32a8
--- /dev/null
+++ b/libjava/gnu/xml/dom/ls/FilteredSAXEventSink.java
@@ -0,0 +1,354 @@
+/* FilteredSAXEventSink.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.dom.ls;
+
+import java.util.LinkedList;
+import org.w3c.dom.Attr;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.Text;
+import org.w3c.dom.ls.LSParserFilter;
+import org.w3c.dom.traversal.NodeFilter;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+
+/**
+ * A SAX event sink that calls out to a parser filter in order to decide
+ * whether to insert nodes into the tree.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+class FilteredSAXEventSink
+ extends SAXEventSink
+{
+
+ final LSParserFilter filter;
+ final int whatToShow;
+
+ /**
+ * Stack of elements to insert.
+ */
+ LinkedList nodes;
+
+ /**
+ * Corresponding stack of filter decisions about the nodes.
+ */
+ LinkedList decisions;
+
+ /**
+ * True when rejecting child nodes.
+ */
+ boolean rejecting;
+
+ FilteredSAXEventSink(LSParserFilter filter)
+ {
+ this.filter = filter;
+ whatToShow = filter.getWhatToShow();
+ }
+
+ public void startDocument()
+ throws SAXException
+ {
+ if (interrupted)
+ {
+ return;
+ }
+ nodes = new LinkedList();
+ decisions = new LinkedList();
+
+ super.startDocument();
+ }
+
+ public void endDocument()
+ throws SAXException
+ {
+ if (interrupted)
+ {
+ return;
+ }
+ super.endDocument();
+
+ switch (getDecision(ctx, false))
+ {
+ case LSParserFilter.FILTER_REJECT:
+ ctx = null;
+ doc = null;
+ break;
+ }
+
+ nodes = null;
+ decisions = null;
+ }
+
+ public void startElement(String uri, String localName, String qName,
+ Attributes atts)
+ throws SAXException
+ {
+ if (rejecting || interrupted)
+ {
+ return;
+ }
+ Element element = createElement(uri, localName, qName, atts);
+ ctx = element;
+
+ short decision = getDecision(element, true);
+ nodes.addLast(element);
+ decisions.addLast(new Short(decision));
+
+ switch (decision)
+ {
+ case LSParserFilter.FILTER_REJECT:
+ rejecting = true;
+ break;
+ case LSParserFilter.FILTER_INTERRUPT:
+ interrupted = true;
+ break;
+ }
+ }
+
+ protected Attr createAttr(Attributes atts, int index)
+ {
+ Attr attr = super.createAttr(atts, index);
+ short decision = getDecision(attr, false);
+ switch (decision)
+ {
+ case LSParserFilter.FILTER_REJECT:
+ return null;
+ case LSParserFilter.FILTER_INTERRUPT:
+ interrupted = true;
+ return null;
+ }
+ return attr;
+ }
+
+ public void endElement(String uri, String localName, String qName)
+ throws SAXException
+ {
+ if (rejecting || interrupted)
+ {
+ return;
+ }
+ super.endElement(uri, localName, qName);
+
+ Element element = (Element) nodes.removeLast();
+ Node parent = nodes.isEmpty() ? doc : (Node) nodes.getLast();
+ ctx = parent;
+ short decision = ((Short) decisions.removeLast()).shortValue();
+ switch (decision)
+ {
+ case LSParserFilter.FILTER_SKIP:
+ // Add all children of element to parent
+ for (Node child = element.getFirstChild(); child != null;
+ child = child.getNextSibling())
+ {
+ parent.insertBefore(child, element);
+ }
+ return;
+ case LSParserFilter.FILTER_REJECT:
+ rejecting = false;
+ break;
+ }
+ decision = getDecision(element, false);
+ switch (decision)
+ {
+ case LSParserFilter.FILTER_ACCEPT:
+ parent.appendChild(element);
+ break;
+ case LSParserFilter.FILTER_INTERRUPT:
+ interrupted = true;
+ break;
+ }
+ }
+
+ public void characters(char[] c, int off, int len)
+ throws SAXException
+ {
+ if (rejecting || interrupted)
+ {
+ return;
+ }
+ Text text = createText(c, off, len);
+ short decision = getDecision(text, false);
+ switch (decision)
+ {
+ case LSParserFilter.FILTER_ACCEPT:
+ ctx.appendChild(text);
+ break;
+ case LSParserFilter.FILTER_INTERRUPT:
+ interrupted = true;
+ break;
+ }
+ }
+
+ public void processingInstruction(String target, String data)
+ throws SAXException
+ {
+ if (rejecting || interrupted || inDTD)
+ {
+ return;
+ }
+ Node pi = createProcessingInstruction(target, data);
+ short decision = getDecision(pi, false);
+ switch (decision)
+ {
+ case LSParserFilter.FILTER_ACCEPT:
+ ctx.appendChild(pi);
+ break;
+ case LSParserFilter.FILTER_INTERRUPT:
+ interrupted = true;
+ break;
+ }
+ }
+
+ public void startDTD(String name, String publicId, String systemId)
+ throws SAXException
+ {
+ if (interrupted)
+ {
+ return;
+ }
+ Node doctype = createDocumentType(name, publicId, systemId);
+ ctx = doctype;
+ inDTD = true;
+ nodes.addLast(doctype);
+ decisions.addLast(new Short(LSParserFilter.FILTER_ACCEPT));
+ }
+
+ public void endDTD()
+ throws SAXException
+ {
+ if (interrupted)
+ {
+ return;
+ }
+ Node doctype = (Node) nodes.removeLast();
+ decisions.removeLast();
+ inDTD = false;
+ ctx = doc;
+ short decision = getDecision(doctype, false);
+ switch (decision)
+ {
+ case LSParserFilter.FILTER_ACCEPT:
+ ctx.appendChild(doctype);
+ break;
+ case LSParserFilter.FILTER_INTERRUPT:
+ interrupted = true;
+ break;
+ }
+ }
+
+ public void comment(char[] c, int off, int len)
+ throws SAXException
+ {
+ if (rejecting || interrupted || inDTD)
+ {
+ return;
+ }
+ Node comment = createComment(c, off, len);
+ short decision = getDecision(comment, false);
+ switch (decision)
+ {
+ case LSParserFilter.FILTER_ACCEPT:
+ ctx.appendChild(comment);
+ break;
+ case LSParserFilter.FILTER_INTERRUPT:
+ interrupted = true;
+ break;
+ }
+ }
+
+ // TODO declarations
+
+ short getDecision(Node node, boolean start)
+ {
+ boolean show = (whatToShow == NodeFilter.SHOW_ALL);
+ if (!show)
+ {
+ switch (node.getNodeType())
+ {
+ case Node.ATTRIBUTE_NODE:
+ show = ((whatToShow & NodeFilter.SHOW_ATTRIBUTE) != 0);
+ break;
+ case Node.TEXT_NODE:
+ show = ((whatToShow & NodeFilter.SHOW_TEXT) != 0);
+ break;
+ case Node.CDATA_SECTION_NODE:
+ show = ((whatToShow & NodeFilter.SHOW_CDATA_SECTION) != 0);
+ break;
+ case Node.ELEMENT_NODE:
+ show = ((whatToShow & NodeFilter.SHOW_ELEMENT) != 0);
+ break;
+ case Node.COMMENT_NODE:
+ show = ((whatToShow & NodeFilter.SHOW_COMMENT) != 0);
+ break;
+ case Node.DOCUMENT_NODE:
+ show = ((whatToShow & NodeFilter.SHOW_DOCUMENT) != 0);
+ break;
+ case Node.PROCESSING_INSTRUCTION_NODE:
+ show = ((whatToShow & NodeFilter.SHOW_PROCESSING_INSTRUCTION) != 0);
+ break;
+ case Node.DOCUMENT_FRAGMENT_NODE:
+ show = ((whatToShow & NodeFilter.SHOW_DOCUMENT_FRAGMENT) != 0);
+ break;
+ case Node.DOCUMENT_TYPE_NODE:
+ show = ((whatToShow & NodeFilter.SHOW_DOCUMENT_TYPE) != 0);
+ break;
+ case Node.ENTITY_REFERENCE_NODE:
+ show = ((whatToShow & NodeFilter.SHOW_ENTITY_REFERENCE) != 0);
+ break;
+ case Node.ENTITY_NODE:
+ show = ((whatToShow & NodeFilter.SHOW_ENTITY) != 0);
+ break;
+ case Node.NOTATION_NODE:
+ show = ((whatToShow & NodeFilter.SHOW_NOTATION) != 0);
+ break;
+ }
+ }
+ if (!show)
+ {
+ return LSParserFilter.FILTER_ACCEPT;
+ }
+ if (start)
+ {
+ return filter.startElement((Element) node);
+ }
+ return filter.acceptNode(node);
+ }
+
+}
+
diff --git a/libjava/gnu/xml/dom/ls/ReaderInputStream.java b/libjava/gnu/xml/dom/ls/ReaderInputStream.java
new file mode 100644
index 0000000..633b968
--- /dev/null
+++ b/libjava/gnu/xml/dom/ls/ReaderInputStream.java
@@ -0,0 +1,237 @@
+/* ReaderInputStream.java --
+ Copyright (C) 1999, 2000, 2001, 2004 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.dom.ls;
+
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.Reader;
+
+/**
+ * Character stream wrapper.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ * @author <a href='mailto:mark@klomp.org'>Mark Wielaard</a>
+ */
+public class ReaderInputStream
+ extends InputStream
+{
+
+ private Reader reader;
+ private String encoding;
+
+ // Holds extra spillover data if necessary
+ private byte extra[];
+ private int pos;
+
+ private byte extra_marked[];
+ private int pos_marked;
+
+ public ReaderInputStream(Reader reader)
+ {
+ this.reader = reader;
+ this.encoding = "UTF-8";
+ }
+
+ void setEncoding(String encoding)
+ {
+ this.encoding = encoding;
+ }
+
+ public int read()
+ throws IOException
+ {
+ if (extra != null)
+ {
+ int result = extra[pos];
+ pos++;
+ if (pos >= extra.length)
+ {
+ extra = null;
+ }
+ return result;
+ }
+ return reader.read();
+ }
+
+ public int read(byte[] b)
+ throws IOException
+ {
+ return read(b, 0, b.length);
+ }
+
+ public int read(byte[] b, int off, int len)
+ throws IOException
+ {
+ if (len == 0)
+ {
+ return 0;
+ }
+
+ if (extra != null)
+ {
+ int available = extra.length - pos;
+ int l = available < len ? available : len;
+ System.arraycopy(extra, 0, b, off, l);
+ pos += l;
+ if (pos >= extra.length)
+ {
+ extra = null;
+ }
+ return l;
+ }
+
+ char[] c = new char[len];
+ int l = reader.read(c, 0, len);
+ if (l == -1)
+ {
+ return -1;
+ }
+
+ String s = new String(c, 0, l);
+ byte[] d = s.getBytes(encoding);
+
+ int available = d.length;
+ int more = d.length - len;
+ if (more > 0)
+ {
+ extra = new byte[more];
+ pos = 0;
+ System.arraycopy(d, len, extra, 0, more);
+ available -= more;
+ }
+
+ System.arraycopy(d, 0, b, off, available);
+ return available;
+ }
+
+ public void close()
+ throws IOException
+ {
+ reader.close();
+ }
+
+ public boolean markSupported()
+ {
+ return reader.markSupported();
+ }
+
+ public void mark(int limit)
+ {
+ if (extra != null)
+ {
+ extra_marked = new byte[extra.length];
+ System.arraycopy(extra, 0, extra_marked, 0, extra.length);
+ pos_marked = pos;
+ }
+ else
+ {
+ extra_marked = null;
+ }
+
+ try
+ {
+ // Note that this might be a bit more than asked for.
+ // Because we might also have the extra_marked bytes.
+ // That is fine (and necessary for reset() to work).
+ reader.mark(limit);
+ }
+ catch (IOException ioe)
+ {
+ throw new RuntimeException(ioe);
+ }
+ }
+
+ public void reset()
+ throws IOException
+ {
+ extra = extra_marked;
+ pos = pos_marked;
+ extra_marked = null;
+
+ reader.reset();
+ }
+
+ public long skip(long n)
+ throws IOException
+ {
+ long done = 0;
+ if (extra != null)
+ {
+ int available = extra.length - pos;
+ done = available < n ? available : n;
+ pos += done;
+ if (pos >= extra.length)
+ {
+ extra = null;
+ }
+ }
+
+ n -= done;
+ if (n > 0)
+ {
+ return reader.skip(n) + done;
+ }
+ else
+ {
+ return done;
+ }
+ }
+
+ /**
+ * Returns conservative number of bytes available without blocking.
+ * Actual number of bytes that can be read without blocking might
+ * be (much) bigger.
+ */
+ public int available()
+ throws IOException
+ {
+ if (extra != null)
+ {
+ return pos - extra.length;
+ }
+
+ return reader.ready() ? 1 : 0;
+ }
+
+ public String toString()
+ {
+ return getClass().getName() + "[" + reader + ", " + encoding + "]";
+ }
+
+}
+
diff --git a/libjava/gnu/xml/dom/ls/SAXEventSink.java b/libjava/gnu/xml/dom/ls/SAXEventSink.java
new file mode 100644
index 0000000..24f6ccc
--- /dev/null
+++ b/libjava/gnu/xml/dom/ls/SAXEventSink.java
@@ -0,0 +1,533 @@
+/* SAXEventSink.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.dom.ls;
+
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import javax.xml.XMLConstants;
+import org.w3c.dom.Attr;
+import org.w3c.dom.DocumentType;
+import org.w3c.dom.Element;
+import org.w3c.dom.Entity;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.Text;
+import org.xml.sax.Attributes;
+import org.xml.sax.DTDHandler;
+import org.xml.sax.Locator;
+import org.xml.sax.SAXException;
+import org.xml.sax.ext.Attributes2;
+import org.xml.sax.ext.DeclHandler;
+import org.xml.sax.ext.LexicalHandler;
+import gnu.xml.aelfred2.ContentHandler2;
+import gnu.xml.dom.DomAttr;
+import gnu.xml.dom.DomDocument;
+import gnu.xml.dom.DomDoctype;
+
+/**
+ * A SAX content and lexical handler used to construct a DOM document.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+class SAXEventSink
+ implements ContentHandler2, LexicalHandler, DTDHandler, DeclHandler
+{
+
+ private static final String XMLNS_URI = XMLConstants.XMLNS_ATTRIBUTE_NS_URI;
+ private static final String XMLNS_PREFIX = XMLConstants.XMLNS_ATTRIBUTE;
+
+ boolean namespaceAware;
+ boolean ignoreWhitespace;
+ boolean expandEntityReferences;
+ boolean ignoreComments;
+ boolean coalescing;
+
+ DomDocument doc; // document being constructed
+ Node ctx; // current context (parent node)
+ LinkedList entityCtx; // entity context
+ List pending; // namespace nodes waiting for a declaring element
+ Locator locator;
+ boolean inCDATA;
+ boolean inDTD;
+ boolean interrupted;
+
+ void interrupt()
+ {
+ interrupted = true;
+ }
+
+ // -- ContentHandler2 --
+
+ public void setDocumentLocator(Locator locator)
+ {
+ this.locator = locator;
+ }
+
+ public void startDocument()
+ throws SAXException
+ {
+ if (namespaceAware)
+ {
+ pending = new LinkedList();
+ }
+ doc = new DomDocument();
+ doc.setStrictErrorChecking(false);
+ doc.setBuilding(true);
+ ctx = doc;
+ }
+
+ public void xmlDecl(String version, String encoding, boolean standalone,
+ String inputEncoding)
+ throws SAXException
+ {
+ if (interrupted)
+ {
+ return;
+ }
+ doc.setXmlVersion(version);
+ doc.setXmlEncoding(encoding);
+ doc.setXmlStandalone(standalone);
+ doc.setInputEncoding(inputEncoding);
+ }
+
+ public void endDocument()
+ throws SAXException
+ {
+ doc.setStrictErrorChecking(true);
+ doc.setBuilding(false);
+ DomDoctype doctype = (DomDoctype) doc.getDoctype();
+ if (doctype != null)
+ {
+ doctype.makeReadonly();
+ }
+ ctx = null;
+ locator = null;
+ }
+
+ public void startPrefixMapping(String prefix, String uri)
+ throws SAXException
+ {
+ if (namespaceAware)
+ {
+ String nsName = (prefix != null && prefix.length() > 0) ?
+ XMLNS_PREFIX + ":" + prefix : XMLNS_PREFIX;
+ DomAttr ns = (DomAttr) doc.createAttributeNS(XMLNS_URI, nsName);
+ ns.setNodeValue(uri);
+ if (ctx.getNodeType() == Node.ATTRIBUTE_NODE)
+ {
+ // Add to owner element
+ Node target = ((Attr) ctx).getOwnerElement();
+ target.getAttributes().setNamedItemNS(ns);
+ }
+ else
+ {
+ // Add to pending list; namespace node will be inserted when
+ // element is seen
+ pending.add(ns);
+ }
+ }
+ }
+
+ public void endPrefixMapping(String prefix)
+ throws SAXException
+ {
+ }
+
+ public void startElement(String uri, String localName, String qName,
+ Attributes atts)
+ throws SAXException
+ {
+ if (interrupted)
+ {
+ return;
+ }
+ Element element = createElement(uri, localName, qName, atts);
+ // add element to context
+ ctx.appendChild(element);
+ ctx = element;
+ }
+
+ protected Element createElement(String uri, String localName, String qName,
+ Attributes atts)
+ throws SAXException
+ {
+ // create element node
+ Element element = namespaceAware ?
+ doc.createElementNS(uri, qName) :
+ doc.createElement(qName);
+ NamedNodeMap attrs = element.getAttributes();
+ if (namespaceAware && !pending.isEmpty())
+ {
+ // add pending namespace nodes
+ for (Iterator i = pending.iterator(); i.hasNext(); )
+ {
+ Node ns = (Node) i.next();
+ attrs.setNamedItemNS(ns);
+ }
+ pending.clear();
+ }
+ // add attributes
+ int len = atts.getLength();
+ for (int i = 0; i < len; i++)
+ {
+ // create attribute
+ Attr attr = createAttr(atts, i);
+ if (attr != null)
+ {
+ // add attribute to element
+ if (namespaceAware)
+ {
+ attrs.setNamedItemNS(attr);
+ }
+ else
+ {
+ attrs.setNamedItem(attr);
+ }
+ }
+ }
+ return element;
+ }
+
+ protected Attr createAttr(Attributes atts, int index)
+ {
+ DomAttr attr;
+ if (namespaceAware)
+ {
+ String a_uri = atts.getURI(index);
+ String a_qName = atts.getQName(index);
+ attr = (DomAttr) doc.createAttributeNS(a_uri, a_qName);
+ }
+ else
+ {
+ String a_qName = atts.getQName(index);
+ attr = (DomAttr) doc.createAttribute(a_qName);
+ }
+ attr.setNodeValue(atts.getValue(index));
+ if (atts instanceof Attributes2)
+ {
+ Attributes2 atts2 = (Attributes2) atts;
+ // TODO attr.setDeclared(atts2.isDeclared(index));
+ attr.setSpecified(atts2.isSpecified(index));
+ }
+ return attr;
+ }
+
+ public void endElement(String uri, String localName, String qName)
+ throws SAXException
+ {
+ if (interrupted)
+ {
+ return;
+ }
+ if (namespaceAware)
+ {
+ pending.clear();
+ }
+ ctx = ctx.getParentNode();
+ }
+
+ public void characters(char[] c, int off, int len)
+ throws SAXException
+ {
+ if (interrupted)
+ {
+ return;
+ }
+ ctx.appendChild(createText(c, off, len));
+ }
+
+ protected Text createText(char[] c, int off, int len)
+ throws SAXException
+ {
+ Text text = (inCDATA && !coalescing) ?
+ doc.createCDATASection(new String(c, off, len)) :
+ doc.createTextNode(new String(c, off, len));
+ return text;
+ }
+
+ public void ignorableWhitespace(char[] c, int off, int len)
+ throws SAXException
+ {
+ if (interrupted)
+ {
+ return;
+ }
+ if (!ignoreWhitespace)
+ {
+ characters(c, off, len);
+ }
+ }
+
+ public void processingInstruction(String target, String data)
+ throws SAXException
+ {
+ if (interrupted)
+ {
+ return;
+ }
+ if (!inDTD)
+ {
+ Node pi = createProcessingInstruction(target, data);
+ ctx.appendChild(pi);
+ }
+ }
+
+ protected Node createProcessingInstruction(String target, String data)
+ {
+ return doc.createProcessingInstruction(target, data);
+ }
+
+ public void skippedEntity(String name)
+ throws SAXException
+ {
+ // This callback is totally pointless
+ }
+
+ // -- LexicalHandler --
+
+ public void startDTD(String name, String publicId, String systemId)
+ throws SAXException
+ {
+ if (interrupted)
+ {
+ return;
+ }
+ Node doctype = createDocumentType(name, publicId, systemId);
+ doc.appendChild(doctype);
+ ctx = doctype;
+ inDTD = true;
+ }
+
+ protected Node createDocumentType(String name, String publicId,
+ String systemId)
+ {
+ return new DomDoctype(doc, name, publicId, systemId);
+ }
+
+ public void endDTD()
+ throws SAXException
+ {
+ if (interrupted)
+ {
+ return;
+ }
+ inDTD = false;
+ ctx = ctx.getParentNode();
+ }
+
+ public void startEntity(String name)
+ throws SAXException
+ {
+ DocumentType doctype = doc.getDoctype();
+ if (doctype == null)
+ {
+ throw new SAXException("SAX parser error: " +
+ "reference to entity in undeclared doctype");
+ }
+ if ("[dtd]".equals(name) || name.charAt(0) == '%')
+ {
+ // Ignore DTD and parameter entities
+ ctx = doctype;
+ return;
+ }
+ // Get entity
+ NamedNodeMap entities = doctype.getEntities();
+ Entity entity = (Entity) entities.getNamedItem(name);
+ if (entity == null)
+ {
+ throw new SAXException("SAX parser error: " +
+ "reference to undeclared entity: " + name);
+ }
+ pushEntity(entity);
+ }
+
+ public void endEntity(String name)
+ throws SAXException
+ {
+ if ("[dtd]".equals(name) || name.charAt(0) == '%')
+ {
+ // Ignore DTD and parameter entities
+ return;
+ }
+ Entity entity = popEntity();
+ // TODO resolve external entities to ensure that entity has content
+ if (expandEntityReferences)
+ {
+ // Get entity content
+ for (Node child = entity.getFirstChild(); child != null;
+ child = child.getNextSibling())
+ {
+ ctx.appendChild(child);
+ }
+ }
+ else
+ {
+ Node entityReference = doc.createEntityReference(name);
+ ctx.appendChild(entityReference);
+ }
+ }
+
+ void pushEntity(Node entity)
+ {
+ if (entityCtx == null)
+ {
+ entityCtx = new LinkedList();
+ }
+ entityCtx.addLast(ctx);
+ ctx = entity;
+ }
+
+ Entity popEntity()
+ {
+ Entity ret = (Entity) ctx;
+ ctx = (Node) entityCtx.removeLast();
+ return ret;
+ }
+
+ public void startCDATA()
+ throws SAXException
+ {
+ inCDATA = true;
+ }
+
+ public void endCDATA()
+ throws SAXException
+ {
+ inCDATA = false;
+ }
+
+ public void comment(char[] c, int off, int len)
+ throws SAXException
+ {
+ if (interrupted)
+ {
+ return;
+ }
+ if (!inDTD)
+ {
+ Node comment = createComment(c, off, len);
+ ctx.appendChild(comment);
+ }
+ }
+
+ protected Node createComment(char[] c, int off, int len)
+ {
+ return doc.createComment(new String(c, off, len));
+ }
+
+ // -- DTDHandler --
+
+ public void notationDecl(String name, String publicId, String systemId)
+ throws SAXException
+ {
+ if (interrupted)
+ {
+ return;
+ }
+ DomDoctype doctype = (DomDoctype) ctx;
+ doctype.declareNotation(name, publicId, systemId);
+ }
+
+ public void unparsedEntityDecl(String name, String publicId, String systemId,
+ String notationName)
+ throws SAXException
+ {
+ if (interrupted)
+ {
+ return;
+ }
+ DomDoctype doctype = (DomDoctype) ctx;
+ Entity entity = doctype.declareEntity(name, publicId, systemId,
+ notationName);
+ }
+
+ // -- DeclHandler --
+
+ public void elementDecl(String name, String model)
+ throws SAXException
+ {
+ if (interrupted)
+ {
+ return;
+ }
+ DomDoctype doctype = (DomDoctype) ctx;
+ doctype.elementDecl(name, model);
+ }
+
+ public void attributeDecl(String eName, String aName, String type,
+ String mode, String value)
+ throws SAXException
+ {
+ if (interrupted)
+ {
+ return;
+ }
+ DomDoctype doctype = (DomDoctype) ctx;
+ doctype.attributeDecl(eName, aName, type, mode, value);
+ }
+
+ public void internalEntityDecl(String name, String value)
+ throws SAXException
+ {
+ if (interrupted)
+ {
+ return;
+ }
+ DomDoctype doctype = (DomDoctype) ctx;
+ Entity entity = doctype.declareEntity(name, null, null, null);
+ if (entity != null)
+ {
+ Node text = doc.createTextNode(value);
+ entity.appendChild(text);
+ }
+ }
+
+ public void externalEntityDecl(String name, String publicId, String systemId)
+ throws SAXException
+ {
+ if (interrupted)
+ {
+ return;
+ }
+ DomDoctype doctype = (DomDoctype) ctx;
+ Entity entity = doctype.declareEntity(name, publicId, systemId, null);
+ }
+
+}
+
diff --git a/libjava/gnu/xml/dom/ls/WriterOutputStream.java b/libjava/gnu/xml/dom/ls/WriterOutputStream.java
new file mode 100644
index 0000000..7eddb2e
--- /dev/null
+++ b/libjava/gnu/xml/dom/ls/WriterOutputStream.java
@@ -0,0 +1,98 @@
+/* WriterOutputStream.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.dom.ls;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.Writer;
+
+/**
+ * Character stream wrapper.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+public class WriterOutputStream
+ extends OutputStream
+{
+
+ private Writer writer;
+ private String encoding;
+
+ public WriterOutputStream(Writer writer)
+ {
+ this.writer = writer;
+ this.encoding = "UTF-8";
+ }
+
+ void setEncoding(String encoding)
+ {
+ this.encoding = encoding;
+ }
+
+ public void write(int c)
+ throws IOException
+ {
+ writer.write(c);
+ }
+
+ public void write(byte[] b)
+ throws IOException
+ {
+ write(b, 0, b.length);
+ }
+
+ public void write(byte[] b, int off, int len)
+ throws IOException
+ {
+ writer.write(new String(b, off, len, encoding));
+ }
+
+ public void close()
+ throws IOException
+ {
+ writer.close();
+ }
+
+ public void flush()
+ throws IOException
+ {
+ writer.flush();
+ }
+
+}
+
diff --git a/libjava/gnu/xml/dom/package.html b/libjava/gnu/xml/dom/package.html
new file mode 100644
index 0000000..fbc864a
--- /dev/null
+++ b/libjava/gnu/xml/dom/package.html
@@ -0,0 +1,273 @@
+<html>
+<body>
+
+<p>
+This is a Free Software DOM Level 3 implementation, supporting these features:
+<ul>
+<li>"XML"</li>
+<li>"Events"</li>
+<li>"MutationEvents"</li>
+<li>"HTMLEvents" (won't generate them though)</li>
+<li>"UIEvents" (also won't generate them)</li>
+<li>"USER-Events" (a conformant extension)</li>
+<li>"Traversal" (optional)</li>
+<li>"XPath"</li>
+<li>"LS" and "LS-Async"</li>
+</ul>
+It is intended to be a reasonable base both for
+experimentation and supporting additional DOM modules as clean layers.
+</p>
+
+<p>
+Note that while DOM does not specify its behavior in the
+face of concurrent access, this implementation does.
+Specifically:
+<ul>
+<li>If only one thread at a time accesses a Document,
+of if several threads cooperate for read-only access,
+then no concurrency conflicts will occur.</li>
+<li>If several threads mutate a given document
+(or send events using it) at the same time,
+there is currently no guarantee that
+they won't interfere with each other.</li>
+</ul>
+</p>
+
+<h3>Design Goals</h3>
+
+<p>
+A number of DOM implementations are available in Java, including
+commercial ones from Sun, IBM, Oracle, and DataChannel as well as
+noncommercial ones from Docuverse, OpenXML, and Silfide. Why have
+another? Some of the goals of this version:
+</p>
+
+<ul>
+<li>Advanced DOM support. This was the first generally available
+implementation of DOM Level 2 in Java, and one of the first Level 3
+and XPath implementations.</li>
+
+<li> Free Software. This one is distributed under the GPL (with
+"library exception") so it can be used with a different class of
+application.</li>
+
+<li>Second implementation syndrome. I can do it simpler this time
+around ... and heck, writing it only takes a bit over a day once you
+know your way around.</li>
+
+<li>Sanity check the then-current Last Call DOM draft. Best to find
+bugs early, when they're relatively fixable. Yes, bugs were found.</li>
+
+<li>Modularity. Most of the implementations mentioned above are part
+of huge packages; take all (including bugs, of which some have far
+too many), or take nothing. I prefer a menu approach, when possible.
+This code is standalone, not beholden to any particular parser or XSL
+or XPath code.</li>
+
+<li>OK, I'm a hacker, I like to write code.</li>
+</ul>
+
+<p>
+This also works with the GNU Compiler for Java (GCJ). GCJ promises
+to be quite the environment for programming Java, both directly and from
+C++ using the new CNI interfaces (which really use C++, unlike JNI). </p>
+
+
+<h3>Open Issues</h3>
+
+<p>At this writing:</p>
+<ul>
+<li>See below for some restrictions on the mutation event
+support ... some events aren't reported (and likely won't be).</li>
+
+<li>More testing and conformance work is needed.</li>
+
+<li>We need an XML Schema validator (actually we need validation in the DOM
+full stop).</li>
+</ul>
+
+<p>
+I ran a profiler a few times and remove some of the performance hotspots,
+but it's not tuned. Reporting mutation events, in particular, is
+rather costly -- it started at about a 40% penalty for appendNode calls,
+I've got it down around 12%, but it'll be hard to shrink it much further.
+The overall code size is relatively small, though you may want to be rid of
+many of the unused DOM interface classes (HTML, CSS, and so on).
+</p>
+
+
+<h2><a name="features">Features of this Package</a></h2>
+
+<p> Starting with DOM Level 2, you can really see that DOM is constructed
+as a bunch of optional modules around a core of either XML or HTML
+functionality. Different implementations will support different optional
+modules. This implementation provides a set of features that should be
+useful if you're not depending on the HTML functionality (lots of convenience
+functions that mostly don't buy much except API surface area) and user
+interface support. That is, browsers will want more -- but what they
+need should be cleanly layered over what's already here. </p>
+
+<h3> Core Feature Set: "XML" </h3>
+
+<p> This DOM implementation supports the "XML" feature set, which basically
+gets you four things over the bare core (which you're officially not supposed
+to implement except in conjunction with the "XML" or "HTML" feature). In
+order of decreasing utility, those four things are: </p> <ol>
+
+ <li> ProcessingInstruction nodes. These are probably the most
+ valuable thing. Handy little buggers, in part because all the APIs
+ you need to use them are provided, and they're designed to let you
+ escape XML document structure rules in controlled ways.</li>
+
+ <li> CDATASection nodes. These are of of limited utility since CDATA
+ is just text that prints funny. These are of use to some sorts of
+ applications, though I encourage folk to not use them. </li>
+
+ <li> DocumentType nodes, and associated Notation and Entity nodes.
+ These appear to be useless. Briefly, these "Type" nodes expose no
+ typing information. They're only really usable to expose some lexical
+ structure that almost every application needs to ignore. (XML editors
+ might like to see them, but they need true typing information much more.)
+ I strongly encourage people not to use these. </li>
+
+ <li> EntityReference nodes can show up. These are actively annoying,
+ since they add an extra level of hierarchy, are the cause of most of
+ the complexity in attribute values, and their contents are immutable.
+ Avoid these.</li>
+
+ </ol>
+
+<h3> Optional Feature Sets: "Events", and friends </h3>
+
+<p> Events may be one of the more interesting new features in Level 2.
+This package provides the core feature set and exposes mutation events.
+No gooey events though; if you want that, write a layered implementation! </p>
+
+<p> Three mutation events aren't currently generated:</p> <ul>
+
+ <li> <em>DOMSubtreeModified</em> is poorly specified. Think of this
+ as generating one such event around the time of finalization, which
+ is a fully conformant implementation. This implementation is exactly
+ as useful as that one. </li>
+
+ <li> <em>DOMNodeRemovedFromDocument</em> and
+ <em>DOMNodeInsertedIntoDocument</em> are supposed to get sent to
+ every node in a subtree that gets removed or inserted (respectively).
+ This can be <em>extremely costly</em>, and the removal and insertion
+ processing is already significantly slower due to event reporting.
+ It's much easier, and more efficient, to have a listener higher in the
+ tree watch removal and insertion events through the bubbling or capture
+ mechanisms, than it is to watch for these two events.</li>
+
+ </ul>
+
+<p> In addition, certain kinds of attribute modification aren't reported.
+A fix is known, but it couldn't report the previous value of the attribute.
+More work could fix all of this (as well as reduce the generally high cost
+of childful attributes), but that's not been done yet. </p>
+
+<p> Also, note that it is a <em>Bad Thing&#153;</em> to have the listener
+for a mutation event change the ancestry for the target of that event.
+Or to prevent mutation events from bubbling to where they're needed.
+Just don't do those, OK? </p>
+
+<p> As an experimental feature (named "USER-Events"), you can provide
+your own "user" events. Just name them anything starting with "USER-"
+and you're set. Dispatch them through, bubbling, capturing, or what
+ever takes your fancy. One important thing you can't currently do is
+pass any data (like an object) with those events. Maybe later there
+will be a "UserEvent" interface letting you get some substantial use
+out of this mechanism even if you're not "inside" of a DOM package.</p>
+
+<p> You can create and send HTML events. Ditto UIEvents. Since DOM
+doesn't require a UI, it's the UI's job to send them; perhaps that's
+part of your application. </p>
+
+<p><em>This package may be built without the ability to report mutation
+events, gaining a significant speedup in DOM construction time. However,
+if that is done then certain other features -- notably node iterators
+and getElementsByTagname -- will not be available.</em>
+
+
+<h3> Optional Feature: "Traversal" </h3>
+
+<p> Each DOM node has all you need to walk to everything connected
+to that node. Lightweight, efficient utilities are easily layered on
+top of just the core APIs. </p>
+
+<p> Traversal APIs are an optional part of DOM Level 2, providing
+a not-so-lightweight way to walk over DOM trees, if your application
+didn't already have such utilities for use with data represented via
+DOM. Implementing this helped debug the (optional) event and mutation
+event subsystems, so it's provided here. </p>
+
+<p> At this writing, the "TreeWalker" interface isn't implemented. </p>
+
+
+
+<h2><a name='avoid'>DOM Functionality to Avoid</a></h2>
+
+<p> For what appear to be a combination of historical and "committee
+logic" reasons, DOM has a number of <em>features which I strongly advise
+you to avoid using</em> in your library and application code. These
+include the following types of DOM nodes; see the documentation for the
+implementation class for more information: <ul>
+
+ <li> CDATASection
+ (<a href='DomCDATA.html'>DomCDATA</a> class)
+ ... use normal Text nodes instead, so you don't have to make
+ every algorithm recognize multiple types of character data
+
+ <li> DocumentType
+ (<a href='DomDoctype.html'>DomDocType</a> class)
+ ... if this held actual typing information, it might be useful
+
+ <li> Entity
+ (<a href='DomEntity.html'>DomEntity</a> class)
+ ... neither parsed nor unparsed entities work well in DOM; it
+ won't even tell you which attributes identify unparsed entities
+
+ <li> EntityReference
+ (<a href='DomEntityReference.html'>DomEntityReference</a> class)
+ ... permitted implementation variances are extreme, all children
+ are readonly, and these can interact poorly with namespaces
+
+ <li> Notation
+ (<a href='DomNotation.html'>DomNotation</a> class)
+ ... only really usable with unparsed entities (which aren't well
+ supported; see above) or perhaps with PIs after the DTD, not with
+ NOTATION attributes
+
+ </ul>
+
+<p> If you really need to use unparsed entities or notations, use SAX;
+it offers better support for all DTD-related functionality.
+It also exposes actual
+document typing information (such as element content models).</p>
+
+<p> Also, when accessing attribute values, use methods that provide their
+values as single strings, rather than those which expose value substructure
+(Text and EntityReference nodes). (See the <a href='DomAttr.html'>DomAttr</a>
+documentation for more information.) </p>
+
+<p> Note that many of these features were provided as partial support for
+editor functionality (including the incomplete DTD access). Full editor
+functionality requires access to potentially malformed lexical structure,
+at the level of unparsed tokens and below. Access at such levels is so
+complex that using it in non-editor applications sacrifices all the
+benefits of XML; editor aplications need extremely specialized APIs. </p>
+
+<p> (This isn't a slam against DTDs, note; only against the broken support
+for them in DOM. Even despite inclusion of some dubious SGML legacy features
+such as notations and unparsed entities,
+and the ongoing proliferation of alternative schema and validation tools,
+DTDs are still the most widely adopted tool
+to constrain XML document structure.
+Alternative schemes generally focus on data transfer style
+applications; open document architectures comparable to
+DocBook 4.0 don't yet exist in the schema world.
+Feel free to use DTDs; just don't expect DOM to help you.) </p>
+
+</body>
+</html>
+
diff --git a/libjava/gnu/xml/pipeline/CallFilter.java b/libjava/gnu/xml/pipeline/CallFilter.java
new file mode 100644
index 0000000..0d858599
--- /dev/null
+++ b/libjava/gnu/xml/pipeline/CallFilter.java
@@ -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);
+ }
+ }
+ }
+}
diff --git a/libjava/gnu/xml/pipeline/DomConsumer.java b/libjava/gnu/xml/pipeline/DomConsumer.java
new file mode 100644
index 0000000..17fdeeb
--- /dev/null
+++ b/libjava/gnu/xml/pipeline/DomConsumer.java
@@ -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;
+ }
+ }
+}
diff --git a/libjava/gnu/xml/pipeline/EventConsumer.java b/libjava/gnu/xml/pipeline/EventConsumer.java
new file mode 100644
index 0000000..5f97373
--- /dev/null
+++ b/libjava/gnu/xml/pipeline/EventConsumer.java
@@ -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);
+}
diff --git a/libjava/gnu/xml/pipeline/EventFilter.java b/libjava/gnu/xml/pipeline/EventFilter.java
new file mode 100644
index 0000000..8587808
--- /dev/null
+++ b/libjava/gnu/xml/pipeline/EventFilter.java
@@ -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);
+ }
+}
diff --git a/libjava/gnu/xml/pipeline/LinkFilter.java b/libjava/gnu/xml/pipeline/LinkFilter.java
new file mode 100644
index 0000000..28a4501
--- /dev/null
+++ b/libjava/gnu/xml/pipeline/LinkFilter.java
@@ -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 ();
+ }
+}
diff --git a/libjava/gnu/xml/pipeline/NSFilter.java b/libjava/gnu/xml/pipeline/NSFilter.java
new file mode 100644
index 0000000..9e8a643
--- /dev/null
+++ b/libjava/gnu/xml/pipeline/NSFilter.java
@@ -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 ();
+ }
+}
diff --git a/libjava/gnu/xml/pipeline/PipelineFactory.java b/libjava/gnu/xml/pipeline/PipelineFactory.java
new file mode 100644
index 0000000..5edca73
--- /dev/null
+++ b/libjava/gnu/xml/pipeline/PipelineFactory.java
@@ -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 ();
+ }
+ }
+/**/
+
+}
diff --git a/libjava/gnu/xml/pipeline/TeeConsumer.java b/libjava/gnu/xml/pipeline/TeeConsumer.java
new file mode 100644
index 0000000..6d3227e
--- /dev/null
+++ b/libjava/gnu/xml/pipeline/TeeConsumer.java
@@ -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 ();
+ }
+}
diff --git a/libjava/gnu/xml/pipeline/TextConsumer.java b/libjava/gnu/xml/pipeline/TextConsumer.java
new file mode 100644
index 0000000..1039b3b
--- /dev/null
+++ b/libjava/gnu/xml/pipeline/TextConsumer.java
@@ -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);
+ }
+}
diff --git a/libjava/gnu/xml/pipeline/ValidationConsumer.java b/libjava/gnu/xml/pipeline/ValidationConsumer.java
new file mode 100644
index 0000000..e73c0ff
--- /dev/null
+++ b/libjava/gnu/xml/pipeline/ValidationConsumer.java
@@ -0,0 +1,1922 @@
+/* ValidationConsumer.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.util.EmptyStackException;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Stack;
+import java.util.StringTokenizer;
+import java.util.Vector;
+
+import org.xml.sax.*;
+import org.xml.sax.ext.*;
+import org.xml.sax.helpers.XMLReaderFactory;
+
+
+/**
+ * This class checks SAX2 events to report validity errors; it works as
+ * both a filter and a terminus on an event pipeline. It relies on the
+ * producer of SAX events to: </p> <ol>
+ *
+ * <li> Conform to the specification of a non-validating XML parser that
+ * reads all external entities, reported using SAX2 events. </li>
+ *
+ * <li> Report ignorable whitespace as such (through the ContentHandler
+ * interface). This is, strictly speaking, optional for nonvalidating
+ * XML processors. </li>
+ *
+ * <li> Make SAX2 DeclHandler callbacks, with default
+ * attribute values already normalized (and without "&lt;").</li>
+ *
+ * <li> Make SAX2 LexicalHandler startDTD() and endDTD ()
+ * callbacks. </li>
+ *
+ * <li> Act as if the <em>(URI)/namespace-prefixes</em> property were
+ * set to true, by providing XML 1.0 names and all <code>xmlns*</code>
+ * attributes (rather than omitting either or both). </li>
+ *
+ * </ol>
+ *
+ * <p> At this writing, the major SAX2 parsers (such as &AElig;lfred2,
+ * Crimson, and Xerces) meet these requirements, and this validation
+ * module is used by the optional &AElig;lfred2 validation support.
+ * </p>
+ *
+ * <p> Note that because this is a layered validator, it has to duplicate some
+ * work that the parser is doing; there are also other cost to layering.
+ * However, <em>because of layering it doesn't need a parser</em> in order
+ * to work! You can use it with anything that generates SAX events, such
+ * as an application component that wants to detect invalid content in
+ * a changed area without validating an entire document, or which wants to
+ * ensure that it doesn't write invalid data to a communications partner.</p>
+ *
+ * <p> Also, note that because this is a layered validator, the line numbers
+ * reported for some errors may seem strange. For example, if an element does
+ * not permit character content, the validator
+ * will use the locator provided to it.
+ * That might reflect the last character of a <em>characters</em> event
+ * callback, rather than the first non-whitespace character. </p>
+ *
+ * <hr />
+ *
+ * <!--
+ * <p> Of interest is the fact that unlike most currently known XML validators,
+ * this one can report some cases of non-determinism in element content models.
+ * It is a compile-time option, enabled by default. This will only report
+ * such XML errors if they relate to content actually appearing in a document;
+ * content models aren't aggressively scanned for non-deterministic structure.
+ * Documents which trigger such non-deterministic transitions may be handled
+ * differently by different validating parsers, without losing conformance
+ * to the XML specification. </p>
+ * -->
+ *
+ * <p> Current limitations of the validation performed are in roughly three
+ * categories. </p>
+ *
+ * <p> The first category represents constraints which demand violations
+ * of software layering: exposing lexical details, one of the first things
+ * that <em>application</em> programming interfaces (APIs) hide. These
+ * invariably relate to XML entity handling, and to historical oddities
+ * of the XML validation semantics. Curiously,
+ * recent (Autumn 1999) conformance testing showed that these constraints are
+ * among those handled worst by existing XML validating parsers. Arguments
+ * have been made that each of these VCs should be turned into WFCs (most
+ * of them) or discarded (popular for the standalone declaration); in short,
+ * that these are bugs in the XML specification (not all via SGML): </p><ul>
+ *
+ * <li> The <em>Proper Declaration/PE Nesting</em> and
+ * <em>Proper Group/PE Nesting</em> VCs can't be tested because they
+ * require access to particularly low level lexical level information.
+ * In essence, the reason XML isn't a simple thing to parse is that
+ * it's not a context free grammar, and these constraints elevate that
+ * SGML-derived context sensitivity to the level of a semantic rule.
+ *
+ * <li> The <em>Standalone Document Declaration</em> VC can't be
+ * tested. This is for two reasons. First, this flag isn't made
+ * available through SAX2. Second, it also requires breaking that
+ * lexical layering boundary. (If you ever wondered why classes
+ * in compiler construction or language design barely mention the
+ * existence of context-sensitive grammars, it's because of messy
+ * issues like these.)
+ *
+ * <li> The <em>Entity Declared</em> VC can't be tested, because it
+ * also requires breaking that lexical layering boundary! There's also
+ * another issue: the VC wording (and seemingly intent) is ambiguous.
+ * (This is still true in the "Second edition" XML spec.)
+ * Since there is a WFC of the same name, everyone's life would be
+ * easier if references to undeclared parsed entities were always well
+ * formedness errors, regardless of whether they're parameter entities
+ * or not. (Note that nonvalidating parsers are not required
+ * to report all such well formedness errors if they don't read external
+ * parameter entities, although currently most XML parsers read them
+ * in an attempt to avoid problems from inconsistent parser behavior.)
+ *
+ * </ul>
+ *
+ * <p> The second category of limitations on this validation represent
+ * constraints associated with information that is not guaranteed to be
+ * available (or in one case, <em>is guaranteed not to be available</em>,
+ * through the SAX2 API: </p><ul>
+ *
+ * <li> The <em>Unique Element Type Declaration</em> VC may not be
+ * reportable, if the underlying parser happens not to expose
+ * multiple declarations. (&AElig;lfred2 reports these validity
+ * errors directly.)</li>
+ *
+ * <li> Similarly, the <em>Unique Notation Name</em> VC, added in the
+ * 14-January-2000 XML spec errata to restrict typing models used by
+ * elements, may not be reportable. (&AElig;lfred reports these
+ * validity errors directly.) </li>
+ *
+ * </ul>
+ *
+ * <p> A third category relates to ease of implementation. (Think of this
+ * as "bugs".) The most notable issue here is character handling. Rather
+ * than attempting to implement the voluminous character tables in the XML
+ * specification (Appendix B), Unicode rules are used directly from
+ * the java.lang.Character class. Recent JVMs have begun to diverge from
+ * the original specification for that class (Unicode 2.0), meaning that
+ * different JVMs may handle that aspect of conformance differently.
+ * </p>
+ *
+ * <p> Note that for some of the validity errors that SAX2 does not
+ * expose, a nonvalidating parser is permitted (by the XML specification)
+ * to report validity errors. When used with a parser that does so for
+ * the validity constraints mentioned above (or any other SAX2 event
+ * stream producer that does the same thing), overall conformance is
+ * substantially improved.
+ *
+ * @see gnu.xml.aelfred2.SAXDriver
+ * @see gnu.xml.aelfred2.XmlReader
+ *
+ * @author David Brownell
+ */
+public final class ValidationConsumer extends EventFilter
+{
+ // report error if we happen to notice a non-deterministic choice?
+ // we won't report buggy content models; just buggy instances
+ private static final boolean warnNonDeterministic = false;
+
+ // for tracking active content models
+ private String rootName;
+ private Stack contentStack = new Stack ();
+
+ // flags for "saved DTD" processing
+ private boolean disableDeclarations;
+ private boolean disableReset;
+
+ //
+ // most VCs get tested when we see element start tags. the per-element
+ // info (including attributes) recorded here duplicates that found inside
+ // many nonvalidating parsers, hence dual lookups etc ... that's why a
+ // layered validator isn't going to be as fast as a non-layered one.
+ //
+
+ // key = element name; value = ElementInfo
+ private Hashtable elements = new Hashtable ();
+
+ // some VCs relate to ID/IDREF/IDREFS attributes
+ // key = id; value = boolean true (defd) or false (refd)
+ private Hashtable ids = new Hashtable ();
+
+ // we just record declared notation and unparsed entity names.
+ // the implementation here is simple/slow; these features
+ // are seldom used, one hopes they'll wither away soon
+ private Vector notations = new Vector (5, 5);
+ private Vector nDeferred = new Vector (5, 5);
+ private Vector unparsed = new Vector (5, 5);
+ private Vector uDeferred = new Vector (5, 5);
+
+ // note: DocBk 3.1.7 XML defines over 2 dozen notations,
+ // used when defining unparsed entities for graphics
+ // (and maybe in other places)
+
+
+
+ /**
+ * Creates a pipeline terminus which consumes all events passed to
+ * it; this will report validity errors as if they were fatal errors,
+ * unless an error handler is assigned.
+ *
+ * @see #setErrorHandler
+ */
+ // constructor used by PipelineFactory
+ // ... and want one taking system ID of an external subset
+ public ValidationConsumer ()
+ {
+ this (null);
+ }
+
+ /**
+ * Creates a pipeline filter which reports validity errors and then
+ * passes events on to the next consumer if they were not fatal.
+ *
+ * @see #setErrorHandler
+ */
+ // constructor used by PipelineFactory
+ // ... and want one taking system ID of an external subset
+ // (which won't send declaration events)
+ public ValidationConsumer (EventConsumer next)
+ {
+ super (next);
+
+ setContentHandler (this);
+ setDTDHandler (this);
+ try { setProperty (DECL_HANDLER, this); }
+ catch (Exception e) { /* "can't happen" */ }
+ try { setProperty (LEXICAL_HANDLER, this); }
+ catch (Exception e) { /* "can't happen" */ }
+ }
+
+
+ private static final String fakeRootName
+ = ":Nobody:in:their_Right.Mind_would:use:this-name:1x:";
+
+ /**
+ * Creates a validation consumer which is preloaded with the DTD provided.
+ * It does this by constructing a document with that DTD, then parsing
+ * that document and recording its DTD declarations. Then it arranges
+ * not to modify that information.
+ *
+ * <p> The resulting validation consumer will only validate against
+ * the specified DTD, regardless of whether some other DTD is found
+ * in a document being parsed.
+ *
+ * @param rootName The name of the required root element; if this is
+ * null, any root element name will be accepted.
+ * @param publicId If non-null and there is a non-null systemId, this
+ * identifier provides an alternate access identifier for the DTD's
+ * external subset.
+ * @param systemId If non-null, this is a URI (normally URL) that
+ * may be used to access the DTD's external subset.
+ * @param internalSubset If non-null, holds literal markup declarations
+ * comprising the DTD's internal subset.
+ * @param resolver If non-null, this will be provided to the parser for
+ * use when resolving parameter entities (including any external subset).
+ * @param resolver If non-null, this will be provided to the parser for
+ * use when resolving parameter entities (including any external subset).
+ * @param minimalElement If non-null, a minimal valid document.
+ *
+ * @exception SAXNotSupportedException If the default SAX parser does
+ * not support the standard lexical or declaration handlers.
+ * @exception SAXParseException If the specified DTD has either
+ * well-formedness or validity errors
+ * @exception IOException If the specified DTD can't be read for
+ * some reason
+ */
+ public ValidationConsumer (
+ String rootName,
+ String publicId,
+ String systemId,
+ String internalSubset,
+ EntityResolver resolver,
+ String minimalDocument
+ ) throws SAXException, IOException
+ {
+ this (null);
+
+ disableReset = true;
+ if (rootName == null)
+ rootName = fakeRootName;
+
+ //
+ // Synthesize document with that DTD; is it possible to do
+ // better for the declaration of the root element?
+ //
+ // NOTE: can't use SAX2 to write internal subsets.
+ //
+ StringWriter writer = new StringWriter ();
+
+ writer.write ("<!DOCTYPE ");
+ writer.write (rootName);
+ if (systemId != null) {
+ writer.write ("\n ");
+ if (publicId != null) {
+ writer.write ("PUBLIC '");
+ writer.write (publicId);
+ writer.write ("'\n\t'");
+ } else
+ writer.write ("SYSTEM '");
+ writer.write (systemId);
+ writer.write ("'");
+ }
+ writer.write (" [ ");
+ if (rootName == fakeRootName) {
+ writer.write ("\n<!ELEMENT ");
+ writer.write (rootName);
+ writer.write (" EMPTY>");
+ }
+ if (internalSubset != null)
+ writer.write (internalSubset);
+ writer.write ("\n ]>");
+
+ if (minimalDocument != null) {
+ writer.write ("\n");
+ writer.write (minimalDocument);
+ writer.write ("\n");
+ } else {
+ writer.write (" <");
+ writer.write (rootName);
+ writer.write ("/>\n");
+ }
+ minimalDocument = writer.toString ();
+
+ //
+ // OK, load it
+ //
+ XMLReader producer;
+
+ producer = XMLReaderFactory.createXMLReader ();
+ bind (producer, this);
+
+ if (resolver != null)
+ producer.setEntityResolver (resolver);
+
+ InputSource in;
+
+ in = new InputSource (new StringReader (minimalDocument));
+ producer.parse (in);
+
+ disableDeclarations = true;
+ if (rootName == fakeRootName)
+ this.rootName = null;
+ }
+
+ private void resetState ()
+ {
+ if (!disableReset) {
+ rootName = null;
+ contentStack.removeAllElements ();
+ elements.clear ();
+ ids.clear ();
+
+ notations.removeAllElements ();
+ nDeferred.removeAllElements ();
+ unparsed.removeAllElements ();
+ uDeferred.removeAllElements ();
+ }
+ }
+
+
+ private void warning (String description)
+ throws SAXException
+ {
+ ErrorHandler errHandler = getErrorHandler ();
+ Locator locator = getDocumentLocator ();
+ SAXParseException err;
+
+ if (errHandler == null)
+ return;
+
+ if (locator == null)
+ err = new SAXParseException (description, null, null, -1, -1);
+ else
+ err = new SAXParseException (description, locator);
+ errHandler.warning (err);
+ }
+
+ // package private (for ChildrenRecognizer)
+ private void error (String description)
+ throws SAXException
+ {
+ ErrorHandler errHandler = getErrorHandler ();
+ Locator locator = getDocumentLocator ();
+ SAXParseException err;
+
+ if (locator == null)
+ err = new SAXParseException (description, null, null, -1, -1);
+ else
+ err = new SAXParseException (description, locator);
+ if (errHandler != null)
+ errHandler.error (err);
+ else // else we always treat it as fatal!
+ throw err;
+ }
+
+ private void fatalError (String description)
+ throws SAXException
+ {
+ ErrorHandler errHandler = getErrorHandler ();
+ Locator locator = getDocumentLocator ();
+ SAXParseException err;
+
+ if (locator != null)
+ err = new SAXParseException (description, locator);
+ else
+ err = new SAXParseException (description, null, null, -1, -1);
+ if (errHandler != null)
+ errHandler.fatalError (err);
+ // we always treat this as fatal, regardless of the handler
+ throw err;
+ }
+
+
+ private static boolean isExtender (char c)
+ {
+ // [88] Extender ::= ...
+ return c == 0x00b7 || c == 0x02d0 || c == 0x02d1 || c == 0x0387
+ || c == 0x0640 || c == 0x0e46 || c == 0x0ec6 || c == 0x3005
+ || (c >= 0x3031 && c <= 0x3035)
+ || (c >= 0x309d && c <= 0x309e)
+ || (c >= 0x30fc && c <= 0x30fe);
+ }
+
+
+ // use augmented Unicode rules, not full XML rules
+ private boolean isName (String name, String context, String id)
+ throws SAXException
+ {
+ char buf [] = name.toCharArray ();
+ boolean pass = true;
+
+ if (!Character.isUnicodeIdentifierStart (buf [0])
+ && ":_".indexOf (buf [0]) == -1)
+ pass = false;
+ else {
+ int max = buf.length;
+ for (int i = 1; pass && i < max; i++) {
+ char c = buf [i];
+ if (!Character.isUnicodeIdentifierPart (c)
+ && ":-_.".indexOf (c) == -1
+ && !isExtender (c))
+ pass = false;
+ }
+ }
+
+ if (!pass)
+ error ("In " + context + " for " + id
+ + ", '" + name + "' is not a name");
+ return pass; // true == OK
+ }
+
+ // use augmented Unicode rules, not full XML rules
+ private boolean isNmtoken (String nmtoken, String context, String id)
+ throws SAXException
+ {
+ char buf [] = nmtoken.toCharArray ();
+ boolean pass = true;
+ int max = buf.length;
+
+ // XXX make this share code with isName
+
+ for (int i = 0; pass && i < max; i++) {
+ char c = buf [i];
+ if (!Character.isUnicodeIdentifierPart (c)
+ && ":-_.".indexOf (c) == -1
+ && !isExtender (c))
+ pass = false;
+ }
+
+ if (!pass)
+ error ("In " + context + " for " + id
+ + ", '" + nmtoken + "' is not a name token");
+ return pass; // true == OK
+ }
+
+ private void checkEnumeration (String value, String type, String name)
+ throws SAXException
+ {
+ if (!hasMatch (value, type))
+ // VC: Enumeration
+ error ("Value '" + value
+ + "' for attribute '" + name
+ + "' is not permitted: " + type);
+ }
+
+ // used to test enumerated attributes and mixed content models
+ // package private
+ static boolean hasMatch (String value, String orList)
+ {
+ int len = value.length ();
+ int max = orList.length () - len;
+
+ for (int start = 0;
+ (start = orList.indexOf (value, start)) != -1;
+ start++) {
+ char c;
+
+ if (start > max)
+ break;
+ c = orList.charAt (start - 1);
+ if (c != '|' && c != '('/*)*/)
+ continue;
+ c = orList.charAt (start + len);
+ if (c != '|' && /*(*/ c != ')')
+ continue;
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * <b>LexicalHandler</b> Records the declaration of the root
+ * element, so it can be verified later.
+ * Passed to the next consumer, unless this one was
+ * preloaded with a particular DTD.
+ */
+ public void startDTD (String name, String publicId, String systemId)
+ throws SAXException
+ {
+ if (disableDeclarations)
+ return;
+
+ rootName = name;
+ super.startDTD (name, publicId, systemId);
+ }
+
+ /**
+ * <b>LexicalHandler</b> Verifies that all referenced notations
+ * and unparsed entities have been declared.
+ * Passed to the next consumer, unless this one was
+ * preloaded with a particular DTD.
+ */
+ public void endDTD ()
+ throws SAXException
+ {
+ if (disableDeclarations)
+ return;
+
+ // this is a convenient hook for end-of-dtd checks, but we
+ // could also trigger it in the first startElement call.
+ // locator info is more appropriate here though.
+
+ // VC: Notation Declared (NDATA can refer to them before decls,
+ // as can NOTATION attribute enumerations and defaults)
+ int length = nDeferred.size ();
+ for (int i = 0; i < length; i++) {
+ String notation = (String) nDeferred.elementAt (i);
+ if (!notations.contains (notation)) {
+ error ("A declaration referred to notation '" + notation
+ + "' which was never declared");
+ }
+ }
+ nDeferred.removeAllElements ();
+
+ // VC: Entity Name (attribute values can refer to them
+ // before they're declared); VC Attribute Default Legal
+ length = uDeferred.size ();
+ for (int i = 0; i < length; i++) {
+ String entity = (String) uDeferred.elementAt (i);
+ if (!unparsed.contains (entity)) {
+ error ("An attribute default referred to entity '" + entity
+ + "' which was never declared");
+ }
+ }
+ uDeferred.removeAllElements ();
+ super.endDTD ();
+ }
+
+
+ // These are interned, so we can rely on "==" to find the type of
+ // all attributes except enumerations ...
+ // "(this|or|that|...)" and "NOTATION (this|or|that|...)"
+ static final String types [] = {
+ "CDATA",
+ "ID", "IDREF", "IDREFS",
+ "NMTOKEN", "NMTOKENS",
+ "ENTITY", "ENTITIES"
+ };
+
+
+ /**
+ * <b>DecllHandler</b> Records attribute declaration for later use
+ * in validating document content, and checks validity constraints
+ * that are applicable to attribute declarations.
+ * Passed to the next consumer, unless this one was
+ * preloaded with a particular DTD.
+ */
+ public void attributeDecl (
+ String eName,
+ String aName,
+ String type,
+ String mode,
+ String value
+ ) throws SAXException
+ {
+ if (disableDeclarations)
+ return;
+
+ ElementInfo info = (ElementInfo) elements.get (eName);
+ AttributeInfo ainfo = new AttributeInfo ();
+ boolean checkOne = false;
+ boolean interned = false;
+
+ // cheap interning of type names and #FIXED, #REQUIRED
+ // for faster startElement (we can use "==")
+ for (int i = 0; i < types.length; i++) {
+ if (types [i].equals (type)) {
+ type = types [i];
+ interned = true;
+ break;
+ }
+ }
+ if ("#FIXED".equals (mode))
+ mode = "#FIXED";
+ else if ("#REQUIRED".equals (mode))
+ mode = "#REQUIRED";
+
+ ainfo.type = type;
+ ainfo.mode = mode;
+ ainfo.value = value;
+
+ // we might not have seen the content model yet
+ if (info == null) {
+ info = new ElementInfo (eName);
+ elements.put (eName, info);
+ }
+ if ("ID" == type) {
+ checkOne = true;
+ if (!("#REQUIRED" == mode || "#IMPLIED".equals (mode))) {
+ // VC: ID Attribute Default
+ error ("ID attribute '" + aName
+ + "' must be #IMPLIED or #REQUIRED");
+ }
+
+ } else if (!interned && type.startsWith ("NOTATION ")) {
+ checkOne = true;
+
+ // VC: Notation Attributes (notations must be declared)
+ StringTokenizer tokens = new StringTokenizer (
+ type.substring (10, type.lastIndexOf (')')),
+ "|");
+ while (tokens.hasMoreTokens ()) {
+ String token = tokens.nextToken ();
+ if (!notations.contains (token))
+ nDeferred.addElement (token);
+ }
+ }
+ if (checkOne) {
+ for (Enumeration e = info.attributes.keys ();
+ e.hasMoreElements ();
+ /* NOP */) {
+ String name;
+ AttributeInfo ainfo2;
+
+ name = (String) e.nextElement ();
+ ainfo2 = (AttributeInfo) info.attributes.get (name);
+ if (type == ainfo2.type || !interned /* NOTATION */) {
+ // VC: One ID per Element Type
+ // VC: One Notation per Element TYpe
+ error ("Element '" + eName
+ + "' already has an attribute of type "
+ + (interned ? "NOTATION" : type)
+ + " ('" + name
+ + "') so '" + aName
+ + "' is a validity error");
+ }
+ }
+ }
+
+ // VC: Attribute Default Legal
+ if (value != null) {
+
+ if ("CDATA" == type) {
+ // event source rejected '<'
+
+ } else if ("NMTOKEN" == type) {
+ // VC: Name Token (is a nmtoken)
+ isNmtoken (value, "attribute default", aName);
+
+ } else if ("NMTOKENS" == type) {
+ // VC: Name Token (is a nmtoken; at least one value)
+ StringTokenizer tokens = new StringTokenizer (value);
+ if (!tokens.hasMoreTokens ())
+ error ("Default for attribute '" + aName
+ + "' must have at least one name token.");
+ else do {
+ String token = tokens.nextToken ();
+ isNmtoken (token, "attribute default", aName);
+ } while (tokens.hasMoreTokens ());
+
+ } else if ("IDREF" == type || "ENTITY" == type) {
+ // VC: Entity Name (is a name)
+ // VC: IDREF (is a name) (is declared)
+ isName (value, "attribute default", aName);
+ if ("ENTITY" == type && !unparsed.contains (value))
+ uDeferred.addElement (value);
+
+ } else if ("IDREFS" == type || "ENTITIES" == type) {
+ // VC: Entity Name (is a name; at least one value)
+ // VC: IDREF (is a name; at least one value)
+ StringTokenizer names = new StringTokenizer (value);
+ if (!names.hasMoreTokens ())
+ error ("Default for attribute '" + aName
+ + "' must have at least one name.");
+ else do {
+ String name = names.nextToken ();
+ isName (name, "attribute default", aName);
+ if ("ENTITIES" == type && !unparsed.contains (name))
+ uDeferred.addElement (value);
+ } while (names.hasMoreTokens ());
+
+ } else if (type.charAt (0) == '(' /*)*/ ) {
+ // VC: Enumeration (must match)
+ checkEnumeration (value, type, aName);
+
+ } else if (!interned && checkOne) { /* NOTATION */
+ // VC: Notation attributes (must be names)
+ isName (value, "attribute default", aName);
+
+ // VC: Notation attributes (must be declared)
+ if (!notations.contains (value))
+ nDeferred.addElement (value);
+
+ // VC: Enumeration (must match)
+ checkEnumeration (value, type, aName);
+
+ } else if ("ID" != type)
+ throw new RuntimeException ("illegal attribute type: " + type);
+ }
+
+ if (info.attributes.get (aName) == null)
+ info.attributes.put (aName, ainfo);
+ /*
+ else
+ warning ("Element '" + eName
+ + "' already has an attribute named '" + aName + "'");
+ */
+
+ if ("xml:space".equals (aName)) {
+ if (!("(default|preserve)".equals (type)
+ || "(preserve|default)".equals (type)
+ // these next two are arguable; XHTML's DTD doesn't
+ // deserve errors. After all, it's not like any
+ // illegal _value_ could pass ...
+ || "(preserve)".equals (type)
+ || "(default)".equals (type)
+ ))
+ error (
+ "xml:space attribute type must be like '(default|preserve)'"
+ + " not '" + type + "'"
+ );
+
+ }
+ super.attributeDecl (eName, aName, type, mode, value);
+ }
+
+ /**
+ * <b>DecllHandler</b> Records the element declaration for later use
+ * when checking document content, and checks validity constraints that
+ * apply to element declarations. Passed to the next consumer, unless
+ * this one was preloaded with a particular DTD.
+ */
+ public void elementDecl (String name, String model)
+ throws SAXException
+ {
+ if (disableDeclarations)
+ return;
+
+ ElementInfo info = (ElementInfo) elements.get (name);
+
+ // we might have seen an attribute decl already
+ if (info == null) {
+ info = new ElementInfo (name);
+ elements.put (name, info);
+ }
+ if (info.model != null) {
+ // NOTE: not all parsers can report such duplicates.
+ // VC: Unique Element Type Declaration
+ error ("Element type '" + name
+ + "' was already declared.");
+ } else {
+ info.model = model;
+
+ // VC: No Duplicate Types (in mixed content models)
+ if (model.charAt (1) == '#') // (#PCDATA...
+ info.getRecognizer (this);
+ }
+ super.elementDecl (name, model);
+ }
+
+ /**
+ * <b>DecllHandler</b> passed to the next consumer, unless this
+ * one was preloaded with a particular DTD
+ */
+ public void internalEntityDecl (String name, String value)
+ throws SAXException
+ {
+ if (!disableDeclarations)
+ super.internalEntityDecl (name, value);
+ }
+
+ /**
+ * <b>DecllHandler</b> passed to the next consumer, unless this
+ * one was preloaded with a particular DTD
+ */
+ public void externalEntityDecl (String name,
+ String publicId, String systemId)
+ throws SAXException
+ {
+ if (!disableDeclarations)
+ super.externalEntityDecl (name, publicId, systemId);
+ }
+
+
+ /**
+ * <b>DTDHandler</b> Records the notation name, for checking
+ * NOTATIONS attribute values and declararations of unparsed
+ * entities. Passed to the next consumer, unless this one was
+ * preloaded with a particular DTD.
+ */
+ public void notationDecl (String name, String publicId, String systemId)
+ throws SAXException
+ {
+ if (disableDeclarations)
+ return;
+
+ notations.addElement (name);
+ super.notationDecl (name, publicId, systemId);
+ }
+
+ /**
+ * <b>DTDHandler</b> Records the entity name, for checking
+ * ENTITY and ENTITIES attribute values; records the notation
+ * name if it hasn't yet been declared. Passed to the next consumer,
+ * unless this one was preloaded with a particular DTD.
+ */
+ public void unparsedEntityDecl (
+ String name,
+ String publicId,
+ String systemId,
+ String notationName
+ ) throws SAXException
+ {
+ if (disableDeclarations)
+ return;
+
+ unparsed.addElement (name);
+ if (!notations.contains (notationName))
+ nDeferred.addElement (notationName);
+ super.unparsedEntityDecl (name, publicId, systemId, notationName);
+ }
+
+
+ /**
+ * <b>ContentHandler</b> Ensures that state from any previous parse
+ * has been deleted.
+ * Passed to the next consumer.
+ */
+ public void startDocument ()
+ throws SAXException
+ {
+ resetState ();
+ super.startDocument ();
+ }
+
+
+ private static boolean isAsciiLetter (char c)
+ {
+ return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
+ }
+
+
+ /**
+ * <b>ContentHandler</b> Reports a fatal exception. Validating
+ * XML processors may not skip any entities.
+ */
+ public void skippedEntity (String name)
+ throws SAXException
+ {
+ fatalError ("may not skip entities");
+ }
+
+ /*
+ * SAX2 doesn't expand non-PE refs in attribute defaults...
+ */
+ private String expandDefaultRefs (String s)
+ throws SAXException
+ {
+ if (s.indexOf ('&') < 0)
+ return s;
+
+// FIXME: handle &#nn; &#xnn; &name;
+ String message = "Can't expand refs in attribute default: " + s;
+ warning (message);
+
+ return s;
+ }
+
+ /**
+ * <b>ContentHandler</b> Performs validity checks against element
+ * (and document) content models, and attribute values.
+ * Passed to the next consumer.
+ */
+ public void startElement (
+ String uri,
+ String localName,
+ String qName,
+ Attributes atts
+ ) throws SAXException
+ {
+ //
+ // First check content model for the enclosing scope.
+ //
+ if (contentStack.isEmpty ()) {
+ // VC: Root Element Type
+ if (!qName.equals (rootName)) {
+ if (rootName == null)
+ warning ("This document has no DTD, can't be valid");
+ else
+ error ("Root element type '" + qName
+ + "' was declared to be '" + rootName + "'");
+ }
+ } else {
+ Recognizer state = (Recognizer) contentStack.peek ();
+
+ if (state != null) {
+ Recognizer newstate = state.acceptElement (qName);
+
+ if (newstate == null)
+ error ("Element type '" + qName
+ + "' in element '" + state.type.name
+ + "' violates content model " + state.type.model
+ );
+ if (newstate != state) {
+ contentStack.pop ();
+ contentStack.push (newstate);
+ }
+ }
+ }
+
+ //
+ // Then check that this element was declared, and push the
+ // object used to validate its content model onto our stack.
+ //
+ // This is where the recognizer gets created, if needed; if
+ // it's a "children" (elements) content model, an NDFA is
+ // created. (One recognizer is used per content type, no
+ // matter how complex that recognizer is.)
+ //
+ ElementInfo info;
+
+ info = (ElementInfo) elements.get (qName);
+ if (info == null || info.model == null) {
+ // VC: Element Valid (base clause)
+ error ("Element type '" + qName + "' was not declared");
+ contentStack.push (null);
+
+ // for less diagnostic noise, fake a declaration.
+ elementDecl (qName, "ANY");
+ } else
+ contentStack.push (info.getRecognizer (this));
+
+ //
+ // Then check each attribute present
+ //
+ int len;
+ String aname;
+ AttributeInfo ainfo;
+
+ if (atts != null)
+ len = atts.getLength ();
+ else
+ len = 0;
+
+ for (int i = 0; i < len; i++) {
+ aname = atts.getQName (i);
+
+ if (info == null
+ || (ainfo = (AttributeInfo) info.attributes.get (aname))
+ == null) {
+ // VC: Attribute Value Type
+ error ("Attribute '" + aname
+ + "' was not declared for element type " + qName);
+ continue;
+ }
+
+ String value = atts.getValue (i);
+
+ // note that "==" for type names and "#FIXED" is correct
+ // (and fast) since we've interned those literals.
+
+ if ("#FIXED" == ainfo.mode) {
+ String expanded = expandDefaultRefs (ainfo.value);
+
+ // VC: Fixed Attribute Default
+ if (!value.equals (expanded)) {
+ error ("Attribute '" + aname
+ + "' must match " + expanded
+ );
+ continue;
+ }
+ }
+
+ if ("CDATA" == ainfo.type)
+ continue;
+
+ //
+ // For all other attribute types, there are various
+ // rules to follow.
+ //
+
+ if ("ID" == ainfo.type) {
+ // VC: ID (must be a name)
+ if (isName (value, "ID attribute", aname)) {
+ if (Boolean.TRUE == ids.get (value))
+ // VC: ID (appears once)
+ error ("ID attribute " + aname
+ + " uses an ID value '" + value
+ + "' which was already declared.");
+ else
+ // any forward refs are no longer problems
+ ids.put (value, Boolean.TRUE);
+ }
+ continue;
+ }
+
+ if ("IDREF" == ainfo.type) {
+ // VC: IDREF (value must be a name)
+ if (isName (value, "IDREF attribute", aname)) {
+ // VC: IDREF (must match some ID attribute)
+ if (ids.get (value) == null)
+ // new -- assume it's a forward ref
+ ids.put (value, Boolean.FALSE);
+ }
+ continue;
+ }
+
+ if ("IDREFS" == ainfo.type) {
+ StringTokenizer tokens = new StringTokenizer (value, " ");
+
+ if (!tokens.hasMoreTokens ()) {
+ // VC: IDREF (one or more values)
+ error ("IDREFS attribute " + aname
+ + " must have at least one ID ref");
+ } else do {
+ String id = tokens.nextToken ();
+
+ // VC: IDREF (value must be a name)
+ if (isName (id, "IDREFS attribute", aname)) {
+ // VC: IDREF (must match some ID attribute)
+ if (ids.get (id) == null)
+ // new -- assume it's a forward ref
+ ids.put (id, Boolean.FALSE);
+ }
+ } while (tokens.hasMoreTokens ());
+ continue;
+ }
+
+ if ("NMTOKEN" == ainfo.type) {
+ // VC: Name Token (is a name token)
+ isNmtoken (value, "NMTOKEN attribute", aname);
+ continue;
+ }
+
+ if ("NMTOKENS" == ainfo.type) {
+ StringTokenizer tokens = new StringTokenizer (value, " ");
+
+ if (!tokens.hasMoreTokens ()) {
+ // VC: Name Token (one or more values)
+ error ("NMTOKENS attribute " + aname
+ + " must have at least one name token");
+ } else do {
+ String token = tokens.nextToken ();
+
+ // VC: Name Token (is a name token)
+ isNmtoken (token, "NMTOKENS attribute", aname);
+ } while (tokens.hasMoreTokens ());
+ continue;
+ }
+
+ if ("ENTITY" == ainfo.type) {
+ if (!unparsed.contains (value))
+ // VC: Entity Name
+ error ("Value of attribute '" + aname
+ + "' refers to unparsed entity '" + value
+ + "' which was not declared.");
+ continue;
+ }
+
+ if ("ENTITIES" == ainfo.type) {
+ StringTokenizer tokens = new StringTokenizer (value, " ");
+
+ if (!tokens.hasMoreTokens ()) {
+ // VC: Entity Name (one or more values)
+ error ("ENTITIES attribute " + aname
+ + " must have at least one name token");
+ } else do {
+ String entity = tokens.nextToken ();
+
+ if (!unparsed.contains (entity))
+ // VC: Entity Name
+ error ("Value of attribute '" + aname
+ + "' refers to unparsed entity '" + entity
+ + "' which was not declared.");
+ } while (tokens.hasMoreTokens ());
+ continue;
+ }
+
+ //
+ // check for enumerations last; more expensive
+ //
+ if (ainfo.type.charAt (0) == '(' /*)*/
+ || ainfo.type.startsWith ("NOTATION ")
+ ) {
+ // VC: Enumeration (value must be defined)
+ checkEnumeration (value, ainfo.type, aname);
+ continue;
+ }
+ }
+
+ //
+ // Last, check that all #REQUIRED attributes were provided
+ //
+ if (info != null) {
+ Hashtable table = info.attributes;
+
+ if (table.size () != 0) {
+ Enumeration e = table.keys ();
+
+ // XXX table.keys uses the heap, bleech -- slows things
+
+ while (e.hasMoreElements ()) {
+ aname = (String) e.nextElement ();
+ ainfo = (AttributeInfo) table.get (aname);
+
+ // "#REQUIRED" mode was interned in attributeDecl
+ if ("#REQUIRED" == ainfo.mode
+ && atts.getValue (aname) == null) {
+ // VC: Required Attribute
+ error ("Attribute '" + aname + "' must be specified "
+ + "for element type " + qName);
+ }
+ }
+ }
+ }
+ super.startElement (uri, localName, qName, atts);
+ }
+
+ /**
+ * <b>ContentHandler</b> Reports a validity error if the element's content
+ * model does not permit character data.
+ * Passed to the next consumer.
+ */
+ public void characters (char ch [], int start, int length)
+ throws SAXException
+ {
+ Recognizer state;
+
+ if (contentStack.empty ())
+ state = null;
+ else
+ state = (Recognizer) contentStack.peek ();
+
+ // NOTE: if this ever supports with SAX parsers that don't
+ // report ignorable whitespace as such (only XP?), this class
+ // needs to morph it into ignorableWhitespace() as needed ...
+
+ if (state != null && !state.acceptCharacters ())
+ // VC: Element Valid (clauses three, four -- see recognizer)
+ error ("Character content not allowed in element "
+ + state.type.name);
+
+ super.characters (ch, start, length);
+ }
+
+
+ /**
+ * <b>ContentHandler</b> Reports a validity error if the element's content
+ * model does not permit end-of-element yet, or a well formedness error
+ * if there was no matching startElement call.
+ * Passed to the next consumer.
+ */
+ public void endElement (String uri, String localName, String qName)
+ throws SAXException
+ {
+ try {
+ Recognizer state = (Recognizer) contentStack.pop ();
+
+ if (state != null && !state.completed ())
+ // VC: Element valid (clauses two, three, four; see Recognizer)
+ error ("Premature end for element '"
+ + state.type.name
+ + "', content model "
+ + state.type.model);
+
+ // could insist on match of start element, but that's
+ // something the input stream must to guarantee.
+
+ } catch (EmptyStackException e) {
+ fatalError ("endElement without startElement: " + qName
+ + ((uri == null)
+ ? ""
+ : ( " { '" + uri + "', " + localName + " }")));
+ }
+ super.endElement (uri, localName, qName);
+ }
+
+ /**
+ * <b>ContentHandler</b> Checks whether all ID values that were
+ * referenced have been declared, and releases all resources.
+ * Passed to the next consumer.
+ *
+ * @see #setDocumentLocator
+ */
+ public void endDocument ()
+ throws SAXException
+ {
+ for (Enumeration idNames = ids.keys ();
+ idNames.hasMoreElements ();
+ /* NOP */) {
+ String id = (String) idNames.nextElement ();
+
+ if (Boolean.FALSE == ids.get (id)) {
+ // VC: IDREF (must match ID)
+ error ("Undeclared ID value '" + id
+ + "' was referred to by an IDREF/IDREFS attribute");
+ }
+ }
+
+ resetState ();
+ super.endDocument ();
+ }
+
+
+ /** Holds per-element declarations */
+ static private final class ElementInfo
+ {
+ String name;
+ String model;
+
+ // key = attribute name; value = AttributeInfo
+ Hashtable attributes = new Hashtable (11);
+
+ ElementInfo (String n) { name = n; }
+
+ private Recognizer recognizer;
+
+ // for validating content models: one per type, shared,
+ // and constructed only on demand ... so unused elements do
+ // not need to consume resources.
+ Recognizer getRecognizer (ValidationConsumer consumer)
+ throws SAXException
+ {
+ if (recognizer == null) {
+ if ("ANY".equals (model))
+ recognizer = ANY;
+ else if ("EMPTY".equals (model))
+ recognizer = new EmptyRecognizer (this);
+ else if ('#' == model.charAt (1))
+ // n.b. this constructor does a validity check
+ recognizer = new MixedRecognizer (this, consumer);
+ else
+ recognizer = new ChildrenRecognizer (this, consumer);
+ }
+ return recognizer;
+ }
+ }
+
+ /** Holds per-attribute declarations */
+ static private final class AttributeInfo
+ {
+ String type;
+ String mode; // #REQUIRED, etc (or null)
+ String value; // or null
+ }
+
+
+ //
+ // Content model validation
+ //
+
+ static private final Recognizer ANY = new Recognizer (null);
+
+
+ // Base class defines the calls used to validate content,
+ // and supports the "ANY" content model
+ static private class Recognizer
+ {
+ final ElementInfo type;
+
+ Recognizer (ElementInfo t) { type = t; }
+
+ // return true iff character data is legal here
+ boolean acceptCharacters ()
+ throws SAXException
+ // VC: Element Valid (third and fourth clauses)
+ { return true; }
+
+ // null return = failure
+ // otherwise, next state (like an FSM)
+ // prerequisite: tested that name was declared
+ Recognizer acceptElement (String name)
+ throws SAXException
+ // VC: Element Valid (fourth clause)
+ { return this; }
+
+ // return true iff model is completed, can finish
+ boolean completed ()
+ throws SAXException
+ // VC: Element Valid (fourth clause)
+ { return true; }
+
+ public String toString ()
+ // n.b. "children" is the interesting case!
+ { return (type == null) ? "ANY" : type.model; }
+ }
+
+ // "EMPTY" content model -- no characters or elements
+ private static final class EmptyRecognizer extends Recognizer
+ {
+ public EmptyRecognizer (ElementInfo type)
+ { super (type); }
+
+ // VC: Element Valid (first clause)
+ boolean acceptCharacters ()
+ { return false; }
+
+ // VC: Element Valid (first clause)
+ Recognizer acceptElement (String name)
+ { return null; }
+ }
+
+ // "Mixed" content model -- ANY, but restricts elements
+ private static final class MixedRecognizer extends Recognizer
+ {
+ private String permitted [];
+
+ // N.B. constructor tests for duplicated element names (VC)
+ public MixedRecognizer (ElementInfo t, ValidationConsumer v)
+ throws SAXException
+ {
+ super (t);
+
+ // (#PCDATA...)* or (#PCDATA) ==> ... or empty
+ // with the "..." being "|elname|..."
+ StringTokenizer tokens = new StringTokenizer (
+ t.model.substring (8, t.model.lastIndexOf (')')),
+ "|");
+ Vector vec = new Vector ();
+
+ while (tokens.hasMoreTokens ()) {
+ String token = tokens.nextToken ();
+
+ if (vec.contains (token))
+ v.error ("element " + token
+ + " is repeated in mixed content model: "
+ + t.model);
+ else
+ vec.addElement (token.intern ());
+ }
+ permitted = new String [vec.size ()];
+ for (int i = 0; i < permitted.length; i++)
+ permitted [i] = (String) vec.elementAt (i);
+
+ // in one large machine-derived DTD sample, most of about
+ // 250 mixed content models were empty, and 25 had ten or
+ // more entries. 2 had over a hundred elements. Linear
+ // search isn't obviously wrong.
+ }
+
+ // VC: Element Valid (third clause)
+ Recognizer acceptElement (String name)
+ {
+ int length = permitted.length;
+
+ // first pass -- optimistic w.r.t. event source interning
+ // (and document validity)
+ for (int i = 0; i < length; i++)
+ if (permitted [i] == name)
+ return this;
+ // second pass -- pessimistic w.r.t. event source interning
+ for (int i = 0; i < length; i++)
+ if (permitted [i].equals (name))
+ return this;
+ return null;
+ }
+ }
+
+
+ // recognizer loop flags, see later
+ private static final int F_LOOPHEAD = 0x01;
+ private static final int F_LOOPNEXT = 0x02;
+
+ // for debugging -- used to label/count nodes in toString()
+ private static int nodeCount;
+
+ /**
+ * "Children" content model -- these are nodes in NDFA state graphs.
+ * They work in fixed space. Note that these graphs commonly have
+ * cycles, handling features such as zero-or-more and one-or-more.
+ *
+ * <p>It's readonly, so only one copy is ever needed. The content model
+ * stack may have any number of pointers into each graph, when a model
+ * happens to be needed more than once due to element nesting. Since
+ * traversing the graph just moves to another node, and never changes
+ * it, traversals never interfere with each other.
+ *
+ * <p>There is an option to report non-deterministic models. These are
+ * always XML errors, but ones which are not often reported despite the
+ * fact that they can lead to different validating parsers giving
+ * different results for the same input. (The XML spec doesn't require
+ * them to be reported.)
+ *
+ * <p><b>FIXME</b> There's currently at least one known bug here, in that
+ * it's not actually detecting the non-determinism it tries to detect.
+ * (Of the "optional.xml" test, the once-or-twice-2* tests are all non-D;
+ * maybe some others.) This may relate to the issue flagged below as
+ * "should not" happen (but it was), which showed up when patching the
+ * graph to have one exit node (or more EMPTY nodes).
+ */
+ private static final class ChildrenRecognizer extends Recognizer
+ implements Cloneable
+ {
+ // for reporting non-deterministic content models
+ // ... a waste of space if we're not reporting those!
+ // ... along with the 'model' member (in base class)
+ private ValidationConsumer consumer;
+
+ // for CHOICE nodes -- each component is an arc that
+ // accepts a different NAME (or is EMPTY indicating
+ // NDFA termination).
+ private Recognizer components [];
+
+ // for NAME/SEQUENCE nodes -- accepts that NAME and
+ // then goes to the next node (CHOICE, NAME, EMPTY).
+ private String name;
+ private Recognizer next;
+
+ // loops always point back to a CHOICE node. we mark such choice
+ // nodes (F_LOOPHEAD) for diagnostics and faster deep cloning.
+ // We also mark nodes before back pointers (F_LOOPNEXT), to ensure
+ // termination when we patch sequences and loops.
+ private int flags;
+
+
+ // prevent a needless indirection between 'this' and 'node'
+ private void copyIn (ChildrenRecognizer node)
+ {
+ // model & consumer are already set
+ components = node.components;
+ name = node.name;
+ next = node.next;
+ flags = node.flags;
+ }
+
+ // used to construct top level "children" content models,
+ public ChildrenRecognizer (ElementInfo type, ValidationConsumer vc)
+ {
+ this (vc, type);
+ populate (type.model.toCharArray (), 0);
+ patchNext (new EmptyRecognizer (type), null);
+ }
+
+ // used internally; populating is separate
+ private ChildrenRecognizer (ValidationConsumer vc, ElementInfo type)
+ {
+ super (type);
+ consumer = vc;
+ }
+
+
+ //
+ // When rewriting some graph nodes we need deep clones in one case;
+ // mostly shallow clones (what the JVM handles for us) are fine.
+ //
+ private ChildrenRecognizer shallowClone ()
+ {
+ try {
+ return (ChildrenRecognizer) clone ();
+ } catch (CloneNotSupportedException e) {
+ throw new Error ("clone");
+ }
+ }
+
+ private ChildrenRecognizer deepClone ()
+ {
+ return deepClone (new Hashtable (37));
+ }
+
+ private ChildrenRecognizer deepClone (Hashtable table)
+ {
+ ChildrenRecognizer retval;
+
+ if ((flags & F_LOOPHEAD) != 0) {
+ retval = (ChildrenRecognizer) table.get (this);
+ if (retval != null)
+ return this;
+
+ retval = shallowClone ();
+ table.put (this, retval);
+ } else
+ retval = shallowClone ();
+
+ if (next != null) {
+ if (next instanceof ChildrenRecognizer)
+ retval.next = ((ChildrenRecognizer)next)
+ .deepClone (table);
+ else if (!(next instanceof EmptyRecognizer))
+ throw new RuntimeException ("deepClone");
+ }
+
+ if (components != null) {
+ retval.components = new Recognizer [components.length];
+ for (int i = 0; i < components.length; i++) {
+ Recognizer temp = components [i];
+
+ if (temp == null)
+ retval.components [i] = null;
+ else if (temp instanceof ChildrenRecognizer)
+ retval.components [i] = ((ChildrenRecognizer)temp)
+ .deepClone (table);
+ else if (!(temp instanceof EmptyRecognizer))
+ throw new RuntimeException ("deepClone");
+ }
+ }
+
+ return retval;
+ }
+
+ // connect subgraphs, first to next (sequencing)
+ private void patchNext (Recognizer theNext, Hashtable table)
+ {
+ // backpointers must not be repatched or followed
+ if ((flags & F_LOOPNEXT) != 0)
+ return;
+
+ // XXX this table "shouldn't" be needed, right?
+ // but some choice nodes looped if it isn't there.
+ if (table != null && table.get (this) != null)
+ return;
+ if (table == null)
+ table = new Hashtable ();
+
+ // NAME/SEQUENCE
+ if (name != null) {
+ if (next == null)
+ next = theNext;
+ else if (next instanceof ChildrenRecognizer) {
+ ((ChildrenRecognizer)next).patchNext (theNext, table);
+ } else if (!(next instanceof EmptyRecognizer))
+ throw new RuntimeException ("patchNext");
+ return;
+ }
+
+ // CHOICE
+ for (int i = 0; i < components.length; i++) {
+ if (components [i] == null)
+ components [i] = theNext;
+ else if (components [i] instanceof ChildrenRecognizer) {
+ ((ChildrenRecognizer)components [i])
+ .patchNext (theNext, table);
+ } else if (!(components [i] instanceof EmptyRecognizer))
+ throw new RuntimeException ("patchNext");
+ }
+
+ if (table != null && (flags | F_LOOPHEAD) != 0)
+ table.put (this, this);
+ }
+
+ /**
+ * Parses a 'children' spec (or recursively 'cp') and makes this
+ * become a regular graph node.
+ *
+ * @return index after this particle
+ */
+ private int populate (char parseBuf [], int startPos)
+ {
+ int nextPos = startPos + 1;
+ char c;
+
+ if (nextPos < 0 || nextPos >= parseBuf.length)
+ throw new IndexOutOfBoundsException ();
+
+ // Grammar of the string is from the XML spec, but
+ // with whitespace removed by the SAX parser.
+
+ // children ::= (choice | seq) ('?' | '*' | '+')?
+ // cp ::= (Name | choice | seq) ('?' | '*' | '+')?
+ // choice ::= '(' cp ('|' choice)* ')'
+ // seq ::= '(' cp (',' choice)* ')'
+
+ // interior nodes only
+ // cp ::= name ...
+ if (parseBuf [startPos] != '('/*)*/) {
+ boolean done = false;
+ do {
+ switch (c = parseBuf [nextPos]) {
+ case '?': case '*': case '+':
+ case '|': case ',':
+ case /*(*/ ')':
+ done = true;
+ continue;
+ default:
+ nextPos++;
+ continue;
+ }
+ } while (!done);
+ name = new String (parseBuf, startPos, nextPos - startPos);
+
+ // interior OR toplevel nodes
+ // cp ::= choice ..
+ // cp ::= seq ..
+ } else {
+ // collect everything as a separate list, and merge it
+ // into "this" later if we can (SEQUENCE or singleton)
+ ChildrenRecognizer first;
+
+ first = new ChildrenRecognizer (consumer, type);
+ nextPos = first.populate (parseBuf, nextPos);
+ c = parseBuf [nextPos++];
+
+ if (c == ',' || c == '|') {
+ ChildrenRecognizer current = first;
+ char separator = c;
+ Vector v = null;
+
+ if (separator == '|') {
+ v = new Vector ();
+ v.addElement (first);
+ }
+
+ do {
+ ChildrenRecognizer link;
+
+ link = new ChildrenRecognizer (consumer, type);
+ nextPos = link.populate (parseBuf, nextPos);
+
+ if (separator == ',') {
+ current.patchNext (link, null);
+ current = link;
+ } else
+ v.addElement (link);
+
+ c = parseBuf [nextPos++];
+ } while (c == separator);
+
+ // choice ... collect everything into one array.
+ if (separator == '|') {
+ // assert v.size() > 1
+ components = new Recognizer [v.size ()];
+ for (int i = 0; i < components.length; i++) {
+ components [i] = (Recognizer)
+ v.elementAt (i);
+ }
+ // assert flags == 0
+
+ // sequence ... merge into "this" to be smaller.
+ } else
+ copyIn (first);
+
+ // treat singletons like one-node sequences.
+ } else
+ copyIn (first);
+
+ if (c != /*(*/ ')')
+ throw new RuntimeException ("corrupt content model");
+ }
+
+ //
+ // Arity is optional, and the root of all fun. We keep the
+ // FSM state graph simple by only having NAME/SEQUENCE and
+ // CHOICE nodes (or EMPTY to terminate a model), easily
+ // evaluated. So we rewrite each node that has arity, using
+ // those primitives. We create loops here, if needed.
+ //
+ if (nextPos < parseBuf.length) {
+ c = parseBuf [nextPos];
+ if (c == '?' || c == '*' || c == '+') {
+ nextPos++;
+
+ // Rewrite 'zero-or-one' "?" arity to a CHOICE:
+ // - SEQUENCE (clone, what's next)
+ // - or, what's next
+ // Size cost: N --> N + 1
+ if (c == '?') {
+ Recognizer once = shallowClone ();
+
+ components = new Recognizer [2];
+ components [0] = once;
+ // components [1] initted to null
+ name = null;
+ next = null;
+ flags = 0;
+
+
+ // Rewrite 'zero-or-more' "*" arity to a CHOICE.
+ // - LOOP (clone, back to this CHOICE)
+ // - or, what's next
+ // Size cost: N --> N + 1
+ } else if (c == '*') {
+ ChildrenRecognizer loop = shallowClone ();
+
+ loop.patchNext (this, null);
+ loop.flags |= F_LOOPNEXT;
+ flags = F_LOOPHEAD;
+
+ components = new Recognizer [2];
+ components [0] = loop;
+ // components [1] initted to null
+ name = null;
+ next = null;
+
+
+ // Rewrite 'one-or-more' "+" arity to a SEQUENCE.
+ // Basically (a)+ --> ((a),(a)*).
+ // - this
+ // - CHOICE
+ // * LOOP (clone, back to the CHOICE)
+ // * or, whatever's next
+ // Size cost: N --> 2N + 1
+ } else if (c == '+') {
+ ChildrenRecognizer loop = deepClone ();
+ ChildrenRecognizer choice;
+
+ choice = new ChildrenRecognizer (consumer, type);
+ loop.patchNext (choice, null);
+ loop.flags |= F_LOOPNEXT;
+ choice.flags = F_LOOPHEAD;
+
+ choice.components = new Recognizer [2];
+ choice.components [0] = loop;
+ // choice.components [1] initted to null
+ // choice.name, choice.next initted to null
+
+ patchNext (choice, null);
+ }
+ }
+ }
+
+ return nextPos;
+ }
+
+ // VC: Element Valid (second clause)
+ boolean acceptCharacters ()
+ { return false; }
+
+ // VC: Element Valid (second clause)
+ Recognizer acceptElement (String type)
+ throws SAXException
+ {
+ // NAME/SEQUENCE
+ if (name != null) {
+ if (name.equals (type))
+ return next;
+ return null;
+ }
+
+ // CHOICE ... optionally reporting nondeterminism we
+ // run across. we won't check out every transition
+ // for nondeterminism; only the ones we follow.
+ Recognizer retval = null;
+
+ for (int i = 0; i < components.length; i++) {
+ Recognizer temp = components [i].acceptElement (type);
+
+ if (temp == null)
+ continue;
+ else if (!warnNonDeterministic)
+ return temp;
+ else if (retval == null)
+ retval = temp;
+ else if (retval != temp)
+ consumer.error ("Content model " + this.type.model
+ + " is non-deterministic for " + type);
+ }
+ return retval;
+ }
+
+ // VC: Element Valid (second clause)
+ boolean completed ()
+ throws SAXException
+ {
+ // expecting a specific element
+ if (name != null)
+ return false;
+
+ // choice, some sequences
+ for (int i = 0; i < components.length; i++) {
+ if (components [i].completed ())
+ return true;
+ }
+
+ return false;
+ }
+
+/** /
+ // FOR DEBUGGING ... flattens the graph for printing.
+
+ public String toString ()
+ {
+ StringBuffer buf = new StringBuffer ();
+
+ // only one set of loop labels can be generated
+ // at a time...
+ synchronized (ANY) {
+ nodeCount = 0;
+
+ toString (buf, new Hashtable ());
+ return buf.toString ();
+ }
+ }
+
+ private void toString (StringBuffer buf, Hashtable table)
+ {
+ // When we visit a node, label and count it.
+ // Nodes are never visited/counted more than once.
+ // For small models labels waste space, but if arity
+ // mappings were used the savings are substantial.
+ // (Plus, the output can be more readily understood.)
+ String temp = (String) table.get (this);
+
+ if (temp != null) {
+ buf.append ('{');
+ buf.append (temp);
+ buf.append ('}');
+ return;
+ } else {
+ StringBuffer scratch = new StringBuffer (15);
+
+ if ((flags & F_LOOPHEAD) != 0)
+ scratch.append ("loop");
+ else
+ scratch.append ("node");
+ scratch.append ('-');
+ scratch.append (++nodeCount);
+ temp = scratch.toString ();
+
+ table.put (this, temp);
+ buf.append ('[');
+ buf.append (temp);
+ buf.append (']');
+ buf.append (':');
+ }
+
+ // NAME/SEQUENCE
+ if (name != null) {
+ // n.b. some output encodings turn some name chars into '?'
+ // e.g. with Japanese names and ASCII output
+ buf.append (name);
+ if (components != null) // bug!
+ buf.append ('$');
+ if (next == null)
+ buf.append (",*");
+ else if (next instanceof EmptyRecognizer) // patch-to-next
+ buf.append (",{}");
+ else if (next instanceof ChildrenRecognizer) {
+ buf.append (',');
+ ((ChildrenRecognizer)next).toString (buf, table);
+ } else // bug!
+ buf.append (",+");
+ return;
+ }
+
+ // CHOICE
+ buf.append ("<");
+ for (int i = 0; i < components.length; i++) {
+ if (i != 0)
+ buf.append ("|");
+ if (components [i] instanceof EmptyRecognizer) {
+ buf.append ("{}");
+ } else if (components [i] == null) { // patch-to-next
+ buf.append ('*');
+ } else {
+ ChildrenRecognizer r;
+
+ r = (ChildrenRecognizer) components [i];
+ r.toString (buf, table);
+ }
+ }
+ buf.append (">");
+ }
+/**/
+ }
+}
diff --git a/libjava/gnu/xml/pipeline/WellFormednessFilter.java b/libjava/gnu/xml/pipeline/WellFormednessFilter.java
new file mode 100644
index 0000000..3047ae3
--- /dev/null
+++ b/libjava/gnu/xml/pipeline/WellFormednessFilter.java
@@ -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 "]]&gt;"
+ *
+ * <li>whitespace ... can't contain CR
+ *
+ * <li>whitespace and character data must be within an element
+ *
+ * <li>processing instruction ... can't contain "?&gt;" 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 "&lt;" 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 ();
+ }
+}
diff --git a/libjava/gnu/xml/pipeline/XIncludeFilter.java b/libjava/gnu/xml/pipeline/XIncludeFilter.java
new file mode 100644
index 0000000..efa05d9
--- /dev/null
+++ b/libjava/gnu/xml/pipeline/XIncludeFilter.java
@@ -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);
+ }
+ }
+}
diff --git a/libjava/gnu/xml/pipeline/XsltFilter.java b/libjava/gnu/xml/pipeline/XsltFilter.java
new file mode 100644
index 0000000..b1bebbe
--- /dev/null
+++ b/libjava/gnu/xml/pipeline/XsltFilter.java
@@ -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);
+ }
+}
diff --git a/libjava/gnu/xml/pipeline/package.html b/libjava/gnu/xml/pipeline/package.html
new file mode 100644
index 0000000..352f4c8
--- /dev/null
+++ b/libjava/gnu/xml/pipeline/package.html
@@ -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&nbsp;(&nbsp;filename&nbsp;)</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>
diff --git a/libjava/gnu/xml/transform/AbstractNumberNode.java b/libjava/gnu/xml/transform/AbstractNumberNode.java
new file mode 100644
index 0000000..097637e
--- /dev/null
+++ b/libjava/gnu/xml/transform/AbstractNumberNode.java
@@ -0,0 +1,321 @@
+/* AbstractNumberNode.java --
+ Copyright (C) 2004 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.transform;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import javax.xml.namespace.QName;
+import javax.xml.transform.TransformerException;
+import org.w3c.dom.Document;
+import org.w3c.dom.DocumentFragment;
+import org.w3c.dom.Node;
+import org.w3c.dom.Text;
+import gnu.xml.xpath.Expr;
+
+/**
+ * A template node representing the XSL <code>number</code> instruction.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+abstract class AbstractNumberNode
+ extends TemplateNode
+{
+
+ static final int ALPHABETIC = 0;
+ static final int TRADITIONAL = 1;
+
+ final TemplateNode format;
+ final String lang;
+ final int letterValue;
+ final String groupingSeparator;
+ final int groupingSize;
+
+ AbstractNumberNode(TemplateNode children, TemplateNode next,
+ TemplateNode format, String lang,
+ int letterValue, String groupingSeparator,
+ int groupingSize)
+ {
+ super(children, next);
+ this.format = format;
+ this.lang = lang;
+ this.letterValue = letterValue;
+ this.groupingSeparator = groupingSeparator;
+ this.groupingSize = groupingSize;
+ }
+
+ void doApply(Stylesheet stylesheet, QName mode,
+ Node context, int pos, int len,
+ Node parent, Node nextSibling)
+ throws TransformerException
+ {
+ Document doc = (parent instanceof Document) ? (Document) parent :
+ parent.getOwnerDocument();
+ DocumentFragment fragment = doc.createDocumentFragment();
+ format.apply(stylesheet, mode, context, pos, len, fragment, null);
+ String f = Expr._string(context, Collections.singleton(fragment));
+ String value = format(f, compute(stylesheet, context, pos, len));
+ Text text = doc.createTextNode(value);
+ if (nextSibling != null)
+ {
+ parent.insertBefore(text, nextSibling);
+ }
+ else
+ {
+ parent.appendChild(text);
+ }
+ // xsl:number doesn't process children
+ if (next != null)
+ {
+ next.apply(stylesheet, mode,
+ context, pos, len,
+ parent, nextSibling);
+ }
+ }
+
+ String format(String format, int[] number)
+ {
+ if (number.length == 0)
+ {
+ return "";
+ }
+ int start = 0, end = 0, len = format.length(); // region of format
+ // Tokenize
+ List tokens = new ArrayList((number.length * 2) + 1);
+ List types = new ArrayList(tokens.size());
+ while (end < len)
+ {
+ while (end < len && !isAlphanumeric(format.charAt(end)))
+ {
+ end++;
+ }
+ if (end > start)
+ {
+ tokens.add(format.substring(start, end));
+ types.add(Boolean.FALSE);
+ }
+ start = end;
+ while (end < len && isAlphanumeric(format.charAt(end)))
+ {
+ end++;
+ }
+ if (end > start)
+ {
+ tokens.add(format.substring(start, end));
+ types.add(Boolean.TRUE);
+ }
+ start = end;
+ }
+ // Process tokens
+ StringBuffer buf = new StringBuffer();
+ len = tokens.size();
+ int pos = 0;
+ for (int i = 0; i < len; i++)
+ {
+ String token = (i < 0) ? "." : (String) tokens.get(i);
+ boolean alpha = (i < 0) ? true :
+ ((Boolean) types.get(i)).booleanValue();
+ if (!alpha)
+ {
+ buf.append(token);
+ }
+ else
+ {
+ if (pos < number.length)
+ {
+ format(buf, number[pos++], token);
+ if (((i + 1 == len) || (i + 2 == len)) &&
+ (pos < number.length))
+ {
+ // More numbers than tokens, reuse last token
+ i -= 2;
+ }
+ }
+ if (pos == number.length && i < (len - 2))
+ {
+ // No more numbers. Skip to the end...
+ i = len - 2;
+ if (((Boolean) types.get(i + 1)).booleanValue())
+ {
+ // number formatting token, ignore
+ i++;
+ }
+ }
+ }
+ }
+ //System.err.println("format: '"+format+"' "+asList(number)+" = '"+buf.toString()+"'");
+ return buf.toString();
+ }
+
+ /*List asList(int[] number)
+ {
+ List l = new ArrayList();
+ for (int i = 0; i < number.length; i++)
+ l.add(new Integer(number[i]));
+ return l;
+ }*/
+
+ void format(StringBuffer buf, int number, String formatToken)
+ {
+ int len = formatToken.length();
+ char c = formatToken.charAt(len - 1);
+ if (Character.digit(c, 10) == 1)
+ {
+ // Check preceding characters
+ for (int i = len - 2; i >= 0; i--)
+ {
+ if (formatToken.charAt(i) != (c - 1))
+ {
+ format(buf, number, "1");
+ return;
+ }
+ }
+ // Decimal representation
+ String val = Integer.toString(number);
+ for (int d = len - val.length(); d > 0; d--)
+ {
+ buf.append('0');
+ }
+ buf.append(val);
+ }
+ else if ("A".equals(formatToken))
+ {
+ buf.append(alphabetic('@', number));
+ }
+ else if ("a".equals(formatToken))
+ {
+ buf.append(alphabetic('`', number));
+ }
+ else if ("i".equals(formatToken))
+ {
+ buf.append(roman(false, number));
+ }
+ else if ("I".equals(formatToken))
+ {
+ buf.append(roman(true, number));
+ }
+ else
+ {
+ // Unknown numbering sequence
+ format(buf, number, "1");
+ }
+ }
+
+ static final boolean isAlphanumeric(char c)
+ {
+ switch (Character.getType(c))
+ {
+ case Character.DECIMAL_DIGIT_NUMBER: // Nd
+ case Character.LETTER_NUMBER: // Nl
+ case Character.OTHER_NUMBER: // No
+ case Character.UPPERCASE_LETTER: // Lu
+ case Character.LOWERCASE_LETTER: // Ll
+ case Character.TITLECASE_LETTER: // Lt
+ case Character.MODIFIER_LETTER: // Lm
+ case Character.OTHER_LETTER: // Lo
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ static final String alphabetic(char offset, int number)
+ {
+ StringBuffer buf = new StringBuffer();
+ while (number > 0)
+ {
+ int r = number % 26;
+ number = number / 26;
+ buf.insert(0, (char) (offset + r));
+ }
+ return buf.toString();
+ }
+
+ static final int[] roman_numbers = {1, 5, 10, 50, 100, 500, 1000};
+ static final char[] roman_chars = {'i', 'v', 'x', 'l', 'c', 'd', 'm'};
+
+ static final String roman(boolean upper, int number)
+ {
+ StringBuffer buf = new StringBuffer();
+ for (int pos = roman_numbers.length - 1; pos >= 0; pos -= 2)
+ {
+ int f = number / roman_numbers[pos];
+ if (f != 0)
+ {
+ number = number % (f * roman_numbers[pos]);
+ }
+ if (f > 4 && f < 9)
+ {
+ buf.append(roman_chars[pos + 1]);
+ f -= 5;
+ }
+ if (f == 4)
+ {
+ buf.append(roman_chars[pos]);
+ buf.append(roman_chars[pos + 1]);
+ }
+ else if (f == 9)
+ {
+ buf.append(roman_chars[pos]);
+ buf.append(roman_chars[pos + 2]);
+ }
+ else
+ {
+ for (; f > 0; f--)
+ {
+ buf.append(roman_chars[pos]);
+ }
+ }
+ }
+ return upper ? buf.toString().toUpperCase() : buf.toString();
+ }
+
+ abstract int[] compute(Stylesheet stylesheet, Node context, int pos, int len)
+ throws TransformerException;
+
+ public String toString()
+ {
+ StringBuffer buf = new StringBuffer(getClass().getName());
+ buf.append('[');
+ buf.append("format=");
+ buf.append(format);
+ buf.append(']');
+ return buf.toString();
+ }
+
+}
diff --git a/libjava/gnu/xml/transform/ApplyImportsNode.java b/libjava/gnu/xml/transform/ApplyImportsNode.java
new file mode 100644
index 0000000..2b28655
--- /dev/null
+++ b/libjava/gnu/xml/transform/ApplyImportsNode.java
@@ -0,0 +1,91 @@
+/* ApplyImportsNode.java --
+ Copyright (C) 2004 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.transform;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.text.DecimalFormat;
+import javax.xml.namespace.QName;
+import javax.xml.transform.TransformerException;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.w3c.dom.Text;
+import gnu.xml.xpath.Expr;
+
+/**
+ * A template node representing an XSLT <code>apply-imports</code> instruction.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+final class ApplyImportsNode
+ extends TemplateNode
+{
+
+ ApplyImportsNode(TemplateNode children, TemplateNode next)
+ {
+ super(children, next);
+ }
+
+ TemplateNode clone(Stylesheet stylesheet)
+ {
+ return new ApplyImportsNode((children == null) ? null :
+ children.clone(stylesheet),
+ (next == null) ? null :
+ next.clone(stylesheet));
+ }
+
+ void doApply(Stylesheet stylesheet, QName mode,
+ Node context, int pos, int len,
+ Node parent, Node nextSibling)
+ throws TransformerException
+ {
+ TemplateNode t = stylesheet.getTemplate(mode, context, true);
+ if (t != null)
+ {
+ t.apply(stylesheet, mode, context, pos, len,
+ parent, nextSibling);
+ }
+ if (next != null)
+ {
+ next.apply(stylesheet, mode, context, pos, len,
+ parent, nextSibling);
+ }
+ }
+
+}
+
diff --git a/libjava/gnu/xml/transform/ApplyTemplatesNode.java b/libjava/gnu/xml/transform/ApplyTemplatesNode.java
new file mode 100644
index 0000000..83e7c55
--- /dev/null
+++ b/libjava/gnu/xml/transform/ApplyTemplatesNode.java
@@ -0,0 +1,193 @@
+/* ApplyTemplatesNode.java --
+ Copyright (C) 2004 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.transform;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import javax.xml.namespace.QName;
+import javax.xml.transform.TransformerException;
+import org.w3c.dom.Node;
+import gnu.xml.xpath.Expr;
+
+/**
+ * A template node representing the XSL <code>apply-templates</code>
+ * instruction.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+final class ApplyTemplatesNode
+ extends TemplateNode
+{
+
+ final Expr select;
+ final QName mode;
+ final List sortKeys;
+ final List withParams;
+ final boolean isDefault;
+
+ ApplyTemplatesNode(TemplateNode children, TemplateNode next,
+ Expr select, QName mode,
+ List sortKeys, List withParams, boolean isDefault)
+ {
+ super(children, next);
+ this.select = select;
+ this.mode = mode;
+ this.sortKeys = sortKeys;
+ this.withParams = withParams;
+ this.isDefault = isDefault;
+ }
+
+ TemplateNode clone(Stylesheet stylesheet)
+ {
+ int len = sortKeys.size();
+ List sortKeys2 = new ArrayList(len);
+ for (int i = 0; i < len; i++)
+ {
+ sortKeys2.add(((Key) sortKeys.get(i)).clone(stylesheet));
+ }
+ len = withParams.size();
+ List withParams2 = new ArrayList(len);
+ for (int i = 0; i < len; i++)
+ {
+ withParams2.add(((WithParam) withParams.get(i)).clone(stylesheet));
+ }
+ return new ApplyTemplatesNode((children == null) ? null :
+ children.clone(stylesheet),
+ (next == null) ? null :
+ next.clone(stylesheet),
+ select.clone(stylesheet),
+ mode, sortKeys2, withParams2, isDefault);
+ }
+
+ void doApply(Stylesheet stylesheet, QName mode,
+ Node context, int pos, int len,
+ Node parent, Node nextSibling)
+ throws TransformerException
+ {
+ Object ret = select.evaluate(context, pos, len);
+ if (ret != null && ret instanceof Collection)
+ {
+ if (withParams != null)
+ {
+ // push the parameter context
+ stylesheet.bindings.push(false);
+ // set the parameters
+ for (Iterator i = withParams.iterator(); i.hasNext(); )
+ {
+ WithParam p = (WithParam) i.next();
+ Object value = p.getValue(stylesheet, mode, context, pos, len);
+ stylesheet.bindings.set(p.name, value, false);
+ }
+ }
+ Collection ns = (Collection) ret;
+ List nodes = new ArrayList(ns);
+ if (sortKeys != null)
+ {
+ for (Iterator i = sortKeys.iterator(); i.hasNext(); )
+ {
+ SortKey sortKey = (SortKey) i.next();
+ sortKey.init(stylesheet, mode, context, pos, len, parent,
+ nextSibling);
+ }
+ Collections.sort(nodes, new XSLComparator(sortKeys));
+ }
+ else
+ {
+ Collections.sort(nodes, documentOrderComparator);
+ }
+ int l = nodes.size();
+ QName effectiveMode = isDefault ? mode : this.mode;
+ for (int i = 0; i < l; i++)
+ {
+ Node node = (Node) nodes.get(i);
+ TemplateNode t = stylesheet.getTemplate(effectiveMode, node,
+ false);
+ if (t != null)
+ {
+ if (stylesheet.debug)
+ {
+ System.err.println("Applying " + t);
+ }
+ stylesheet.current = node;
+ t.apply(stylesheet, effectiveMode, node, i + 1, l,
+ parent, nextSibling);
+ }
+ }
+ if (withParams != null)
+ {
+ // pop the variable context
+ stylesheet.bindings.pop(false);
+ }
+ }
+ // apply-templates doesn't have processable children
+ if (next != null)
+ {
+ next.apply(stylesheet, mode,
+ context, pos, len,
+ parent, nextSibling);
+ }
+ }
+
+ public String toString()
+ {
+ StringBuffer buf = new StringBuffer(getClass().getName());
+ buf.append('[');
+ boolean o = false;
+ if (select != null)
+ {
+ buf.append("select=");
+ buf.append(select);
+ o = true;
+ }
+ if (mode != null)
+ {
+ if (o)
+ {
+ buf.append(',');
+ }
+ buf.append("mode=");
+ buf.append(mode);
+ }
+ buf.append(']');
+ return buf.toString();
+ }
+
+}
diff --git a/libjava/gnu/xml/transform/AttributeNode.java b/libjava/gnu/xml/transform/AttributeNode.java
new file mode 100644
index 0000000..c4409db
--- /dev/null
+++ b/libjava/gnu/xml/transform/AttributeNode.java
@@ -0,0 +1,246 @@
+/* AttributeNode.java --
+ Copyright (C) 2004 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.transform;
+
+import javax.xml.XMLConstants;
+import javax.xml.namespace.QName;
+import javax.xml.transform.TransformerException;
+import org.w3c.dom.Document;
+import org.w3c.dom.DocumentFragment;
+import org.w3c.dom.Attr;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import gnu.xml.xpath.Expr;
+
+/**
+ * A template node representing an XSL <code>attribute</code> instruction.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+final class AttributeNode
+ extends TemplateNode
+{
+
+ final TemplateNode name;
+ final TemplateNode namespace;
+ final Node source;
+
+ AttributeNode(TemplateNode children, TemplateNode next, TemplateNode name,
+ TemplateNode namespace, Node source)
+ {
+ super(children, next);
+ this.name = name;
+ this.namespace = namespace;
+ this.source = source;
+ }
+
+ TemplateNode clone(Stylesheet stylesheet)
+ {
+ return new AttributeNode((children == null) ? null :
+ children.clone(stylesheet),
+ (next == null) ? null : next.clone(stylesheet),
+ name.clone(stylesheet),
+ (namespace == null) ? null :
+ namespace.clone(stylesheet),
+ source);
+ }
+
+ void doApply(Stylesheet stylesheet, QName mode,
+ Node context, int pos, int len,
+ Node parent, Node nextSibling)
+ throws TransformerException
+ {
+ Document doc = (parent instanceof Document) ? (Document) parent :
+ parent.getOwnerDocument();
+ // Create a document fragment to hold the name
+ DocumentFragment fragment = doc.createDocumentFragment();
+ // Apply name to the fragment
+ name.apply(stylesheet, mode,
+ context, pos, len,
+ fragment, null);
+ // Use XPath string-value of fragment
+ String nameValue = Expr.stringValue(fragment);
+
+ String namespaceValue = null;
+ if (namespace != null)
+ {
+ // Create a document fragment to hold the namespace
+ fragment = doc.createDocumentFragment();
+ // Apply namespace to the fragment
+ namespace.apply(stylesheet, mode,
+ context, pos, len,
+ fragment, null);
+ // Use XPath string-value of fragment
+ namespaceValue = Expr.stringValue(fragment);
+ if (namespaceValue.length() == 0)
+ {
+ namespaceValue = null;
+ }
+ }
+
+ String prefix = getPrefix(nameValue);
+ if (namespaceValue == null)
+ {
+ if (prefix != null)
+ {
+ if (XMLConstants.XML_NS_PREFIX.equals(prefix))
+ {
+ namespaceValue = XMLConstants.XML_NS_URI;
+ }
+ else
+ {
+ // Resolve namespace for this prefix
+ namespaceValue = source.lookupNamespaceURI(prefix);
+ }
+ }
+ }
+ else
+ {
+ if (prefix != null)
+ {
+ String ns2 = source.lookupNamespaceURI(prefix);
+ if (ns2 != null && !ns2.equals(namespaceValue))
+ {
+ // prefix clashes, reset it
+ prefix = null;
+ int ci = nameValue.indexOf(':');
+ nameValue = nameValue.substring(ci + 1);
+ }
+ }
+ }
+ if (prefix == null)
+ {
+ // Resolve prefix for this namespace
+ prefix = source.lookupPrefix(namespaceValue);
+ if (prefix != null)
+ {
+ nameValue = prefix + ":" + nameValue;
+ }
+ else
+ {
+ if (namespaceValue != null)
+ {
+ // Must invent a prefix
+ prefix = inventPrefix(parent);
+ nameValue = prefix + ":" + nameValue;
+ }
+ }
+ }
+ NamedNodeMap attrs = parent.getAttributes();
+ boolean insert = true;
+ if (XMLConstants.XMLNS_ATTRIBUTE_NS_URI.equals(namespaceValue) ||
+ XMLConstants.XMLNS_ATTRIBUTE.equals(nameValue) ||
+ nameValue.startsWith("xmlns:"))
+ {
+ // Namespace declaration, do not output
+ insert = false;
+ }
+ if (prefix != null && namespaceValue == null)
+ {
+ // Not a QName
+ insert = false;
+ }
+ if (parent.getNodeType() == Node.ELEMENT_NODE &&
+ parent.getFirstChild() != null)
+ {
+ // XSLT 7.1.3 Adding an attribute to an element after children have
+ // been added to it is an error
+ insert = false;
+ }
+ if (insert)
+ {
+ // Insert attribute
+ Attr attr = (namespaceValue != null) ?
+ doc.createAttributeNS(namespaceValue, nameValue) :
+ doc.createAttribute(nameValue);
+ if (attrs != null)
+ {
+ if (namespace != null)
+ {
+ attrs.setNamedItemNS(attr);
+ }
+ else
+ {
+ attrs.setNamedItem(attr);
+ }
+ }
+ if (children != null)
+ {
+ children.apply(stylesheet, mode,
+ context, pos, len,
+ attr, null);
+ }
+ }
+ if (next != null)
+ {
+ next.apply(stylesheet, mode,
+ context, pos, len,
+ parent, nextSibling);
+ }
+ }
+
+ final String getPrefix(String name)
+ {
+ int ci = name.indexOf(':');
+ return (ci == -1) ? null : name.substring(0, ci);
+ }
+
+ final String inventPrefix(Node parent)
+ {
+ String base = "ns";
+ int count = 0;
+ String ret = base + Integer.toString(count);
+ while (parent.lookupNamespaceURI(ret) != null)
+ {
+ count++;
+ ret = base + Integer.toString(count);
+ }
+ return ret;
+ }
+
+ public String toString()
+ {
+ StringBuffer buf = new StringBuffer(getClass().getName());
+ buf.append('[');
+ buf.append("name=");
+ buf.append(name);
+ buf.append(']');
+ return buf.toString();
+ }
+
+}
diff --git a/libjava/gnu/xml/transform/AttributeSet.java b/libjava/gnu/xml/transform/AttributeSet.java
new file mode 100644
index 0000000..92869d1
--- /dev/null
+++ b/libjava/gnu/xml/transform/AttributeSet.java
@@ -0,0 +1,67 @@
+/* AttributeSet.java --
+ Copyright (C) 2004 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.transform;
+
+/**
+ * An attribute-set entry in a stylesheet.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+final class AttributeSet
+{
+
+ final TemplateNode children;
+ final String name;
+ final String uas;
+
+ AttributeSet(TemplateNode children, String name, String uas)
+ {
+ this.children = children;
+ this.name = name;
+ this.uas = uas;
+ }
+
+ AttributeSet clone(Stylesheet stylesheet)
+ {
+ return new AttributeSet((children == null) ? null :
+ children.clone(stylesheet),
+ name, uas);
+ }
+
+}
+
diff --git a/libjava/gnu/xml/transform/Bindings.java b/libjava/gnu/xml/transform/Bindings.java
new file mode 100644
index 0000000..f898a72
--- /dev/null
+++ b/libjava/gnu/xml/transform/Bindings.java
@@ -0,0 +1,246 @@
+/* Bindings.java --
+ Copyright (C) 2004 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.transform;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.Map;
+import javax.xml.namespace.QName;
+import javax.xml.transform.TransformerException;
+import javax.xml.xpath.XPathVariableResolver;
+import org.w3c.dom.Document;
+import org.w3c.dom.DocumentFragment;
+import org.w3c.dom.Node;
+import gnu.xml.xpath.Expr;
+
+/**
+ * The set of variable bindings in effect for a stylesheet.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+public class Bindings
+ implements XPathVariableResolver, Cloneable
+{
+
+ final Stylesheet stylesheet;
+
+ /**
+ * Global variables.
+ */
+ final LinkedList variables;
+
+ /**
+ * Parameter value stack.
+ */
+ final LinkedList parameters;
+
+ Bindings(Stylesheet stylesheet)
+ {
+ this.stylesheet = stylesheet;
+ variables = new LinkedList();
+ parameters = new LinkedList();
+ push(true);
+ push(false);
+ }
+
+ public Object clone()
+ {
+ try
+ {
+ return (Bindings) super.clone();
+ }
+ catch (CloneNotSupportedException e)
+ {
+ throw new Error(e.getMessage());
+ }
+ }
+
+ void push(boolean global)
+ {
+ if (global)
+ {
+ variables.addFirst(new HashMap());
+ }
+ else
+ {
+ parameters.addFirst(new HashMap());
+ }
+ }
+
+ void pop(boolean global)
+ {
+ if (global)
+ {
+ variables.removeFirst();
+ }
+ else
+ {
+ parameters.removeFirst();
+ }
+ }
+
+ public boolean containsKey(String name, boolean global)
+ {
+ Iterator i = global ? variables.iterator() : parameters.iterator();
+ while (i.hasNext())
+ {
+ Map ctx = (Map) i.next();
+ if (ctx.containsKey(name))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public Object get(String name, Node context, int pos, int len)
+ {
+ //System.err.println("bindings.get: "+name);
+ //System.err.println("\t"+toString());
+ Object ret = null;
+ for (Iterator i = variables.iterator(); i.hasNext() && ret == null; )
+ {
+ Map vctx = (Map) i.next();
+ ret = vctx.get(name);
+ }
+ if (ret == null)
+ {
+ for (Iterator i = parameters.iterator(); i.hasNext() && ret == null; )
+ {
+ Map pctx = (Map) i.next();
+ ret = pctx.get(name);
+ }
+ }
+ /*if (ret instanceof Expr && context != null)
+ {
+ Expr expr = (Expr) ret;
+ ret = expr.evaluate(context, 1, 1);
+ }*/
+ if (ret instanceof Node)
+ {
+ ret = Collections.singleton(ret);
+ }
+ if (ret == null)
+ {
+ ret = "";
+ }
+ //System.err.println("\tret="+ret);
+ return ret;
+ }
+
+ void set(String name, Object value, boolean global)
+ {
+ if (global)
+ {
+ Map context = (Map) variables.getFirst();
+ context.put(name, value);
+ }
+ else
+ {
+ Map context = (Map) parameters.getFirst();
+ context.put(name, value);
+ }
+ }
+
+ public Object resolveVariable(QName qName)
+ {
+ return get(qName.toString(), null, 1, 1);
+ }
+
+ public String toString()
+ {
+ StringBuffer buf = new StringBuffer();
+ boolean next = false;
+ Collection seen = new HashSet();
+ buf.append('{');
+ for (Iterator i = variables.iterator(); i.hasNext(); )
+ {
+ Map ctx = (Map) i.next();
+ for (Iterator j = ctx.entrySet().iterator(); j.hasNext(); )
+ {
+ if (next)
+ {
+ buf.append(',');
+ }
+ else
+ {
+ next = true;
+ }
+ Map.Entry entry = (Map.Entry) j.next();
+ Object key = entry.getKey();
+ if (!seen.contains(key))
+ {
+ buf.append(key);
+ buf.append('=');
+ buf.append(entry.getValue());
+ seen.add(key);
+ }
+ }
+ }
+ for (Iterator i = parameters.iterator(); i.hasNext(); )
+ {
+ Map ctx = (Map) i.next();
+ for (Iterator j = ctx.entrySet().iterator(); j.hasNext(); )
+ {
+ if (next)
+ {
+ buf.append(',');
+ }
+ else
+ {
+ next = true;
+ }
+ Map.Entry entry = (Map.Entry) j.next();
+ Object key = entry.getKey();
+ if (!seen.contains(key))
+ {
+ buf.append(key);
+ buf.append('=');
+ buf.append(entry.getValue());
+ seen.add(key);
+ }
+ }
+ }
+ buf.append('}');
+ return buf.toString();
+ }
+}
diff --git a/libjava/gnu/xml/transform/CallTemplateNode.java b/libjava/gnu/xml/transform/CallTemplateNode.java
new file mode 100644
index 0000000..ee64e2e
--- /dev/null
+++ b/libjava/gnu/xml/transform/CallTemplateNode.java
@@ -0,0 +1,130 @@
+/* CallTemplateNode.java --
+ Copyright (C) 2004 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.transform;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import javax.xml.namespace.QName;
+import javax.xml.transform.TransformerException;
+import org.w3c.dom.Node;
+
+/**
+ * A template node representing the XSL <code>call-template</code>
+ * instruction.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+final class CallTemplateNode
+ extends TemplateNode
+{
+
+ final QName name;
+ final List withParams;
+
+ CallTemplateNode(TemplateNode children, TemplateNode next,
+ QName name, List withParams)
+ {
+ super(children, next);
+ this.name = name;
+ this.withParams = withParams;
+ }
+
+ TemplateNode clone(Stylesheet stylesheet)
+ {
+ int len = withParams.size();
+ List withParams2 = new ArrayList(len);
+ for (int i = 0; i < len; i++)
+ {
+ withParams2.add(((WithParam) withParams.get(i)).clone(stylesheet));
+ }
+ return new CallTemplateNode((children == null) ? null :
+ children.clone(stylesheet),
+ (next == null) ? null :
+ next.clone(stylesheet),
+ name, withParams2);
+ }
+
+ void doApply(Stylesheet stylesheet, QName mode,
+ Node context, int pos, int len,
+ Node parent, Node nextSibling)
+ throws TransformerException
+ {
+ if (withParams != null)
+ {
+ // push the parameter context
+ stylesheet.bindings.push(false);
+ // set the parameters
+ for (Iterator i = withParams.iterator(); i.hasNext(); )
+ {
+ WithParam p = (WithParam) i.next();
+ Object value = p.getValue(stylesheet, mode, context, pos, len);
+ stylesheet.bindings.set(p.name, value, false);
+ }
+ }
+ TemplateNode t = stylesheet.getTemplate(mode, name);
+ if (t != null)
+ {
+ t.apply(stylesheet, mode, context, pos, len,
+ parent, nextSibling);
+ }
+ if (withParams != null)
+ {
+ // pop the variable context
+ stylesheet.bindings.pop(false);
+ }
+ // call-template doesn't have processable children
+ if (next != null)
+ {
+ next.apply(stylesheet, mode,
+ context, pos, len,
+ parent, nextSibling);
+ }
+ }
+
+ public String toString()
+ {
+ StringBuffer buf = new StringBuffer(getClass().getName());
+ buf.append('[');
+ buf.append("name=");
+ buf.append(name);
+ buf.append(']');
+ return buf.toString();
+ }
+
+}
diff --git a/libjava/gnu/xml/transform/ChooseNode.java b/libjava/gnu/xml/transform/ChooseNode.java
new file mode 100644
index 0000000..76b0d8d
--- /dev/null
+++ b/libjava/gnu/xml/transform/ChooseNode.java
@@ -0,0 +1,93 @@
+/* ChooseNode.java --
+ Copyright (C) 2004 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.transform;
+
+import javax.xml.namespace.QName;
+import javax.xml.transform.TransformerException;
+import org.w3c.dom.Node;
+
+/**
+ * A template node representing an XSL <code>choose</code> instruction.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+final class ChooseNode
+ extends TemplateNode
+{
+
+ ChooseNode(TemplateNode children, TemplateNode next)
+ {
+ super(children, next);
+ }
+
+ TemplateNode clone(Stylesheet stylesheet)
+ {
+ return new ChooseNode((children == null) ? null :
+ children.clone(stylesheet),
+ (next == null) ? null :
+ next.clone(stylesheet));
+ }
+
+ void doApply(Stylesheet stylesheet, QName mode,
+ Node context, int pos, int len,
+ Node parent, Node nextSibling)
+ throws TransformerException
+ {
+ if (children != null)
+ {
+ children.apply(stylesheet, mode,
+ context, pos, len,
+ parent, nextSibling);
+ }
+ if (next != null)
+ {
+ next.apply(stylesheet, mode,
+ context, pos, len,
+ parent, nextSibling);
+ }
+ }
+
+ public String toString()
+ {
+ StringBuffer buf = new StringBuffer(getClass().getName());
+ buf.append('[');
+ buf.append(']');
+ return buf.toString();
+ }
+
+}
diff --git a/libjava/gnu/xml/transform/CommentNode.java b/libjava/gnu/xml/transform/CommentNode.java
new file mode 100644
index 0000000..99870cd
--- /dev/null
+++ b/libjava/gnu/xml/transform/CommentNode.java
@@ -0,0 +1,115 @@
+/* CommentNode.java --
+ Copyright (C) 2004 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.transform;
+
+import javax.xml.namespace.QName;
+import javax.xml.transform.TransformerException;
+import org.w3c.dom.Comment;
+import org.w3c.dom.Document;
+import org.w3c.dom.DocumentFragment;
+import org.w3c.dom.Node;
+import gnu.xml.xpath.Expr;
+
+/**
+ * A template node representing the XSL <code>comment</code> instruction.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+final class CommentNode
+ extends TemplateNode
+{
+
+ CommentNode(TemplateNode children, TemplateNode next)
+ {
+ super(children, next);
+ }
+
+ TemplateNode clone(Stylesheet stylesheet)
+ {
+ return new CommentNode((children == null) ? null :
+ children.clone(stylesheet),
+ (next == null) ? null :
+ next.clone(stylesheet));
+ }
+
+ void doApply(Stylesheet stylesheet, QName mode,
+ Node context, int pos, int len,
+ Node parent, Node nextSibling)
+ throws TransformerException
+ {
+ String value = "";
+ Document doc = (parent instanceof Document) ? (Document) parent :
+ parent.getOwnerDocument();
+ if (children != null)
+ {
+ // Create a document fragment to hold the text
+ DocumentFragment fragment = doc.createDocumentFragment();
+ // Apply children to the fragment
+ children.apply(stylesheet, mode,
+ context, pos, len,
+ fragment, null);
+ // Use XPath string-value of fragment
+ value = Expr.stringValue(fragment);
+ }
+ Comment comment = doc.createComment(value);
+ // Insert into result tree
+ if (nextSibling != null)
+ {
+ parent.insertBefore(comment, nextSibling);
+ }
+ else
+ {
+ parent.appendChild(comment);
+ }
+ if (next != null)
+ {
+ next.apply(stylesheet, mode,
+ context, pos, len,
+ parent, nextSibling);
+ }
+ }
+
+ public String toString()
+ {
+ StringBuffer buf = new StringBuffer(getClass().getName());
+ buf.append('[');
+ buf.append(']');
+ return buf.toString();
+ }
+
+}
diff --git a/libjava/gnu/xml/transform/CopyNode.java b/libjava/gnu/xml/transform/CopyNode.java
new file mode 100644
index 0000000..d06a0af
--- /dev/null
+++ b/libjava/gnu/xml/transform/CopyNode.java
@@ -0,0 +1,179 @@
+/* CopyNode.java --
+ Copyright (C) 2004 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.transform;
+
+import java.util.Iterator;
+import java.util.StringTokenizer;
+import javax.xml.namespace.QName;
+import javax.xml.transform.TransformerException;
+import org.w3c.dom.Document;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+
+/**
+ * A template node representing the XSL <code>copy</code> instruction.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+final class CopyNode
+ extends TemplateNode
+{
+
+ final String uas;
+
+ CopyNode(TemplateNode children, TemplateNode next, String uas)
+ {
+ super(children, next);
+ this.uas = uas;
+ }
+
+ TemplateNode clone(Stylesheet stylesheet)
+ {
+ return new CopyNode((children == null) ? null :
+ children.clone(stylesheet),
+ (next == null) ? null :
+ next.clone(stylesheet),
+ uas);
+ }
+
+ void doApply(Stylesheet stylesheet, QName mode,
+ Node context, int pos, int len,
+ Node parent, Node nextSibling)
+ throws TransformerException
+ {
+ Node copy = parent;
+ switch (context.getNodeType())
+ {
+ case Node.TEXT_NODE:
+ case Node.ATTRIBUTE_NODE:
+ case Node.ELEMENT_NODE:
+ case Node.PROCESSING_INSTRUCTION_NODE:
+ case Node.COMMENT_NODE:
+ Document doc = (parent instanceof Document) ? (Document) parent :
+ parent.getOwnerDocument();
+ copy = context.cloneNode(false);
+ copy = doc.adoptNode(copy);
+ if (copy.getNodeType() == Node.ATTRIBUTE_NODE)
+ {
+ if (parent.getFirstChild() != null)
+ {
+ // Ignore attempt to add attribute after children
+ }
+ else
+ {
+ NamedNodeMap attrs = parent.getAttributes();
+ if (attrs != null)
+ {
+ attrs.setNamedItemNS(copy);
+ }
+ }
+ }
+ else
+ {
+ if (nextSibling != null)
+ {
+ parent.insertBefore(copy, nextSibling);
+ }
+ else
+ {
+ parent.appendChild(copy);
+ }
+ }
+ }
+ if (uas != null)
+ {
+ StringTokenizer st = new StringTokenizer(uas, " ");
+ while (st.hasMoreTokens())
+ {
+ addAttributeSet(stylesheet, mode, context, pos, len,
+ copy, null, st.nextToken());
+ }
+ }
+ if (children != null)
+ {
+ children.apply(stylesheet, mode,
+ context, pos, len,
+ copy, null);
+ }
+ if (next != null)
+ {
+ next.apply(stylesheet, mode,
+ context, pos, len,
+ parent, nextSibling);
+ }
+ }
+
+ void addAttributeSet(Stylesheet stylesheet, QName mode,
+ Node context, int pos, int len,
+ Node parent, Node nextSibling, String attributeSet)
+ throws TransformerException
+ {
+ for (Iterator i = stylesheet.attributeSets.iterator(); i.hasNext(); )
+ {
+ AttributeSet as = (AttributeSet) i.next();
+ if (!as.name.equals(attributeSet))
+ {
+ continue;
+ }
+ if (as.uas != null)
+ {
+ StringTokenizer st = new StringTokenizer(as.uas, " ");
+ while (st.hasMoreTokens())
+ {
+ addAttributeSet(stylesheet, mode, context, pos, len,
+ parent, nextSibling, st.nextToken());
+ }
+ }
+ if (as.children != null)
+ {
+ as.children.apply(stylesheet, mode,
+ context, pos, len,
+ parent, nextSibling);
+ }
+ }
+ }
+
+ public String toString()
+ {
+ StringBuffer buf = new StringBuffer(getClass().getName());
+ buf.append('[');
+ buf.append(']');
+ return buf.toString();
+ }
+
+}
diff --git a/libjava/gnu/xml/transform/CopyOfNode.java b/libjava/gnu/xml/transform/CopyOfNode.java
new file mode 100644
index 0000000..7ca1006
--- /dev/null
+++ b/libjava/gnu/xml/transform/CopyOfNode.java
@@ -0,0 +1,179 @@
+/* CopyOfNode.java --
+ Copyright (C) 2004 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.transform;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import javax.xml.namespace.QName;
+import javax.xml.transform.TransformerException;
+import org.w3c.dom.Document;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.Text;
+import gnu.xml.xpath.Expr;
+
+/**
+ * A template node representing an XSLT <code>copy-of</code> instruction.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+final class CopyOfNode
+ extends TemplateNode
+{
+
+ final Expr select;
+
+ CopyOfNode(TemplateNode children, TemplateNode next, Expr select)
+ {
+ super(children, next);
+ this.select = select;
+ }
+
+ TemplateNode clone(Stylesheet stylesheet)
+ {
+ return new CopyOfNode((children == null) ? null :
+ children.clone(stylesheet),
+ (next == null) ? null :
+ next.clone(stylesheet),
+ select.clone(stylesheet));
+ }
+
+ void doApply(Stylesheet stylesheet, QName mode,
+ Node context, int pos, int len,
+ Node parent, Node nextSibling)
+ throws TransformerException
+ {
+ Object ret = select.evaluate(context, pos, len);
+ Document doc = (parent instanceof Document) ? (Document) parent :
+ parent.getOwnerDocument();
+ if (ret instanceof Collection)
+ {
+ Collection ns = (Collection) ret;
+ List list = new ArrayList(ns);
+ Collections.sort(list, documentOrderComparator);
+ for (Iterator i = list.iterator(); i.hasNext(); )
+ {
+ Node src = (Node) i.next();
+ short nodeType = src.getNodeType();
+ if (nodeType == Node.DOCUMENT_NODE)
+ {
+ // Use document element
+ src = ((Document) src).getDocumentElement();
+ if (src == null)
+ {
+ continue;
+ }
+ nodeType = Node.ELEMENT_NODE;
+ }
+ else if (nodeType == Node.ATTRIBUTE_NODE)
+ {
+ if (parent.getFirstChild() != null)
+ {
+ // Ignore attempt to add attribute after children
+ continue;
+ }
+ }
+ if (parent.getNodeType() == Node.ATTRIBUTE_NODE &&
+ nodeType != Node.TEXT_NODE &&
+ nodeType != Node.ENTITY_REFERENCE_NODE)
+ {
+ // Ignore
+ continue;
+ }
+ Node node = src.cloneNode(true);
+ node = doc.adoptNode(node);
+ if (nodeType == Node.ATTRIBUTE_NODE)
+ {
+ NamedNodeMap attrs = parent.getAttributes();
+ if (attrs != null)
+ {
+ attrs.setNamedItemNS(node);
+ }
+ }
+ else
+ {
+ if (nextSibling != null)
+ {
+ parent.insertBefore(node, nextSibling);
+ }
+ else
+ {
+ parent.appendChild(node);
+ }
+ }
+ }
+ }
+ else
+ {
+ String value = Expr._string(context, ret);
+ if (value != null && value.length() > 0)
+ {
+ Text textNode = doc.createTextNode(value);
+ if (nextSibling != null)
+ {
+ parent.insertBefore(textNode, nextSibling);
+ }
+ else
+ {
+ parent.appendChild(textNode);
+ }
+ }
+ }
+ // copy-of doesn't process children
+ if (next != null)
+ {
+ next.apply(stylesheet, mode,
+ context, pos, len,
+ parent, nextSibling);
+ }
+ }
+
+ public String toString()
+ {
+ StringBuffer buf = new StringBuffer(getClass().getName());
+ buf.append('[');
+ buf.append("select=");
+ buf.append(select);
+ buf.append(']');
+ return buf.toString();
+ }
+
+}
diff --git a/libjava/gnu/xml/transform/CurrentFunction.java b/libjava/gnu/xml/transform/CurrentFunction.java
new file mode 100644
index 0000000..0c901be
--- /dev/null
+++ b/libjava/gnu/xml/transform/CurrentFunction.java
@@ -0,0 +1,98 @@
+/* CurrentFunction.java --
+ Copyright (C) 2004 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.transform;
+
+import java.util.Collections;
+import java.util.List;
+import javax.xml.xpath.XPathFunction;
+import javax.xml.xpath.XPathFunctionException;
+import org.w3c.dom.Node;
+import gnu.xml.xpath.Expr;
+import gnu.xml.xpath.Function;
+
+/**
+ * The XSLT <code>current()</code>function.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+final class CurrentFunction
+ extends Expr
+ implements Function, XPathFunction
+{
+
+ final Stylesheet stylesheet;
+
+ CurrentFunction(Stylesheet stylesheet)
+ {
+ this.stylesheet = stylesheet;
+ }
+
+ public Object evaluate(List args)
+ throws XPathFunctionException
+ {
+ // We can't do anything useful here.
+ // So much for the JAXP API...
+ return Collections.EMPTY_SET;
+ }
+
+ public void setArguments(List args)
+ {
+ }
+
+ public Object evaluate(Node context, int pos, int len)
+ {
+ return Collections.singleton(stylesheet.current);
+ }
+
+ public Expr clone(Object context)
+ {
+ Stylesheet s = stylesheet;
+ if (context instanceof Stylesheet)
+ {
+ s = (Stylesheet) context;
+ }
+ return new CurrentFunction(s);
+ }
+
+ public String toString()
+ {
+ return "current()";
+ }
+
+}
+
diff --git a/libjava/gnu/xml/transform/DOMSourceLocator.java b/libjava/gnu/xml/transform/DOMSourceLocator.java
new file mode 100644
index 0000000..b9ef705
--- /dev/null
+++ b/libjava/gnu/xml/transform/DOMSourceLocator.java
@@ -0,0 +1,84 @@
+/* DOMSourceLocator.java --
+ Copyright (C) 2004 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.transform;
+
+import javax.xml.transform.dom.DOMLocator;
+import org.w3c.dom.Node;
+
+/**
+ * Simple DOMLocator implementation.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+class DOMSourceLocator
+ implements DOMLocator
+{
+
+ final Node node;
+
+ DOMSourceLocator(Node node)
+ {
+ this.node = node;
+ }
+
+ public Node getOriginatingNode()
+ {
+ return node;
+ }
+
+ public String getPublicId()
+ {
+ return null;
+ }
+
+ public String getSystemId()
+ {
+ return null;
+ }
+
+ public int getLineNumber()
+ {
+ return -1;
+ }
+
+ public int getColumnNumber()
+ {
+ return -1;
+ }
+
+}
diff --git a/libjava/gnu/xml/transform/DocumentFunction.java b/libjava/gnu/xml/transform/DocumentFunction.java
new file mode 100644
index 0000000..03f22b4
--- /dev/null
+++ b/libjava/gnu/xml/transform/DocumentFunction.java
@@ -0,0 +1,249 @@
+/* DocumentFunction.java --
+ Copyright (C) 2004 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.transform;
+
+import java.io.InputStream;
+import java.io.IOException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.TreeSet;
+import javax.xml.transform.ErrorListener;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.URIResolver;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.xpath.XPathFunction;
+import javax.xml.xpath.XPathFunctionException;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import gnu.xml.xpath.Constant;
+import gnu.xml.xpath.Expr;
+import gnu.xml.xpath.Function;
+import gnu.xml.xpath.IdFunction;
+
+/**
+ * The XSLT <code>document()</code>function.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+final class DocumentFunction
+ extends Expr
+ implements Function, XPathFunction
+{
+
+ final Stylesheet stylesheet;
+ final Node base;
+ List args;
+ List values;
+
+ DocumentFunction(Stylesheet stylesheet, Node base)
+ {
+ this.stylesheet = stylesheet;
+ this.base = base;
+ }
+
+ public Object evaluate(List args)
+ throws XPathFunctionException
+ {
+ values = args;
+ return evaluate(null, 1, 1);
+ }
+
+ public void setArguments(List args)
+ {
+ this.args = args;
+ }
+
+ public Object evaluate(Node context, int pos, int len)
+ {
+ int arity = args.size();
+ if (values == null)
+ {
+ values = new ArrayList(arity);
+ for (int i = 0; i < arity; i++)
+ {
+ Expr arg = (Expr) args.get(i);
+ values.add(arg.evaluate(context, pos, len));
+ }
+ }
+ Object ret;
+ switch (arity)
+ {
+ case 1:
+ Object arg = values.get(0);
+ if (arg instanceof Collection)
+ {
+ Collection ns = (Collection) arg;
+ Collection acc = new TreeSet();
+ for (Iterator i = ns.iterator(); i.hasNext(); )
+ {
+ Node node = (Node) i.next();
+ String s = Expr.stringValue(node);
+ acc.addAll(document(s, node.getBaseURI()));
+ }
+ ret = acc;
+ }
+ else
+ {
+ String s = Expr._string(context, arg);
+ ret = document(s, base.getBaseURI());
+ }
+ break;
+ case 2:
+ Object arg1 = values.get(0);
+ Object arg2 = values.get(1);
+ if (!(arg2 instanceof Collection))
+ {
+ throw new RuntimeException("second argument is not a node-set");
+ }
+ Collection arg2ns = (Collection) arg2;
+ String base2 = arg2ns.isEmpty() ? null :
+ ((Node) arg2ns.iterator().next()).getBaseURI();
+ if (arg1 instanceof Collection)
+ {
+ Collection arg1ns = (Collection) arg1;
+ Collection acc = new TreeSet();
+ for (Iterator i = arg1ns.iterator(); i.hasNext(); )
+ {
+ Node node = (Node) i.next();
+ String s = Expr.stringValue(node);
+ acc.addAll(document(s, base2));
+ }
+ ret = acc;
+ }
+ else
+ {
+ String s = Expr._string(context, arg1);
+ ret = document(s, base2);
+ }
+ break;
+ default:
+ throw new RuntimeException("invalid arity");
+ }
+ values = null;
+ return ret;
+ }
+
+ /**
+ * The XSL <code>document</code> function.
+ * @see XSLT 12.1
+ * @param uri the URI from which to retrieve nodes
+ * @param base the base URI for relative URIs
+ */
+ Collection document(String uri, String base)
+ {
+ if ("".equals(uri) || uri == null)
+ {
+ uri = this.base.getBaseURI();
+ }
+
+ // Get fragment
+ Expr fragment = null;
+ int hi = uri.indexOf('#');
+ if (hi != -1)
+ {
+ String f = uri.substring(hi + 1);
+ uri = uri.substring(0, hi);
+ // TODO handle xpointer() here
+ // this only handles IDs
+ fragment = new IdFunction(new Constant(f));
+ }
+
+ // Get document source
+ try
+ {
+ DOMSource source;
+ XSLURIResolver resolver = stylesheet.factory.resolver;
+ synchronized (resolver)
+ {
+ if (stylesheet.transformer != null)
+ {
+ resolver.setUserResolver(stylesheet.transformer.uriResolver);
+ resolver.setUserListener(stylesheet.transformer.errorListener);
+ }
+ source = resolver.resolveDOM(null, base, uri);
+ }
+ Node node = source.getNode();
+ if (fragment == null)
+ {
+ return Collections.singleton(node);
+ }
+ else
+ {
+ Object ret = fragment.evaluate(node, 1, 1);
+ if (!(ret instanceof Collection))
+ {
+ // XXX Report error?
+ return Collections.EMPTY_SET;
+ }
+ return (Collection) ret;
+ }
+ }
+ catch (TransformerException e)
+ {
+ String msg = "can't open " + uri;
+ if (base != null)
+ {
+ msg += " with base " + base;
+ }
+ throw new RuntimeException(msg);
+ }
+ }
+
+ public Expr clone(Object context)
+ {
+ Stylesheet s = stylesheet;
+ if (context instanceof Stylesheet)
+ {
+ s = (Stylesheet) context;
+ }
+ DocumentFunction f = new DocumentFunction(s, base);
+ int len = args.size();
+ List args2 = new ArrayList(len);
+ for (int i = 0; i < len; i++)
+ {
+ args2.add(((Expr) args.get(i)).clone(context));
+ }
+ f.setArguments(args2);
+ return f;
+ }
+
+}
diff --git a/libjava/gnu/xml/transform/ElementAvailableFunction.java b/libjava/gnu/xml/transform/ElementAvailableFunction.java
new file mode 100644
index 0000000..0c9ce44
--- /dev/null
+++ b/libjava/gnu/xml/transform/ElementAvailableFunction.java
@@ -0,0 +1,169 @@
+/* ElementAvailableFunction.java --
+ Copyright (C) 2004 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.transform;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.TreeSet;
+import javax.xml.namespace.NamespaceContext;
+import javax.xml.xpath.XPathFunction;
+import javax.xml.xpath.XPathFunctionException;
+import org.w3c.dom.Node;
+import gnu.xml.xpath.Expr;
+import gnu.xml.xpath.Function;
+
+/**
+ * The XSLT <code>element-available</code> function.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+class ElementAvailableFunction
+ extends Expr
+ implements Function, XPathFunction
+{
+
+ static final Collection elements;
+ static
+ {
+ TreeSet acc = new TreeSet();
+ acc.add("stylesheet");
+ acc.add("template");
+ acc.add("param");
+ acc.add("variable");
+ acc.add("include");
+ acc.add("import");
+ acc.add("output");
+ acc.add("preserve-space");
+ acc.add("strip-space");
+ acc.add("key");
+ acc.add("decimal-format");
+ acc.add("namespace-alias");
+ acc.add("attribute-set");
+ acc.add("apply-templates");
+ acc.add("call-template");
+ acc.add("value-of");
+ acc.add("for-each");
+ acc.add("if");
+ acc.add("choose");
+ acc.add("when");
+ acc.add("otherwise");
+ acc.add("element");
+ acc.add("attribute");
+ acc.add("text");
+ acc.add("copy");
+ acc.add("processing-instruction");
+ acc.add("comment");
+ acc.add("number");
+ acc.add("copy-of");
+ acc.add("message");
+ acc.add("sort");
+ acc.add("with-param");
+ acc.add("fallback");
+ acc.add("apply-imports");
+ elements = Collections.unmodifiableSet(acc);
+ }
+
+ final NamespaceContext nsctx;
+ List args;
+
+ ElementAvailableFunction(NamespaceContext nsctx)
+ {
+ this.nsctx = nsctx;
+ }
+
+ public Object evaluate(List args)
+ throws XPathFunctionException
+ {
+ // Useless...
+ return Collections.EMPTY_SET;
+ }
+
+ public void setArguments(List args)
+ {
+ this.args = args;
+ }
+
+ public Object evaluate(Node context, int pos, int len)
+ {
+ Expr arg = (Expr) args.get(0);
+ Object val = arg.evaluate(context, pos, len);
+ String name = _string(context, val);
+ String prefix, localName, uri;
+ int ci = name.indexOf(':');
+ if (ci == -1)
+ {
+ prefix = null;
+ localName = name;
+ }
+ else
+ {
+ prefix = name.substring(0, ci);
+ localName = name.substring(ci + 1);
+ }
+ uri = nsctx.getNamespaceURI(prefix);
+ if (Stylesheet.XSL_NS.equals(uri))
+ {
+ return elements.contains(localName) ?
+ Boolean.TRUE : Boolean.FALSE;
+ // TODO extension elements
+ }
+ return Boolean.FALSE;
+ }
+
+ public Expr clone(Object context)
+ {
+ NamespaceContext n = nsctx;
+ if (context instanceof NamespaceContext)
+ {
+ n = (NamespaceContext) context;
+ }
+ ElementAvailableFunction f = new ElementAvailableFunction(n);
+ int len = args.size();
+ List args2 = new ArrayList(len);
+ for (int i = 0; i < len; i++)
+ {
+ args2.add(((Expr) args.get(i)).clone(context));
+ }
+ f.setArguments(args2);
+ return f;
+ }
+
+}
+
diff --git a/libjava/gnu/xml/transform/ElementNode.java b/libjava/gnu/xml/transform/ElementNode.java
new file mode 100644
index 0000000..305989c0
--- /dev/null
+++ b/libjava/gnu/xml/transform/ElementNode.java
@@ -0,0 +1,279 @@
+/* ElementNode.java --
+ Copyright (C) 2004 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.transform;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.StringTokenizer;
+import javax.xml.XMLConstants;
+import javax.xml.namespace.QName;
+import javax.xml.transform.TransformerException;
+import org.w3c.dom.Document;
+import org.w3c.dom.DocumentFragment;
+import org.w3c.dom.Element;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import gnu.xml.xpath.Expr;
+
+/**
+ * A template node representing an XSL <code>element</code> instruction.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+final class ElementNode
+ extends TemplateNode
+{
+
+ final TemplateNode name;
+ final TemplateNode namespace;
+ final String uas;
+ final Node source;
+ final Collection elementExcludeResultPrefixes;
+
+ ElementNode(TemplateNode children, TemplateNode next, TemplateNode name,
+ TemplateNode namespace, String uas, Node source)
+ {
+ super(children, next);
+ this.name = name;
+ this.namespace = namespace;
+ this.uas = uas;
+ this.source = source;
+ NamedNodeMap attrs = source.getAttributes();
+ Node attr = attrs.getNamedItemNS(Stylesheet.XSL_NS,
+ "exclude-result-prefixes");
+ if (attr != null)
+ {
+ elementExcludeResultPrefixes = new HashSet();
+ StringTokenizer st = new StringTokenizer(attr.getNodeValue());
+ while (st.hasMoreTokens())
+ {
+ elementExcludeResultPrefixes.add(st.nextToken());
+ }
+ }
+ else
+ {
+ elementExcludeResultPrefixes = Collections.EMPTY_SET;
+ }
+ }
+
+ TemplateNode clone(Stylesheet stylesheet)
+ {
+ return new ElementNode((children == null) ? null :
+ children.clone(stylesheet),
+ (next == null) ? null :
+ next.clone(stylesheet),
+ name.clone(stylesheet),
+ (namespace == null) ? null :
+ namespace.clone(stylesheet),
+ uas, source);
+ }
+
+ void doApply(Stylesheet stylesheet, QName mode,
+ Node context, int pos, int len,
+ Node parent, Node nextSibling)
+ throws TransformerException
+ {
+ Document doc = (parent instanceof Document) ? (Document) parent :
+ parent.getOwnerDocument();
+ // Create a document fragment to hold the name
+ DocumentFragment fragment = doc.createDocumentFragment();
+ // Apply name to the fragment
+ name.apply(stylesheet, mode,
+ context, pos, len,
+ fragment, null);
+ // Use XPath string-value of fragment
+ String nameValue = Expr.stringValue(fragment);
+
+ String namespaceValue = null;
+ if (namespace != null)
+ {
+ // Create a document fragment to hold the namespace
+ fragment = doc.createDocumentFragment();
+ // Apply namespace to the fragment
+ namespace.apply(stylesheet, mode,
+ context, pos, len,
+ fragment, null);
+ // Use XPath string-value of fragment
+ namespaceValue = Expr.stringValue(fragment);
+ if (namespaceValue.length() == 0)
+ {
+ namespaceValue = null;
+ }
+ }
+
+ String prefix = getPrefix(nameValue);
+ if (XMLConstants.XMLNS_ATTRIBUTE.equals(prefix))
+ {
+ int ci = nameValue.indexOf(':');
+ nameValue = nameValue.substring(ci + 1);
+ }
+ else
+ {
+ // Namespace aliasing
+ if (prefix == null)
+ {
+ prefix = "#default";
+ }
+ String resultPrefix =
+ (String) stylesheet.namespaceAliases.get(prefix);
+ if (resultPrefix != null)
+ {
+ if ("#default".equals(resultPrefix))
+ {
+ resultPrefix = null;
+ }
+ namespaceValue = source.lookupNamespaceURI(resultPrefix);
+ }
+ if (prefix == "#default")
+ {
+ prefix = null;
+ }
+ // Look up ordinary namespace for this prefix
+ if (namespaceValue == null)
+ {
+ if (XMLConstants.XML_NS_PREFIX.equals(prefix))
+ {
+ namespaceValue = XMLConstants.XML_NS_URI;
+ }
+ else
+ {
+ // Resolve namespace for this prefix
+ namespaceValue = source.lookupNamespaceURI(prefix);
+ }
+ }
+ /*if (prefix == null)
+ {
+ // Resolve prefix for this namespace
+ prefix = parent.lookupPrefix(namespaceValue);
+ if (prefix != null)
+ {
+ nameValue = prefix + ":" + nameValue;
+ }
+ }*/
+ }
+ // Create element
+ Element element = (namespaceValue != null) ?
+ doc.createElementNS(namespaceValue, nameValue) :
+ doc.createElement(nameValue);
+ if (nextSibling != null)
+ {
+ parent.insertBefore(element, nextSibling);
+ }
+ else
+ {
+ parent.appendChild(element);
+ }
+ stylesheet.addNamespaceNodes(source, element, doc,
+ elementExcludeResultPrefixes);
+ if (uas != null)
+ {
+ StringTokenizer st = new StringTokenizer(uas, " ");
+ while (st.hasMoreTokens())
+ {
+ addAttributeSet(stylesheet, mode, context, pos, len,
+ element, null, st.nextToken());
+ }
+ }
+ if (children != null)
+ {
+ children.apply(stylesheet, mode,
+ context, pos, len,
+ element, null);
+ }
+ if (next != null)
+ {
+ next.apply(stylesheet, mode,
+ context, pos, len,
+ parent, nextSibling);
+ }
+ }
+
+ final String getPrefix(String name)
+ {
+ int ci = name.indexOf(':');
+ return (ci == -1) ? null : name.substring(0, ci);
+ }
+
+ void addAttributeSet(Stylesheet stylesheet, QName mode,
+ Node context, int pos, int len,
+ Node parent, Node nextSibling, String attributeSet)
+ throws TransformerException
+ {
+ for (Iterator i = stylesheet.attributeSets.iterator(); i.hasNext(); )
+ {
+ AttributeSet as = (AttributeSet) i.next();
+ if (!as.name.equals(attributeSet))
+ {
+ continue;
+ }
+ if (as.uas != null)
+ {
+ StringTokenizer st = new StringTokenizer(as.uas, " ");
+ while (st.hasMoreTokens())
+ {
+ addAttributeSet(stylesheet, mode, context, pos, len,
+ parent, nextSibling, st.nextToken());
+ }
+ }
+ if (as.children != null)
+ {
+ as.children.apply(stylesheet, mode,
+ context, pos, len,
+ parent, nextSibling);
+ }
+ }
+ }
+
+ public String toString()
+ {
+ StringBuffer buf = new StringBuffer(getClass().getName());
+ buf.append('[');
+ buf.append("name=");
+ buf.append(name);
+ if (uas != null)
+ {
+ buf.append(",uas=");
+ buf.append(uas);
+ }
+ buf.append(']');
+ return buf.toString();
+ }
+
+}
diff --git a/libjava/gnu/xml/transform/ErrorListenerErrorHandler.java b/libjava/gnu/xml/transform/ErrorListenerErrorHandler.java
new file mode 100644
index 0000000..929f7f0
--- /dev/null
+++ b/libjava/gnu/xml/transform/ErrorListenerErrorHandler.java
@@ -0,0 +1,101 @@
+/* ErrorListenerErrorHandler.java --
+ Copyright (C) 2004 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.transform;
+
+import javax.xml.transform.ErrorListener;
+import javax.xml.transform.TransformerException;
+import org.xml.sax.ErrorHandler;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+
+/**
+ * An ErrorHandler that wraps an ErrorListener.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+class ErrorListenerErrorHandler
+ implements ErrorHandler
+{
+
+ final ErrorListener listener;
+
+ ErrorListenerErrorHandler(ErrorListener listener)
+ {
+ this.listener = listener;
+ }
+
+ public void warning(SAXParseException e)
+ throws SAXException
+ {
+ try
+ {
+ listener.warning(new TransformerException(e));
+ }
+ catch (TransformerException e2)
+ {
+ throw new SAXException(e2);
+ }
+ }
+
+ public void error(SAXParseException e)
+ throws SAXException
+ {
+ try
+ {
+ listener.error(new TransformerException(e));
+ }
+ catch (TransformerException e2)
+ {
+ throw new SAXException(e2);
+ }
+ }
+
+ public void fatalError(SAXParseException e)
+ throws SAXException
+ {
+ try
+ {
+ listener.fatalError(new TransformerException(e));
+ }
+ catch (TransformerException e2)
+ {
+ throw new SAXException(e2);
+ }
+ }
+
+}
diff --git a/libjava/gnu/xml/transform/ForEachNode.java b/libjava/gnu/xml/transform/ForEachNode.java
new file mode 100644
index 0000000..af96628
--- /dev/null
+++ b/libjava/gnu/xml/transform/ForEachNode.java
@@ -0,0 +1,149 @@
+/* ForEachNode.java --
+ Copyright (C) 2004 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.transform;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import javax.xml.namespace.QName;
+import javax.xml.transform.TransformerException;
+import org.w3c.dom.Node;
+import gnu.xml.xpath.Expr;
+
+/**
+ * A template node representing an XSLT <code>for-each</code> instruction.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+final class ForEachNode
+ extends TemplateNode
+{
+
+ final Expr select;
+ final List sortKeys;
+
+ ForEachNode(TemplateNode children, TemplateNode next, Expr select,
+ List sortKeys)
+ {
+ super(children, next);
+ this.select = select;
+ this.sortKeys = sortKeys;
+ }
+
+ TemplateNode clone(Stylesheet stylesheet)
+ {
+ int len = sortKeys.size();
+ List sortKeys2 = new ArrayList(len);
+ for (int i = 0; i < len; i++)
+ {
+ sortKeys2.add(((Key) sortKeys.get(i)).clone(stylesheet));
+ }
+ return new ForEachNode((children == null) ? null :
+ children.clone(stylesheet),
+ (next == null) ? null :
+ next.clone(stylesheet),
+ select.clone(stylesheet),
+ sortKeys2);
+ }
+
+ void doApply(Stylesheet stylesheet, QName mode,
+ Node context, int pos, int len,
+ Node parent, Node nextSibling)
+ throws TransformerException
+ {
+ if (children != null)
+ {
+ // Set current template to null
+ Template saved = stylesheet.currentTemplate;
+ stylesheet.currentTemplate = null;
+ Object ret = select.evaluate(context, pos, len);
+ //System.err.println(toString() + ": " + context+" -> "+ret);
+ if (ret instanceof Collection)
+ {
+ Collection ns = (Collection) ret;
+ List list = new ArrayList(ns);
+ if (sortKeys != null)
+ {
+ for (Iterator i = sortKeys.iterator(); i.hasNext(); )
+ {
+ SortKey sortKey = (SortKey) i.next();
+ sortKey.init(stylesheet, mode, context, pos, len, parent,
+ nextSibling);
+ }
+ Collections.sort(list, new XSLComparator(sortKeys));
+ }
+ else
+ {
+ Collections.sort(list, documentOrderComparator);
+ }
+ // Perform children for each node
+ int l = list.size();
+ int p = 1;
+ for (Iterator i = list.iterator(); i.hasNext(); )
+ {
+ Node node = (Node) i.next();
+ stylesheet.current = node;
+ children.apply(stylesheet, mode,
+ node, p++, l,
+ parent, nextSibling);
+ }
+ }
+ // Restore current template
+ stylesheet.currentTemplate = saved;
+ }
+ if (next != null)
+ {
+ next.apply(stylesheet, mode,
+ context, pos, len,
+ parent, nextSibling);
+ }
+ }
+
+ public String toString()
+ {
+ StringBuffer buf = new StringBuffer(getClass().getName());
+ buf.append('[');
+ buf.append("select=");
+ buf.append(select);
+ buf.append(']');
+ return buf.toString();
+ }
+
+}
diff --git a/libjava/gnu/xml/transform/FormatNumberFunction.java b/libjava/gnu/xml/transform/FormatNumberFunction.java
new file mode 100644
index 0000000..73c5af4
--- /dev/null
+++ b/libjava/gnu/xml/transform/FormatNumberFunction.java
@@ -0,0 +1,133 @@
+/* FormatNumberFunction.java --
+ Copyright (C) 2004 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.transform;
+
+import java.text.DecimalFormat;
+import java.text.DecimalFormatSymbols;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import javax.xml.xpath.XPathFunction;
+import javax.xml.xpath.XPathFunctionException;
+import org.w3c.dom.Node;
+import gnu.xml.xpath.Expr;
+import gnu.xml.xpath.Function;
+
+/**
+ * The XSLT <code>format-number()</code>function.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+final class FormatNumberFunction
+ extends Expr
+ implements XPathFunction, Function
+{
+
+ final Stylesheet stylesheet;
+ List args;
+
+ FormatNumberFunction(Stylesheet stylesheet)
+ {
+ this.stylesheet = stylesheet;
+ }
+
+ public Object evaluate(List args)
+ throws XPathFunctionException
+ {
+ // Useless...
+ return Collections.EMPTY_SET;
+ }
+
+ public void setArguments(List args)
+ {
+ this.args = args;
+ }
+
+ public Object evaluate(Node context, int pos, int len)
+ {
+ int arity = args.size();
+ List values = new ArrayList(arity);
+ for (int i = 0; i < arity; i++)
+ {
+ Expr arg = (Expr) args.get(i);
+ values.add(arg.evaluate(context, pos, len));
+ }
+ double number = _number(context, values.get(0));
+ String pattern = _string(context, values.get(1));
+ // Currency symbol &#x00a4; is not supposed to be present
+ if (pattern.indexOf('\u00a4') != -1)
+ {
+ // Replace with $ (Xalan does this)
+ pattern = pattern.replace('\u00a4', '$');
+ }
+ String dfName = null;
+ if (arity > 2)
+ {
+ dfName = _string(context, values.get(2));
+ // otherwise the default decimal-format will be used
+ }
+ DecimalFormat df = (DecimalFormat) stylesheet.decimalFormats.get(dfName);
+ if (df == null)
+ {
+ throw new IllegalArgumentException("No such decimal-format: " +
+ dfName);
+ }
+ df.applyLocalizedPattern(pattern);
+ return df.format(number);
+ }
+
+ public Expr clone(Object context)
+ {
+ Stylesheet s = stylesheet;
+ if (context instanceof Stylesheet)
+ {
+ s = (Stylesheet) context;
+ }
+ FormatNumberFunction f = new FormatNumberFunction(s);
+ int len = args.size();
+ List args2 = new ArrayList(len);
+ for (int i = 0; i < len; i++)
+ {
+ args2.add(((Expr) args.get(i)).clone(context));
+ }
+ f.setArguments(args2);
+ return f;
+ }
+
+}
+
diff --git a/libjava/gnu/xml/transform/FunctionAvailableFunction.java b/libjava/gnu/xml/transform/FunctionAvailableFunction.java
new file mode 100644
index 0000000..2931082
--- /dev/null
+++ b/libjava/gnu/xml/transform/FunctionAvailableFunction.java
@@ -0,0 +1,175 @@
+/* FunctionAvailableFunction.java --
+ Copyright (C) 2004 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.transform;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.TreeSet;
+import javax.xml.namespace.NamespaceContext;
+import javax.xml.xpath.XPathFunction;
+import javax.xml.xpath.XPathFunctionException;
+import org.w3c.dom.Node;
+import gnu.xml.xpath.Expr;
+import gnu.xml.xpath.Function;
+
+/**
+ * The XSLT <code>function-available</code> function.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+class FunctionAvailableFunction
+ extends Expr
+ implements Function, XPathFunction
+{
+
+ static final Collection xsltFunctions;
+ static final Collection xpathFunctions;
+ static
+ {
+ TreeSet acc = new TreeSet();
+ acc.add("document");
+ acc.add("key");
+ acc.add("format-number");
+ acc.add("current");
+ acc.add("unparsed-entity-uri");
+ acc.add("generate-id");
+ acc.add("system-property");
+ acc.add("element-available");
+ acc.add("function-available");
+ xsltFunctions = Collections.unmodifiableSet(acc);
+ acc = new TreeSet();
+ acc.add("boolean");
+ acc.add("ceiling");
+ acc.add("concat");
+ acc.add("contains");
+ acc.add("count");
+ acc.add("false");
+ acc.add("floor");
+ acc.add("id");
+ acc.add("lang");
+ acc.add("last");
+ acc.add("local-name");
+ acc.add("name");
+ acc.add("namespace-uri");
+ acc.add("normalize-space");
+ acc.add("not");
+ acc.add("number");
+ acc.add("position");
+ acc.add("round");
+ acc.add("starts-with");
+ acc.add("string");
+ acc.add("string-length");
+ acc.add("substring-after");
+ acc.add("substring-before");
+ acc.add("substring");
+ acc.add("sum");
+ acc.add("translate");
+ acc.add("true");
+ xpathFunctions = Collections.unmodifiableSet(acc);
+ }
+
+ final NamespaceContext nsctx;
+ List args;
+
+ FunctionAvailableFunction(NamespaceContext nsctx)
+ {
+ this.nsctx = nsctx;
+ }
+
+ public Object evaluate(List args)
+ throws XPathFunctionException
+ {
+ // Useless...
+ return Collections.EMPTY_SET;
+ }
+
+ public void setArguments(List args)
+ {
+ this.args = args;
+ }
+
+ public Object evaluate(Node context, int pos, int len)
+ {
+ Expr arg = (Expr) args.get(0);
+ Object val = arg.evaluate(context, pos, len);
+ String name = _string(context, val);
+ String prefix, localName, uri;
+ int ci = name.indexOf(':');
+ if (ci == -1)
+ {
+ prefix = null;
+ localName = name;
+ }
+ else
+ {
+ prefix = name.substring(0, ci);
+ localName = name.substring(ci + 1);
+ }
+ uri = nsctx.getNamespaceURI(prefix);
+ if (uri == null)
+ {
+ return xsltFunctions.contains(localName) ||
+ xpathFunctions.contains(localName) ?
+ Boolean.TRUE : Boolean.FALSE;
+ // TODO extension functions
+ }
+ return Boolean.FALSE;
+ }
+
+ public Expr clone(Object context)
+ {
+ NamespaceContext n = nsctx;
+ if (context instanceof NamespaceContext)
+ {
+ n = (NamespaceContext) context;
+ }
+ FunctionAvailableFunction f = new FunctionAvailableFunction(n);
+ int len = args.size();
+ List args2 = new ArrayList(len);
+ for (int i = 0; i < len; i++)
+ {
+ args2.add(((Expr) args.get(i)).clone(context));
+ }
+ f.setArguments(args2);
+ return f;
+ }
+
+}
+
diff --git a/libjava/gnu/xml/transform/GenerateIdFunction.java b/libjava/gnu/xml/transform/GenerateIdFunction.java
new file mode 100644
index 0000000..9e48f79
--- /dev/null
+++ b/libjava/gnu/xml/transform/GenerateIdFunction.java
@@ -0,0 +1,126 @@
+/* GenerateIdFunction.java --
+ Copyright (C) 2004 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.transform;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import javax.xml.xpath.XPathFunction;
+import javax.xml.xpath.XPathFunctionException;
+import org.w3c.dom.Node;
+import gnu.xml.xpath.Expr;
+import gnu.xml.xpath.Function;
+
+/**
+ * The XSLT <code>generate-id()</code>function.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+final class GenerateIdFunction
+ extends Expr
+ implements XPathFunction, Function
+{
+
+ List args;
+
+ public Object evaluate(List args)
+ throws XPathFunctionException
+ {
+ // Useless...
+ return Collections.EMPTY_SET;
+ }
+
+ public void setArguments(List args)
+ {
+ this.args = args;
+ }
+
+ public Object evaluate(Node context, int pos, int len)
+ {
+ int arity = args.size();
+ List values = new ArrayList(arity);
+ for (int i = 0; i < arity; i++)
+ {
+ Expr arg = (Expr) args.get(i);
+ values.add(arg.evaluate(context, pos, len));
+ }
+ Node node;
+ Collection ns = (arity == 0) ? Collections.EMPTY_SET :
+ (Collection) values.get(0);
+ if (ns.isEmpty())
+ {
+ node = context;
+ }
+ else
+ {
+ List list = new ArrayList(ns);
+ Collections.sort(list, documentOrderComparator);
+ node = (Node) list.get(0);
+ }
+
+ String name = node.getNodeName();
+ int index = 0, depth = 0;
+ for (Node ctx = node.getPreviousSibling(); ctx != null;
+ ctx = ctx.getPreviousSibling())
+ {
+ index++;
+ }
+ for (Node ctx = node.getParentNode(); ctx != null;
+ ctx = ctx.getParentNode())
+ {
+ depth++;
+ }
+ return name + "-" + index + "-" + depth;
+ }
+
+ public Expr clone(Object context)
+ {
+ GenerateIdFunction f = new GenerateIdFunction();
+ int len = args.size();
+ List args2 = new ArrayList(len);
+ for (int i = 0; i < len; i++)
+ {
+ args2.add(((Expr) args.get(i)).clone(context));
+ }
+ f.setArguments(args2);
+ return f;
+ }
+
+}
+
diff --git a/libjava/gnu/xml/transform/IfNode.java b/libjava/gnu/xml/transform/IfNode.java
new file mode 100644
index 0000000..c977348
--- /dev/null
+++ b/libjava/gnu/xml/transform/IfNode.java
@@ -0,0 +1,107 @@
+/* IfNode.java --
+ Copyright (C) 2004 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.transform;
+
+import javax.xml.namespace.QName;
+import javax.xml.transform.TransformerException;
+import org.w3c.dom.Node;
+import gnu.xml.xpath.Expr;
+
+/**
+ * A template node representing an XSL <code>if</code> instruction.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+final class IfNode
+ extends TemplateNode
+{
+
+ final Expr test;
+
+ IfNode(TemplateNode children, TemplateNode next, Expr test)
+ {
+ super(children, next);
+ this.test = test;
+ }
+
+ TemplateNode clone(Stylesheet stylesheet)
+ {
+ return new IfNode((children == null) ? null :
+ children.clone(stylesheet),
+ (next == null) ? null :
+ next.clone(stylesheet),
+ test.clone(stylesheet));
+ }
+
+ void doApply(Stylesheet stylesheet, QName mode,
+ Node context, int pos, int len,
+ Node parent, Node nextSibling)
+ throws TransformerException
+ {
+ Object ret = test.evaluate(context, pos, len);
+ boolean success = (ret instanceof Boolean) ?
+ ((Boolean) ret).booleanValue() :
+ Expr._boolean(context, ret);
+ if (success)
+ {
+ if (children != null)
+ {
+ children.apply(stylesheet, mode,
+ context, pos, len,
+ parent, nextSibling);
+ }
+ }
+ if (next != null)
+ {
+ next.apply(stylesheet, mode,
+ context, pos, len,
+ parent, nextSibling);
+ }
+ }
+
+ public String toString()
+ {
+ StringBuffer buf = new StringBuffer(getClass().getName());
+ buf.append('[');
+ buf.append("test=");
+ buf.append(test);
+ buf.append(']');
+ return buf.toString();
+ }
+
+}
diff --git a/libjava/gnu/xml/transform/Key.java b/libjava/gnu/xml/transform/Key.java
new file mode 100644
index 0000000..008400f
--- /dev/null
+++ b/libjava/gnu/xml/transform/Key.java
@@ -0,0 +1,71 @@
+/* Key.java --
+ Copyright (C) 2004 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.transform;
+
+import javax.xml.namespace.QName;
+import gnu.xml.xpath.Expr;
+import gnu.xml.xpath.Pattern;
+
+/**
+ * An XSL key.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+class Key
+{
+
+ final QName name;
+ final Pattern match;
+ final Expr use;
+
+ Key(QName name, Pattern match, Expr use)
+ {
+ this.name = name;
+ this.match = match;
+ this.use = use;
+ }
+
+ Key clone(Stylesheet stylesheet)
+ {
+ return new Key(name,
+ (Pattern) match.clone(stylesheet),
+ use.clone(stylesheet));
+ }
+
+}
+
diff --git a/libjava/gnu/xml/transform/KeyFunction.java b/libjava/gnu/xml/transform/KeyFunction.java
new file mode 100644
index 0000000..233c12f
--- /dev/null
+++ b/libjava/gnu/xml/transform/KeyFunction.java
@@ -0,0 +1,216 @@
+/* KeyFunction.java --
+ Copyright (C) 2004 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.transform;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.LinkedList;
+import java.util.List;
+import javax.xml.namespace.QName;
+import javax.xml.xpath.XPathFunction;
+import javax.xml.xpath.XPathFunctionException;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import gnu.xml.xpath.Expr;
+import gnu.xml.xpath.Function;
+import gnu.xml.xpath.Pattern;
+
+/**
+ * The XSLT <code>key()</code>function.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+final class KeyFunction
+ extends Pattern
+ implements XPathFunction, Function
+{
+
+ final Stylesheet stylesheet;
+ List args;
+
+ KeyFunction(Stylesheet stylesheet)
+ {
+ this.stylesheet = stylesheet;
+ }
+
+ public Object evaluate(List args)
+ throws XPathFunctionException
+ {
+ // Useless...
+ return Collections.EMPTY_SET;
+ }
+
+ public void setArguments(List args)
+ {
+ this.args = args;
+ }
+
+ public boolean matches(Node context)
+ {
+ Object ret = evaluate(context, 1, 1);
+ return !((Collection) ret).isEmpty();
+ }
+
+ public Object evaluate(Node context, int pos, int len)
+ {
+ // Evaluate arguments
+ int arity = args.size();
+ List values = new ArrayList(arity);
+ for (int i = 0; i < arity; i++)
+ {
+ Expr arg = (Expr) args.get(i);
+ values.add(arg.evaluate(context, pos, len));
+ }
+ // Get key name
+ QName keyName = QName.valueOf(_string(context, values.get(0)));
+ // Expand qualified name
+ String uri = keyName.getNamespaceURI();
+ String prefix = keyName.getPrefix();
+ if ((uri == null || uri.length() == 0) &&
+ (prefix != null && prefix.length() > 0))
+ {
+ uri = stylesheet.getNamespaceURI(prefix);
+ if (uri != null && uri.length() > 0)
+ {
+ String localName = keyName.getLocalPart();
+ keyName = new QName(uri, localName, prefix);
+ }
+ }
+ // Compute matching key set
+ Collection keySet = new LinkedList();
+ for (Iterator i = stylesheet.keys.iterator(); i.hasNext(); )
+ {
+ Key key = (Key) i.next();
+ if (key.name.equals(keyName))
+ {
+ keySet.add(key);
+ }
+ }
+ // Get target
+ Object target = values.get(1);
+ Collection acc = new LinkedHashSet();
+ Document doc = (context instanceof Document) ? (Document) context :
+ context.getOwnerDocument();
+ if (target instanceof Collection)
+ {
+ for (Iterator i = ((Collection) target).iterator(); i.hasNext(); )
+ {
+ String val = Expr.stringValue((Node) i.next());
+ addKeyNodes(doc, keySet, val, acc);
+ }
+ }
+ else
+ {
+ String val = Expr._string(context, target);
+ addKeyNodes(doc, keySet, val, acc);
+ }
+ List ret = new ArrayList(acc);
+ Collections.sort(ret, documentOrderComparator);
+ return ret;
+ }
+
+ final void addKeyNodes(Node node, Collection keySet,
+ String value, Collection acc)
+ {
+ addKeyNodeIfMatch(node, keySet, value, acc);
+ // Apply children
+ for (Node ctx = node.getFirstChild(); ctx != null;
+ ctx = ctx.getNextSibling())
+ {
+ addKeyNodes(ctx, keySet, value, acc);
+ }
+ }
+
+ final void addKeyNodeIfMatch(Node node, Collection keySet,
+ String value, Collection acc)
+ {
+ for (Iterator i = keySet.iterator(); i.hasNext(); )
+ {
+ Key key = (Key) i.next();
+ if (key.match.matches(node))
+ {
+ Object eval = key.use.evaluate(node, 1, 1);
+ if (eval instanceof Collection)
+ {
+ for (Iterator j = ((Collection) eval).iterator();
+ j.hasNext(); )
+ {
+ String keyValue = Expr.stringValue((Node) j.next());
+ if (value.equals(keyValue))
+ {
+ acc.add(node);
+ return;
+ }
+ }
+ }
+ else
+ {
+ String keyValue = Expr._string(node, eval);
+ if (value.equals(keyValue))
+ {
+ acc.add(node);
+ return;
+ }
+ }
+ }
+ }
+ }
+
+ public Expr clone(Object context)
+ {
+ Stylesheet s = stylesheet;
+ if (context instanceof Stylesheet)
+ {
+ s = (Stylesheet) context;
+ }
+ KeyFunction f = new KeyFunction(s);
+ int len = args.size();
+ List args2 = new ArrayList(len);
+ for (int i = 0; i < len; i++)
+ {
+ args2.add(((Expr) args.get(i)).clone(context));
+ }
+ f.setArguments(args2);
+ return f;
+ }
+
+}
+
diff --git a/libjava/gnu/xml/transform/LiteralNode.java b/libjava/gnu/xml/transform/LiteralNode.java
new file mode 100644
index 0000000..1b5af1a
--- /dev/null
+++ b/libjava/gnu/xml/transform/LiteralNode.java
@@ -0,0 +1,225 @@
+/* LiteralNode.java --
+ Copyright (C) 2004 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.transform;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.StringTokenizer;
+import javax.xml.namespace.QName;
+import javax.xml.transform.TransformerException;
+import org.w3c.dom.Document;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+
+/**
+ * A template node that copies a DOM node in the template to the result
+ * tree.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+final class LiteralNode
+ extends TemplateNode
+{
+
+ /**
+ * The source node in the XSL template.
+ */
+ final Node source;
+
+ final Collection elementExcludeResultPrefixes;
+
+ LiteralNode(TemplateNode children, TemplateNode next, Node source)
+ {
+ super(children, next);
+ this.source = source;
+ if (source.getNodeType() == Node.ELEMENT_NODE)
+ {
+ NamedNodeMap attrs = source.getAttributes();
+ Node attr = attrs.getNamedItemNS(Stylesheet.XSL_NS,
+ "exclude-result-prefixes");
+ if (attr != null)
+ {
+ elementExcludeResultPrefixes = new HashSet();
+ StringTokenizer st = new StringTokenizer(attr.getNodeValue());
+ while (st.hasMoreTokens())
+ {
+ elementExcludeResultPrefixes.add(st.nextToken());
+ }
+ }
+ else
+ {
+ elementExcludeResultPrefixes = Collections.EMPTY_SET;
+ }
+ }
+ else
+ {
+ elementExcludeResultPrefixes = null;
+ }
+ }
+
+ TemplateNode clone(Stylesheet stylesheet)
+ {
+ return new LiteralNode((children == null) ? null :
+ children.clone(stylesheet),
+ (next == null) ? null :
+ next.clone(stylesheet),
+ source);
+ }
+
+ void doApply(Stylesheet stylesheet, QName mode,
+ Node context, int pos, int len,
+ Node parent, Node nextSibling)
+ throws TransformerException
+ {
+ Node result = null;
+ Document doc = (parent instanceof Document) ? (Document) parent :
+ parent.getOwnerDocument();
+ short nodeType = source.getNodeType();
+ if (nodeType == Node.ATTRIBUTE_NODE &&
+ parent.getFirstChild() != null)
+ {
+ // Ignore attributes added after child elements
+ }
+ else
+ {
+ // Namespace aliasing
+ if (nodeType == Node.ELEMENT_NODE)
+ {
+ String prefix = source.getPrefix();
+ if (prefix == null)
+ {
+ prefix = "#default";
+ }
+ String resultPrefix =
+ (String) stylesheet.namespaceAliases.get(prefix);
+ if (resultPrefix != null)
+ {
+ if ("#default".equals(resultPrefix))
+ {
+ resultPrefix = null;
+ }
+ String uri = source.lookupNamespaceURI(resultPrefix);
+ String name = source.getNodeName();
+ // Create a new element node in the result document
+ result = doc.createElementNS(uri, name);
+ // copy attributes
+ NamedNodeMap srcAttrs = source.getAttributes();
+ NamedNodeMap dstAttrs = result.getAttributes();
+ int l = srcAttrs.getLength();
+ for (int i = 0; i < l; i++)
+ {
+ Node attr = srcAttrs.item(i);
+ if (!Stylesheet.XSL_NS.equals(attr.getNamespaceURI()))
+ {
+ attr = attr.cloneNode(true);
+ attr = doc.adoptNode(attr);
+ dstAttrs.setNamedItemNS(attr);
+ }
+ }
+ }
+ }
+ if (result == null)
+ {
+ // Create result node
+ result = source.cloneNode(false);
+ // Remove any XSL attributes
+ NamedNodeMap attrs = result.getAttributes();
+ if (attrs != null)
+ {
+ int l = attrs.getLength();
+ for (int i = 0; i < l; i++)
+ {
+ Node attr = attrs.item(i);
+ if (Stylesheet.XSL_NS.equals(attr.getNamespaceURI()))
+ {
+ attrs.removeNamedItem(attr.getNodeName());
+ i--;
+ l--;
+ }
+ }
+ }
+ result = doc.adoptNode(result);
+ if (result == null)
+ {
+ String msg = "Error adopting node to result tree";
+ DOMSourceLocator l = new DOMSourceLocator(context);
+ throw new TransformerException(msg, l);
+ }
+ }
+ if (nextSibling != null)
+ {
+ parent.insertBefore(result, nextSibling);
+ }
+ else
+ {
+ parent.appendChild(result);
+ }
+ if (nodeType == Node.ELEMENT_NODE)
+ {
+ stylesheet.addNamespaceNodes(source, result, doc,
+ elementExcludeResultPrefixes);
+ }
+ // children
+ if (children != null)
+ {
+ children.apply(stylesheet, mode,
+ context, pos, len,
+ result, null);
+ }
+ }
+ // next sibling
+ if (next != null)
+ {
+ next.apply(stylesheet, mode,
+ context, pos, len,
+ parent, nextSibling);
+ }
+ }
+
+ public String toString()
+ {
+ StringBuffer buf = new StringBuffer(getClass().getName());
+ buf.append('[');
+ buf.append("source=");
+ buf.append(source);
+ buf.append(']');
+ return buf.toString();
+ }
+
+}
diff --git a/libjava/gnu/xml/transform/MessageNode.java b/libjava/gnu/xml/transform/MessageNode.java
new file mode 100644
index 0000000..c79060a
--- /dev/null
+++ b/libjava/gnu/xml/transform/MessageNode.java
@@ -0,0 +1,97 @@
+/* MessageNode.java --
+ Copyright (C) 2004 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.transform;
+
+import javax.xml.namespace.QName;
+import javax.xml.transform.TransformerException;
+import org.w3c.dom.Document;
+import org.w3c.dom.DocumentFragment;
+import org.w3c.dom.Node;
+import gnu.xml.xpath.Expr;
+
+/**
+ * An XSL <code>message</code> instruction.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+final class MessageNode
+ extends TemplateNode
+{
+
+ final boolean terminate;
+
+ MessageNode(TemplateNode children, TemplateNode next, boolean terminate)
+ {
+ super(children, next);
+ this.terminate = terminate;
+ }
+
+ TemplateNode clone(Stylesheet stylesheet)
+ {
+ return new MessageNode((children == null) ? null :
+ children.clone(stylesheet),
+ (next == null) ? null :
+ next.clone(stylesheet),
+ terminate);
+ }
+
+ void doApply(Stylesheet stylesheet, QName mode,
+ Node context, int pos, int len,
+ Node parent, Node nextSibling)
+ throws TransformerException
+ {
+ if (children != null)
+ {
+ Document doc = (parent instanceof Document) ? (Document) parent :
+ parent.getOwnerDocument();
+ DocumentFragment fragment = doc.createDocumentFragment();
+ children.apply(stylesheet, mode, context, pos, len, fragment, null);
+ String message = Expr.stringValue(fragment);
+ System.err.println(message);
+ if (terminate)
+ {
+ stylesheet.terminated = true;
+ }
+ }
+ if (next != null && !terminate)
+ {
+ next.apply(stylesheet, mode, context, pos, len, parent, nextSibling);
+ }
+ }
+
+}
diff --git a/libjava/gnu/xml/transform/NodeNumberNode.java b/libjava/gnu/xml/transform/NodeNumberNode.java
new file mode 100644
index 0000000..76f8d7e
--- /dev/null
+++ b/libjava/gnu/xml/transform/NodeNumberNode.java
@@ -0,0 +1,261 @@
+/* NodeNumberNode.java --
+ Copyright (C) 2004 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.transform;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import javax.xml.transform.TransformerException;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import gnu.xml.xpath.Expr;
+import gnu.xml.xpath.NodeTypeTest;
+import gnu.xml.xpath.Pattern;
+import gnu.xml.xpath.Selector;
+import gnu.xml.xpath.Test;
+import gnu.xml.xpath.UnionExpr;
+
+/**
+ * A template node representing the XSL <code>number</code> instruction
+ * with no <code>value</code> expression, i.e. the value is computed from
+ * the document position of the context node.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+final class NodeNumberNode
+ extends AbstractNumberNode
+{
+
+ static final int SINGLE = 0;
+ static final int MULTIPLE = 1;
+ static final int ANY = 2;
+
+ final int level;
+ final Pattern count;
+ final Pattern from;
+
+ NodeNumberNode(TemplateNode children, TemplateNode next,
+ int level, Pattern count, Pattern from,
+ TemplateNode format, String lang,
+ int letterValue, String groupingSeparator, int groupingSize)
+ {
+ super(children, next, format, lang, letterValue, groupingSeparator,
+ groupingSize);
+ this.level = level;
+ this.count = count;
+ this.from = from;
+ }
+
+ TemplateNode clone(Stylesheet stylesheet)
+ {
+ return new NodeNumberNode((children == null) ? null :
+ children.clone(stylesheet),
+ (next == null) ? null :
+ next.clone(stylesheet),
+ level,
+ (count == null) ? null :
+ (Pattern) count.clone(stylesheet),
+ (from == null) ? from :
+ (Pattern) from.clone(stylesheet),
+ format, lang, letterValue,
+ groupingSeparator, groupingSize);
+ }
+
+ int[] compute(Stylesheet stylesheet, Node context, int pos, int len)
+ throws TransformerException
+ {
+ /*if (from != null)
+ {
+ Object ret = from.evaluate(context, pos, len);
+ if (ret instanceof Collection)
+ {
+ Collection ns = (Collection) ret;
+ if (ns.size() > 0)
+ {
+ List list = new ArrayList(ns);
+ Collections.sort(list, documentOrderComparator);
+ context = (Node) list.get(0);
+ }
+ else
+ {
+ return new int[0];
+ }
+ }
+ else
+ {
+ return new int[0];
+ }
+ }*/
+ Node current = context;
+ switch (level)
+ {
+ case SINGLE:
+ if (from == null)
+ {
+ while (context != null && !countMatches(current, context))
+ {
+ context = context.getParentNode();
+ }
+ }
+ else
+ {
+ while (context != null && !countMatches(current, context) &&
+ !fromMatches(context))
+ {
+ context = context.getParentNode();
+ }
+ }
+ return (context == null) ? new int[0] :
+ new int[] { (context == current) ? pos : getIndex(current, context) };
+ case MULTIPLE:
+ List ancestors = new ArrayList();
+ while (context != null)
+ {
+ if (countMatches(current, context))
+ {
+ if (from == null || fromMatches(context))
+ {
+ ancestors.add(context);
+ }
+ }
+ context = context.getParentNode();
+ }
+ Collections.sort(ancestors, documentOrderComparator);
+ int[] ret = new int[ancestors.size()];
+ for (int i = 0; i < ret.length; i++)
+ {
+ ret[i] = getIndex(current, (Node) ancestors.get(i));
+ }
+ return ret;
+ case ANY:
+ Expr preceding = new Selector(Selector.PRECEDING,
+ Collections.EMPTY_LIST);
+ Expr ancestorOrSelf = new Selector(Selector.ANCESTOR_OR_SELF,
+ Collections.EMPTY_LIST);
+ Expr any = new UnionExpr(preceding, ancestorOrSelf);
+ Object eval = any.evaluate(context, pos, len);
+ if (eval instanceof Collection)
+ {
+ Collection ns = (Collection) eval;
+ List candidates = new ArrayList();
+ for (Iterator i = ns.iterator(); i.hasNext(); )
+ {
+ Node candidate = (Node) i.next();
+ if (countMatches(current, candidate))
+ {
+ candidates.add(candidate);
+ if (from != null && from.matches(candidate))
+ {
+ break;
+ }
+ }
+ }
+ return new int[] { candidates.size() };
+ }
+ return new int[0];
+ default:
+ throw new TransformerException("invalid level");
+ }
+ }
+
+ boolean countMatches(Node current, Node node)
+ {
+ if (count == null)
+ {
+ int cnt = current.getNodeType();
+ int nnt = node.getNodeType();
+ if (cnt != nnt)
+ {
+ return false;
+ }
+ if (nnt == Node.ELEMENT_NODE || nnt == Node.ATTRIBUTE_NODE)
+ {
+ String curi = current.getNamespaceURI();
+ String nuri = node.getNamespaceURI();
+ if ((curi == null && nuri != null) ||
+ (curi != null && !curi.equals(nuri)))
+ {
+ return false;
+ }
+ String cn = current.getLocalName();
+ String nn = current.getLocalName();
+ if (!cn.equals(nn))
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+ else
+ {
+ return count.matches(node);
+ }
+ }
+
+ boolean fromMatches(Node node)
+ {
+ for (Node ctx = node.getParentNode(); ctx != null;
+ ctx = ctx.getParentNode())
+ {
+ if (from.matches(ctx))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ int getIndex(Node current, Node node)
+ {
+ int index = 0;
+ do
+ {
+ do
+ {
+ node = node.getPreviousSibling();
+ }
+ while (node != null && !countMatches(current, node));
+ index++;
+ }
+ while (node != null);
+ return index;
+ }
+
+}
diff --git a/libjava/gnu/xml/transform/NumberNode.java b/libjava/gnu/xml/transform/NumberNode.java
new file mode 100644
index 0000000..0970ac9
--- /dev/null
+++ b/libjava/gnu/xml/transform/NumberNode.java
@@ -0,0 +1,86 @@
+/* NumberNode.java --
+ Copyright (C) 2004 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.transform;
+
+import javax.xml.transform.TransformerException;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import gnu.xml.xpath.Expr;
+
+/**
+ * A template node representing the XSL <code>number</code> instruction
+ * with a <code>value</code> expression.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+final class NumberNode
+ extends AbstractNumberNode
+{
+
+ final Expr value;
+
+ NumberNode(TemplateNode children, TemplateNode next,
+ Expr value, TemplateNode format, String lang,
+ int letterValue, String groupingSeparator, int groupingSize)
+ {
+ super(children, next, format, lang, letterValue, groupingSeparator,
+ groupingSize);
+ this.value = value;
+ }
+
+ TemplateNode clone(Stylesheet stylesheet)
+ {
+ return new NumberNode((children == null) ? null :
+ children.clone(stylesheet),
+ (next == null) ? null :
+ next.clone(stylesheet),
+ value.clone(stylesheet),
+ format, lang, letterValue,
+ groupingSeparator, groupingSize);
+ }
+
+ int[] compute(Stylesheet stylesheet, Node context, int pos, int len)
+ throws TransformerException
+ {
+ Object ret = value.evaluate(context, pos, len);
+ Double d = (ret instanceof Double) ? ((Double) ret) :
+ new Double(Expr._number(context, ret));
+ return new int[] { d.intValue() };
+ }
+
+}
diff --git a/libjava/gnu/xml/transform/OtherwiseNode.java b/libjava/gnu/xml/transform/OtherwiseNode.java
new file mode 100644
index 0000000..1d9590e
--- /dev/null
+++ b/libjava/gnu/xml/transform/OtherwiseNode.java
@@ -0,0 +1,93 @@
+/* OtherwiseNode.java --
+ Copyright (C) 2004 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.transform;
+
+import javax.xml.namespace.QName;
+import javax.xml.transform.TransformerException;
+import org.w3c.dom.Node;
+
+/**
+ * A template node representing an XSL <code>otherwise</code> instruction.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+final class OtherwiseNode
+ extends TemplateNode
+{
+
+ OtherwiseNode(TemplateNode children, TemplateNode next)
+ {
+ super(children, next);
+ }
+
+ TemplateNode clone(Stylesheet stylesheet)
+ {
+ return new OtherwiseNode((children == null) ? null :
+ children.clone(stylesheet),
+ (next == null) ? null :
+ next.clone(stylesheet));
+ }
+
+ void doApply(Stylesheet stylesheet, QName mode,
+ Node context, int pos, int len,
+ Node parent, Node nextSibling)
+ throws TransformerException
+ {
+ if (children != null)
+ {
+ children.apply(stylesheet, mode,
+ context, pos, len,
+ parent, nextSibling);
+ }
+ if (next != null)
+ {
+ next.apply(stylesheet, mode,
+ context, pos, len,
+ parent, nextSibling);
+ }
+ }
+
+ public String toString()
+ {
+ StringBuffer buf = new StringBuffer(getClass().getName());
+ buf.append('[');
+ buf.append(']');
+ return buf.toString();
+ }
+
+}
diff --git a/libjava/gnu/xml/transform/ParameterNode.java b/libjava/gnu/xml/transform/ParameterNode.java
new file mode 100644
index 0000000..5b766d5
--- /dev/null
+++ b/libjava/gnu/xml/transform/ParameterNode.java
@@ -0,0 +1,156 @@
+/* ParameterNode.java --
+ Copyright (C) 2004 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.transform;
+
+import java.util.Collections;
+import javax.xml.namespace.QName;
+import javax.xml.transform.TransformerException;
+import org.w3c.dom.Document;
+import org.w3c.dom.DocumentFragment;
+import org.w3c.dom.Node;
+import gnu.xml.xpath.Expr;
+
+/**
+ * A template node that sets a variable or parameter during template
+ * processing.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+final class ParameterNode
+ extends TemplateNode
+{
+
+ final String name;
+ final Expr select;
+ final boolean global;
+
+ ParameterNode(TemplateNode children, TemplateNode next,
+ String name, Expr select, boolean global)
+ {
+ super(children, next);
+ this.name = name;
+ this.select = select;
+ this.global = global;
+ }
+
+ TemplateNode clone(Stylesheet stylesheet)
+ {
+ return new ParameterNode((children == null) ? null :
+ children.clone(stylesheet),
+ (next == null) ? null :
+ next.clone(stylesheet),
+ name,
+ select.clone(stylesheet),
+ global);
+ }
+
+ void doApply(Stylesheet stylesheet, QName mode,
+ Node context, int pos, int len,
+ Node parent, Node nextSibling)
+ throws TransformerException
+ {
+ boolean apply = global || !stylesheet.bindings.containsKey(name, global);
+ if (apply)
+ {
+ // push the variable context
+ stylesheet.bindings.push(global);
+ // set the variable
+ Object value = getValue(stylesheet, mode, context, pos, len);
+ if (value != null)
+ {
+ stylesheet.bindings.set(name, value, global);
+ }
+ }
+ // variable and param don't process children as such
+ // all subsequent instructions are processed with that variable context
+ if (next != null)
+ {
+ next.apply(stylesheet, mode,
+ context, pos, len,
+ parent, nextSibling);
+ }
+ if (apply)
+ {
+ // pop the variable context
+ stylesheet.bindings.pop(global);
+ }
+ }
+
+ Object getValue(Stylesheet stylesheet, QName mode,
+ Node context, int pos, int len)
+ throws TransformerException
+ {
+ if (select != null)
+ {
+ return select.evaluate(context, pos, len);
+ }
+ else if (children != null)
+ {
+ Document doc = (context instanceof Document) ? (Document) context :
+ context.getOwnerDocument();
+ DocumentFragment fragment = doc.createDocumentFragment();
+ children.apply(stylesheet, mode, context, pos, len, fragment, null);
+ return Collections.singleton(fragment);
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+ public String toString()
+ {
+ StringBuffer buf = new StringBuffer(getClass().getName());
+ buf.append('[');
+ buf.append("name=");
+ buf.append(name);
+ if (select != null)
+ {
+ buf.append(",select=");
+ buf.append(select);
+ }
+ if (global)
+ {
+ buf.append(",global");
+ }
+ buf.append(']');
+ return buf.toString();
+ }
+
+}
+
diff --git a/libjava/gnu/xml/transform/ProcessingInstructionNode.java b/libjava/gnu/xml/transform/ProcessingInstructionNode.java
new file mode 100644
index 0000000..1eb2bd6
--- /dev/null
+++ b/libjava/gnu/xml/transform/ProcessingInstructionNode.java
@@ -0,0 +1,123 @@
+/* ProcessingInstructionNode.java --
+ Copyright (C) 2004 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.transform;
+
+import javax.xml.namespace.QName;
+import javax.xml.transform.TransformerException;
+import org.w3c.dom.Document;
+import org.w3c.dom.DocumentFragment;
+import org.w3c.dom.Node;
+import org.w3c.dom.ProcessingInstruction;
+import gnu.xml.xpath.Expr;
+
+/**
+ * A template node representing the XSL <code>processing-instruction</code>
+ * instruction.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+final class ProcessingInstructionNode
+ extends TemplateNode
+{
+
+ final String name;
+
+ ProcessingInstructionNode(TemplateNode children, TemplateNode next,
+ String name)
+ {
+ super(children, next);
+ this.name = name;
+ }
+
+ TemplateNode clone(Stylesheet stylesheet)
+ {
+ return new ProcessingInstructionNode((children == null) ? null :
+ children.clone(stylesheet),
+ (next == null) ? null :
+ next.clone(stylesheet),
+ name);
+ }
+
+ void doApply(Stylesheet stylesheet, QName mode,
+ Node context, int pos, int len,
+ Node parent, Node nextSibling)
+ throws TransformerException
+ {
+ String data = null;
+ Document doc = (parent instanceof Document) ? (Document) parent :
+ parent.getOwnerDocument();
+ if (children != null)
+ {
+ // Create a document fragment to hold the text
+ DocumentFragment fragment = doc.createDocumentFragment();
+ // Apply children to the fragment
+ children.apply(stylesheet, mode,
+ context, pos, len,
+ fragment, null);
+ // Use XPath string-value of fragment
+ data = Expr.stringValue(fragment);
+ }
+ ProcessingInstruction pi = doc.createProcessingInstruction(name, data);
+ // Insert into result tree
+ if (nextSibling != null)
+ {
+ parent.insertBefore(pi, nextSibling);
+ }
+ else
+ {
+ parent.appendChild(pi);
+ }
+ if (next != null)
+ {
+ next.apply(stylesheet, mode,
+ context, pos, len,
+ parent, nextSibling);
+ }
+ }
+
+ public String toString()
+ {
+ StringBuffer buf = new StringBuffer(getClass().getName());
+ buf.append('[');
+ buf.append("name=");
+ buf.append(name);
+ buf.append(']');
+ return buf.toString();
+ }
+
+}
diff --git a/libjava/gnu/xml/transform/SAXSerializer.java b/libjava/gnu/xml/transform/SAXSerializer.java
new file mode 100644
index 0000000..c5e3820
--- /dev/null
+++ b/libjava/gnu/xml/transform/SAXSerializer.java
@@ -0,0 +1,305 @@
+/* SAXSerializer.java --
+ Copyright (C) 2004 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.transform;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import org.w3c.dom.Attr;
+import org.w3c.dom.DocumentType;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.xml.sax.Attributes;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.SAXException;
+import org.xml.sax.ext.LexicalHandler;
+
+/**
+ * Serializes a DOM node to a sequence of SAX events.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+class SAXSerializer
+ implements Attributes
+{
+
+ transient NamedNodeMap attrs;
+ transient LinkedList namespaces = new LinkedList();
+
+ boolean isDefined(String prefix, String uri)
+ {
+ for (Iterator i = namespaces.iterator(); i.hasNext(); )
+ {
+ HashMap ctx = (HashMap) i.next();
+ if (uri.equals(ctx.get(prefix)))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ void define(String prefix, String uri)
+ {
+ for (Iterator i = namespaces.iterator(); i.hasNext(); )
+ {
+ HashMap ctx = (HashMap) i.next();
+ if (ctx.containsKey(prefix))
+ {
+ HashMap newCtx = new HashMap();
+ newCtx.put(prefix, uri);
+ namespaces.addFirst(newCtx);
+ return;
+ }
+ }
+ HashMap ctx;
+ if (namespaces.isEmpty())
+ {
+ ctx = new HashMap();
+ namespaces.add(ctx);
+ }
+ else
+ {
+ ctx = (HashMap) namespaces.getFirst();
+ }
+ ctx.put(prefix, uri);
+ }
+
+ void undefine(String prefix, String uri)
+ {
+ for (Iterator i = namespaces.iterator(); i.hasNext(); )
+ {
+ HashMap ctx = (HashMap) i.next();
+ if (uri.equals(ctx.get(prefix)))
+ {
+ ctx.remove(prefix);
+ if (ctx.isEmpty())
+ {
+ namespaces.remove(ctx);
+ }
+ return;
+ }
+ }
+ }
+
+ public int getLength()
+ {
+ return attrs.getLength();
+ }
+
+ public String getURI(int index)
+ {
+ return attrs.item(index).getNamespaceURI();
+ }
+
+ public String getLocalName(int index)
+ {
+ return attrs.item(index).getLocalName();
+ }
+
+ public String getQName(int index)
+ {
+ return attrs.item(index).getNodeName();
+ }
+
+ public String getType(int index)
+ {
+ Attr attr = (Attr) attrs.item(index);
+ return attr.isId() ? "ID" : "CDATA";
+ }
+
+ public String getValue(int index)
+ {
+ return attrs.item(index).getNodeValue();
+ }
+
+ public int getIndex(String uri, String localName)
+ {
+ int len = attrs.getLength();
+ for (int i = 0; i < len; i++)
+ {
+ Node attr = attrs.item(i);
+ String a_uri = attr.getNamespaceURI();
+ String a_localName = attr.getLocalName();
+ if (((a_uri == null && uri == null) ||
+ (a_uri != null && a_uri.equals(uri))) &&
+ a_localName.equals(localName))
+ {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ public int getIndex(String qName)
+ {
+ int len = attrs.getLength();
+ for (int i = 0; i < len; i++)
+ {
+ Node attr = attrs.item(i);
+ String a_name = attr.getNodeName();
+ if (a_name.equals(qName))
+ {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ public String getType(String uri, String localName)
+ {
+ Attr attr = (Attr) attrs.getNamedItemNS(uri, localName);
+ return attr.isId() ? "ID" : "CDATA";
+ }
+
+ public String getType(String qName)
+ {
+ Attr attr = (Attr) attrs.getNamedItem(qName);
+ return attr.isId() ? "ID" : "CDATA";
+ }
+
+ public String getValue(String uri, String localName)
+ {
+ return attrs.getNamedItemNS(uri, localName).getNodeValue();
+ }
+
+ public String getValue(String qName)
+ {
+ return attrs.getNamedItem(qName).getNodeValue();
+ }
+
+ void serialize(Node node, ContentHandler ch, LexicalHandler lh)
+ throws SAXException
+ {
+ attrs = node.getAttributes();
+ Node children;
+ Node next = node.getNextSibling();
+ switch (node.getNodeType())
+ {
+ case Node.ELEMENT_NODE:
+ String uri = node.getNamespaceURI();
+ String prefix = node.getPrefix();
+ boolean defined = isDefined(prefix, uri);
+ if (!defined)
+ {
+ define(prefix, uri);
+ ch.startPrefixMapping(prefix, uri);
+ }
+ String localName = node.getLocalName();
+ String qName = node.getNodeName();
+ ch.startElement(uri, localName, qName, this);
+ children = node.getFirstChild();
+ if (children != null)
+ {
+ serialize(children, ch, lh);
+ }
+ ch.endElement(uri, localName, qName);
+ if (!defined)
+ {
+ ch.endPrefixMapping(prefix);
+ undefine(prefix, uri);
+ }
+ break;
+ case Node.TEXT_NODE:
+ char[] chars = node.getNodeValue().toCharArray();
+ ch.characters(chars, 0, chars.length);
+ break;
+ case Node.CDATA_SECTION_NODE:
+ char[] cdata = node.getNodeValue().toCharArray();
+ if (lh != null)
+ {
+ lh.startCDATA();
+ ch.characters(cdata, 0, cdata.length);
+ lh.endCDATA();
+ }
+ else
+ {
+ ch.characters(cdata, 0, cdata.length);
+ }
+ break;
+ case Node.COMMENT_NODE:
+ if (lh != null)
+ {
+ char[] comment = node.getNodeValue().toCharArray();
+ lh.comment(comment, 0, comment.length);
+ }
+ break;
+ case Node.DOCUMENT_NODE:
+ case Node.DOCUMENT_FRAGMENT_NODE:
+ ch.startDocument();
+ children = node.getFirstChild();
+ if (children != null)
+ {
+ serialize(children, ch, lh);
+ }
+ ch.endDocument();
+ break;
+ case Node.DOCUMENT_TYPE_NODE:
+ if (lh != null)
+ {
+ DocumentType doctype = (DocumentType) node;
+ String publicId = doctype.getPublicId();
+ String systemId = doctype.getSystemId();
+ lh.startDTD(node.getNodeName(), publicId, systemId);
+ NamedNodeMap entities = doctype.getEntities();
+ int len = entities.getLength();
+ for (int i = 0; i < len; i++)
+ {
+ Node entity = entities.item(i);
+ String entityName = entity.getNodeName();
+ lh.startEntity(entityName);
+ lh.endEntity(entityName);
+ }
+ lh.endDTD();
+ }
+ break;
+ case Node.PROCESSING_INSTRUCTION_NODE:
+ ch.processingInstruction(node.getNodeName(), node.getNodeValue());
+ break;
+ case Node.ENTITY_REFERENCE_NODE:
+ ch.skippedEntity(node.getNodeName());
+ break;
+ }
+ attrs = null;
+ if (next != null)
+ {
+ serialize(next, ch, lh);
+ }
+ }
+
+}
diff --git a/libjava/gnu/xml/transform/SortKey.java b/libjava/gnu/xml/transform/SortKey.java
new file mode 100644
index 0000000..9ecc919
--- /dev/null
+++ b/libjava/gnu/xml/transform/SortKey.java
@@ -0,0 +1,154 @@
+/* SortKey.java --
+ Copyright (C) 2004 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.transform;
+
+import javax.xml.namespace.QName;
+import javax.xml.transform.TransformerException;
+import org.w3c.dom.Document;
+import org.w3c.dom.DocumentFragment;
+import org.w3c.dom.Node;
+import gnu.xml.xpath.Expr;
+
+/**
+ * An XSL sort key.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+final class SortKey
+{
+
+ static final int DEFAULT = 0;
+ static final int UPPER_FIRST = 1;
+ static final int LOWER_FIRST = 2;
+
+ final Expr select;
+ final TemplateNode langTemplate;
+ final TemplateNode dataTypeTemplate;
+ final TemplateNode orderTemplate;
+ final TemplateNode caseOrderTemplate;
+
+ transient String lang;
+ transient String dataType;
+ transient boolean descending;
+ transient int caseOrder;
+
+ SortKey(Expr select, TemplateNode lang, TemplateNode dataType,
+ TemplateNode order, TemplateNode caseOrder)
+ {
+ this.select = select;
+ this.langTemplate = lang;
+ this.dataTypeTemplate = dataType;
+ this.orderTemplate = order;
+ this.caseOrderTemplate = caseOrder;
+ }
+
+ String key(Node node)
+ {
+ Object ret = select.evaluate(node, 1, 1);
+ if (ret instanceof String)
+ {
+ return (String) ret;
+ }
+ else
+ {
+ return Expr._string(node, ret);
+ }
+ }
+
+ /**
+ * Prepare for a sort.
+ * This sets all transient variables from their AVTs.
+ */
+ void init(Stylesheet stylesheet, QName mode,
+ Node context, int pos, int len,
+ Node parent, Node nextSibling)
+ throws TransformerException
+ {
+ Document doc = (context instanceof Document) ? (Document) context :
+ context.getOwnerDocument();
+ if (langTemplate == null)
+ {
+ lang = null;
+ }
+ else
+ {
+ DocumentFragment fragment = doc.createDocumentFragment();
+ langTemplate.apply(stylesheet, mode, context, pos, len,
+ fragment, null);
+ lang = Expr.stringValue(fragment);
+ }
+ if (dataTypeTemplate == null)
+ {
+ dataType = "text";
+ }
+ else
+ {
+ DocumentFragment fragment = doc.createDocumentFragment();
+ dataTypeTemplate.apply(stylesheet, mode, context, pos, len,
+ fragment, null);
+ dataType = Expr.stringValue(fragment);
+ }
+ if (orderTemplate == null)
+ {
+ descending = false;
+ }
+ else
+ {
+ DocumentFragment fragment = doc.createDocumentFragment();
+ orderTemplate.apply(stylesheet, mode, context, pos, len,
+ fragment, null);
+ String order = Expr.stringValue(fragment);
+ descending = "descending".equals(order);
+ }
+ if (caseOrderTemplate == null)
+ {
+ caseOrder = DEFAULT;
+ }
+ else
+ {
+ DocumentFragment fragment = doc.createDocumentFragment();
+ caseOrderTemplate.apply(stylesheet, mode, context, pos, len,
+ fragment, null);
+ String co = Expr.stringValue(fragment);
+ caseOrder = "upper-first".equals(co) ? UPPER_FIRST :
+ "lower-first".equals(co) ? LOWER_FIRST :
+ DEFAULT;
+ }
+ }
+
+}
diff --git a/libjava/gnu/xml/transform/StreamSerializer.java b/libjava/gnu/xml/transform/StreamSerializer.java
new file mode 100644
index 0000000..136105a
--- /dev/null
+++ b/libjava/gnu/xml/transform/StreamSerializer.java
@@ -0,0 +1,632 @@
+/* StreamSerializer.java --
+ Copyright (C) 2004 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.transform;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import javax.xml.XMLConstants;
+import org.w3c.dom.Attr;
+import org.w3c.dom.Document;
+import org.w3c.dom.DocumentType;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+
+/**
+ * Serializes a DOM node to an output stream.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+public class StreamSerializer
+{
+
+ static final int SPACE = 0x20;
+ static final int BANG = 0x21; // !
+ static final int APOS = 0x27; // '
+ static final int SLASH = 0x2f; // /
+ static final int BRA = 0x3c; // <
+ static final int KET = 0x3e; // >
+ static final int EQ = 0x3d; // =
+
+ protected String encoding;
+ boolean compatibilityMode;
+ final int mode;
+ final Map namespaces;
+ protected String eol;
+ Collection cdataSectionElements = Collections.EMPTY_SET;
+
+ protected boolean discardDefaultContent;
+ protected boolean xmlDeclaration = true;
+
+ public StreamSerializer()
+ {
+ this(Stylesheet.OUTPUT_XML, null, null);
+ }
+
+ public StreamSerializer(String encoding)
+ {
+ this(Stylesheet.OUTPUT_XML, encoding, null);
+ }
+
+ public StreamSerializer(int mode, String encoding, String eol)
+ {
+ this.mode = mode;
+ if (encoding == null)
+ {
+ encoding = "UTF-8";
+ }
+ this.encoding = encoding.intern();
+ compatibilityMode = true;
+ if (encoding.length() > 3)
+ {
+ String p = encoding.substring(0, 3);
+ if (p.equalsIgnoreCase("UTF") ||
+ p.equalsIgnoreCase("UCS"))
+ {
+ compatibilityMode = false;
+ }
+ }
+ this.eol = (eol != null) ? eol : System.getProperty("line.separator");
+ namespaces = new HashMap();
+ }
+
+ void setCdataSectionElements(Collection c)
+ {
+ cdataSectionElements = c;
+ }
+
+ public void serialize(final Node node, final OutputStream out)
+ throws IOException
+ {
+ serialize(node, out, false);
+ }
+
+ void serialize(final Node node, final OutputStream out,
+ boolean convertToCdata)
+ throws IOException
+ {
+ if (out == null)
+ {
+ throw new NullPointerException("no output stream");
+ }
+ String value, prefix;
+ Node children;
+ Node next = node.getNextSibling();
+ String uri = node.getNamespaceURI();
+ boolean defined = false;
+ short nt = node.getNodeType();
+ if (convertToCdata && nt == Node.TEXT_NODE)
+ {
+ nt = Node.CDATA_SECTION_NODE;
+ }
+ switch (nt)
+ {
+ case Node.ATTRIBUTE_NODE:
+ prefix = node.getPrefix();
+ if (XMLConstants.XMLNS_ATTRIBUTE_NS_URI.equals(uri) ||
+ XMLConstants.XMLNS_ATTRIBUTE.equals(prefix) ||
+ (prefix != null && prefix.startsWith("xmlns:")))
+ {
+ String nsuri = node.getNodeValue();
+ if (isDefined(nsuri))
+ {
+ break;
+ }
+ define(nsuri, node.getLocalName());
+ }
+ else if (uri != null && !isDefined(uri))
+ {
+ prefix = define(uri, prefix);
+ String nsname = (prefix == null) ? "xmlns" : "xmlns:" + prefix;
+ out.write(SPACE);
+ out.write(encodeText(nsname));
+ out.write(EQ);
+ String nsvalue = "'" + encode(uri, true, true) + "'";
+ out.write(nsvalue.getBytes(encoding));
+ defined = true;
+ }
+ out.write(SPACE);
+ String a_nodeName = node.getNodeName();
+ out.write(encodeText(a_nodeName));
+ String a_nodeValue = node.getNodeValue();
+ if (mode == Stylesheet.OUTPUT_HTML &&
+ a_nodeName.equals(a_nodeValue))
+ {
+ break;
+ }
+ out.write(EQ);
+ value = "'" + encode(a_nodeValue, true, true) + "'";
+ out.write(encodeText(value));
+ break;
+ case Node.ELEMENT_NODE:
+ value = node.getNodeName();
+ out.write(BRA);
+ out.write(encodeText(value));
+ if (uri != null && !isDefined(uri))
+ {
+ prefix = define(uri, node.getPrefix());
+ String nsname = (prefix == null) ? "xmlns" : "xmlns:" + prefix;
+ out.write(SPACE);
+ out.write(encodeText(nsname));
+ out.write(EQ);
+ String nsvalue = "'" + encode(uri, true, true) + "'";
+ out.write(encodeText(nsvalue));
+ defined = true;
+ }
+ NamedNodeMap attrs = node.getAttributes();
+ if (attrs != null)
+ {
+ int len = attrs.getLength();
+ for (int i = 0; i < len; i++)
+ {
+ Attr attr = (Attr) attrs.item(i);
+ if (discardDefaultContent && !attr.getSpecified())
+ {
+ // NOOP
+ }
+ else
+ {
+ serialize(attr, out, false);
+ }
+ }
+ }
+ convertToCdata = cdataSectionElements.contains(value);
+ children = node.getFirstChild();
+ if (children == null)
+ {
+ out.write(SLASH);
+ out.write(KET);
+ }
+ else
+ {
+ out.write(KET);
+ serialize(children, out, convertToCdata);
+ out.write(BRA);
+ out.write(SLASH);
+ out.write(encodeText(value));
+ out.write(KET);
+ }
+ break;
+ case Node.TEXT_NODE:
+ value = node.getNodeValue();
+ if (!"yes".equals(node.getUserData("disable-output-escaping")))
+ {
+ value = encode(value, false, false);
+ }
+ out.write(encodeText(value));
+ break;
+ case Node.CDATA_SECTION_NODE:
+ value = "<![CDATA[" + node.getNodeValue() + "]]>";
+ out.write(encodeText(value));
+ break;
+ case Node.COMMENT_NODE:
+ value = "<!--" + node.getNodeValue() + "-->";
+ out.write(encodeText(value));
+ Node cp = node.getParentNode();
+ if (cp != null && cp.getNodeType() == Node.DOCUMENT_NODE)
+ {
+ out.write(encodeText(eol));
+ }
+ break;
+ case Node.DOCUMENT_NODE:
+ case Node.DOCUMENT_FRAGMENT_NODE:
+ if (mode == Stylesheet.OUTPUT_XML)
+ {
+ if ("UTF-16".equalsIgnoreCase(encoding))
+ {
+ out.write(0xfe);
+ out.write(0xff);
+ }
+ if (!"yes".equals(node.getUserData("omit-xml-declaration")) &&
+ xmlDeclaration)
+ {
+ Document doc = (node instanceof Document) ?
+ (Document) node : null;
+ String version = (doc != null) ? doc.getXmlVersion() : null;
+ if (version == null)
+ {
+ version = (String) node.getUserData("version");
+ }
+ if (version == null)
+ {
+ version = "1.0";
+ }
+ out.write(BRA);
+ out.write(0x3f);
+ out.write("xml version='".getBytes("US-ASCII"));
+ out.write(version.getBytes("US-ASCII"));
+ out.write(APOS);
+ if (!("UTF-8".equalsIgnoreCase(encoding)))
+ {
+ out.write(" encoding='".getBytes("US-ASCII"));
+ out.write(encoding.getBytes("US-ASCII"));
+ out.write(APOS);
+ }
+ if ((doc != null && doc.getXmlStandalone()) ||
+ "yes".equals(node.getUserData("standalone")))
+ {
+ out.write(" standalone='yes'".getBytes("US-ASCII"));
+ }
+ out.write(0x3f);
+ out.write(KET);
+ out.write(encodeText(eol));
+ }
+ // TODO warn if not outputting the declaration would be a
+ // problem
+ }
+ else if (mode == Stylesheet.OUTPUT_HTML)
+ {
+ // Ensure that encoding is accessible
+ String mediaType = (String) node.getUserData("media-type");
+ if (mediaType == null)
+ {
+ mediaType = "text/html";
+ }
+ String contentType = mediaType + "; charset=" +
+ ((encoding.indexOf(' ') != -1) ?
+ "\"" + encoding + "\"" :
+ encoding);
+ Document doc = (node instanceof Document) ? (Document) node :
+ node.getOwnerDocument();
+ Node html = null;
+ for (Node ctx = node.getFirstChild(); ctx != null;
+ ctx = ctx.getNextSibling())
+ {
+ if (ctx.getNodeType() == Node.ELEMENT_NODE)
+ {
+ html = ctx;
+ break;
+ }
+ }
+ if (html == null)
+ {
+ html = doc.createElement("html");
+ node.appendChild(html);
+ }
+ Node head = null;
+ for (Node ctx = html.getFirstChild(); ctx != null;
+ ctx = ctx.getNextSibling())
+ {
+ if (ctx.getNodeType() == Node.ELEMENT_NODE &&
+ "head".equalsIgnoreCase(ctx.getLocalName()))
+ {
+ head = ctx;
+ break;
+ }
+ }
+ if (head == null)
+ {
+ head = doc.createElement("head");
+ Node c1 = null;
+ for (Node ctx = html.getFirstChild(); ctx != null;
+ ctx = ctx.getNextSibling())
+ {
+ if (ctx.getNodeType() == Node.ELEMENT_NODE)
+ {
+ c1 = ctx;
+ break;
+ }
+ }
+ if (c1 != null)
+ {
+ html.insertBefore(head, c1);
+ }
+ else
+ {
+ html.appendChild(head);
+ }
+ }
+ Node meta = null;
+ Node metaContent = null;
+ for (Node ctx = head.getFirstChild(); ctx != null;
+ ctx = ctx.getNextSibling())
+ {
+ if (ctx.getNodeType() == Node.ELEMENT_NODE &&
+ "meta".equalsIgnoreCase(ctx.getLocalName()))
+ {
+ NamedNodeMap metaAttrs = ctx.getAttributes();
+ int len = metaAttrs.getLength();
+ String httpEquiv = null;
+ Node content = null;
+ for (int i = 0; i < len; i++)
+ {
+ Node attr = metaAttrs.item(i);
+ String attrName = attr.getNodeName();
+ if ("http-equiv".equalsIgnoreCase(attrName))
+ {
+ httpEquiv = attr.getNodeValue();
+ }
+ else if ("content".equalsIgnoreCase(attrName))
+ {
+ content = attr;
+ }
+ }
+ if ("Content-Type".equalsIgnoreCase(httpEquiv))
+ {
+ meta = ctx;
+ metaContent = content;
+ break;
+ }
+ }
+ }
+ if (meta == null)
+ {
+ meta = doc.createElement("meta");
+ // Insert first
+ Node first = head.getFirstChild();
+ if (first == null)
+ {
+ head.appendChild(meta);
+ }
+ else
+ {
+ head.insertBefore(meta, first);
+ }
+ Node metaHttpEquiv = doc.createAttribute("http-equiv");
+ meta.getAttributes().setNamedItem(metaHttpEquiv);
+ metaHttpEquiv.setNodeValue("Content-Type");
+ }
+ if (metaContent == null)
+ {
+ metaContent = doc.createAttribute("content");
+ meta.getAttributes().setNamedItem(metaContent);
+ }
+ metaContent.setNodeValue(contentType);
+ // phew
+ }
+ children = node.getFirstChild();
+ if (children != null)
+ {
+ serialize(children, out, convertToCdata);
+ }
+ break;
+ case Node.DOCUMENT_TYPE_NODE:
+ DocumentType doctype = (DocumentType) node;
+ out.write(BRA);
+ out.write(BANG);
+ value = doctype.getNodeName();
+ out.write(encodeText(value));
+ String publicId = doctype.getPublicId();
+ if (publicId != null)
+ {
+ out.write(encodeText(" PUBLIC "));
+ out.write(APOS);
+ out.write(encodeText(publicId));
+ out.write(APOS);
+ }
+ String systemId = doctype.getSystemId();
+ if (systemId != null)
+ {
+ out.write(encodeText(" SYSTEM "));
+ out.write(APOS);
+ out.write(encodeText(systemId));
+ out.write(APOS);
+ }
+ String internalSubset = doctype.getInternalSubset();
+ if (internalSubset != null)
+ {
+ out.write(encodeText(internalSubset));
+ }
+ out.write(KET);
+ out.write(eol.getBytes(encoding));
+ break;
+ case Node.ENTITY_REFERENCE_NODE:
+ value = "&" + node.getNodeValue() + ";";
+ out.write(encodeText(value));
+ break;
+ case Node.PROCESSING_INSTRUCTION_NODE:
+ value = "<?" + node.getNodeName() + " " + node.getNodeValue() + "?>";
+ out.write(encodeText(value));
+ Node pp = node.getParentNode();
+ if (pp != null && pp.getNodeType() == Node.DOCUMENT_NODE)
+ {
+ out.write(encodeText(eol));
+ }
+ break;
+ }
+ if (defined)
+ {
+ undefine(uri);
+ }
+ if (next != null)
+ {
+ serialize(next, out, convertToCdata);
+ }
+ }
+
+ boolean isDefined(String uri)
+ {
+ return XMLConstants.XML_NS_URI.equals(uri) ||
+ XMLConstants.XMLNS_ATTRIBUTE_NS_URI.equals(uri) ||
+ namespaces.containsKey(uri);
+ }
+
+ String define(String uri, String prefix)
+ {
+ while (namespaces.containsValue(prefix))
+ {
+ // Fabricate new prefix
+ prefix = prefix + "_";
+ }
+ namespaces.put(uri, prefix);
+ return prefix;
+ }
+
+ void undefine(String uri)
+ {
+ namespaces.remove(uri);
+ }
+
+ final byte[] encodeText(String text)
+ throws UnsupportedEncodingException
+ {
+ if (compatibilityMode)
+ {
+ int len = text.length();
+ StringBuffer buf = null;
+ for (int i = 0; i < len; i++)
+ {
+ char c = text.charAt(i);
+ if (c >= 127)
+ {
+ if (buf == null)
+ {
+ buf = new StringBuffer(text.substring(0, i));
+ }
+ buf.append('&');
+ buf.append('#');
+ buf.append((int) c);
+ buf.append(';');
+ }
+ else if (buf != null)
+ {
+ buf.append(c);
+ }
+ }
+ if (buf != null)
+ {
+ text = buf.toString();
+ }
+ }
+ return text.getBytes(encoding);
+ }
+
+ String encode(String text, boolean encodeCtl, boolean inAttr)
+ {
+ int len = text.length();
+ StringBuffer buf = null;
+ for (int i = 0; i < len; i++)
+ {
+ char c = text.charAt(i);
+ if (c == '<')
+ {
+ if (buf == null)
+ {
+ buf = new StringBuffer(text.substring(0, i));
+ }
+ buf.append("&lt;");
+ }
+ else if (c == '>')
+ {
+ if (buf == null)
+ {
+ buf = new StringBuffer(text.substring(0, i));
+ }
+ buf.append("&gt;");
+ }
+ else if (c == '&')
+ {
+ if (mode == Stylesheet.OUTPUT_HTML && (i + 1) < len &&
+ text.charAt(i + 1) == '{')
+ {
+ if (buf != null)
+ {
+ buf.append(c);
+ }
+ }
+ else
+ {
+ if (buf == null)
+ {
+ buf = new StringBuffer(text.substring(0, i));
+ }
+ buf.append("&amp;");
+ }
+ }
+ else if (c == '\'' && inAttr)
+ {
+ if (buf == null)
+ {
+ buf = new StringBuffer(text.substring(0, i));
+ }
+ buf.append("&apos;");
+ }
+ else if (c == '"' && inAttr)
+ {
+ if (buf == null)
+ {
+ buf = new StringBuffer(text.substring(0, i));
+ }
+ buf.append("&quot;");
+ }
+ else if (encodeCtl)
+ {
+ if (c < 0x20)
+ {
+ if (buf == null)
+ {
+ buf = new StringBuffer(text.substring(0, i));
+ }
+ buf.append('&');
+ buf.append('#');
+ buf.append((int) c);
+ buf.append(';');
+ }
+ else if (buf != null)
+ {
+ buf.append(c);
+ }
+ }
+ else if (buf != null)
+ {
+ buf.append(c);
+ }
+ }
+ return (buf == null) ? text : buf.toString();
+ }
+
+ String toString(Node node)
+ {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ try
+ {
+ serialize(node, out);
+ return new String(out.toByteArray(), encoding);
+ }
+ catch (IOException e)
+ {
+ throw new RuntimeException(e.getMessage());
+ }
+ }
+
+}
diff --git a/libjava/gnu/xml/transform/Stylesheet.java b/libjava/gnu/xml/transform/Stylesheet.java
new file mode 100644
index 0000000..80f5781
--- /dev/null
+++ b/libjava/gnu/xml/transform/Stylesheet.java
@@ -0,0 +1,1730 @@
+/* Stylesheet.java --
+ Copyright (C) 2004 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.transform;
+
+import java.text.DecimalFormat;
+import java.text.DecimalFormatSymbols;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.StringTokenizer;
+import java.util.TreeSet;
+import javax.xml.XMLConstants;
+import javax.xml.namespace.NamespaceContext;
+import javax.xml.namespace.QName;
+import javax.xml.transform.Source;
+import javax.xml.transform.TransformerConfigurationException;
+import javax.xml.transform.TransformerException;
+import javax.xml.xpath.XPathConstants;
+import javax.xml.xpath.XPathFunction;
+import javax.xml.xpath.XPathFunctionResolver;
+import javax.xml.xpath.XPathExpressionException;
+import org.w3c.dom.Attr;
+import org.w3c.dom.Document;
+import org.w3c.dom.DOMException;
+import org.w3c.dom.Element;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.w3c.dom.Text;
+import org.w3c.dom.UserDataHandler;
+import gnu.xml.xpath.Expr;
+import gnu.xml.xpath.NameTest;
+import gnu.xml.xpath.NodeTypeTest;
+import gnu.xml.xpath.Pattern;
+import gnu.xml.xpath.Selector;
+import gnu.xml.xpath.Root;
+import gnu.xml.xpath.Test;
+import gnu.xml.xpath.XPathImpl;
+
+/**
+ * An XSL stylesheet.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+class Stylesheet
+ implements NamespaceContext, XPathFunctionResolver, UserDataHandler, Cloneable
+{
+
+ static final String XSL_NS = "http://www.w3.org/1999/XSL/Transform";
+
+ static final int OUTPUT_XML = 0;
+ static final int OUTPUT_HTML = 1;
+ static final int OUTPUT_TEXT = 2;
+
+ final TransformerFactoryImpl factory;
+ TransformerImpl transformer;
+ Stylesheet parent;
+ final XPathImpl xpath;
+ final String systemId;
+ final int precedence;
+
+ final boolean debug;
+
+ /**
+ * Version of XSLT.
+ */
+ String version;
+
+ Collection extensionElementPrefixes;
+ Collection excludeResultPrefixes;
+
+ /**
+ * Set of element names for which we should strip whitespace.
+ */
+ Set stripSpace;
+
+ /**
+ * Set of element names for which we should preserve whitespace.
+ */
+ Set preserveSpace;
+
+ /**
+ * Output options.
+ */
+ int outputMethod;
+ String outputVersion;
+ String outputEncoding;
+ boolean outputOmitXmlDeclaration;
+ boolean outputStandalone;
+ String outputPublicId;
+ String outputSystemId;
+ Collection outputCdataSectionElements;
+ boolean outputIndent;
+ String outputMediaType;
+
+ /**
+ * Keys.
+ */
+ Collection keys;
+
+ /**
+ * Decimal formats.
+ */
+ Map decimalFormats;
+
+ /**
+ * Namespace aliases.
+ */
+ Map namespaceAliases;
+
+ /**
+ * Attribute-sets.
+ */
+ List attributeSets;
+
+ /**
+ * Variables.
+ */
+ List variables;
+
+ /**
+ * Variable and parameter bindings.
+ */
+ Bindings bindings;
+
+ /**
+ * Templates.
+ */
+ LinkedList templates;
+
+ TemplateNode builtInNodeTemplate;
+ TemplateNode builtInTextTemplate;
+
+ /**
+ * Holds the current node while parsing.
+ * Necessary to associate the document function with its declaring node,
+ * to resolve namespaces, and to maintain the current node for the
+ * current() function.
+ */
+ Node current;
+
+ /**
+ * Set by a terminating message.
+ */
+ transient boolean terminated;
+
+ /**
+ * Current template in force.
+ */
+ transient Template currentTemplate;
+
+ Stylesheet(TransformerFactoryImpl factory,
+ Stylesheet parent,
+ Document doc,
+ String systemId,
+ int precedence)
+ throws TransformerConfigurationException
+ {
+ this.factory = factory;
+ this.systemId = systemId;
+ this.precedence = precedence;
+ this.parent = parent;
+ extensionElementPrefixes = new HashSet();
+ excludeResultPrefixes = new HashSet();
+ stripSpace = new LinkedHashSet();
+ preserveSpace = new LinkedHashSet();
+ outputCdataSectionElements = new LinkedHashSet();
+ xpath = (XPathImpl) factory.xpathFactory.newXPath();
+ if (parent == null)
+ {
+ bindings = new Bindings(this);
+ attributeSets = new LinkedList();
+ variables = new LinkedList();
+ namespaceAliases = new LinkedHashMap();
+ templates = new LinkedList();
+ keys = new LinkedList();
+ decimalFormats = new LinkedHashMap();
+ initDefaultDecimalFormat();
+ xpath.setNamespaceContext(this);
+ xpath.setXPathFunctionResolver(this);
+ }
+ else
+ {
+ /* Test for import circularity */
+ for (Stylesheet ctx = this; ctx.parent != null; ctx = ctx.parent)
+ {
+ if (systemId != null && systemId.equals(ctx.parent.systemId))
+ {
+ String msg = "circularity importing " + systemId;
+ throw new TransformerConfigurationException(msg);
+ }
+ }
+ /* OK */
+ Stylesheet root = getRootStylesheet();
+ bindings = root.bindings;
+ attributeSets = root.attributeSets;
+ variables = root.variables;
+ namespaceAliases = root.namespaceAliases;
+ templates = root.templates;
+ keys = root.keys;
+ decimalFormats = root.decimalFormats;
+ xpath.setNamespaceContext(root);
+ xpath.setXPathFunctionResolver(root);
+ }
+ xpath.setXPathVariableResolver(bindings);
+
+ Test anyNode = new NodeTypeTest((short) 0);
+ List tests = Collections.singletonList(anyNode);
+ builtInNodeTemplate =
+ new ApplyTemplatesNode(null, null,
+ new Selector(Selector.CHILD, tests),
+ null, null, null, true);
+ builtInTextTemplate =
+ new ValueOfNode(null, null,
+ new Selector(Selector.SELF, tests),
+ false);
+
+ parse(doc.getDocumentElement(), true);
+ current = doc; // Alow namespace resolution during processing
+
+ debug = ("yes".equals(System.getProperty("xsl.debug")));
+
+ if (debug)
+ {
+ System.err.println("Stylesheet: " + doc.getDocumentURI());
+ for (Iterator i = templates.iterator(); i.hasNext(); )
+ {
+ Template t = (Template) i.next();
+ t.list(System.err);
+ System.err.println("--------------------");
+ }
+ }
+ }
+
+ Stylesheet getRootStylesheet()
+ {
+ Stylesheet stylesheet = this;
+ while (stylesheet.parent != null)
+ {
+ stylesheet = stylesheet.parent;
+ }
+ return stylesheet;
+ }
+
+ void initDefaultDecimalFormat()
+ {
+ DecimalFormat defaultDecimalFormat = new DecimalFormat();
+ DecimalFormatSymbols symbols = new DecimalFormatSymbols();
+ symbols.setDecimalSeparator('.');
+ symbols.setGroupingSeparator(',');
+ symbols.setPercent('%');
+ symbols.setPerMill('\u2030');
+ symbols.setZeroDigit('0');
+ symbols.setDigit('#');
+ symbols.setPatternSeparator(';');
+ symbols.setInfinity("Infinity");
+ symbols.setNaN("NaN");
+ symbols.setMinusSign('-');
+ defaultDecimalFormat.setDecimalFormatSymbols(symbols);
+ decimalFormats.put(null, defaultDecimalFormat);
+ }
+
+ // -- Cloneable --
+
+ public Object clone()
+ {
+ try
+ {
+ Stylesheet clone = (Stylesheet) super.clone();
+ clone.bindings = (Bindings) bindings.clone();
+
+ LinkedList templates2 = new LinkedList();
+ for (Iterator i = templates.iterator(); i.hasNext(); )
+ {
+ Template t = (Template) i.next();
+ templates2.add(t.clone(clone));
+ }
+ clone.templates = templates2;
+
+ LinkedList attributeSets2 = new LinkedList();
+ for (Iterator i = attributeSets.iterator(); i.hasNext(); )
+ {
+ AttributeSet as = (AttributeSet) i.next();
+ attributeSets2.add(as.clone(clone));
+ }
+ clone.attributeSets = attributeSets2;
+
+ LinkedList variables2 = new LinkedList();
+ for (Iterator i = variables.iterator(); i.hasNext(); )
+ {
+ ParameterNode var = (ParameterNode) i.next();
+ variables2.add(var.clone(clone));
+ }
+ clone.variables = variables2;
+
+ LinkedList keys2 = new LinkedList();
+ for (Iterator i = keys.iterator(); i.hasNext(); )
+ {
+ Key k = (Key) i.next();
+ keys2.add(k.clone(clone));
+ }
+ clone.keys = keys2;
+
+ return clone;
+ }
+ catch (CloneNotSupportedException e)
+ {
+ throw new Error(e.getMessage());
+ }
+ }
+
+ // -- Variable evaluation --
+
+ void initTopLevelVariables(Node context)
+ throws TransformerException
+ {
+ for (Iterator i = variables.iterator(); i.hasNext(); )
+ {
+ ParameterNode var = (ParameterNode) i.next();
+ bindings.set(var.name,
+ var.getValue(this, null, context, 1, 1),
+ var.global);
+ }
+ }
+
+ // -- NamespaceContext --
+
+ public String getNamespaceURI(String prefix)
+ {
+ return (current == null) ? null : current.lookupNamespaceURI(prefix);
+ }
+
+ public String getPrefix(String namespaceURI)
+ {
+ return (current == null) ? null : current.lookupPrefix(namespaceURI);
+ }
+
+ public Iterator getPrefixes(String namespaceURI)
+ {
+ // TODO
+ return Collections.singleton(getPrefix(namespaceURI)).iterator();
+ }
+
+ // -- Template selection --
+
+ TemplateNode getTemplate(QName mode, Node context, boolean applyImports)
+ throws TransformerException
+ {
+ //System.err.println("getTemplate: mode="+mode+" context="+context);
+ Set candidates = new TreeSet();
+ for (Iterator j = templates.iterator(); j.hasNext(); )
+ {
+ Template t = (Template) j.next();
+ boolean isMatch = t.matches(mode, context);
+ if (applyImports)
+ {
+ if (currentTemplate == null)
+ {
+ String msg = "current template may not be null " +
+ "during apply-imports";
+ throw new TransformerException(msg);
+ }
+ if (!currentTemplate.imports(t))
+ {
+ isMatch = false;
+ }
+ }
+ //System.err.println("\t"+context+" "+t+"="+isMatch);
+ if (isMatch)
+ {
+ candidates.add(t);
+ }
+ }
+ //System.err.println("\tcandidates="+candidates);
+ if (candidates.isEmpty())
+ {
+ // Apply built-in template
+ // Current template is unchanged
+ //System.err.println("\tbuiltInTemplate context="+context);
+ switch (context.getNodeType())
+ {
+ case Node.ELEMENT_NODE:
+ case Node.DOCUMENT_NODE:
+ case Node.DOCUMENT_FRAGMENT_NODE:
+ case Node.PROCESSING_INSTRUCTION_NODE:
+ case Node.COMMENT_NODE:
+ return builtInNodeTemplate;
+ case Node.TEXT_NODE:
+ case Node.ATTRIBUTE_NODE:
+ return builtInTextTemplate;
+ default:
+ return null;
+ }
+ }
+ else
+ {
+ Template t = (Template) candidates.iterator().next();
+ // Set current template
+ currentTemplate = t;
+ //System.err.println("\ttemplate="+t+" context="+context);
+ return t.node;
+ }
+ }
+
+ TemplateNode getTemplate(QName mode, QName name)
+ throws TransformerException
+ {
+ //System.err.println("getTemplate: mode="+mode+" name="+name);
+ Set candidates = new TreeSet();
+ for (Iterator j = templates.iterator(); j.hasNext(); )
+ {
+ Template t = (Template) j.next();
+ boolean isMatch = t.matches(name);
+ //System.err.println("\t"+name+" "+t+"="+isMatch);
+ if (isMatch)
+ {
+ candidates.add(t);
+ }
+ }
+ if (candidates.isEmpty())
+ {
+ return null;
+ //throw new TransformerException("template '" + name + "' not found");
+ }
+ Template t = (Template) candidates.iterator().next();
+ //System.err.println("\ttemplate="+t+" context="+context);
+ return t.node;
+ }
+
+ /**
+ * template
+ */
+ final Template parseTemplate(Node node, NamedNodeMap attrs)
+ throws TransformerConfigurationException, XPathExpressionException
+ {
+ String n = getAttribute(attrs, "name");
+ QName name = (n == null) ? null : getQName(n);
+ String m = getAttribute(attrs, "match");
+ Pattern match = null;
+ if (m != null)
+ {
+ try
+ {
+ match = (Pattern) xpath.compile(m);
+ }
+ catch (ClassCastException e)
+ {
+ String msg = "illegal pattern: " + m;
+ throw new TransformerConfigurationException(msg);
+ }
+ }
+ String p = getAttribute(attrs, "priority");
+ String mm = getAttribute(attrs, "mode");
+ QName mode = (mm == null) ? null : getQName(mm);
+ double priority = (p == null) ? Template.DEFAULT_PRIORITY :
+ Double.parseDouble(p);
+ return new Template(this, name, match, parse(node.getFirstChild()),
+ precedence, priority, mode);
+ }
+
+ /**
+ * output
+ */
+ final void parseOutput(Node node, NamedNodeMap attrs)
+ throws TransformerConfigurationException
+ {
+ String method = getAttribute(attrs, "method");
+ if ("xml".equals(method) || method == null)
+ {
+ outputMethod = OUTPUT_XML;
+ }
+ else if ("html".equals(method))
+ {
+ outputMethod = OUTPUT_HTML;
+ }
+ else if ("text".equals(method))
+ {
+ outputMethod = OUTPUT_TEXT;
+ }
+ else
+ {
+ String msg = "unsupported output method: " + method;
+ DOMSourceLocator l = new DOMSourceLocator(node);
+ throw new TransformerConfigurationException(msg, l);
+ }
+ outputPublicId = getAttribute(attrs, "public-id");
+ outputSystemId = getAttribute(attrs, "system-id");
+ outputEncoding = getAttribute(attrs, "encoding");
+ String indent = getAttribute(attrs, "indent");
+ if (indent != null)
+ {
+ outputIndent = "yes".equals(indent);
+ }
+ outputVersion = getAttribute(attrs, "version");
+ String omitXmlDecl = getAttribute(attrs, "omit-xml-declaration");
+ if (omitXmlDecl != null)
+ {
+ outputOmitXmlDeclaration = "yes".equals(omitXmlDecl);
+ }
+ String standalone = getAttribute(attrs, "standalone");
+ if (standalone != null)
+ {
+ outputStandalone = "yes".equals(standalone);
+ }
+ outputMediaType = getAttribute(attrs, "media-type");
+ String cdataSectionElements =
+ getAttribute(attrs, "cdata-section-elements");
+ if (cdataSectionElements != null)
+ {
+ StringTokenizer st = new StringTokenizer(cdataSectionElements, " ");
+ while (st.hasMoreTokens())
+ {
+ outputCdataSectionElements.add(st.nextToken());
+ }
+ }
+ }
+
+ /**
+ * key
+ */
+ final void parseKey(Node node, NamedNodeMap attrs)
+ throws TransformerConfigurationException, XPathExpressionException
+ {
+ String n = getRequiredAttribute(attrs, "name", node);
+ String m = getRequiredAttribute(attrs, "match", node);
+ String u = getRequiredAttribute(attrs, "use", node);
+ QName name = getQName(n);
+ Expr use = (Expr) xpath.compile(u);
+ try
+ {
+ Pattern match = (Pattern) xpath.compile(m);
+ Key key = new Key(name, match, use);
+ keys.add(key);
+ }
+ catch (ClassCastException e)
+ {
+ throw new TransformerConfigurationException("invalid pattern: " + m);
+ }
+ }
+
+ /**
+ * decimal-format
+ */
+ final void parseDecimalFormat(Node node, NamedNodeMap attrs)
+ throws TransformerConfigurationException
+ {
+ String dfName = getAttribute(attrs, "name");
+ DecimalFormat df = new DecimalFormat();
+ DecimalFormatSymbols symbols = new DecimalFormatSymbols();
+ symbols.setDecimalSeparator(parseDFChar(attrs, "decimal-separator", '.'));
+ symbols.setGroupingSeparator(parseDFChar(attrs, "grouping-separator", ','));
+ symbols.setInfinity(parseDFString(attrs, "infinity", "Infinity"));
+ symbols.setMinusSign(parseDFChar(attrs, "minus-sign", '-'));
+ symbols.setNaN(parseDFString(attrs, "NaN", "NaN"));
+ symbols.setPercent(parseDFChar(attrs, "percent", '%'));
+ symbols.setPerMill(parseDFChar(attrs, "per-mille", '\u2030'));
+ symbols.setZeroDigit(parseDFChar(attrs, "zero-digit", '0'));
+ symbols.setDigit(parseDFChar(attrs, "digit", '#'));
+ symbols.setPatternSeparator(parseDFChar(attrs, "pattern-separator", ';'));
+ df.setDecimalFormatSymbols(symbols);
+ decimalFormats.put(dfName, df);
+ }
+
+ private final char parseDFChar(NamedNodeMap attrs, String name, char def)
+ throws TransformerConfigurationException
+ {
+ Node attr = attrs.getNamedItem(name);
+ try
+ {
+ return (attr == null) ? def : attr.getNodeValue().charAt(0);
+ }
+ catch (StringIndexOutOfBoundsException e)
+ {
+ throw new TransformerConfigurationException("empty attribute '" +
+ name +
+ "' in decimal-format", e);
+ }
+ }
+
+ private final String parseDFString(NamedNodeMap attrs, String name,
+ String def)
+ {
+ Node attr = attrs.getNamedItem(name);
+ return (attr == null) ? def : attr.getNodeValue();
+ }
+
+ /**
+ * namespace-alias
+ */
+ final void parseNamespaceAlias(Node node, NamedNodeMap attrs)
+ throws TransformerConfigurationException
+ {
+ String sp = getRequiredAttribute(attrs, "stylesheet-prefix", node);
+ String rp = getRequiredAttribute(attrs, "result-prefix", node);
+ namespaceAliases.put(sp, rp);
+ }
+
+ /**
+ * attribute-set
+ */
+ final void parseAttributeSet(Node node, NamedNodeMap attrs)
+ throws TransformerConfigurationException, XPathExpressionException
+ {
+ TemplateNode children = parse(node.getFirstChild());
+ String name = getRequiredAttribute(attrs, "name", node);
+ String uas = getAttribute(attrs, "use-attribute-sets");
+ attributeSets.add(new AttributeSet(children, name, uas));
+ }
+
+ /**
+ * Parse top-level elements.
+ */
+ void parse(Node node, boolean root)
+ throws TransformerConfigurationException
+ {
+ if (node == null)
+ {
+ return;
+ }
+ current = node;
+ try
+ {
+ String namespaceUri = node.getNamespaceURI();
+ if (XSL_NS.equals(namespaceUri) &&
+ node.getNodeType() == Node.ELEMENT_NODE)
+ {
+ String name = node.getLocalName();
+ NamedNodeMap attrs = node.getAttributes();
+ if ("stylesheet".equals(name))
+ {
+ version = getAttribute(attrs, "version");
+ String eep = getAttribute(attrs, "extension-element-prefixes");
+ if (eep != null)
+ {
+ StringTokenizer st = new StringTokenizer(eep);
+ while (st.hasMoreTokens())
+ {
+ extensionElementPrefixes.add(st.nextToken());
+ }
+ }
+ String erp = getAttribute(attrs, "exclude-result-prefixes");
+ if (erp != null)
+ {
+ StringTokenizer st = new StringTokenizer(erp);
+ while (st.hasMoreTokens())
+ {
+ excludeResultPrefixes.add(st.nextToken());
+ }
+ }
+ parse(node.getFirstChild(), false);
+ return;
+ }
+ else if ("template".equals(name))
+ {
+ templates.addFirst(parseTemplate(node, attrs));
+ }
+ else if ("param".equals(name) ||
+ "variable".equals(name))
+ {
+ boolean global = "variable".equals(name);
+ TemplateNode content = parse(node.getFirstChild());
+ String paramName = getRequiredAttribute(attrs, "name", node);
+ String select = getAttribute(attrs, "select");
+ if (select != null && select.length() > 0)
+ {
+ if (content != null)
+ {
+ String msg = "parameter '" + paramName +
+ "' has both select and content";
+ DOMSourceLocator l = new DOMSourceLocator(node);
+ throw new TransformerConfigurationException(msg, l);
+ }
+ Expr expr = (Expr) xpath.compile(select);
+ variables.add(new ParameterNode(null, null,
+ paramName,
+ expr, global));
+ }
+ else
+ {
+ variables.add(new ParameterNode(content, null,
+ paramName,
+ null, global));
+ }
+ bindings.set(paramName, content, global);
+ }
+ else if ("include".equals(name) || "import".equals(name))
+ {
+ int delta = "import".equals(name) ? -1 : 0;
+ String href = getRequiredAttribute(attrs, "href", node);
+ Source source;
+ synchronized (factory.resolver)
+ {
+ if (transformer != null)
+ {
+ factory.resolver
+ .setUserResolver(transformer.getURIResolver());
+ factory.resolver
+ .setUserListener(transformer.getErrorListener());
+ }
+ source = factory.resolver.resolve(systemId, href);
+ }
+ factory.newStylesheet(source, precedence + delta, this);
+ }
+ else if ("output".equals(name))
+ {
+ parseOutput(node, attrs);
+ }
+ else if ("preserve-space".equals(name))
+ {
+ String elements =
+ getRequiredAttribute(attrs, "elements", node);
+ StringTokenizer st = new StringTokenizer(elements,
+ " \t\n\r");
+ while (st.hasMoreTokens())
+ {
+ preserveSpace.add(parseNameTest(st.nextToken()));
+ }
+ }
+ else if ("strip-space".equals(name))
+ {
+ String elements =
+ getRequiredAttribute(attrs, "elements", node);
+ StringTokenizer st = new StringTokenizer(elements,
+ " \t\n\r");
+ while (st.hasMoreTokens())
+ {
+ stripSpace.add(parseNameTest(st.nextToken()));
+ }
+ }
+ else if ("key".equals(name))
+ {
+ parseKey(node, attrs);
+ }
+ else if ("decimal-format".equals(name))
+ {
+ parseDecimalFormat(node, attrs);
+ }
+ else if ("namespace-alias".equals(name))
+ {
+ parseNamespaceAlias(node, attrs);
+ }
+ else if ("attribute-set".equals(name))
+ {
+ parseAttributeSet(node, attrs);
+ }
+ parse(node.getNextSibling(), false);
+ }
+ else if (root)
+ {
+ // Literal document element
+ Attr versionNode =
+ ((Element)node).getAttributeNodeNS(XSL_NS, "version");
+ if (versionNode == null)
+ {
+ String msg = "no xsl:version attribute on literal result node";
+ DOMSourceLocator l = new DOMSourceLocator(node);
+ throw new TransformerConfigurationException(msg, l);
+ }
+ version = versionNode.getValue();
+ Node rootClone = node.cloneNode(true);
+ NamedNodeMap attrs = rootClone.getAttributes();
+ attrs.removeNamedItemNS(XSL_NS, "version");
+ templates.add(new Template(this, null, new Root(),
+ parse(rootClone),
+ precedence,
+ Template.DEFAULT_PRIORITY,
+ null));
+ }
+ else
+ {
+ // Skip unknown elements, text, comments, etc
+ parse(node.getNextSibling(), false);
+ }
+ }
+ catch (TransformerException e)
+ {
+ DOMSourceLocator l = new DOMSourceLocator(node);
+ throw new TransformerConfigurationException(e.getMessage(), l, e);
+ }
+ catch (DOMException e)
+ {
+ DOMSourceLocator l = new DOMSourceLocator(node);
+ throw new TransformerConfigurationException(e.getMessage(), l, e);
+ }
+ catch (XPathExpressionException e)
+ {
+ DOMSourceLocator l = new DOMSourceLocator(node);
+ throw new TransformerConfigurationException(e.getMessage(), l, e);
+ }
+ }
+
+ final NameTest parseNameTest(String token)
+ {
+ if ("*".equals(token))
+ {
+ return new NameTest(null, true, true);
+ }
+ else if (token.endsWith(":*"))
+ {
+ QName qName = getQName(token.substring(0, token.length() - 2));
+ return new NameTest(qName, true, false);
+ }
+ else
+ {
+ QName qName = getQName(token);
+ return new NameTest(qName, false, false);
+ }
+ }
+
+ final QName getQName(String name)
+ {
+ QName qName = QName.valueOf(name);
+ String prefix = qName.getPrefix();
+ String uri = qName.getNamespaceURI();
+ if (prefix != null && (uri == null || uri.length() == 0))
+ {
+ uri = getNamespaceURI(prefix);
+ String localName = qName.getLocalPart();
+ qName = new QName(uri, localName, prefix);
+ }
+ return qName;
+ }
+
+ final TemplateNode parseAttributeValueTemplate(String value, Node source)
+ throws TransformerConfigurationException, XPathExpressionException
+ {
+ current = source;
+ // Tokenize
+ int len = value.length();
+ int off = 0;
+ List tokens = new ArrayList(); // text tokens
+ List types = new ArrayList(); // literal or expression
+ int depth = 0;
+ for (int i = 0; i < len; i++)
+ {
+ char c = value.charAt(i);
+ if (c == '{')
+ {
+ if (i < (len - 1) && value.charAt(i + 1) == '{')
+ {
+ tokens.add(value.substring(off, i + 1));
+ types.add(Boolean.FALSE);
+ i++;
+ off = i + 1;
+ continue;
+ }
+ if (depth == 0)
+ {
+ if (i - off > 0)
+ {
+ tokens.add(value.substring(off, i));
+ types.add(Boolean.FALSE);
+ }
+ off = i + 1;
+ }
+ depth++;
+ }
+ else if (c == '}')
+ {
+ if (i < (len - 1) && value.charAt(i + 1) == '}')
+ {
+ tokens.add(value.substring(off, i + 1));
+ types.add(Boolean.FALSE);
+ i++;
+ off = i + 1;
+ continue;
+ }
+ if (depth == 1)
+ {
+ if (i - off > 0)
+ {
+ tokens.add(value.substring(off, i));
+ types.add(Boolean.TRUE);
+ }
+ else
+ {
+ String msg = "attribute value template " +
+ "must contain expression: " + value;
+ DOMSourceLocator l = new DOMSourceLocator(source);
+ throw new TransformerConfigurationException(msg, l);
+ }
+ off = i + 1;
+ }
+ depth--;
+ }
+ }
+ if (depth > 0)
+ {
+ String msg = "invalid attribute value template: " + value;
+ throw new TransformerConfigurationException(msg);
+ }
+ if (len - off > 0)
+ {
+ // Trailing text
+ tokens.add(value.substring(off));
+ types.add(Boolean.FALSE);
+ }
+
+ // Construct template node tree
+ TemplateNode ret = null;
+ Document doc = source.getOwnerDocument();
+ len = tokens.size();
+ for (int i = len - 1; i >= 0; i--)
+ {
+ String token = (String) tokens.get(i);
+ Boolean type = (Boolean) types.get(i);
+ if (type == Boolean.TRUE)
+ {
+ // Expression text
+ Expr select = (Expr) xpath.compile(token);
+ ret = new ValueOfNode(null, ret, select, false);
+ }
+ else
+ {
+ // Verbatim text
+ ret = new LiteralNode(null, ret, doc.createTextNode(token));
+ }
+ }
+ return ret;
+ }
+
+ boolean isPreserved(Text text)
+ throws TransformerConfigurationException
+ {
+ // Check characters in text
+ String value = text.getData();
+ if (value != null)
+ {
+ int len = value.length();
+ for (int i = 0; i < len; i++)
+ {
+ char c = value.charAt(i);
+ if (c != 0x20 && c != 0x09 && c != 0x0a && c != 0x0d)
+ {
+ return true;
+ }
+ }
+ }
+ // Check parent node
+ Node ctx = text.getParentNode();
+ if (!preserveSpace.isEmpty())
+ {
+ for (Iterator i = preserveSpace.iterator(); i.hasNext(); )
+ {
+ NameTest preserveTest = (NameTest) i.next();
+ if (preserveTest.matches(ctx, 1, 1))
+ {
+ boolean override = false;
+ if (!stripSpace.isEmpty())
+ {
+ for (Iterator j = stripSpace.iterator(); j.hasNext(); )
+ {
+ NameTest stripTest = (NameTest) j.next();
+ if (stripTest.matches(ctx, 1, 1))
+ {
+ override = true;
+ break;
+ }
+ }
+ }
+ if (!override)
+ {
+ return true;
+ }
+ }
+ }
+ }
+ // Check whether any ancestor specified xml:space
+ while (ctx != null)
+ {
+ if (ctx.getNodeType() == Node.ELEMENT_NODE)
+ {
+ Element element = (Element) ctx;
+ String xmlSpace = element.getAttribute("xml:space");
+ if ("default".equals(xmlSpace))
+ {
+ break;
+ }
+ else if ("preserve".equals(xmlSpace))
+ {
+ return true;
+ }
+ else if (xmlSpace.length() > 0)
+ {
+ String msg = "Illegal value for xml:space: " + xmlSpace;
+ throw new TransformerConfigurationException(msg);
+ }
+ else if ("text".equals(ctx.getLocalName()) &&
+ XSL_NS.equals(ctx.getNamespaceURI()))
+ {
+ // xsl:text implies xml:space='preserve'
+ return true;
+ }
+ }
+ ctx = ctx.getParentNode();
+ }
+ return false;
+ }
+
+ public XPathFunction resolveFunction(QName name, int arity)
+ {
+ String uri = name.getNamespaceURI();
+ if (XSL_NS.equals(uri) || uri == null || uri.length() == 0)
+ {
+ String localName = name.getLocalPart();
+ if ("document".equals(localName) && (arity == 1 || arity == 2))
+ {
+ if (current == null)
+ {
+ throw new RuntimeException("current is null");
+ }
+ return new DocumentFunction(getRootStylesheet(), current);
+ }
+ else if ("key".equals(localName) && (arity == 2))
+ {
+ return new KeyFunction(getRootStylesheet());
+ }
+ else if ("format-number".equals(localName) &&
+ (arity == 2 || arity == 3))
+ {
+ return new FormatNumberFunction(getRootStylesheet());
+ }
+ else if ("current".equals(localName) && (arity == 0))
+ {
+ return new CurrentFunction(getRootStylesheet());
+ }
+ else if ("unparsed-entity-uri".equals(localName) && (arity == 1))
+ {
+ return new UnparsedEntityUriFunction();
+ }
+ else if ("generate-id".equals(localName) &&
+ (arity == 1 || arity == 0))
+ {
+ return new GenerateIdFunction();
+ }
+ else if ("system-property".equals(localName) && (arity == 1))
+ {
+ return new SystemPropertyFunction();
+ }
+ else if ("element-available".equals(localName) && (arity == 1))
+ {
+ return new ElementAvailableFunction(this);
+ }
+ else if ("function-available".equals(localName) && (arity == 1))
+ {
+ return new FunctionAvailableFunction(this);
+ }
+ }
+ return null;
+ }
+
+ // -- Parsing --
+
+ /**
+ * apply-templates
+ */
+ final TemplateNode parseApplyTemplates(Node node, Node children, Node next)
+ throws TransformerConfigurationException, XPathExpressionException
+ {
+ NamedNodeMap attrs = node.getAttributes();
+ String m = getAttribute(attrs, "mode");
+ QName mode = (m == null) ? null : getQName(m);
+ String s = getAttribute(attrs, "select");
+ if (s == null)
+ {
+ s = "child::node()";
+ }
+ List sortKeys = parseSortKeys(children);
+ List withParams = parseWithParams(children);
+ Expr select = (Expr) xpath.compile(s);
+ return new ApplyTemplatesNode(null, parse(next),
+ select, mode,
+ sortKeys, withParams, false);
+ }
+
+ /**
+ * call-template
+ */
+ final TemplateNode parseCallTemplate(Node node, Node children, Node next)
+ throws TransformerConfigurationException, XPathExpressionException
+ {
+ NamedNodeMap attrs = node.getAttributes();
+ String n = getRequiredAttribute(attrs, "name", node);
+ QName name = getQName(n);
+ List withParams = parseWithParams(children);
+ return new CallTemplateNode(null, parse(next), name,
+ withParams);
+ }
+
+ /**
+ * value-of
+ */
+ final TemplateNode parseValueOf(Node node, Node children, Node next)
+ throws TransformerConfigurationException, XPathExpressionException
+ {
+ NamedNodeMap attrs = node.getAttributes();
+ String s = getRequiredAttribute(attrs, "select", node);
+ String doe = getAttribute(attrs, "disable-output-escaping");
+ boolean d = "yes".equals(doe);
+ Expr select = (Expr) xpath.compile(s);
+ return new ValueOfNode(null, parse(next), select, d);
+ }
+
+ /**
+ * for-each
+ */
+ final TemplateNode parseForEach(Node node, Node children, Node next)
+ throws TransformerConfigurationException, XPathExpressionException
+ {
+ NamedNodeMap attrs = node.getAttributes();
+ String s = getRequiredAttribute(attrs, "select", node);
+ List sortKeys = parseSortKeys(children);
+ Expr select = (Expr) xpath.compile(s);
+ return new ForEachNode(parse(children), parse(next), select, sortKeys);
+ }
+
+ /**
+ * if
+ */
+ final TemplateNode parseIf(Node node, Node children, Node next)
+ throws TransformerConfigurationException, XPathExpressionException
+ {
+ NamedNodeMap attrs = node.getAttributes();
+ String t = getRequiredAttribute(attrs, "test", node);
+ Expr test = (Expr) xpath.compile(t);
+ return new IfNode(parse(children), parse(next), test);
+ }
+
+ /**
+ * when
+ */
+ final TemplateNode parseWhen(Node node, Node children, Node next)
+ throws TransformerConfigurationException, XPathExpressionException
+ {
+ NamedNodeMap attrs = node.getAttributes();
+ String t = getRequiredAttribute(attrs, "test", node);
+ Expr test = (Expr) xpath.compile(t);
+ return new WhenNode(parse(children), parse(next), test);
+ }
+
+ /**
+ * element
+ */
+ final TemplateNode parseElement(Node node, Node children, Node next)
+ throws TransformerConfigurationException, XPathExpressionException
+ {
+ NamedNodeMap attrs = node.getAttributes();
+ String name = getRequiredAttribute(attrs, "name", node);
+ String namespace = getAttribute(attrs, "namespace");
+ String uas = getAttribute(attrs, "use-attribute-sets");
+ TemplateNode n = parseAttributeValueTemplate(name, node);
+ TemplateNode ns = (namespace == null) ? null :
+ parseAttributeValueTemplate(namespace, node);
+ return new ElementNode(parse(children), parse(next), n, ns, uas, node);
+ }
+
+ /**
+ * attribute
+ */
+ final TemplateNode parseAttribute(Node node, Node children, Node next)
+ throws TransformerConfigurationException, XPathExpressionException
+ {
+ NamedNodeMap attrs = node.getAttributes();
+ String name = getRequiredAttribute(attrs, "name", node);
+ String namespace = getAttribute(attrs, "namespace");
+ TemplateNode n = parseAttributeValueTemplate(name, node);
+ TemplateNode ns = (namespace == null) ? null :
+ parseAttributeValueTemplate(namespace, node);
+ return new AttributeNode(parse(children), parse(next), n, ns, node);
+ }
+
+ /**
+ * text
+ */
+ final TemplateNode parseText(Node node, Node children, Node next)
+ throws TransformerConfigurationException, XPathExpressionException
+ {
+ NamedNodeMap attrs = node.getAttributes();
+ String doe = getAttribute(attrs, "disable-output-escaping");
+ boolean d = "yes".equals(doe);
+ return new TextNode(parse(children), parse(next), d);
+ }
+
+ /**
+ * copy
+ */
+ final TemplateNode parseCopy(Node node, Node children, Node next)
+ throws TransformerConfigurationException, XPathExpressionException
+ {
+ NamedNodeMap attrs = node.getAttributes();
+ String uas = getAttribute(attrs, "use-attribute-sets");
+ return new CopyNode(parse(children), parse(next), uas);
+ }
+
+ /**
+ * processing-instruction
+ */
+ final TemplateNode parseProcessingInstruction(Node node, Node children,
+ Node next)
+ throws TransformerConfigurationException, XPathExpressionException
+ {
+ NamedNodeMap attrs = node.getAttributes();
+ String name = getRequiredAttribute(attrs, "name", node);
+ return new ProcessingInstructionNode(parse(children),
+ parse(next), name);
+ }
+
+ /**
+ * number
+ */
+ final TemplateNode parseNumber(Node node, Node children, Node next)
+ throws TransformerConfigurationException, XPathExpressionException
+ {
+ NamedNodeMap attrs = node.getAttributes();
+ String v = getAttribute(attrs, "value");
+ String ff = getAttribute(attrs, "format");
+ if (ff == null)
+ {
+ ff = "1";
+ }
+ TemplateNode format = parseAttributeValueTemplate(ff, node);
+ String lang = getAttribute(attrs, "lang");
+ String lv = getAttribute(attrs, "letter-value");
+ int letterValue = "traditional".equals(lv) ?
+ AbstractNumberNode.TRADITIONAL :
+ AbstractNumberNode.ALPHABETIC;
+ String gs = getAttribute(attrs, "grouping-separator");
+ String gz = getAttribute(attrs, "grouping-size");
+ int gz2 = (gz != null && gz.length() > 0) ?
+ Integer.parseInt(gz) : 1;
+ if (v != null && v.length() > 0)
+ {
+ Expr value = (Expr) xpath.compile(v);
+ return new NumberNode(parse(children), parse(next),
+ value, format, lang,
+ letterValue, gs, gz2);
+ }
+ else
+ {
+ String l = getAttribute(attrs, "level");
+ int level =
+ "multiple".equals(l) ? NodeNumberNode.MULTIPLE :
+ "any".equals(l) ? NodeNumberNode.ANY :
+ NodeNumberNode.SINGLE;
+ String c = getAttribute(attrs, "count");
+ String f = getAttribute(attrs, "from");
+ Pattern count = null;
+ Pattern from = null;
+ if (c != null)
+ {
+ try
+ {
+ count = (Pattern) xpath.compile(c);
+ }
+ catch (ClassCastException e)
+ {
+ String msg = "invalid pattern: " + c;
+ throw new TransformerConfigurationException(msg);
+ }
+ }
+ if (f != null)
+ {
+ try
+ {
+ from = (Pattern) xpath.compile(f);
+ }
+ catch (ClassCastException e)
+ {
+ String msg = "invalid pattern: " + f;
+ throw new TransformerConfigurationException(msg);
+ }
+ }
+ return new NodeNumberNode(parse(children), parse(next),
+ level, count, from,
+ format, lang,
+ letterValue, gs, gz2);
+ }
+ }
+
+ /**
+ * copy-of
+ */
+ final TemplateNode parseCopyOf(Node node, Node children, Node next)
+ throws TransformerConfigurationException, XPathExpressionException
+ {
+ NamedNodeMap attrs = node.getAttributes();
+ String s = getRequiredAttribute(attrs, "select", node);
+ Expr select = (Expr) xpath.compile(s);
+ return new CopyOfNode(parse(children), parse(next), select);
+ }
+
+ /**
+ * message
+ */
+ final TemplateNode parseMessage(Node node, Node children, Node next)
+ throws TransformerConfigurationException, XPathExpressionException
+ {
+ NamedNodeMap attrs = node.getAttributes();
+ String t = getAttribute(attrs, "terminate");
+ boolean terminate = "yes".equals(t);
+ return new MessageNode(parse(children), parse(next), terminate);
+ }
+
+ /**
+ * Parse template-level elements.
+ */
+ final TemplateNode parse(Node node)
+ throws TransformerConfigurationException
+ {
+ if (node == null)
+ {
+ return null;
+ }
+ // Hack to associate the document function with its declaring node
+ current = node;
+ Node children = node.getFirstChild();
+ Node next = node.getNextSibling();
+ try
+ {
+ String namespaceUri = node.getNamespaceURI();
+ if (Stylesheet.XSL_NS.equals(namespaceUri) &&
+ Node.ELEMENT_NODE == node.getNodeType())
+ {
+ String name = node.getLocalName();
+ if ("apply-templates".equals(name))
+ {
+ return parseApplyTemplates(node, children, next);
+ }
+ else if ("call-template".equals(name))
+ {
+ return parseCallTemplate(node, children, next);
+ }
+ else if ("value-of".equals(name))
+ {
+ return parseValueOf(node, children, next);
+ }
+ else if ("for-each".equals(name))
+ {
+ return parseForEach(node, children, next);
+ }
+ else if ("if".equals(name))
+ {
+ return parseIf(node, children, next);
+ }
+ else if ("choose".equals(name))
+ {
+ return new ChooseNode(parse(children), parse(next));
+ }
+ else if ("when".equals(name))
+ {
+ return parseWhen(node, children, next);
+ }
+ else if ("otherwise".equals(name))
+ {
+ return new OtherwiseNode(parse(children), parse(next));
+ }
+ else if ("element".equals(name))
+ {
+ return parseElement(node, children, next);
+ }
+ else if ("attribute".equals(name))
+ {
+ return parseAttribute(node, children, next);
+ }
+ else if ("text".equals(name))
+ {
+ return parseText(node, children, next);
+ }
+ else if ("copy".equals(name))
+ {
+ return parseCopy(node, children, next);
+ }
+ else if ("processing-instruction".equals(name))
+ {
+ return parseProcessingInstruction(node, children, next);
+ }
+ else if ("comment".equals(name))
+ {
+ return new CommentNode(parse(children), parse(next));
+ }
+ else if ("number".equals(name))
+ {
+ return parseNumber(node, children, next);
+ }
+ else if ("param".equals(name) ||
+ "variable".equals(name))
+ {
+ boolean global = "variable".equals(name);
+ NamedNodeMap attrs = node.getAttributes();
+ TemplateNode content = parse(children);
+ String paramName = getRequiredAttribute(attrs, "name", node);
+ String select = getAttribute(attrs, "select");
+ if (select != null)
+ {
+ if (content != null)
+ {
+ String msg = "parameter '" + paramName +
+ "' has both select and content";
+ DOMSourceLocator l = new DOMSourceLocator(node);
+ throw new TransformerConfigurationException(msg, l);
+ }
+ Expr expr = (Expr) xpath.compile(select);
+ return new ParameterNode(null, parse(next),
+ paramName, expr, global);
+ }
+ else
+ {
+ return new ParameterNode(content, parse(next),
+ paramName, null, global);
+ }
+ }
+ else if ("copy-of".equals(name))
+ {
+ return parseCopyOf(node, children, next);
+ }
+ else if ("message".equals(name))
+ {
+ return parseMessage(node, children, next);
+ }
+ else if ("apply-imports".equals(name))
+ {
+ return new ApplyImportsNode(parse(children), parse(next));
+ }
+ else
+ {
+ // xsl:fallback
+ // Pass over any other XSLT nodes
+ return parse(next);
+ }
+ }
+ String prefix = node.getPrefix();
+ if (extensionElementPrefixes.contains(prefix))
+ {
+ // Pass over extension elements
+ return parse(next);
+ }
+ switch (node.getNodeType())
+ {
+ case Node.TEXT_NODE:
+ // Determine whether to strip whitespace
+ Text text = (Text) node;
+ if (!isPreserved(text))
+ {
+ // Strip
+ text.getParentNode().removeChild(text);
+ return parse(next);
+ }
+ break;
+ case Node.COMMENT_NODE:
+ // Ignore comments
+ return parse(next);
+ case Node.ELEMENT_NODE:
+ // Check for attribute value templates and use-attribute-sets
+ NamedNodeMap attrs = node.getAttributes();
+ boolean convert = false;
+ String useAttributeSets = null;
+ int len = attrs.getLength();
+ for (int i = 0; i < len; i++)
+ {
+ Node attr = attrs.item(i);
+ String value = attr.getNodeValue();
+ if (Stylesheet.XSL_NS.equals(attr.getNamespaceURI()) &&
+ "use-attribute-sets".equals(attr.getLocalName()))
+ {
+ useAttributeSets = value;
+ convert = true;
+ break;
+ }
+ int start = value.indexOf('{');
+ int end = value.indexOf('}');
+ if (start != -1 || end != -1)
+ {
+ convert = true;
+ break;
+ }
+ }
+ if (convert)
+ {
+ // Create an element-producing template node instead
+ // with appropriate attribute-producing child template nodes
+ TemplateNode child = parse(children);
+ for (int i = 0; i < len; i++)
+ {
+ Node attr = attrs.item(i);
+ String ans = attr.getNamespaceURI();
+ String aname = attr.getNodeName();
+ if (Stylesheet.XSL_NS.equals(ans) &&
+ "use-attribute-sets".equals(attr.getLocalName()))
+ {
+ continue;
+ }
+ String value = attr.getNodeValue();
+ TemplateNode grandchild =
+ parseAttributeValueTemplate(value, node);
+ TemplateNode n =
+ parseAttributeValueTemplate(aname, node);
+ TemplateNode ns = (ans == null) ? null :
+ parseAttributeValueTemplate(ans, node);
+ child = new AttributeNode(grandchild, child, n, ns, attr);
+ }
+ String ename = node.getNodeName();
+ TemplateNode n = parseAttributeValueTemplate(ename, node);
+ TemplateNode ns = (namespaceUri == null) ? null :
+ parseAttributeValueTemplate(namespaceUri, node);
+ return new ElementNode(child, parse(next),
+ n, ns, useAttributeSets,
+ node);
+ }
+ // Otherwise fall through
+ break;
+ }
+ }
+ catch (XPathExpressionException e)
+ {
+ DOMSourceLocator l = new DOMSourceLocator(node);
+ throw new TransformerConfigurationException(e.getMessage(), l, e);
+ }
+ return new LiteralNode(parse(children), parse(next), node);
+ }
+
+ final List parseSortKeys(Node node)
+ throws TransformerConfigurationException, XPathExpressionException
+ {
+ List ret = new LinkedList();
+ while (node != null)
+ {
+ String namespaceUri = node.getNamespaceURI();
+ if (Stylesheet.XSL_NS.equals(namespaceUri) &&
+ Node.ELEMENT_NODE == node.getNodeType() &&
+ "sort".equals(node.getLocalName()))
+ {
+ NamedNodeMap attrs = node.getAttributes();
+ String s = getAttribute(attrs, "select");
+ if (s == null)
+ {
+ s = ".";
+ }
+ Expr select = (Expr) xpath.compile(s);
+ String l = getAttribute(attrs, "lang");
+ TemplateNode lang = (l == null) ? null :
+ parseAttributeValueTemplate(l, node);
+ String dt = getAttribute(attrs, "data-type");
+ TemplateNode dataType = (dt == null) ? null :
+ parseAttributeValueTemplate(dt, node);
+ String o = getAttribute(attrs, "order");
+ TemplateNode order = (o == null) ? null :
+ parseAttributeValueTemplate(o, node);
+ String co = getAttribute(attrs, "case-order");
+ TemplateNode caseOrder = (co == null) ? null :
+ parseAttributeValueTemplate(co, node);
+ ret.add(new SortKey(select, lang, dataType, order, caseOrder));
+ }
+ node = node.getNextSibling();
+ }
+ return ret.isEmpty() ? null : ret;
+ }
+
+ final List parseWithParams(Node node)
+ throws TransformerConfigurationException, XPathExpressionException
+ {
+ List ret = new LinkedList();
+ while (node != null)
+ {
+ String namespaceUri = node.getNamespaceURI();
+ if (Stylesheet.XSL_NS.equals(namespaceUri) &&
+ Node.ELEMENT_NODE == node.getNodeType() &&
+ "with-param".equals(node.getLocalName()))
+ {
+ NamedNodeMap attrs = node.getAttributes();
+ TemplateNode content = parse(node.getFirstChild());
+ String name = getRequiredAttribute(attrs, "name", node);
+ String select = getAttribute(attrs, "select");
+ if (select != null)
+ {
+ if (content != null)
+ {
+ String msg = "parameter '" + name +
+ "' has both select and content";
+ DOMSourceLocator l = new DOMSourceLocator(node);
+ throw new TransformerConfigurationException(msg, l);
+ }
+ Expr expr = (Expr) xpath.compile(select);
+ ret.add(new WithParam(name, expr));
+ }
+ else
+ {
+ ret.add(new WithParam(name, content));
+ }
+ }
+ node = node.getNextSibling();
+ }
+ return ret.isEmpty() ? null : ret;
+ }
+
+ /**
+ * Created element nodes have a copy of the namespace nodes in the
+ * stylesheet, except the XSLT namespace, extension namespaces, and
+ * exclude-result-prefixes.
+ */
+ final void addNamespaceNodes(Node source, Node target, Document doc,
+ Collection elementExcludeResultPrefixes)
+ {
+ NamedNodeMap attrs = source.getAttributes();
+ if (attrs != null)
+ {
+ int len = attrs.getLength();
+ for (int i = 0; i < len; i++)
+ {
+ Node attr = attrs.item(i);
+ String uri = attr.getNamespaceURI();
+ if (uri == XMLConstants.XMLNS_ATTRIBUTE_NS_URI)
+ {
+ String prefix = attr.getLocalName();
+ if (XMLConstants.XMLNS_ATTRIBUTE.equals(prefix))
+ {
+ prefix = "#default";
+ }
+ String ns = attr.getNodeValue();
+ // Should the namespace be excluded?
+ if (XSL_NS.equals(ns) ||
+ extensionElementPrefixes.contains(prefix) ||
+ elementExcludeResultPrefixes.contains(prefix) ||
+ excludeResultPrefixes.contains(prefix))
+ {
+ continue;
+ }
+ // Is the namespace already defined on the target?
+ if (prefix == "#default")
+ {
+ prefix = null;
+ }
+ if (target.lookupNamespaceURI(prefix) != null)
+ {
+ continue;
+ }
+ attr = attr.cloneNode(true);
+ attr = doc.adoptNode(attr);
+ target.getAttributes().setNamedItemNS(attr);
+ }
+ }
+ }
+ Node parent = source.getParentNode();
+ if (parent != null)
+ {
+ addNamespaceNodes(parent, target, doc, elementExcludeResultPrefixes);
+ }
+ }
+
+ static final String getAttribute(NamedNodeMap attrs, String name)
+ {
+ Node attr = attrs.getNamedItem(name);
+ if (attr == null)
+ {
+ return null;
+ }
+ String ret = attr.getNodeValue();
+ if (ret.length() == 0)
+ {
+ return null;
+ }
+ return ret;
+ }
+
+ static final String getRequiredAttribute(NamedNodeMap attrs, String name,
+ Node source)
+ throws TransformerConfigurationException
+ {
+ String value = getAttribute(attrs, name);
+ if (value == null || value.length() == 0)
+ {
+ String msg =
+ name + " attribute is required on " + source.getNodeName();
+ DOMSourceLocator l = new DOMSourceLocator(source);
+ throw new TransformerConfigurationException(msg, l);
+ }
+ return value;
+ }
+
+ // Handle user data changes when nodes are cloned etc
+
+ public void handle(short op, String key, Object data, Node src, Node dst)
+ {
+ dst.setUserData(key, data, this);
+ }
+
+}
+
diff --git a/libjava/gnu/xml/transform/SystemPropertyFunction.java b/libjava/gnu/xml/transform/SystemPropertyFunction.java
new file mode 100644
index 0000000..3d95d21d
--- /dev/null
+++ b/libjava/gnu/xml/transform/SystemPropertyFunction.java
@@ -0,0 +1,129 @@
+/* SystemPropertyFunction.java --
+ Copyright (C) 2004 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.transform;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import javax.xml.namespace.QName;
+import javax.xml.xpath.XPathFunction;
+import javax.xml.xpath.XPathFunctionException;
+import org.w3c.dom.Node;
+import gnu.xml.xpath.Expr;
+import gnu.xml.xpath.Function;
+
+/**
+ * The XSLT <code>system-property()</code>function.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+final class SystemPropertyFunction
+ extends Expr
+ implements XPathFunction, Function
+{
+
+ List args;
+
+ public Object evaluate(List args)
+ throws XPathFunctionException
+ {
+ String name = (String) args.get(0);
+ return systemProperty(QName.valueOf(name));
+ }
+
+ public void setArguments(List args)
+ {
+ this.args = args;
+ }
+
+ public Object evaluate(Node context, int pos, int len)
+ {
+ int arity = args.size();
+ List values = new ArrayList(arity);
+ for (int i = 0; i < arity; i++)
+ {
+ Expr arg = (Expr) args.get(i);
+ values.add(arg.evaluate(context, pos, len));
+ }
+ String name = _string(context, values.get(0));
+ return systemProperty(QName.valueOf(name));
+ }
+
+ Object systemProperty(QName name)
+ {
+ String localName = name.getLocalPart();
+ String prefix = name.getPrefix();
+ String uri = name.getNamespaceURI();
+ if (Stylesheet.XSL_NS.equals(uri) ||
+ "xsl".equals(prefix))
+ {
+ if ("version".equals(localName))
+ {
+ return new Double(1.0d);
+ }
+ else if ("vendor".equals(localName))
+ {
+ return "The Free Software Foundation";
+ }
+ else if ("vendor-url".equals(localName))
+ {
+ return "http://www.gnu.org/";
+ }
+ else
+ {
+ return "";
+ }
+ }
+ return System.getProperty(localName);
+ }
+
+ public Expr clone(Object context)
+ {
+ SystemPropertyFunction f = new SystemPropertyFunction();
+ int len = args.size();
+ List args2 = new ArrayList(len);
+ for (int i = 0; i < len; i++)
+ {
+ args2.add(((Expr) args.get(i)).clone(context));
+ }
+ f.setArguments(args2);
+ return f;
+ }
+
+}
+
diff --git a/libjava/gnu/xml/transform/Template.java b/libjava/gnu/xml/transform/Template.java
new file mode 100644
index 0000000..6aaa4c1
--- /dev/null
+++ b/libjava/gnu/xml/transform/Template.java
@@ -0,0 +1,254 @@
+/* Template.java --
+ Copyright (C) 2004 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.transform;
+
+import java.io.PrintStream;
+import javax.xml.namespace.QName;
+import javax.xml.transform.TransformerConfigurationException;
+import javax.xml.transform.TransformerException;
+import javax.xml.xpath.XPathExpressionException;
+import org.w3c.dom.Node;
+import gnu.xml.xpath.Expr;
+import gnu.xml.xpath.NameTest;
+import gnu.xml.xpath.NodeTypeTest;
+import gnu.xml.xpath.Pattern;
+import gnu.xml.xpath.Selector;
+import gnu.xml.xpath.Test;
+
+/**
+ * A template in an XSL stylesheet.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+class Template
+ implements Comparable
+{
+
+ static final double DEFAULT_PRIORITY = 0.5d;
+
+ final Stylesheet stylesheet;
+ final QName name;
+ final Pattern match;
+ final TemplateNode node;
+ final double priority;
+ final int precedence;
+ final QName mode;
+
+ Template(Stylesheet stylesheet,
+ QName name, Pattern match, TemplateNode node,
+ int precedence, double priority, QName mode)
+ {
+ this.stylesheet = stylesheet;
+ this.name = name;
+ this.match = match;
+ this.node = node;
+ // adjust priority if necessary
+ // see XSLT section 5.5
+ Test test = getNodeTest(match);
+ if (test != null)
+ {
+ if (test instanceof NameTest)
+ {
+ NameTest nameTest = (NameTest) test;
+ if (nameTest.matchesAny() ||
+ nameTest.matchesAnyLocalName())
+ {
+ priority = -0.25d;
+ }
+ else
+ {
+ priority = 0.0d;
+ }
+ }
+ else
+ {
+ NodeTypeTest nodeTypeTest = (NodeTypeTest) test;
+ if (nodeTypeTest.getNodeType() ==
+ Node.PROCESSING_INSTRUCTION_NODE &&
+ nodeTypeTest.getData() != null)
+ {
+ priority = 0.0d;
+ }
+ else
+ {
+ priority = -0.5d;
+ }
+ }
+ }
+ this.precedence = precedence;
+ this.priority = priority;
+ this.mode = mode;
+ }
+
+ Template clone(Stylesheet stylesheet)
+ {
+ // FIXME by cloning we lose the imports() functionality, so
+ // apply-imports will be broken.
+ return new Template(stylesheet,
+ name,
+ (match == null) ? null :
+ (Pattern) match.clone(stylesheet),
+ (node == null) ? null : node.clone(stylesheet),
+ precedence,
+ priority,
+ mode);
+ }
+
+ public int compareTo(Object other)
+ {
+ if (other instanceof Template)
+ {
+ Template t = (Template) other;
+ int d = t.precedence - precedence;
+ if (d != 0)
+ {
+ return d;
+ }
+ double d2 = t.priority - priority;
+ if (d2 != 0.0d)
+ {
+ return (int) Math.round(d2 * 1000.0d);
+ }
+ }
+ return 0;
+ }
+
+ Test getNodeTest(Expr expr)
+ {
+ if (expr instanceof Selector)
+ {
+ Selector selector = (Selector) expr;
+ Test[] tests = selector.getTests();
+ if (tests.length > 0)
+ {
+ return tests[0];
+ }
+ }
+ return null;
+ }
+
+ boolean matches(QName mode, Node node)
+ {
+ if ((mode == null && this.mode != null) ||
+ (mode != null && !mode.equals(this.mode)))
+ {
+ return false;
+ }
+ if (match == null)
+ {
+ return false;
+ }
+ return match.matches(node);
+ }
+
+ boolean matches(QName name)
+ {
+ return name.equals(this.name);
+ }
+
+ boolean imports(Template other)
+ {
+ for (Stylesheet ctx = other.stylesheet.parent;
+ ctx != null;
+ ctx = ctx.parent)
+ {
+ if (ctx == stylesheet)
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * @param stylesheet the stylesheet
+ * @param parent the parent of result nodes
+ * @param context the context node in the source document
+ * @param pos the context position
+ * @param len the context size
+ * @param nextSibling if non-null, add result nodes before this node
+ */
+ void apply(Stylesheet stylesheet, QName mode,
+ Node context, int pos, int len,
+ Node parent, Node nextSibling)
+ throws TransformerException
+ {
+ //System.err.println("...applying " + toString() + " to " + context);
+ if (node != null)
+ {
+ node.apply(stylesheet, mode,
+ context, pos, len,
+ parent, nextSibling);
+ }
+ }
+
+ public String toString()
+ {
+ StringBuffer buf = new StringBuffer(getClass().getName());
+ buf.append('[');
+ if (name != null)
+ {
+ buf.append("name=");
+ buf.append(name);
+ }
+ else if (match != null)
+ {
+ buf.append("match=");
+ buf.append(match);
+ }
+ if (mode != null)
+ {
+ buf.append(",mode=");
+ buf.append(mode);
+ }
+ buf.append(']');
+ return buf.toString();
+
+ //return (name != null) ? name.toString() : match.toString();
+ }
+
+ void list(PrintStream out)
+ {
+ out.println(toString());
+ if (node != null)
+ {
+ node.list(1, out, true);
+ }
+ }
+
+}
diff --git a/libjava/gnu/xml/transform/TemplateNode.java b/libjava/gnu/xml/transform/TemplateNode.java
new file mode 100644
index 0000000..1fe37c2
--- /dev/null
+++ b/libjava/gnu/xml/transform/TemplateNode.java
@@ -0,0 +1,115 @@
+/* TemplateNode.java --
+ Copyright (C) 2004 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.transform;
+
+import java.io.PrintStream;
+import java.util.Comparator;
+import javax.xml.namespace.QName;
+import javax.xml.transform.TransformerException;
+import org.w3c.dom.Node;
+import gnu.xml.xpath.DocumentOrderComparator;
+
+/**
+ * Wrapper for a source node in a template.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+abstract class TemplateNode
+{
+
+ static final Comparator documentOrderComparator =
+ new DocumentOrderComparator();
+
+ final TemplateNode children;
+ final TemplateNode next;
+
+ TemplateNode(TemplateNode children, TemplateNode next)
+ {
+ this.children = children;
+ this.next = next;
+ }
+
+ final void apply(Stylesheet stylesheet, QName mode,
+ Node context, int pos, int len,
+ Node parent, Node nextSibling)
+ throws TransformerException
+ {
+ if (stylesheet.terminated)
+ {
+ return;
+ }
+ if (Thread.currentThread().isInterrupted())
+ {
+ // Try to head off any infinite loops at the pass
+ return;
+ }
+ if (stylesheet.debug)
+ {
+ System.err.println("Applying " + toString());
+ }
+ doApply(stylesheet, mode, context, pos, len, parent, nextSibling);
+ }
+
+ abstract void doApply(Stylesheet stylesheet, QName mode,
+ Node context, int pos, int len,
+ Node parent, Node nextSibling)
+ throws TransformerException;
+
+ abstract TemplateNode clone(Stylesheet stylesheet);
+
+ /**
+ * Debugging
+ */
+ void list(int depth, PrintStream out, boolean listNext)
+ {
+ for (int i = 0; i < depth; i++)
+ {
+ out.print(" ");
+ }
+ out.println(toString());
+ if (children != null)
+ {
+ children.list(depth + 1, out, true);
+ }
+ if (listNext && next != null)
+ {
+ next.list(depth, out, listNext);
+ }
+ }
+
+}
diff --git a/libjava/gnu/xml/transform/TemplatesImpl.java b/libjava/gnu/xml/transform/TemplatesImpl.java
new file mode 100644
index 0000000..a7403e9
--- /dev/null
+++ b/libjava/gnu/xml/transform/TemplatesImpl.java
@@ -0,0 +1,83 @@
+/* TemplatesImpl.java --
+ Copyright (C) 2004 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.transform;
+
+import java.util.Properties;
+import javax.xml.transform.ErrorListener;
+import javax.xml.transform.Source;
+import javax.xml.transform.Templates;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerConfigurationException;
+import javax.xml.transform.URIResolver;
+
+/**
+ * GNU precompiled stylesheet implementation.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+class TemplatesImpl
+ implements Templates
+{
+
+ final TransformerFactoryImpl factory;
+ final Stylesheet stylesheet;
+ final Properties outputProperties;
+
+ TemplatesImpl(TransformerFactoryImpl factory, Stylesheet stylesheet)
+ {
+ this.factory = factory;
+ this.stylesheet = stylesheet;
+ outputProperties = new TransformerOutputProperties(stylesheet);
+ }
+
+ public Transformer newTransformer()
+ throws TransformerConfigurationException
+ {
+ Stylesheet stylesheet = (Stylesheet) this.stylesheet.clone();
+ TransformerImpl transformer =
+ new TransformerImpl(factory, stylesheet, outputProperties);
+ stylesheet.transformer = transformer;
+ return transformer;
+ }
+
+ public Properties getOutputProperties()
+ {
+ return (Properties) outputProperties.clone();
+ }
+
+}
diff --git a/libjava/gnu/xml/transform/TextNode.java b/libjava/gnu/xml/transform/TextNode.java
new file mode 100644
index 0000000..70a8ce4
--- /dev/null
+++ b/libjava/gnu/xml/transform/TextNode.java
@@ -0,0 +1,116 @@
+/* TextNode.java --
+ Copyright (C) 2004 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.transform;
+
+import javax.xml.namespace.QName;
+import javax.xml.transform.TransformerException;
+import org.w3c.dom.Document;
+import org.w3c.dom.DocumentFragment;
+import org.w3c.dom.Node;
+import org.w3c.dom.Text;
+import gnu.xml.xpath.Expr;
+
+/**
+ * A template node representing the XSL <code>text</code> instruction.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+final class TextNode
+ extends TemplateNode
+{
+
+ final boolean disableOutputEscaping;
+
+ TextNode(TemplateNode children, TemplateNode next,
+ boolean disableOutputEscaping)
+ {
+ super(children, next);
+ this.disableOutputEscaping = disableOutputEscaping;
+ }
+
+ TemplateNode clone(Stylesheet stylesheet)
+ {
+ return new TextNode((children == null) ? null :
+ children.clone(stylesheet),
+ (next == null) ? null :
+ next.clone(stylesheet),
+ disableOutputEscaping);
+ }
+
+ void doApply(Stylesheet stylesheet, QName mode,
+ Node context, int pos, int len,
+ Node parent, Node nextSibling)
+ throws TransformerException
+ {
+ String value = "";
+ Document doc = (parent instanceof Document) ? (Document) parent :
+ parent.getOwnerDocument();
+ if (children != null)
+ {
+ // Create a document fragment to hold the text
+ DocumentFragment fragment = doc.createDocumentFragment();
+ // Apply children to the fragment
+ children.apply(stylesheet, mode,
+ context, pos, len,
+ fragment, null);
+ // Use XPath string-value of fragment
+ value = Expr.stringValue(fragment);
+ }
+ Text text = doc.createTextNode(value);
+ if (disableOutputEscaping)
+ {
+ text.setUserData("disable-output-escaping", "yes", stylesheet);
+ }
+ // Insert into result tree
+ if (nextSibling != null)
+ {
+ parent.insertBefore(text, nextSibling);
+ }
+ else
+ {
+ parent.appendChild(text);
+ }
+ if (next != null)
+ {
+ next.apply(stylesheet, mode,
+ context, pos, len,
+ parent, nextSibling);
+ }
+ }
+
+}
diff --git a/libjava/gnu/xml/transform/TransformerFactoryImpl.java b/libjava/gnu/xml/transform/TransformerFactoryImpl.java
new file mode 100644
index 0000000..9c4d9da
--- /dev/null
+++ b/libjava/gnu/xml/transform/TransformerFactoryImpl.java
@@ -0,0 +1,345 @@
+/* TransformerFactoryImpl.java --
+ Copyright (C) 2004 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.transform;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.Map;
+import java.util.Properties;
+import javax.xml.transform.ErrorListener;
+import javax.xml.transform.Source;
+import javax.xml.transform.Templates;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerConfigurationException;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.URIResolver;
+import javax.xml.transform.dom.DOMResult;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.sax.SAXResult;
+import javax.xml.transform.sax.SAXSource;
+import javax.xml.transform.stream.StreamResult;
+import javax.xml.transform.stream.StreamSource;
+import javax.xml.xpath.XPathFactory;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import gnu.xml.dom.DomDocument;
+
+/**
+ * GNU transformer factory implementation.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+public class TransformerFactoryImpl
+ extends TransformerFactory
+{
+
+ final XPathFactory xpathFactory;
+ final XSLURIResolver resolver;
+ ErrorListener userListener;
+ URIResolver userResolver;
+
+ public TransformerFactoryImpl()
+ {
+ xpathFactory = new gnu.xml.xpath.XPathFactoryImpl();
+ resolver = new XSLURIResolver();
+ }
+
+ public Transformer newTransformer(Source source)
+ throws TransformerConfigurationException
+ {
+ Stylesheet stylesheet = newStylesheet(source, 0, null);
+ Properties outputProperties =
+ new TransformerOutputProperties(stylesheet);
+ TransformerImpl transformer =
+ new TransformerImpl(this, stylesheet, outputProperties);
+ stylesheet.transformer = transformer;
+ return transformer;
+ }
+
+ public Transformer newTransformer()
+ throws TransformerConfigurationException
+ {
+ return new TransformerImpl(this, null, new Properties());
+ }
+
+ public Templates newTemplates(Source source)
+ throws TransformerConfigurationException
+ {
+ Stylesheet stylesheet = newStylesheet(source, 0, null);
+ return new TemplatesImpl(this, stylesheet);
+ }
+
+ Stylesheet newStylesheet(Source source, int precedence, Stylesheet parent)
+ throws TransformerConfigurationException
+ {
+ Document doc = null;
+ String systemId = null;
+ if (source != null)
+ {
+ try
+ {
+ DOMSource ds;
+ synchronized (resolver)
+ {
+ resolver.setUserResolver(userResolver);
+ resolver.setUserListener(userListener);
+ ds = resolver.resolveDOM(source, null, null);
+ }
+ Node node = ds.getNode();
+ if (node == null)
+ {
+ throw new TransformerConfigurationException("no source document");
+ }
+ doc = (node instanceof Document) ? (Document) node :
+ node.getOwnerDocument();
+ systemId = ds.getSystemId();
+ }
+ catch (TransformerException e)
+ {
+ throw new TransformerConfigurationException(e);
+ }
+ }
+ return new Stylesheet(this, parent, doc, systemId, precedence);
+ }
+
+ public Source getAssociatedStylesheet(Source source,
+ String media,
+ String title,
+ String charset)
+ throws TransformerConfigurationException
+ {
+ try
+ {
+ DOMSource ds;
+ synchronized (resolver)
+ {
+ resolver.setUserResolver(userResolver);
+ resolver.setUserListener(userListener);
+ ds = resolver.resolveDOM(source, null, null);
+ }
+ Node node = ds.getNode();
+ if (node == null)
+ {
+ throw new TransformerConfigurationException("no source document");
+ }
+ Document doc = (node instanceof Document) ? (Document) node :
+ node.getOwnerDocument();
+ LinkedList matches = new LinkedList();
+ for (node = doc.getFirstChild();
+ node != null;
+ node = node.getNextSibling())
+ {
+ if (node.getNodeType() == Node.PROCESSING_INSTRUCTION_NODE &&
+ "xml-stylesheet".equals(node.getNodeName()))
+ {
+ Map params = parseParameters(node.getNodeValue());
+ if (media != null && !media.equals(params.get("media")))
+ {
+ continue;
+ }
+ if (title != null && !title.equals(params.get("title")))
+ {
+ continue;
+ }
+ if (charset != null && !charset.equals(params.get("charset")))
+ {
+ continue;
+ }
+ String href = (String) params.get("href");
+ URL url = resolver.resolveURL(null, node.getBaseURI(), href);
+ matches.add(url);
+ }
+ }
+ switch (matches.size())
+ {
+ case 0:
+ return null;
+ case 1:
+ return new StreamSource(((URL) matches.getFirst()).toString());
+ default:
+ // Create a source representing a stylesheet with a list of
+ // imports
+ DomDocument ssDoc = new DomDocument();
+ ssDoc.setBuilding(true);
+ // Create document element
+ Node root =
+ ssDoc.createElementNS(Stylesheet.XSL_NS, "stylesheet");
+ Node version =
+ ssDoc.createAttributeNS(null, "version");
+ version.setNodeValue("1.0");
+ root.getAttributes().setNamedItemNS(version);
+ ssDoc.appendChild(root);
+ // Create xsl:import for each URL
+ for (Iterator i = matches.iterator(); i.hasNext(); )
+ {
+ URL url = (URL) i.next();
+ Node imp =
+ ssDoc.createElementNS(Stylesheet.XSL_NS, "import");
+ Node href =
+ ssDoc.createAttributeNS(null, "href");
+ href.setNodeValue(url.toString());
+ imp.getAttributes().setNamedItemNS(href);
+ root.appendChild(imp);
+ }
+ ssDoc.setBuilding(false);
+ return new DOMSource(ssDoc);
+ }
+ }
+ catch (IOException e)
+ {
+ throw new TransformerConfigurationException(e);
+ }
+ catch (TransformerException e)
+ {
+ throw new TransformerConfigurationException(e);
+ }
+ }
+
+ Map parseParameters(String data)
+ {
+ Map ret = new LinkedHashMap();
+ int len = data.length();
+ String key = null;
+ int start = 0;
+ char quoteChar = '\u0000';
+ for (int i = 0; i < len; i++)
+ {
+ char c = data.charAt(i);
+ if (quoteChar == '\u0000' && c == ' ')
+ {
+ if (key == null && start < i)
+ {
+ key = data.substring(start, i);
+ }
+ else
+ {
+ String val = unquote(data.substring(start, i).trim());
+ ret.put(key, val);
+ key = null;
+ }
+ start = i + 1;
+ }
+ else if (c == '"')
+ {
+ quoteChar = (quoteChar == c) ? '\u0000' : c;
+ }
+ else if (c == '\'')
+ {
+ quoteChar = (quoteChar == c) ? '\u0000' : c;
+ }
+ }
+ if (start < len && key != null)
+ {
+ String val = unquote(data.substring(start, len).trim());
+ ret.put(key, val);
+ }
+ return ret;
+ }
+
+ String unquote(String text)
+ {
+ int end = text.length() - 1;
+ if (text.charAt(0) == '\'' && text.charAt(end) == '\'')
+ {
+ return text.substring(1, end);
+ }
+ if (text.charAt(0) == '"' && text.charAt(end) == '"')
+ {
+ return text.substring(1, end);
+ }
+ return text;
+ }
+
+ public void setURIResolver(URIResolver resolver)
+ {
+ userResolver = resolver;
+ }
+
+ public URIResolver getURIResolver()
+ {
+ return userResolver;
+ }
+
+ public void setFeature(String name, boolean value)
+ throws TransformerConfigurationException
+ {
+ throw new TransformerConfigurationException("not supported");
+ }
+
+ public boolean getFeature(String name)
+ {
+ if (SAXSource.FEATURE.equals(name) ||
+ SAXResult.FEATURE.equals(name) ||
+ StreamSource.FEATURE.equals(name) ||
+ StreamResult.FEATURE.equals(name) ||
+ DOMSource.FEATURE.equals(name) ||
+ DOMResult.FEATURE.equals(name))
+ {
+ return true;
+ }
+ return false;
+ }
+
+ public void setAttribute(String name, Object value)
+ throws IllegalArgumentException
+ {
+ throw new IllegalArgumentException("not supported");
+ }
+
+ public Object getAttribute(String name)
+ throws IllegalArgumentException
+ {
+ throw new IllegalArgumentException("not supported");
+ }
+
+ public void setErrorListener(ErrorListener listener)
+ throws IllegalArgumentException
+ {
+ userListener = listener;
+ }
+
+ public ErrorListener getErrorListener()
+ {
+ return userListener;
+ }
+
+}
diff --git a/libjava/gnu/xml/transform/TransformerImpl.java b/libjava/gnu/xml/transform/TransformerImpl.java
new file mode 100644
index 0000000..fb4632e
--- /dev/null
+++ b/libjava/gnu/xml/transform/TransformerImpl.java
@@ -0,0 +1,586 @@
+/* TransformerImpl.java --
+ Copyright (C) 2004 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.transform;
+
+import java.io.BufferedOutputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+import java.io.Writer;
+import java.net.MalformedURLException;
+import java.net.UnknownServiceException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Properties;
+import javax.xml.transform.ErrorListener;
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Result;
+import javax.xml.transform.Source;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerConfigurationException;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.URIResolver;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.dom.DOMResult;
+import javax.xml.transform.sax.SAXResult;
+import javax.xml.transform.stream.StreamResult;
+import org.w3c.dom.Document;
+import org.w3c.dom.DocumentType;
+import org.w3c.dom.DOMImplementation;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.Text;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.SAXException;
+import org.xml.sax.ext.LexicalHandler;
+import gnu.xml.dom.DomDoctype;
+import gnu.xml.dom.DomDocument;
+import gnu.xml.dom.ls.WriterOutputStream;
+import gnu.xml.xpath.Expr;
+import gnu.xml.xpath.Root;
+
+/**
+ * The transformation process for a given stylesheet.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+class TransformerImpl
+ extends Transformer
+{
+
+ final TransformerFactoryImpl factory;
+ final Stylesheet stylesheet;
+ URIResolver uriResolver;
+ ErrorListener errorListener;
+ Properties outputProperties;
+
+ TransformerImpl(TransformerFactoryImpl factory,
+ Stylesheet stylesheet,
+ Properties outputProperties)
+ throws TransformerConfigurationException
+ {
+ this.factory = factory;
+ uriResolver = factory.userResolver;
+ errorListener = factory.userListener;
+ this.stylesheet = stylesheet;
+ this.outputProperties = outputProperties;
+ if (stylesheet != null)
+ {
+ // Set up parameter context for this transformer
+ stylesheet.bindings.push(false);
+ }
+ }
+
+ public void transform(Source xmlSource, Result outputTarget)
+ throws TransformerException
+ {
+ // Get the source tree
+ DOMSource source;
+ synchronized (factory.resolver)
+ {
+ factory.resolver.setUserResolver(uriResolver);
+ factory.resolver.setUserListener(errorListener);
+ source = factory.resolver.resolveDOM(xmlSource, null, null);
+ }
+ Node context = source.getNode();
+ Document doc = (context instanceof Document) ? (Document) context :
+ context.getOwnerDocument();
+ if (doc instanceof DomDocument)
+ {
+ // Suppress mutation events
+ ((DomDocument) doc).setBuilding(true);
+ // TODO find a better/more generic way of doing this than
+ // casting
+ }
+ // Get the result tree
+ Node parent = null, nextSibling = null;
+ if (outputTarget instanceof DOMResult)
+ {
+ DOMResult dr = (DOMResult) outputTarget;
+ parent = dr.getNode();
+ nextSibling = dr.getNextSibling();
+
+ Document rdoc = (parent instanceof Document) ? (Document) parent :
+ parent.getOwnerDocument();
+ if (rdoc instanceof DomDocument)
+ {
+ // Suppress mutation events and allow multiple root elements
+ DomDocument drdoc = (DomDocument) rdoc;
+ drdoc.setBuilding(true);
+ drdoc.setCheckWellformedness(false);
+ // TODO find a better/more generic way of doing this than
+ // casting
+ }
+ }
+ boolean created = false;
+ // Transformation
+ if (stylesheet != null)
+ {
+ if (parent == null)
+ {
+ // Create a new document to hold the result
+ DomDocument resultDoc = new DomDocument();
+ resultDoc.setBuilding(true);
+ resultDoc.setCheckWellformedness(false);
+ parent = resultDoc;
+ created = true;
+ }
+ // Make a copy of the source node, and strip it
+ context = context.cloneNode(true);
+ strip(context);
+ // XSLT transformation
+ try
+ {
+ // Set output properties in the underlying stylesheet
+ ((TransformerOutputProperties) outputProperties).apply();
+ stylesheet.initTopLevelVariables(context);
+ TemplateNode t = stylesheet.getTemplate(null, context, false);
+ if (t != null)
+ {
+ stylesheet.current = context;
+ t.apply(stylesheet, null, context, 1, 1, parent, nextSibling);
+ }
+ }
+ catch (TransformerException e)
+ {
+ // Done transforming, reset document
+ if (doc instanceof DomDocument)
+ {
+ ((DomDocument) doc).setBuilding(false);
+ }
+ throw e;
+ }
+ }
+ else
+ {
+ // Identity transform
+ Node clone = context.cloneNode(true);
+ if (context.getNodeType() != Node.DOCUMENT_NODE)
+ {
+ Document resultDoc;
+ if (parent == null)
+ {
+ // Create a new document to hold the result
+ DomDocument rd = new DomDocument();
+ rd.setBuilding(true);
+ rd.setCheckWellformedness(false);
+ parent = resultDoc = rd;
+ created = true;
+ }
+ else
+ {
+ resultDoc = (parent instanceof Document) ?
+ (Document) parent :
+ parent.getOwnerDocument();
+ }
+ Document sourceDoc = context.getOwnerDocument();
+ if (sourceDoc != resultDoc)
+ {
+ clone = resultDoc.adoptNode(clone);
+ }
+ if (nextSibling != null)
+ {
+ parent.insertBefore(clone, nextSibling);
+ }
+ else
+ {
+ parent.appendChild(clone);
+ }
+ }
+ else
+ {
+ // Cannot append document to another tree
+ parent = clone;
+ created = true;
+ }
+ }
+ String method = outputProperties.getProperty(OutputKeys.METHOD);
+ int outputMethod = "html".equals(method) ? Stylesheet.OUTPUT_HTML :
+ "text".equals(method) ? Stylesheet.OUTPUT_TEXT :
+ Stylesheet.OUTPUT_XML;
+ String encoding = outputProperties.getProperty(OutputKeys.ENCODING);
+ String publicId = outputProperties.getProperty(OutputKeys.DOCTYPE_PUBLIC);
+ String systemId = outputProperties.getProperty(OutputKeys.DOCTYPE_SYSTEM);
+ String version = outputProperties.getProperty(OutputKeys.VERSION);
+ boolean omitXmlDeclaration =
+ "yes".equals(outputProperties.getProperty(OutputKeys.OMIT_XML_DECLARATION));
+ boolean standalone =
+ "yes".equals(outputProperties.getProperty(OutputKeys.STANDALONE));
+ String mediaType = outputProperties.getProperty(OutputKeys.MEDIA_TYPE);
+ // TODO cdata-section-elements
+ // TODO indent
+ if (created)
+ {
+ // Discover document element
+ DomDocument resultDoc = (DomDocument) parent;
+ Node root = resultDoc.getDocumentElement();
+ // Add doctype if specified
+ if ((publicId != null || systemId != null) &&
+ root != null)
+ {
+ // We must know the name of the root element to
+ // create the document type
+ resultDoc.appendChild(new DomDoctype(resultDoc,
+ root.getNodeName(),
+ publicId,
+ systemId));
+ }
+ resultDoc.setBuilding(false);
+ resultDoc.setCheckWellformedness(true);
+ }
+ else if (publicId != null || systemId != null)
+ {
+ switch (parent.getNodeType())
+ {
+ case Node.DOCUMENT_NODE:
+ case Node.DOCUMENT_FRAGMENT_NODE:
+ Document resultDoc = (parent instanceof Document) ?
+ (Document) parent :
+ parent.getOwnerDocument();
+ DOMImplementation impl = resultDoc.getImplementation();
+ DocumentType doctype =
+ impl.createDocumentType(resultDoc.getNodeName(),
+ publicId,
+ systemId);
+ // Try to insert doctype before first element
+ Node ctx = parent.getFirstChild();
+ for (; ctx != null &&
+ ctx.getNodeType() != Node.ELEMENT_NODE;
+ ctx = ctx.getNextSibling())
+ {
+ }
+ if (ctx != null)
+ {
+ parent.insertBefore(doctype, ctx);
+ }
+ else
+ {
+ parent.appendChild(doctype);
+ }
+ }
+ }
+ if (version != null)
+ {
+ parent.setUserData("version", version, stylesheet);
+ }
+ if (omitXmlDeclaration)
+ {
+ parent.setUserData("omit-xml-declaration", "yes", stylesheet);
+ }
+ if (standalone)
+ {
+ parent.setUserData("standalone", "yes", stylesheet);
+ }
+ if (mediaType != null)
+ {
+ parent.setUserData("media-type", mediaType, stylesheet);
+ }
+ // Render result to the target device
+ if (outputTarget instanceof DOMResult)
+ {
+ if (created)
+ {
+ DOMResult dr = (DOMResult) outputTarget;
+ dr.setNode(parent);
+ dr.setNextSibling(null);
+ }
+ }
+ else if (outputTarget instanceof StreamResult)
+ {
+ StreamResult sr = (StreamResult) outputTarget;
+ IOException ex = null;
+ try
+ {
+ writeStreamResult(parent, sr, outputMethod, encoding);
+ }
+ catch (UnsupportedEncodingException e)
+ {
+ try
+ {
+ writeStreamResult(parent, sr, outputMethod, "UTF-8");
+ }
+ catch (IOException e2)
+ {
+ ex = e2;
+ }
+ }
+ catch (IOException e)
+ {
+ ex = e;
+ }
+ if (ex != null)
+ {
+ if (errorListener != null)
+ {
+ errorListener.error(new TransformerException(ex));
+ }
+ else
+ {
+ ex.printStackTrace(System.err);
+ }
+ }
+ }
+ else if (outputTarget instanceof SAXResult)
+ {
+ SAXResult sr = (SAXResult) outputTarget;
+ try
+ {
+ ContentHandler ch = sr.getHandler();
+ LexicalHandler lh = sr.getLexicalHandler();
+ if (lh == null && ch instanceof LexicalHandler)
+ {
+ lh = (LexicalHandler) ch;
+ }
+ SAXSerializer serializer = new SAXSerializer();
+ serializer.serialize(parent, ch, lh);
+ }
+ catch (SAXException e)
+ {
+ if (errorListener != null)
+ {
+ errorListener.error(new TransformerException(e));
+ }
+ else
+ {
+ e.printStackTrace(System.err);
+ }
+ }
+ }
+ }
+
+ /**
+ * Strip whitespace from the source tree.
+ */
+ void strip(Node node)
+ throws TransformerConfigurationException
+ {
+ short nt = node.getNodeType();
+ if (nt == Node.ENTITY_REFERENCE_NODE)
+ {
+ // Replace entity reference with its content
+ Node parent = node.getParentNode();
+ Node child = node.getFirstChild();
+ if (child != null)
+ {
+ strip(child);
+ }
+ while (child != null)
+ {
+ Node next = child.getNextSibling();
+ node.removeChild(child);
+ parent.insertBefore(child, node);
+ child = next;
+ }
+ parent.removeChild(node);
+ }
+ if (nt == Node.TEXT_NODE) // CDATA sections ?
+ {
+ if (!stylesheet.isPreserved((Text) node))
+ {
+ node.getParentNode().removeChild(node);
+ }
+ }
+ else
+ {
+ for (Node child = node.getFirstChild(); child != null;
+ child = child.getNextSibling())
+ {
+ strip(child);
+ }
+ }
+ }
+
+ /**
+ * Obtain a suitable output stream for writing the result to,
+ * and use the StreamSerializer to write the result tree to the stream.
+ */
+ void writeStreamResult(Node node, StreamResult sr, int outputMethod,
+ String encoding)
+ throws IOException
+ {
+ OutputStream out = null;
+ try
+ {
+ out = sr.getOutputStream();
+ if (out == null)
+ {
+ Writer writer = sr.getWriter();
+ if (writer != null)
+ {
+ out = new WriterOutputStream(writer);
+ }
+ }
+ if (out == null)
+ {
+ String systemId = sr.getSystemId();
+ try
+ {
+ URL url = new URL(systemId);
+ URLConnection connection = url.openConnection();
+ connection.setDoOutput(true);
+ out = connection.getOutputStream();
+ }
+ catch (MalformedURLException e)
+ {
+ out = new FileOutputStream(systemId);
+ }
+ catch (UnknownServiceException e)
+ {
+ URL url = new URL(systemId);
+ out = new FileOutputStream(url.getPath());
+ }
+ }
+ out = new BufferedOutputStream(out);
+ StreamSerializer serializer =
+ new StreamSerializer(outputMethod, encoding, null);
+ if (stylesheet != null)
+ {
+ Collection celem = stylesheet.outputCdataSectionElements;
+ serializer.setCdataSectionElements(celem);
+ }
+ serializer.serialize(node, out);
+ out.flush();
+ }
+ finally
+ {
+ try
+ {
+ if (out != null)
+ {
+ out.close();
+ }
+ }
+ catch (IOException e)
+ {
+ }
+ }
+ }
+
+ void copyChildren(Document dstDoc, Node src, Node dst)
+ {
+ Node srcChild = src.getFirstChild();
+ while (srcChild != null)
+ {
+ Node dstChild = dstDoc.adoptNode(srcChild);
+ dst.appendChild(dstChild);
+ srcChild = srcChild.getNextSibling();
+ }
+ }
+
+ public void setParameter(String name, Object value)
+ {
+ if (stylesheet != null)
+ {
+ stylesheet.bindings.set(name, value, false);
+ }
+ }
+
+ public Object getParameter(String name)
+ {
+ if (stylesheet != null)
+ {
+ return stylesheet.bindings.get(name, null, 1, 1);
+ }
+ return null;
+ }
+
+ public void clearParameters()
+ {
+ if (stylesheet != null)
+ {
+ stylesheet.bindings.pop(false);
+ stylesheet.bindings.push(false);
+ }
+ }
+
+ public void setURIResolver(URIResolver resolver)
+ {
+ uriResolver = resolver;
+ }
+
+ public URIResolver getURIResolver()
+ {
+ return uriResolver;
+ }
+
+ public void setOutputProperties(Properties oformat)
+ throws IllegalArgumentException
+ {
+ if (oformat == null)
+ {
+ outputProperties.clear();
+ }
+ else
+ {
+ outputProperties.putAll(oformat);
+ }
+ }
+
+ public Properties getOutputProperties()
+ {
+ return (Properties) outputProperties.clone();
+ }
+
+ public void setOutputProperty(String name, String value)
+ throws IllegalArgumentException
+ {
+ outputProperties.put(name, value);
+ }
+
+ public String getOutputProperty(String name)
+ throws IllegalArgumentException
+ {
+ return outputProperties.getProperty(name);
+ }
+
+ public void setErrorListener(ErrorListener listener)
+ {
+ errorListener = listener;
+ }
+
+ public ErrorListener getErrorListener()
+ {
+ return errorListener;
+ }
+
+}
diff --git a/libjava/gnu/xml/transform/TransformerOutputProperties.java b/libjava/gnu/xml/transform/TransformerOutputProperties.java
new file mode 100644
index 0000000..d56e795
--- /dev/null
+++ b/libjava/gnu/xml/transform/TransformerOutputProperties.java
@@ -0,0 +1,185 @@
+/* TransformerOutputProperties.java --
+ Copyright (C) 2004 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.transform;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.Properties;
+import java.util.StringTokenizer;
+import javax.xml.transform.OutputKeys;
+
+/**
+ * Helper class to manage JAXP user setting of output properties.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+class TransformerOutputProperties
+ extends Properties
+{
+
+ final Properties defaultProperties;
+ final Stylesheet stylesheet;
+ boolean dirty;
+
+ TransformerOutputProperties(Stylesheet stylesheet)
+ {
+ this.stylesheet = stylesheet;
+ defaultProperties = new Properties();
+ switch (stylesheet.outputMethod)
+ {
+ case Stylesheet.OUTPUT_XML:
+ defaultProperties.put(OutputKeys.METHOD, "xml");
+ break;
+ case Stylesheet.OUTPUT_HTML:
+ defaultProperties.put(OutputKeys.METHOD, "html");
+ break;
+ case Stylesheet.OUTPUT_TEXT:
+ defaultProperties.put(OutputKeys.METHOD, "text");
+ break;
+ }
+ if (stylesheet.outputVersion != null)
+ {
+ defaultProperties.put(OutputKeys.VERSION, stylesheet.outputVersion);
+ }
+ if (stylesheet.outputEncoding != null)
+ {
+ defaultProperties.put(OutputKeys.ENCODING, stylesheet.outputEncoding);
+ }
+ defaultProperties.put(OutputKeys.OMIT_XML_DECLARATION,
+ stylesheet.outputOmitXmlDeclaration ? "yes" : "no");
+ defaultProperties.put(OutputKeys.STANDALONE,
+ stylesheet.outputStandalone ? "yes" : "no");
+ if (stylesheet.outputPublicId != null)
+ {
+ defaultProperties.put(OutputKeys.DOCTYPE_PUBLIC,
+ stylesheet.outputPublicId);
+ }
+ if (stylesheet.outputSystemId != null)
+ {
+ defaultProperties.put(OutputKeys.DOCTYPE_SYSTEM,
+ stylesheet.outputSystemId);
+ }
+ StringBuffer buf = new StringBuffer();
+ for (Iterator i = stylesheet.outputCdataSectionElements.iterator();
+ i.hasNext(); )
+ {
+ if (buf.length() > 0)
+ {
+ buf.append(' ');
+ }
+ buf.append((String) i.next());
+ }
+ defaultProperties.put(OutputKeys.CDATA_SECTION_ELEMENTS, buf.toString());
+ defaultProperties.put(OutputKeys.INDENT,
+ stylesheet.outputIndent ? "yes" : "no");
+ if (stylesheet.outputMediaType != null)
+ {
+ defaultProperties.put(OutputKeys.MEDIA_TYPE,
+ stylesheet.outputMediaType);
+ }
+ }
+
+ public String getProperty(String key)
+ {
+ String val = super.getProperty(key);
+ if (val == null)
+ {
+ val = defaultProperties.getProperty(key);
+ }
+ return val;
+ }
+
+ public Object put(Object key, Object value)
+ {
+ Object ret = super.put(key, value);
+ dirty = true;
+ return ret;
+ }
+
+ public void clear()
+ {
+ super.clear();
+ dirty = true;
+ }
+
+ /**
+ * Applies the current set of properties to the underlying stylesheet.
+ */
+ void apply()
+ {
+ if (!dirty)
+ {
+ return;
+ }
+ String method = getProperty(OutputKeys.METHOD);
+ if ("xml".equals(method))
+ {
+ stylesheet.outputMethod = Stylesheet.OUTPUT_XML;
+ }
+ else if ("html".equals(method))
+ {
+ stylesheet.outputMethod = Stylesheet.OUTPUT_HTML;
+ }
+ else if ("text".equals(method))
+ {
+ stylesheet.outputMethod = Stylesheet.OUTPUT_TEXT;
+ }
+ stylesheet.outputVersion = getProperty(OutputKeys.VERSION);
+ stylesheet.outputEncoding = getProperty(OutputKeys.ENCODING);
+ stylesheet.outputOmitXmlDeclaration =
+ "yes".equals(getProperty(OutputKeys.OMIT_XML_DECLARATION));
+ stylesheet.outputStandalone =
+ "yes".equals(getProperty(OutputKeys.STANDALONE));
+ stylesheet.outputPublicId = getProperty(OutputKeys.DOCTYPE_PUBLIC);
+ stylesheet.outputSystemId = getProperty(OutputKeys.DOCTYPE_SYSTEM);
+ StringTokenizer st =
+ new StringTokenizer(getProperty(OutputKeys.CDATA_SECTION_ELEMENTS));
+ Collection acc = new LinkedHashSet();
+ while (st.hasMoreTokens())
+ {
+ acc.add(st.nextToken());
+ }
+ stylesheet.outputCdataSectionElements = acc;
+ stylesheet.outputIndent = "yes".equals(getProperty(OutputKeys.INDENT));
+ stylesheet.outputMediaType = getProperty(OutputKeys.MEDIA_TYPE);
+ dirty = false;
+ }
+
+}
+
diff --git a/libjava/gnu/xml/transform/URIResolverEntityResolver.java b/libjava/gnu/xml/transform/URIResolverEntityResolver.java
new file mode 100644
index 0000000..ef29015
--- /dev/null
+++ b/libjava/gnu/xml/transform/URIResolverEntityResolver.java
@@ -0,0 +1,83 @@
+/* URIResolverEntityResolver.java --
+ Copyright (C) 2004 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.transform;
+
+import java.io.IOException;
+import javax.xml.transform.Source;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.URIResolver;
+import javax.xml.transform.sax.SAXSource;
+import org.xml.sax.EntityResolver;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+
+/**
+ * EntityResolver that wraps a URIResolver.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+class URIResolverEntityResolver
+ implements EntityResolver
+{
+
+ final URIResolver resolver;
+
+ URIResolverEntityResolver(URIResolver resolver)
+ {
+ this.resolver = resolver;
+ }
+
+ public InputSource resolveEntity(String publicId, String systemId)
+ throws SAXException, IOException
+ {
+ try
+ {
+ Source source = resolver.resolve(null, systemId);
+ if (source == null)
+ {
+ return null;
+ }
+ return SAXSource.sourceToInputSource(source);
+ }
+ catch (TransformerException e)
+ {
+ throw new SAXException(e);
+ }
+ }
+
+}
diff --git a/libjava/gnu/xml/transform/UnparsedEntityUriFunction.java b/libjava/gnu/xml/transform/UnparsedEntityUriFunction.java
new file mode 100644
index 0000000..41a606e
--- /dev/null
+++ b/libjava/gnu/xml/transform/UnparsedEntityUriFunction.java
@@ -0,0 +1,118 @@
+/* UnparsedEntityUriFunction.java --
+ Copyright (C) 2004 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.transform;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import javax.xml.xpath.XPathFunction;
+import javax.xml.xpath.XPathFunctionException;
+import org.w3c.dom.DocumentType;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.Notation;
+import gnu.xml.xpath.Expr;
+import gnu.xml.xpath.Function;
+
+/**
+ * The XSLT <code>unparsed-entity-uri()</code>function.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+final class UnparsedEntityUriFunction
+ extends Expr
+ implements XPathFunction, Function
+{
+
+ List args;
+
+ public Object evaluate(List args)
+ throws XPathFunctionException
+ {
+ // Useless...
+ return Collections.EMPTY_SET;
+ }
+
+ public void setArguments(List args)
+ {
+ this.args = args;
+ }
+
+ public Object evaluate(Node context, int pos, int len)
+ {
+ int arity = args.size();
+ List values = new ArrayList(arity);
+ for (int i = 0; i < arity; i++)
+ {
+ Expr arg = (Expr) args.get(i);
+ values.add(arg.evaluate(context, pos, len));
+ }
+ String name = _string(context, values.get(0));
+ DocumentType doctype = context.getOwnerDocument().getDoctype();
+ if (doctype != null)
+ {
+ NamedNodeMap notations = doctype.getNotations();
+ Notation notation = (Notation) notations.getNamedItem(name);
+ if (notation != null)
+ {
+ String systemId = notation.getSystemId();
+ // XXX absolutize?
+ if (systemId != null)
+ {
+ return systemId;
+ }
+ }
+ }
+ return "";
+ }
+
+ public Expr clone(Object context)
+ {
+ UnparsedEntityUriFunction f = new UnparsedEntityUriFunction();
+ int len = args.size();
+ List args2 = new ArrayList(len);
+ for (int i = 0; i < len; i++)
+ {
+ args2.add(((Expr) args.get(i)).clone(context));
+ }
+ f.setArguments(args2);
+ return f;
+ }
+
+}
+
diff --git a/libjava/gnu/xml/transform/ValueOfNode.java b/libjava/gnu/xml/transform/ValueOfNode.java
new file mode 100644
index 0000000..6027052
--- /dev/null
+++ b/libjava/gnu/xml/transform/ValueOfNode.java
@@ -0,0 +1,129 @@
+/* ValueOfNode.java --
+ Copyright (C) 2004 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.transform;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.text.DecimalFormat;
+import javax.xml.namespace.QName;
+import javax.xml.transform.TransformerException;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.w3c.dom.Text;
+import gnu.xml.xpath.Expr;
+
+/**
+ * A template node representing an XSLT <code>value-of</code> instruction.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+final class ValueOfNode
+ extends TemplateNode
+{
+
+ final Expr select;
+ final boolean disableOutputEscaping;
+
+ ValueOfNode(TemplateNode children, TemplateNode next, Expr select,
+ boolean disableOutputEscaping)
+ {
+ super(children, next);
+ this.select = select;
+ this.disableOutputEscaping = disableOutputEscaping;
+ }
+
+ TemplateNode clone(Stylesheet stylesheet)
+ {
+ return new ValueOfNode((children == null) ? null :
+ children.clone(stylesheet),
+ (next == null) ? null :
+ next.clone(stylesheet),
+ select.clone(stylesheet),
+ disableOutputEscaping);
+ }
+
+ void doApply(Stylesheet stylesheet, QName mode,
+ Node context, int pos, int len,
+ Node parent, Node nextSibling)
+ throws TransformerException
+ {
+ Object ret = select.evaluate(context, pos, len);
+ String value = Expr._string(context, ret);
+ //System.err.println("value-of: "+context+" "+ select + " -> "+ value);
+ if (value != null && value.length() > 0)
+ {
+ Document doc = (parent instanceof Document) ?
+ (Document) parent : parent.getOwnerDocument();
+ Text textNode = doc.createTextNode(value);
+ if (disableOutputEscaping)
+ {
+ textNode.setUserData("disable-output-escaping", "yes", stylesheet);
+ }
+ if (nextSibling != null)
+ {
+ parent.insertBefore(textNode, nextSibling);
+ }
+ else
+ {
+ parent.appendChild(textNode);
+ }
+ }
+ // value-of doesn't process children
+ if (next != null)
+ {
+ next.apply(stylesheet, mode,
+ context, pos, len,
+ parent, nextSibling);
+ }
+ }
+
+ public String toString()
+ {
+ StringBuffer buf = new StringBuffer(getClass().getName());
+ buf.append('[');
+ buf.append("select=");
+ buf.append(select);
+ if (disableOutputEscaping)
+ {
+ buf.append(",disableOutputEscaping");
+ }
+ buf.append(']');
+ return buf.toString();
+ }
+
+}
diff --git a/libjava/gnu/xml/transform/WhenNode.java b/libjava/gnu/xml/transform/WhenNode.java
new file mode 100644
index 0000000..f220466
--- /dev/null
+++ b/libjava/gnu/xml/transform/WhenNode.java
@@ -0,0 +1,110 @@
+/* WhenNode.java --
+ Copyright (C) 2004 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.transform;
+
+import javax.xml.namespace.QName;
+import javax.xml.transform.TransformerException;
+import org.w3c.dom.Node;
+import gnu.xml.xpath.Expr;
+
+/**
+ * A template node representing an XSL <code>when</code> instruction.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+final class WhenNode
+ extends TemplateNode
+{
+
+ final Expr test;
+
+ WhenNode(TemplateNode children, TemplateNode next, Expr test)
+ {
+ super(children, next);
+ this.test = test;
+ }
+
+ TemplateNode clone(Stylesheet stylesheet)
+ {
+ return new WhenNode((children == null) ? null :
+ children.clone(stylesheet),
+ (next == null) ? null :
+ next.clone(stylesheet),
+ test.clone(stylesheet));
+ }
+
+ void doApply(Stylesheet stylesheet, QName mode,
+ Node context, int pos, int len,
+ Node parent, Node nextSibling)
+ throws TransformerException
+ {
+ Object ret = test.evaluate(context, pos, len);
+ boolean success = (ret instanceof Boolean) ?
+ ((Boolean) ret).booleanValue() :
+ Expr._boolean(context, ret);
+ if (success)
+ {
+ if (children != null)
+ {
+ children.apply(stylesheet, mode,
+ context, pos, len,
+ parent, nextSibling);
+ }
+ }
+ else
+ {
+ if (next != null)
+ {
+ next.apply(stylesheet, mode,
+ context, pos, len,
+ parent, nextSibling);
+ }
+ }
+ }
+
+ public String toString()
+ {
+ StringBuffer buf = new StringBuffer(getClass().getName());
+ buf.append('[');
+ buf.append("test=");
+ buf.append(test);
+ buf.append(']');
+ return buf.toString();
+ }
+
+}
diff --git a/libjava/gnu/xml/transform/WithParam.java b/libjava/gnu/xml/transform/WithParam.java
new file mode 100644
index 0000000..c8804f8
--- /dev/null
+++ b/libjava/gnu/xml/transform/WithParam.java
@@ -0,0 +1,110 @@
+/* WithParam.java --
+ Copyright (C) 2004 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.transform;
+
+import java.util.Collections;
+import javax.xml.namespace.QName;
+import javax.xml.transform.TransformerException;
+import org.w3c.dom.Document;
+import org.w3c.dom.DocumentFragment;
+import org.w3c.dom.Node;
+import gnu.xml.xpath.Expr;
+
+/**
+ * A specification for setting a variable or parameter during template
+ * processing with <code>apply-templates</code> or
+ * <code>call-template</code>.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+final class WithParam
+{
+
+ final String name;
+ final Expr select;
+ final TemplateNode content;
+
+ WithParam(String name, Expr select)
+ {
+ this.name = name;
+ this.select = select;
+ content = null;
+ }
+
+ WithParam(String name, TemplateNode content)
+ {
+ this.name = name;
+ this.content = content;
+ select = null;
+ }
+
+ Object getValue(Stylesheet stylesheet, QName mode,
+ Node context, int pos, int len)
+ throws TransformerException
+ {
+ if (select != null)
+ {
+ return select.evaluate(context, pos, len);
+ }
+ else
+ {
+ Document doc = (context instanceof Document) ? (Document) context :
+ context.getOwnerDocument();
+ DocumentFragment fragment = doc.createDocumentFragment();
+ content.apply(stylesheet, mode,
+ context, pos, len,
+ fragment, null);
+ return Collections.singleton(fragment);
+ }
+ }
+
+ WithParam clone(Stylesheet stylesheet)
+ {
+ if (content == null)
+ {
+ return new WithParam(name,
+ select.clone(stylesheet));
+ }
+ else
+ {
+ return new WithParam(name,
+ content.clone(stylesheet));
+ }
+ }
+
+}
diff --git a/libjava/gnu/xml/transform/XSLComparator.java b/libjava/gnu/xml/transform/XSLComparator.java
new file mode 100644
index 0000000..1b76ed6
--- /dev/null
+++ b/libjava/gnu/xml/transform/XSLComparator.java
@@ -0,0 +1,124 @@
+/* XSLComparator.java --
+ Copyright (C) 2004 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.transform;
+
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.text.Collator;
+import org.w3c.dom.Node;
+import gnu.xml.xpath.Expr;
+
+/**
+ * Comparator for sorting lists of nodes according to a list of sort keys.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+class XSLComparator
+ implements Comparator
+{
+
+ final List sortKeys;
+
+ XSLComparator(List sortKeys)
+ {
+ this.sortKeys = sortKeys;
+ }
+
+ public int compare(Object o1, Object o2)
+ {
+ if (o1 instanceof Node && o2 instanceof Node)
+ {
+ Node n1 = (Node) o1;
+ Node n2 = (Node) o2;
+ for (Iterator i = sortKeys.iterator(); i.hasNext(); )
+ {
+ SortKey sortKey = (SortKey) i.next();
+ String k1 = sortKey.key(n1);
+ String k2 = sortKey.key(n2);
+ if ("text".equals(sortKey.dataType))
+ {
+ Locale locale = (sortKey.lang == null) ? Locale.getDefault() :
+ new Locale(sortKey.lang);
+ Collator collator = Collator.getInstance(locale);
+ int d = collator.compare(k1, k2);
+ if (d != 0)
+ {
+ switch (sortKey.caseOrder)
+ {
+ case SortKey.UPPER_FIRST:
+ // TODO
+ break;
+ case SortKey.LOWER_FIRST:
+ // TODO
+ break;
+ }
+ if (sortKey.descending)
+ {
+ d = -d;
+ }
+ return d;
+ }
+ }
+ else if ("number".equals(sortKey.dataType))
+ {
+ double kn1 = Expr._number(n1, k1);
+ double kn2 = Expr._number(n2, k2);
+ int d;
+ if (Double.isNaN(kn1) || Double.isInfinite(kn2))
+ {
+ d = -1;
+ }
+ else if (Double.isNaN(kn2) || Double.isInfinite(kn1))
+ {
+ d = 1;
+ }
+ else
+ {
+ // conversion to int may give 0 for small numbers
+ d = (kn1 > kn2) ? 1 : (kn1 < kn2) ? -1 : 0;
+ }
+ return (sortKey.descending) ? -d : d;
+ }
+ }
+ }
+ return 0;
+ }
+
+}
diff --git a/libjava/gnu/xml/transform/XSLURIResolver.java b/libjava/gnu/xml/transform/XSLURIResolver.java
new file mode 100644
index 0000000..2603cdd
--- /dev/null
+++ b/libjava/gnu/xml/transform/XSLURIResolver.java
@@ -0,0 +1,270 @@
+/* XSLURIResolver.java --
+ Copyright (C) 2004 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.transform;
+
+import java.io.File;
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.Reader;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.HashMap;
+import java.util.Map;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.transform.ErrorListener;
+import javax.xml.transform.Source;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.URIResolver;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamSource;
+import org.w3c.dom.Node;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import gnu.xml.dom.ls.ReaderInputStream;
+
+/**
+ * URI resolver for XSLT.
+ * This resolver parses external entities into DOMSources. It
+ * maintains a cache of URIs to DOMSources to avoid expensive re-parsing.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+class XSLURIResolver
+ implements URIResolver
+{
+
+ Map lastModifiedCache = new HashMap();
+ Map nodeCache = new HashMap();
+ DocumentBuilder builder;
+ URIResolver userResolver;
+ ErrorListener userListener;
+
+ void setUserResolver(URIResolver userResolver)
+ {
+ this.userResolver = userResolver;
+ }
+
+ void setUserListener(ErrorListener userListener)
+ {
+ this.userListener = userListener;
+ }
+
+ /**
+ * Clear the cache.
+ */
+ void flush()
+ {
+ lastModifiedCache.clear();
+ nodeCache.clear();
+ }
+
+ public Source resolve(String href, String base)
+ throws TransformerException
+ {
+ Source source = null;
+ if (userResolver != null)
+ {
+ source = userResolver.resolve(base, href);
+ }
+ return resolveDOM(source, href, base);
+ }
+
+ DOMSource resolveDOM(Source source, String base, String href)
+ throws TransformerException
+ {
+ if (source != null && source instanceof DOMSource)
+ {
+ return (DOMSource) source;
+ }
+ String systemId = (source == null) ? null : source.getSystemId();
+ long lastModified = 0L, lastLastModified = 0L;
+
+ try
+ {
+ URL url = resolveURL(systemId, base, href);
+ Node node = null;
+ InputStream in = null;
+ if (source instanceof StreamSource)
+ {
+ StreamSource ss = (StreamSource) source;
+ in = ss.getInputStream();
+ if (in == null)
+ {
+ Reader reader = ss.getReader();
+ if (reader != null)
+ {
+ in = new ReaderInputStream(reader);
+ }
+ }
+ }
+ if (in == null && url != null)
+ {
+ systemId = url.toString();
+ node = (Node) nodeCache.get(systemId);
+ // Is the resource up to date?
+ URLConnection conn = url.openConnection();
+ Long llm = (Long) lastModifiedCache.get(systemId);
+ if (llm != null)
+ {
+ lastLastModified = llm.longValue();
+ conn.setIfModifiedSince(lastLastModified);
+ }
+ conn.connect();
+ lastModified = conn.getLastModified();
+ if (node != null &&
+ lastModified > 0L &&
+ lastModified <= lastLastModified)
+ {
+ // Resource unchanged
+ return new DOMSource(node, systemId);
+ }
+ else
+ {
+ // Resource new or modified
+ in = conn.getInputStream();
+ nodeCache.put(systemId, node);
+ lastModifiedCache.put(systemId, new Long(lastModified));
+ }
+ }
+ InputSource input = new InputSource(in);
+ input.setSystemId(systemId);
+ DocumentBuilder builder = getDocumentBuilder();
+ node = builder.parse(input);
+ return new DOMSource(node, systemId);
+ }
+ catch (IOException e)
+ {
+ throw new TransformerException(e);
+ }
+ catch (SAXException e)
+ {
+ throw new TransformerException(e);
+ }
+ }
+
+ URL resolveURL(String systemId, String base, String href)
+ throws IOException
+ {
+ URL url = null;
+ try
+ {
+ if (systemId != null)
+ {
+ try
+ {
+ url = new URL(systemId);
+ }
+ catch (MalformedURLException e)
+ {
+ // Try building from base + href
+ }
+ }
+ if (url == null)
+ {
+ if (base != null)
+ {
+ URL baseURL = new URL(base);
+ url = new URL(baseURL, href);
+ }
+ else if (href != null)
+ {
+ url = new URL(href);
+ }
+ }
+ return url;
+ }
+ catch (MalformedURLException e)
+ {
+ // Fall back to local filesystem
+ File file = null;
+ if (href == null)
+ {
+ href = systemId;
+ }
+ if (base != null)
+ {
+ int lsi = base.lastIndexOf(File.separatorChar);
+ if (lsi != -1 && lsi < base.length() - 1)
+ {
+ base = base.substring(0, lsi);
+ }
+ File baseFile = new File(base);
+ file = new File(baseFile, href);
+ }
+ else if (href != null)
+ {
+ file = new File(href);
+ }
+ return (file == null) ? null : file.toURL();
+ }
+ }
+
+ DocumentBuilder getDocumentBuilder()
+ throws TransformerException
+ {
+ try
+ {
+ if (builder == null)
+ {
+ DocumentBuilderFactory factory =
+ DocumentBuilderFactory.newInstance();
+ factory.setNamespaceAware(true);
+ factory.setExpandEntityReferences(true);
+ builder = factory.newDocumentBuilder();
+ }
+ if (userResolver != null)
+ {
+ builder.setEntityResolver(new URIResolverEntityResolver(userResolver));
+ }
+ if (userListener != null)
+ {
+ builder.setErrorHandler(new ErrorListenerErrorHandler(userListener));
+ }
+ return builder;
+ }
+ catch (Exception e)
+ {
+ throw new TransformerException(e);
+ }
+ }
+
+}
+
diff --git a/libjava/gnu/xml/transform/package.html b/libjava/gnu/xml/transform/package.html
new file mode 100644
index 0000000..d435596
--- /dev/null
+++ b/libjava/gnu/xml/transform/package.html
@@ -0,0 +1,77 @@
+<html>
+<body>
+
+<h1>GNU JAXP XSL transformer</h1>
+
+<div>
+This package contains a Java XSL transformer compliant with the JAXP
+specification. It depends on the GNU DOM and XPath implementations, and
+will generate GNU DOM nodes unless a specific target from another
+implementation was given. It understands DOM, SAX, and stream sources
+and result sinks and supports these JAXP features.
+</div>
+
+<div>
+To use this transformer, set the system property
+<code>javax.xml.transform.TransformerFactory</code> to the value
+<code>gnu.xml.transform.TransformerFactoryImpl</code>. You can then
+instantiate <a href='TransformerFactory.html'>TransformerFactory</a>
+and transformers in the ordinary manner. Reuse of stylesheets is
+supported using the JAXP <a href='Templates.html'>Templates</a>
+mechanism.
+</div>
+
+<h3>Architecture</h3>
+
+<div>
+When given a stylesheet source, this implementation compiles it internally
+into a Stylesheet object, which is a container for templates and state.
+Each stylesheet instruction is represented by a subclass of TemplateNode,
+which is arranged in a directed graph: each TemplateNode has a reference
+to its first child and the next node.
+</div>
+
+<div>
+The transformation process consists of identifying the Template that matches
+the root of the source context, and calling <code>apply</code> on its
+corresponding TemplateNode. This in turn processes its children and next
+TemplateNode, depending on the semantics of each node type.
+</div>
+
+<div>
+Template nodes may reference XPath expressions or patterns. These are fully
+compiled to objects of type <a href='../xpath/Expr.html'>Expr</a> at the
+time the stylesheet is compiled.
+</div>
+
+<h3>Conformance</h3>
+
+<div>
+This implementation is feature complete, but the XSLT specification is
+large and there are still many bugs that need to be ironed out. It has
+been tested against the OASIS XSLT TC test suite, comprising unit tests
+from the Xalan project and Microsoft. Conformance to these unit tests
+is approximately 70% at the current time, although normal usage of the
+transformer should involve relatively few surprises (the test suite is
+designed to test very complex and obscure functionality).
+</div>
+
+<h3>Known bugs</h3>
+
+<ul>
+<li>When reusing stylesheets using the JAXP Templates mechanism, XSL
+<code>apply-imports</code> instructions will not work.</li>
+<li>XPath filter expressions do not always work as expected (this is a
+problem with the GNU XPath implementation rather than the transformer).
+This can result in problems with the <code>position()</code> function,
+as well as <code>select</code> expressions and numbering.</li>
+</ul>
+
+<div>
+Obviously we'd like to improve conformance and fix these bugs. If you're
+interested in working on any of these issues please
+<a href='mailto:classpathx-xml@gnu.org'>contact us</a>.
+</div>
+
+</body>
+</html>
diff --git a/libjava/gnu/xml/util/DoParse.java b/libjava/gnu/xml/util/DoParse.java
new file mode 100644
index 0000000..23721de
--- /dev/null
+++ b/libjava/gnu/xml/util/DoParse.java
@@ -0,0 +1,306 @@
+/* DoParse.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.util;
+
+import java.io.*;
+
+import org.xml.sax.*;
+import org.xml.sax.helpers.XMLReaderFactory;
+import org.xml.sax.ext.*;
+
+// import gnu.xml.pipeline.*;
+// Added imports directly to fix a GCJ problem.
+import gnu.xml.pipeline.CallFilter;
+import gnu.xml.pipeline.DomConsumer;
+import gnu.xml.pipeline.EventConsumer;
+import gnu.xml.pipeline.EventFilter;
+import gnu.xml.pipeline.LinkFilter;
+import gnu.xml.pipeline.NSFilter;
+import gnu.xml.pipeline.PipelineFactory;
+import gnu.xml.pipeline.TeeConsumer;
+import gnu.xml.pipeline.TextConsumer;
+import gnu.xml.pipeline.ValidationConsumer;
+import gnu.xml.pipeline.WellFormednessFilter;
+import gnu.xml.pipeline.XIncludeFilter;
+import gnu.xml.pipeline.XsltFilter;
+
+
+/**
+ * This class provides a driver which may be invoked from the command line
+ * to process a document using a SAX2 parser and a specified XML processing
+ * pipeline.
+ * This facilitates some common types of command line tools, such as parsing an
+ * XML document in order test it for well formedness or validity.
+ *
+ * <p>The SAX2 XMLReaderFactory should return a SAX2 XML parser which
+ * supports both of the standardized extension handlers (for declaration
+ * and lexical events). That parser will be used to produce events.
+ *
+ * <p>The first parameter to the command gives the name of the document that
+ * will be given to that processor. If it is a file name, it is converted
+ * to a URL first.
+ *
+ * <p>The second parameter describes a simple processing pipeline, and will
+ * be used as input to {@link gnu.xml.pipeline.PipelineFactory}
+ * methods which identify the processing to be done. Examples of such a
+ * pipeline include <pre>
+ *
+ * nsfix | validate <em>to validate the input document </em>
+ * nsfix | write ( stdout ) <em>to echo the file as XML text</em>
+ * dom | nsfix | write ( stdout ) <em>parse into DOM, print the result</em>
+ * </pre>
+ *
+ * <p> Relatively complex pipelines can be described on the command line, but
+ * not all interesting ones will require as little configuration as can be done
+ * in that way. Put filters like "nsfix", perhaps followed by "validate",
+ * at the front of the pipeline so they can be optimized out if a parser
+ * supports those modes natively.
+ *
+ * <p> If the parsing is aborted for any reason, the JVM will exit with a
+ * failure code. If a validating parse was done then both validation and
+ * well formedness errors will cause a failure. A non-validating parse
+ * will report failure on well formedness errors.
+ *
+ * @see gnu.xml.pipeline.PipelineFactory
+ *
+ * @author David Brownell
+ */
+final public class DoParse
+{
+ private DoParse () { /* no instances allowed */ }
+
+ // first reported nonrecoverable error
+ private static SAXParseException fatal;
+
+ // error categories
+ private static int errorCount;
+ private static int fatalCount;
+
+ /**
+ * Command line invoker for this class; pass a filename or URL
+ * as the first argument, and a pipeline description as the second.
+ * Make sure to use filters to condition the input to stages that
+ * require it; an <em>nsfix</em> filter will be a common requirement,
+ * to restore syntax that SAX2 parsers delete by default. Some
+ * conditioning filters may be eliminated by setting parser options.
+ * (For example, "nsfix" can set the "namespace-prefixes" feature to
+ * a non-default value of "true". In the same way, "validate" can set
+ * the "validation" feature to "true".)
+ */
+ public static void main (String argv [])
+ throws IOException
+ {
+ int exitStatus = 1;
+
+ if (argv.length != 2) {
+ System.err.println ("Usage: DoParse [filename|URL] pipeline-spec");
+ System.err.println ("Example pipeline specs:");
+ System.err.println (" 'nsfix | validate'");
+ System.err.println (
+ " ... restore namespace syntax, validate");
+ System.err.println (" 'nsfix | write ( stdout )'");
+ System.err.println (
+ " ... restore namespace syntax, write to stdout as XML"
+ );
+ System.exit (1);
+ }
+
+ try {
+ //
+ // Get input source for specified document (or try ;-)
+ //
+ argv [0] = Resolver.getURL (argv [0]);
+ InputSource input = new InputSource (argv [0]);
+
+ //
+ // Get the producer, using the system default parser (which
+ // can be overridden for this particular invocation).
+ //
+ // And the pipeline, using commandline options.
+ //
+ XMLReader producer;
+ EventConsumer consumer;
+
+ producer = XMLReaderFactory.createXMLReader ();
+
+ //
+ // XXX pipeline factory now has a pre-tokenized input
+ // method, use it ... that way at least some params
+ // can be written using quotes (have spaces, ...)
+ //
+ consumer = PipelineFactory.createPipeline (argv [1]);
+
+ //
+ // XXX want commandline option for tweaking error handler.
+ // Want to be able to present warnings.
+ //
+ producer.setErrorHandler (new MyErrorHandler ());
+
+ // XXX need facility enabling resolving to local DTDs
+
+ //
+ // Parse. The pipeline may get optimized a bit, so we
+ // can't always fail cleanly for validation without taking
+ // a look at the filter stages.
+ //
+ EventFilter.bind (producer, consumer);
+ producer.parse (input);
+
+ try {
+ if (producer.getFeature (
+ "http://org.xml/sax/features/validation"))
+ exitStatus = ((errorCount + fatalCount) > 0) ? 1 : 0;
+ else if (fatalCount == 0)
+ exitStatus = 0;
+ } catch (SAXException e) {
+ if (hasValidator (consumer))
+ exitStatus = ((errorCount + fatalCount) > 0) ? 1 : 0;
+ else if (fatalCount == 0)
+ exitStatus = 0;
+ }
+
+ } catch (java.net.MalformedURLException e) {
+ System.err.println ("** Malformed URL: " + e.getMessage ());
+ System.err.println ("Is '" + argv [0] + "' a non-existent file?");
+ e.printStackTrace ();
+ // e.g. FNF
+
+ } catch (SAXParseException e) {
+ if (e != fatal) {
+ System.err.print (printParseException ("Parsing Aborted", e));
+ e.printStackTrace ();
+ if (e.getException () != null) {
+ System.err.println ("++ Wrapped exception:");
+ e.getException ().printStackTrace ();
+ }
+ }
+
+ } catch (SAXException e) {
+ Exception x = e;
+ if (e.getException () != null)
+ x = e.getException ();
+ x.printStackTrace ();
+
+ } catch (Throwable t) {
+ t.printStackTrace ();
+ }
+
+ System.exit (exitStatus);
+ }
+
+ // returns true if saw a validator (before end or unrecognized node)
+ // false otherwise
+ private static boolean hasValidator (EventConsumer e)
+ {
+ if (e == null)
+ return false;
+ if (e instanceof ValidationConsumer)
+ return true;
+ if (e instanceof TeeConsumer) {
+ TeeConsumer t = (TeeConsumer) e;
+ return hasValidator (t.getFirst ())
+ || hasValidator (t.getRest ());
+ }
+ if (e instanceof WellFormednessFilter
+ || e instanceof NSFilter
+ )
+ return hasValidator (((EventFilter)e).getNext ());
+
+ // else ... gee, we can't know. Assume not.
+
+ return false;
+ }
+
+ static class MyErrorHandler implements ErrorHandler
+ {
+ // dump validation errors, but continue
+ public void error (SAXParseException e)
+ throws SAXParseException
+ {
+ errorCount++;
+ System.err.print (printParseException ("Error", e));
+ }
+
+ public void warning (SAXParseException e)
+ throws SAXParseException
+ {
+ // System.err.print (printParseException ("Warning", e));
+ }
+
+ // try to continue fatal errors, in case a parser reports more
+ public void fatalError (SAXParseException e)
+ throws SAXParseException
+ {
+ fatalCount++;
+ if (fatal == null)
+ fatal = e;
+ System.err.print (printParseException ("Nonrecoverable Error", e));
+ }
+ }
+
+ static private String printParseException (
+ String label,
+ SAXParseException e
+ ) {
+ StringBuffer buf = new StringBuffer ();
+ int temp;
+
+ buf.append ("** ");
+ buf.append (label);
+ buf.append (": ");
+ buf.append (e.getMessage ());
+ buf.append ('\n');
+ if (e.getSystemId () != null) {
+ buf.append (" URI: ");
+ buf.append (e.getSystemId ());
+ buf.append ('\n');
+ }
+ if ((temp = e.getLineNumber ()) != -1) {
+ buf.append (" line: ");
+ buf.append (temp);
+ buf.append ('\n');
+ }
+ if ((temp = e.getColumnNumber ()) != -1) {
+ buf.append (" char: ");
+ buf.append (temp);
+ buf.append ('\n');
+ }
+
+ return buf.toString ();
+ }
+}
diff --git a/libjava/gnu/xml/util/DomParser.java b/libjava/gnu/xml/util/DomParser.java
new file mode 100644
index 0000000..0a75d54
--- /dev/null
+++ b/libjava/gnu/xml/util/DomParser.java
@@ -0,0 +1,804 @@
+/* DomParser.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.util;
+
+import java.util.Enumeration;
+import java.util.Locale;
+
+import org.xml.sax.*;
+import org.xml.sax.helpers.AttributesImpl;
+import org.xml.sax.helpers.NamespaceSupport;
+import org.xml.sax.ext.DeclHandler;
+import org.xml.sax.ext.DefaultHandler2;
+import org.xml.sax.ext.LexicalHandler;
+
+import org.w3c.dom.*;
+
+
+/**
+ * This parser emits SAX2 parsing events as it traverses a DOM tree, using
+ * any conformant implementation of DOM. It exposes all SAX1 features,
+ * and the following SAX2 features and properties (as
+ * identified by standard URIs which are not fully provided here). Note
+ * that if a Level 1 DOM implementation is given, then this behaves as if
+ * namespaces were disabled, and namespace prefixes were enabled. </p>
+ *
+ * <table border="1" width='100%' cellpadding='3' cellspacing='0'>
+ * <tr bgcolor='#ccccff'>
+ * <th><font size='+1'>Name</font></th>
+ * <th><font size='+1'>Notes</font></th></tr>
+ *
+ * <tr><td colspan=2><center><em>Features ... URL prefix is
+ * <b>http://xml.org/sax/features/</b></em></center></td></tr>
+ *
+ * <tr><td>(URL)/external-general-entities</td>
+ * <td>false (does no parsing)</td></tr>
+ * <tr><td>(URL)/external-parameter-entities</td>
+ * <td>false (does no parsing)</td></tr>
+ * <tr><td>(URL)/namespaces</td>
+ * <td>Value is fixed at <em>true</em></td></tr>
+ * <tr><td>(URL)/namespace-prefixes</td>
+ * <td>Value is settable, defaulting to <em>false</em>
+ * (<code>xmlns</code> attributes hidden, and names aren't prefixed)
+ * </td></tr>
+ * <tr><td>(URL)/string-interning</td>
+ * <td>Value is fixed at <em>false</em> (DOM provides no
+ * guarantees as to interning)</td></tr>
+ * <tr><td>(URL)/validation</td>
+ * <td>false (does no parsing)</td></tr>
+ * <tr><td>(URL)/lexical-handler/parameter-entities</td>
+ * <td>false (DOM doesn't do parameter entities)</td></tr>
+ *
+ * <tr><td colspan=2><center><em>Properties ... URL prefix is
+ * <b>http://xml.org/sax/properties/</b></em></center></td></tr>
+ *
+ *
+ * <tr><td>(URL)/dom-node</td>
+ * <td>This property may be set before parsing to hold a DOM
+ * <em>Document</em> node; any arguments given to <em>parse</em>
+ * methods are ignored. When retrieved
+ * during a parse, this value contains the "current" DOM node.
+ * </td></tr>
+ * <tr><td>(URL)/declaration-handler</td>
+ * <td>A declaration handler may be provided. Declaration of external
+ * general entities is exposed, but not parameter entities; none of the
+ * entity names reported here will begin with "%". </td></tr>
+ * <tr><td>(URL)/lexical-handler</td>
+ * <td>A lexical handler may be provided. While the start and end of
+ * any external subset are reported, expansion of other parameter
+ * entities (e.g. inside attribute list declarations) is not exposed.
+ * Expansion of general entities within attributes is also not exposed
+ * (see below).</td></tr>
+ * </table>
+ *
+ * <P> The consequences of modifying a DOM document tree as it is being walked
+ * by this "parser" are unspecified; don't do it! </P>
+ *
+ * @author David Brownell
+ */
+final public class DomParser implements XMLReader
+{
+ // Stuff used internally to route events correctly
+ private DefaultHandler2 defaultHandler = new DefaultHandler2 ();
+
+ // per-parse SAX stuff
+ private ContentHandler contentHandler = defaultHandler;
+ private DTDHandler dtdHandler = defaultHandler;
+ private DeclHandler declHandler = defaultHandler;
+ private LexicalHandler lexicalHandler = defaultHandler;
+
+ // shared context
+ private ErrorHandler errHandler = defaultHandler;
+ private EntityResolver resolver = defaultHandler;
+ private Locale locale = Locale.getDefault ();
+
+ // parser state
+ private Node start;
+ private Node current;
+ private boolean isL2;
+ private boolean showNamespaces = true;
+ private boolean showXML1_0 = false;
+ private NamespaceSupport prefixStack = new NamespaceSupport ();
+ private boolean isDocument;
+
+
+ /**
+ * Constructs an unitialized <b>SAX2</b> parser.
+ */
+ public DomParser () {
+ }
+
+ /**
+ * Constructs an <b>SAX2</b> parser initialized to traverse the specified
+ * DOM tree. If the node is a document, the startDocument() and
+ * endDocument() calls bracket the calls exposing children.
+ */
+ public DomParser (Node node) {
+ setStart (node);
+ }
+
+
+ // stuff that most components in an application should be sharing:
+ // resolver and error locale.
+
+ /**
+ * <b>SAX2</b>: Returns the object used when resolving external
+ * entities during parsing (both general and parameter entities).
+ */
+ public EntityResolver getEntityResolver ()
+ {
+ return resolver;
+ }
+
+ /**
+ * <b>SAX1</b>: Provides an object which may be used when resolving external
+ * entities during parsing (both general and parameter entities).
+ */
+ public void setEntityResolver (EntityResolver resolver)
+ {
+ if (resolver == null)
+ resolver = defaultHandler;
+ this.resolver = resolver;
+ }
+
+ /**
+ * <b>SAX1</b>: Identifies the locale which the parser should use for the
+ * diagnostics it provides.
+ *
+ * @exception SAXException as defined in the specification for
+ * <em>org.xml.sax.Parser.setLocale()</em>
+ */
+ public void setLocale (Locale locale)
+ throws SAXException
+ {
+ if (locale == null)
+ locale = Locale.getDefault ();
+ this.locale = locale;
+ }
+
+
+ // different modules will tend to handle error handling the same,
+ // but it may not be the same through the whole app
+
+ /**
+ * <b>SAX2</b>: Returns the object used to receive callbacks for XML
+ * errors of all levels (fatal, nonfatal, warning).
+ */
+ public ErrorHandler getErrorHandler ()
+ {
+ return errHandler;
+ }
+
+ /**
+ * <b>SAX1</b>: Provides an object which receives callbacks for XML errors
+ * of all levels (fatal, nonfatal, warning).
+ */
+ public void setErrorHandler (ErrorHandler handler)
+ {
+ if (handler == null)
+ handler = defaultHandler;
+ errHandler = handler;
+ }
+
+
+ // stuff different parts of a module will handle differently
+
+ /**
+ * <b>SAX2</b>: Returns the object used to report the logical
+ * content of an XML document.
+ */
+ public ContentHandler getContentHandler ()
+ {
+ return contentHandler;
+ }
+
+ /**
+ * <b>SAX2</b>: Assigns the object used to report the logical
+ * content of an XML document.
+ */
+ public void setContentHandler (ContentHandler handler)
+ {
+ if (handler == null)
+ handler = defaultHandler;
+ contentHandler = handler;
+ }
+
+ /**
+ * <b>SAX2</b>: Returns the object used to process declarations related
+ * to notations and unparsed entities.
+ */
+ public DTDHandler getDTDHandler ()
+ {
+ return dtdHandler;
+ }
+
+ /**
+ * <b>SAX1</b>: Provides an object which may be used to intercept
+ * declarations related to notations and unparsed entities.
+ */
+ public void setDTDHandler (DTDHandler handler)
+ {
+ if (handler == null)
+ handler = defaultHandler;
+ dtdHandler = handler;
+ }
+
+
+ /**
+ * <b>SAX1</b>: Parses the previously provided DOM document (the
+ * input parameter is ignored). When this returns, that same
+ * document may be parsed again without needing a "reset".
+ *
+ * @param uri ignored (pass an empty string)
+ * @exception SAXException as defined in the specification for
+ * <em>org.xml.sax.Parser.parse()</em>
+ */
+ public void parse (String uri) throws SAXException
+ {
+ parse ();
+ }
+
+ /**
+ * <b>SAX1</b>: Parses the previously provided DOM document (the
+ * input parameter is ignored). When this returns, that same
+ * document may be parsed again without needing a "reset".
+ *
+ * @param input ignored
+ * @exception SAXException as defined in the specification for
+ * <em>org.xml.sax.Parser.parse()</em>
+ */
+ public void parse (InputSource input) throws SAXException
+ {
+ parse ();
+ }
+
+ private void parse () throws SAXException
+ {
+ try {
+ walk ();
+ } finally {
+ if (isDocument)
+ contentHandler.endDocument ();
+ current = null;
+ prefixStack.reset ();
+ }
+ }
+
+ private boolean getIsL2 (Node node)
+ {
+ DOMImplementation impl;
+ Document doc;
+
+ if (node instanceof Document)
+ doc = (Document) node;
+ else
+ doc = node.getOwnerDocument ();
+ if (doc == null)
+ throw new RuntimeException ("? unowned node - L2 DTD ?");
+ impl = doc.getImplementation ();
+ return impl.hasFeature ("XML", "2.0");
+ }
+
+
+ private static final String FEATURES = "http://xml.org/sax/features/";
+ private static final String HANDLERS = "http://xml.org/sax/properties/";
+
+ /**
+ * <b>SAX2</b>: Tells whether this parser supports the specified feature.
+ */
+ public boolean getFeature (String name)
+ throws SAXNotRecognizedException, SAXNotSupportedException
+ {
+ // basically, none are relevant -- they relate more to
+ // parsing than to walking a "parse tree".
+
+ // FIXME: DOM feature to expose interning?
+
+ if ((FEATURES + "validation").equals (name)
+ || (FEATURES + "external-general-entities")
+ .equals (name)
+ || (FEATURES + "external-parameter-entities")
+ .equals (name)
+ || (FEATURES + "string-interning").equals (name)
+ )
+ return false;
+
+ if ((FEATURES + "namespaces").equals (name))
+ return showNamespaces;
+ if ((FEATURES + "namespace-prefixes").equals (name))
+ return showXML1_0;
+
+ throw new SAXNotRecognizedException (name);
+ }
+
+ /**
+ * <b>SAX2</b>: Returns the specified property. At this time only
+ * the declaration and lexical handlers, and current the "DOM" node,
+ * are supported.
+ */
+ public Object getProperty (String name)
+ throws SAXNotRecognizedException, SAXNotSupportedException
+ {
+ if ((HANDLERS + "declaration-handler").equals (name))
+ return declHandler == defaultHandler ? null : declHandler;
+ if ((HANDLERS + "lexical-handler").equals (name))
+ return lexicalHandler == defaultHandler ? null : lexicalHandler;
+
+ if ((HANDLERS + "dom-node").equals (name))
+ return current;
+
+ // unknown properties
+ throw new SAXNotRecognizedException (name);
+ }
+
+ /**
+ * <b>SAX2</b>: Sets the state of features supported in this parser.
+ * Only the namespace support features are mutable.
+ */
+ public void setFeature (String name, boolean state)
+ throws SAXNotRecognizedException, SAXNotSupportedException
+ {
+ if (current != null)
+ throw new IllegalStateException ("feature change midparse");
+
+ boolean value = getFeature (name);
+
+ if (value == state)
+ return;
+
+ if ((FEATURES + "namespaces").equals (name)) {
+ if (!showXML1_0 && state == false)
+ throw new SAXNotSupportedException ("Illegal namespace "
+ + "processing configuration");
+ showNamespaces = state;
+ return;
+ }
+ if ((FEATURES + "namespace-prefixes").equals (name)) {
+ if (!showNamespaces && state == false)
+ throw new SAXNotSupportedException ("Illegal namespace "
+ + "processing configuration");
+ showXML1_0 = state;
+ return;
+ }
+
+ throw new SAXNotSupportedException (name);
+ }
+
+ /**
+ * <b>SAX2</b>: Assigns the specified property. At this time only
+ * declaration and lexical handlers, and the initial DOM document, are
+ * supported. These must not be changed to values of the wrong type.
+ * Like SAX1 handlers, these handlers may be changed at any time.
+ * Like SAX1 input source or document URI, the initial DOM document
+ * may not be changed during a parse.
+ */
+ public void setProperty (String name, Object state)
+ throws SAXNotRecognizedException, SAXNotSupportedException
+ {
+ if ((HANDLERS + "declaration-handler").equals (name)) {
+ if (!(state instanceof DeclHandler || state == null))
+ throw new SAXNotSupportedException (name);
+ declHandler = (DeclHandler) state;
+ return;
+ }
+
+ if ((HANDLERS + "lexical-handler").equals (name)) {
+ if (!(state instanceof LexicalHandler || state == null))
+ throw new SAXNotSupportedException (name);
+ lexicalHandler = (LexicalHandler) state;
+ return;
+ }
+
+ if ((HANDLERS + "dom-node").equals (name)) {
+ if (state == null || state instanceof Node) {
+ if (current != null)
+ throw new SAXNotSupportedException (
+ "property is readonly during parse: " + name);
+ setStart ((Node) state);
+ return;
+ }
+ throw new SAXNotSupportedException ("not a DOM Node");
+ }
+
+ // unknown properties
+ throw new SAXNotRecognizedException (name);
+ }
+
+ private void setStart (Node property)
+ {
+ start = property;
+ if (start != null) {
+ isL2 = getIsL2 (start);
+ isDocument = (start instanceof Document);
+ }
+ }
+
+ //
+ // Non-recursive walk, using DOM state when backtracking is needed
+ //
+ private void walk ()
+ throws SAXException
+ {
+ int type;
+ NamedNodeMap nodes;
+ int length;
+ AttributesImpl attrs = new AttributesImpl ();
+ char chars [];
+ String ns, local;
+
+ synchronized (this) {
+ if (current != null)
+ throw new IllegalStateException ("already walking tree");
+
+ // JVM guarantees assignments are atomic; so no other
+ // thread could get this far till this walk's done.
+ current = start;
+ }
+
+ for (;;) {
+ type = current.getNodeType ();
+
+ //
+ // First, visit the current node, including any "start" calls
+ //
+ switch (type) {
+
+ case Node.DOCUMENT_NODE:
+ contentHandler.startDocument ();
+ break;
+
+ case Node.ELEMENT_NODE:
+ nodes = current.getAttributes ();
+ length = nodes.getLength ();
+ prefixStack.pushContext ();
+ for (int i = 0; i < length; i++) {
+ Attr attr = (Attr) nodes.item (i);
+ String name = attr.getNodeName ();
+
+ if (showNamespaces && name.startsWith ("xmlns")) {
+ String prefix;
+ String uri;
+
+ // NOTE: DOM L2 (CR2+ and REC) violate the
+ // Namespaces REC, treat "xmlns" like a strange
+ // attribute instead of a magic token
+ if ("xmlns".equals (name))
+ prefix = "";
+ else
+ prefix = name.substring (6);
+ uri = attr.getNodeValue ();
+
+ prefixStack.declarePrefix (prefix, uri);
+ contentHandler.startPrefixMapping (prefix, uri);
+
+ if (!showXML1_0)
+ continue;
+ }
+
+ //
+ // NOTE: DOM doesn't record the attribute type info
+ // which SAX exposes; so this always reports CDATA.
+ //
+ // NOTE: SAX doesn't expose the isSpecified info which
+ // DOM exposes; that's discarded here. Similarly with
+ // the information DOM hides inside itself about what
+ // the default values for an attribute are.
+ //
+ if (showNamespaces) {
+ if (isL2) {
+ if ((ns = attr.getNamespaceURI ()) == null)
+ ns = "";
+ // Note: SAX2 and DOM handle "local" names
+ // differently
+ if ((local = attr.getLocalName ()) == null)
+ local = name;
+ } else {
+// XXX
+ throw new RuntimeException (
+ "NYI, ns lookup when parsing L1 DOM");
+ }
+ } else
+ ns = local = "";
+ attrs.addAttribute (ns, local, name,
+ "CDATA", attr.getNodeValue ());
+ }
+ if (showNamespaces) {
+ if (isL2) {
+ if ((ns = current.getNamespaceURI ()) == null)
+ ns = "";
+ // Note: SAX2 and DOM handle "local" names differently
+ if ((local = current.getLocalName ()) == null)
+ local = current.getNodeName ();
+ } else {
+// XXX
+ throw new RuntimeException (
+ "NYI, ns lookup when parsing L1 DOM");
+ }
+ } else
+ ns = local = "";
+ contentHandler.startElement (ns, local,
+ current.getNodeName (), attrs);
+ if (length != 0)
+ attrs.clear ();
+ break;
+
+ case Node.CDATA_SECTION_NODE:
+ lexicalHandler.startCDATA ();
+ chars = current.getNodeValue ().toCharArray ();
+ contentHandler.characters (chars, 0, chars.length);
+ lexicalHandler.endCDATA ();
+ break;
+
+ case Node.COMMENT_NODE:
+ chars = current.getNodeValue ().toCharArray ();
+ lexicalHandler.comment (chars, 0, chars.length);
+ break;
+
+ case Node.DOCUMENT_TYPE_NODE:
+ {
+ DocumentType doctype = (DocumentType) current;
+
+ //
+ // Only DOM L2 supports recreating even some DTDs in full.
+ //
+ if (isL2) {
+ lexicalHandler.startDTD (doctype.getName (),
+ doctype.getPublicId (),
+ doctype.getSystemId ());
+ } else
+ lexicalHandler.startDTD (doctype.getName (),
+ null, null);
+
+ //
+ // The only sure way to recreate is to provide both the
+ // internal and external subsets. Otherwise, only part
+ // of the job can be done ... because from the DTD, DOM
+ // discards both the critical data, like the attribute and
+ // element declarations, as well as the PIs and comments
+ // that are used to hold their documentation.
+ //
+ // Even the entity and notation declarations that it can
+ // expose can't be recorded without proprietary extensions.
+ //
+ // We construct a comment to tell what we know about how
+ // (in)complete this particular really DTD is.
+ //
+ {
+ String message;
+ char buf [];
+
+ //
+ // Though DOM L2 lets the whole doctype be recreated,
+ // SAX2 can't represent it (input or output).
+ // So this will be the typical case.
+ //
+ if (isL2 && doctype.getInternalSubset () != null)
+ message =
+ " Full DTD known; can't be shown using SAX2. ";
+
+ //
+ // Otherwise, we'll concoct a partial DTD. If there's
+ // any more data here at all, it was provided using a
+ // (proprietary) extension to DOM.
+ //
+ else
+ message =
+ " This DTD was was recreated using incomplete DOM L2 records. ";
+
+ buf = message.toCharArray ();
+ lexicalHandler.comment (buf, 0, buf.length);
+ }
+
+ // report notations first
+ nodes = doctype.getNotations ();
+ length = nodes.getLength ();
+ for (int i = 0; i < length; i++) {
+ Notation notation = (Notation) nodes.item (i);
+ dtdHandler.notationDecl (
+ notation.getNodeName (),
+ notation.getPublicId (),
+ notation.getSystemId ());
+ }
+
+ // then parsed and unparsed external general entities
+ nodes = doctype.getEntities ();
+ length = nodes.getLength ();
+ for (int i = 0; i < length; i++) {
+ Entity entity = (Entity) nodes.item (i);
+ String notation = entity.getNotationName ();
+
+ if (notation != null)
+ dtdHandler.unparsedEntityDecl (
+ entity.getNodeName (),
+ entity.getPublicId (),
+ entity.getSystemId (),
+ notation);
+ else if (entity.getSystemId () != null)
+ declHandler.externalEntityDecl (
+ entity.getNodeName (),
+ entity.getPublicId (),
+ entity.getSystemId ());
+
+ //
+ // NOTE: DOM doesn't clearly provide internal
+ // entity support; but in case someone tries to
+ // fudge such support, we defend ourselves above.
+ //
+ // NOTE: DOM doesn't expose parameter entities
+ // (thank you thank you thank you thank you)
+ //
+ }
+
+ //
+ // NOTE: DOM (levels 1 and 2) doesn't expose real
+ // typing information (element or attribute decls),
+ // as exposed by SAX2 declaration handlers.
+ //
+ lexicalHandler.endDTD ();
+ }
+ break;
+
+ case Node.ENTITY_REFERENCE_NODE:
+ // this isn't done except (a) in content, and
+ // (b) not within a start tag (att value)
+ lexicalHandler.startEntity (current.getNodeName ());
+ break;
+
+ case Node.PROCESSING_INSTRUCTION_NODE:
+ contentHandler.processingInstruction (
+ current.getNodeName (), current.getNodeValue ());
+ break;
+
+ case Node.TEXT_NODE:
+ chars = current.getNodeValue ().toCharArray ();
+ contentHandler.characters (chars, 0, chars.length);
+ break;
+
+ default:
+ // e.g. fragments, entities, notations, attributes
+ throw new SAXException ("Illegal DOM Node type in Document: "
+ + current.getNodeType ());
+ }
+
+ //
+ // Then, pick the next node to visit. If the next node isn't
+ // a child, an "end" call may be needed before moving on.
+ // If there's no next node, we're done.
+ //
+ Node next;
+
+ switch (type) {
+ case Node.DOCUMENT_NODE:
+ case Node.ELEMENT_NODE:
+ case Node.ENTITY_REFERENCE_NODE:
+ //
+ // For elements that can have children, visit those
+ // children before any siblings (i.e. depth first)
+ // and after visiting this node (i.e. preorder)
+ //
+ next = current.getFirstChild ();
+ if (next != null) {
+ current = next;
+ break;
+ }
+ //
+ // Else treat this like other childless nodes, but
+ // handle this node's "end" immediately.
+ //
+ callEnd (current);
+
+ // FALLTHROUGH
+
+ case Node.CDATA_SECTION_NODE:
+ case Node.COMMENT_NODE:
+ case Node.DOCUMENT_TYPE_NODE:
+ case Node.ENTITY_NODE:
+ case Node.PROCESSING_INSTRUCTION_NODE:
+ case Node.TEXT_NODE:
+ //
+ // Use next sibling, if there is one.
+ // Else, climb up a level (calling "end")
+ // until we find an ancestral sibling
+ // or until we we climb off the top (FINISH)
+ //
+ for (;;) {
+ if ((next = current.getNextSibling ()) != null)
+ break;
+ current = current.getParentNode ();
+ if (current == null || current == start)
+ return;
+ callEnd (current);
+ }
+ current = next;
+ break;
+
+ default:
+ throw new SAXException (
+ "Illegal DOM Node type found: " + current.getNodeType ());
+ }
+ }
+ }
+
+ private void callEnd (Node node) throws SAXException
+ {
+ switch (node.getNodeType ()) {
+ // only these three container types may ever be found
+ // directly inside a Document.
+ case Node.DOCUMENT_NODE:
+ // for SAX conformance, endDocument must always
+ // be called ... it's done in a "finally" clause)
+ return;
+
+ case Node.ELEMENT_NODE:
+ if (showNamespaces) {
+ if (isL2)
+ contentHandler.endElement (
+ node.getNamespaceURI (),
+ node.getLocalName (),
+ node.getNodeName ());
+ else
+// XXX
+ throw new RuntimeException (
+ "NYI, ns lookup when parsing L1 DOM");
+ for (Enumeration e = prefixStack.getDeclaredPrefixes ();
+ e.hasMoreElements ();
+ ) {
+ contentHandler.endPrefixMapping ((String) e.nextElement ());
+ }
+ } else
+ contentHandler.endElement ("", "", node.getNodeName ());
+ prefixStack.popContext ();
+ return;
+
+ case Node.ENTITY_REFERENCE_NODE:
+ // see above -- in content, outside start tags.
+ lexicalHandler.endEntity (node.getNodeName ());
+ return;
+
+ // these can be given at the top level
+ case Node.DOCUMENT_FRAGMENT_NODE:
+ case Node.ATTRIBUTE_NODE:
+ return;
+
+ default:
+ throw new SAXException (
+ "Illegal DOM container type found: "
+ + current.getNodeType ());
+ }
+ }
+}
diff --git a/libjava/gnu/xml/util/Resolver.java b/libjava/gnu/xml/util/Resolver.java
new file mode 100644
index 0000000..4ca0787
--- /dev/null
+++ b/libjava/gnu/xml/util/Resolver.java
@@ -0,0 +1,263 @@
+/* Resolver.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.util;
+
+import java.io.*;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+import org.xml.sax.*;
+
+
+/**
+ * Utility implementation of a SAX resolver, which can be used to improve
+ * network utilization of SAX based XML components. It does this by
+ * supporting local caches of external entities.
+ * SAX parsers <em>should</em> use such local caches when possible.
+ *
+ * @see XCat
+ */
+public class Resolver implements EntityResolver, Cloneable
+{
+ /**
+ * Updates a dictionary used to map PUBLIC identifiers to file names,
+ * so that it uses the mappings in a specified directory.
+ *
+ * @param mappings Array of string pairs, where the first member
+ * of each pair is a PUBLIC identifier and the second is the
+ * name of a file, relative to the specified directory.
+ * @param directory File holding the specified files.
+ */
+ public static void addDirectoryMapping (
+ Dictionary table,
+ String mappings [][],
+ File directory
+ ) throws IOException
+ {
+ for (int i = 0; i < mappings.length; i++) {
+ File file = new File (directory, mappings [i][1]);
+ String temp;
+
+ if (!file.exists ()) // ?? log a warning ??
+ continue;
+
+ temp = fileToURL (file);
+ table.put (mappings [i][0], temp);
+ }
+ }
+
+ // FIXME: these *URL routines don't quite belong here, except
+ // that they're all in the same spirit of making it easy to
+ // use local filesystem URIs with XML parsers.
+
+ /**
+ * Provides the URL for a named file, without relying on the JDK 1.2
+ * {@link java.io.File#toURL File.toURL}() utility method.
+ *
+ * @param filename the file name to convert. Relative file names
+ * are resolved the way the JVM resolves them (current to the
+ * process-global current working directory).
+ *
+ * @exception IOException if the file does not exist
+ */
+ public static String fileNameToURL (String filename)
+ throws IOException
+ {
+ return fileToURL (new File (filename));
+ }
+
+ /**
+ * Provides the URL for a file, without relying on the JDK 1.2
+ * {@link java.io.File#toURL File.toURL}() utility method.
+ *
+ * @param f the file to convert. Relative file names
+ * are resolved the way the JVM resolves them (current to the
+ * process-global current working directory).
+ *
+ * @exception IOException if the file does not exist
+ */
+ public static String fileToURL (File f)
+ throws IOException
+ {
+ String temp;
+
+ // NOTE: the javax.xml.parsers.DocumentBuilder and
+ // javax.xml.transform.stream.StreamSource versions
+ // of this don't have this test. Some JVM versions
+ // don't report this error sanely through URL code.
+ if (!f.exists ())
+ throw new IOException ("no such file: " + f.getName ());
+
+ // FIXME: getAbsolutePath() seems buggy; I'm seeing components
+ // like "/foo/../" which are clearly not "absolute"
+ // and should have been resolved with the filesystem.
+
+ // Substituting "/" would be wrong, "foo" may have been
+ // symlinked ... the URL code will make that change
+ // later, so that things can get _really_ broken!
+
+ temp = f.getAbsolutePath ();
+
+ if (File.separatorChar != '/')
+ temp = temp.replace (File.separatorChar, '/');
+ if (!temp.startsWith ("/"))
+ temp = "/" + temp;
+ if (!temp.endsWith ("/") && f.isDirectory ())
+ temp = temp + "/";
+ return "file:" + temp;
+ }
+
+
+ /**
+ * Returns a URL string. Note that if a malformed URL is provided, or
+ * the parameter names a nonexistent file, the resulting URL may be
+ * malformed.
+ *
+ * @param fileOrURL If this is the name of a file which exists,
+ * then its URL is returned. Otherwise the argument is returned.
+ */
+ public static String getURL (String fileOrURL)
+ {
+ try {
+ return fileNameToURL (fileOrURL);
+ } catch (Exception e) {
+ return fileOrURL;
+ }
+ }
+
+
+
+ // note: cloneable, this is just copied; unguarded against mods
+ private Dictionary pubidMapping;
+
+ /**
+ * Constructs a resolver which understands how to map PUBLIC identifiers
+ * to other URIs, typically for local copies of standard DTD components.
+ *
+ * @param dictionary maps PUBLIC identifiers to URIs. This is not
+ * copied; subsequent modifications will be reported through the
+ * resolution operations.
+ */
+ public Resolver (Dictionary dict)
+ { pubidMapping = dict; }
+
+
+ // FIXME: want notion of a "system default" resolver, presumably
+ // loaded with all sorts of useful stuff. At the same time need
+ // a notion of resolver chaining (failure --> next) so that subsystems
+ // can set up things that won't interfere with other ones.
+
+ /**
+ * This parses most MIME content type strings that have <em>charset=...</em>
+ * encoding declarations to and returns the specified encoding. This
+ * conforms to RFC 3023, and is useful when constructing InputSource
+ * objects from URLConnection objects or other objects using MIME
+ * content typing.
+ *
+ * @param contentType the MIME content type that will be parsed; must
+ * not be null.
+ * @return the appropriate encoding, or null if the content type is
+ * not text and there's no <code>charset=...</code> attribute
+ */
+ static public String getEncoding (String contentType)
+ {
+ // currently a dumb parsing algorithm that works "mostly" and handles
+ // ..anything...charset=ABC
+ // ..anything...charset=ABC;otherAttr=DEF
+ // ..anything...charset=ABC (comment);otherAttr=DEF
+ // ..anything...charset= "ABC" (comment);otherAttr=DEF
+
+ int temp;
+ String encoding;
+ String defValue = null;
+
+ if (contentType.startsWith ("text/"))
+ defValue = contentType.startsWith ("text/html")
+ ? "ISO-8859-1" : "US-ASCII";
+
+ // Assumes 'charset' is only an attribute name, not part
+ // of a value, comment, or other attribute name
+ // ALSO assumes no escaped values like "\;" or "\)"
+ if ((temp = contentType.indexOf ("charset")) != -1) {
+ // strip out everything up to '=' ...
+ temp = contentType.indexOf ('=', temp);
+ if (temp == -1)
+ return defValue;
+ encoding = contentType.substring (temp + 1);
+ // ... and any subsequent attributes
+ if ((temp = encoding.indexOf (';')) != -1)
+ encoding = encoding.substring (0, temp);
+ // ... and any comments after value
+ if ((temp = encoding.indexOf ('(')) != -1)
+ encoding = encoding.substring (0, temp);
+ // ... then whitespace, and any (double) quotes
+ encoding = encoding.trim ();
+ if (encoding.charAt (0) == '"')
+ encoding = encoding.substring (1, encoding.length () - 1);
+ } else
+ encoding = defValue;
+ return encoding;
+ }
+
+
+ /**
+ * Uses a local dictionary of public identifiers to resolve URIs,
+ * normally with the goal of minimizing network traffic or latencies.
+ */
+ public InputSource resolveEntity (String publicId, String systemId)
+ throws IOException, SAXException
+ {
+ InputSource retval = null;
+ String uri;
+
+ if (publicId != null
+ && ((uri = (String) pubidMapping.get (publicId)) != null)) {
+ retval = new InputSource (uri);
+ retval.setPublicId (publicId);
+ }
+
+ // Could do URN resolution here
+
+ // URL resolution always done by parser
+
+ // FIXME: chain to "next" resolver
+
+ return retval;
+ }
+}
diff --git a/libjava/gnu/xml/util/SAXNullTransformerFactory.java b/libjava/gnu/xml/util/SAXNullTransformerFactory.java
new file mode 100644
index 0000000..3b199f6
--- /dev/null
+++ b/libjava/gnu/xml/util/SAXNullTransformerFactory.java
@@ -0,0 +1,675 @@
+/* SAXNullTransformerFactory.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.util;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.Hashtable;
+import java.util.Properties;
+
+import gnu.xml.dom.Consumer;
+import gnu.xml.dom.DomDocument;
+import gnu.xml.pipeline.DomConsumer;
+import gnu.xml.pipeline.EventFilter;
+
+import javax.xml.transform.*;
+import javax.xml.transform.dom.*;
+import javax.xml.transform.sax.*;
+import javax.xml.transform.stream.*;
+
+import org.xml.sax.*;
+import org.xml.sax.helpers.XMLReaderFactory;
+import org.xml.sax.helpers.LocatorImpl;
+
+
+/**
+ * Implements null transforms. XSLT stylesheets are not supported.
+ * This class provides a way to translate three representations of
+ * XML data (SAX event stream, DOM tree, and XML text) into each other.
+ * In essence it's a thinnish wrapper around basic SAX event
+ * <a href="../pipeline/package-summary.html">pipeline</a> facilities, which
+ * exposes only limited functionality. The <em>javax.xml.transform</em>
+ * functionality is implemented as follows: <ul>
+ *
+ * <li>The {@link javax.xml.transform.sax.SAXSource SAXSource} class
+ * just wraps an {@link XMLReader} and {@link InputSource}, while the
+ * {@link javax.xml.transform.sax.SAXResult SAXResult} class is less
+ * functional than a {@link gnu.xml.pipeline.EventConsumer EventConsumer}.
+ * (Notably, it drops all but one declaration from any DTD.)</li>
+ *
+ * <li>The {@link javax.xml.transform.dom.DOMSource DOMSource} class
+ * corresponds to special SAX parsers like {@link DomParser}, and the
+ * {@link javax.xml.transform.dom.DOMResult DOMResult} class corresponds
+ * to a {@link gnu.xml.pipeline.DomConsumer DomConsumer}.</li>
+ *
+ * <li>The {@link javax.xml.transform.stream.StreamSource StreamSource}
+ * class corresponds to a SAX {@link InputSource}, and the
+ * {@link javax.xml.transform.stream.StreamResult StreamResult} class
+ * corresponds to a {@link gnu.xml.pipeline.TextConsumer TextConsumer}.</li>
+ *
+ * </ul>
+ *
+ * <p><em>This implementation is preliminary.</em>
+ *
+ * @see gnu.xml.pipeline.XsltFilter
+ *
+ * @author David Brownell
+ */
+public class SAXNullTransformerFactory extends SAXTransformerFactory
+{
+
+ private ErrorListener errListener;
+ private URIResolver uriResolver;
+
+ /** Default constructor */
+ public SAXNullTransformerFactory () { }
+
+ //
+ // only has stuff that makes sense with null transforms
+ //
+
+ /**
+ * Returns true if the requested feature is supported.
+ * All three kinds of input and output are accepted:
+ * XML text, SAX events, and DOM nodes.
+ */
+ public boolean getFeature (String feature)
+ {
+ return SAXTransformerFactory.FEATURE.equals (feature)
+ || SAXResult.FEATURE.equals (feature)
+ || SAXSource.FEATURE.equals (feature)
+ || DOMResult.FEATURE.equals (feature)
+ || DOMSource.FEATURE.equals (feature)
+ || StreamResult.FEATURE.equals (feature)
+ || StreamSource.FEATURE.equals (feature)
+ ;
+ }
+
+ public void setFeature(String name, boolean value)
+ throws TransformerConfigurationException
+ {
+ throw new TransformerConfigurationException(name);
+ }
+
+
+ /** Throws an exception (no implementation attributes are supported) */
+ public void setAttribute (String key, Object value)
+ {
+ throw new IllegalArgumentException ();
+ }
+
+ /** Throws an exception (no implementation attributes are supported) */
+ public Object getAttribute (String key)
+ {
+ throw new IllegalArgumentException ();
+ }
+
+ /** (not yet implemented) */
+ public Source getAssociatedStylesheet (Source source,
+ String media,
+ String title,
+ String charset)
+ throws TransformerConfigurationException
+ {
+ // parse, and find the appropriate xsl-stylesheet PI contents
+ throw new IllegalArgumentException ();
+ }
+
+ public Transformer newTransformer ()
+ throws TransformerConfigurationException
+ {
+ return new NullTransformer ();
+ }
+
+ /**
+ * Returns a TransformerHandler that knows how to generate output
+ * in all three standard formats. Output text is generated using
+ * {@link XMLWriter}, and the GNU implementation of
+ * {@link DomDocument DOM} is used.
+ *
+ * @see SAXResult
+ * @see StreamResult
+ * @see DOMResult
+ */
+ public TransformerHandler newTransformerHandler ()
+ throws TransformerConfigurationException
+ {
+ NullTransformer transformer = new NullTransformer ();
+ return transformer.handler;
+ }
+
+ //
+ // Stuff that depends on XSLT support, which we don't provide
+ //
+ private static final String noXSLT = "No XSLT support";
+
+ /** Throws an exception (XSLT is not supported). */
+ public Transformer newTransformer (Source stylesheet)
+ throws TransformerConfigurationException
+ {
+ throw new TransformerConfigurationException (noXSLT);
+ }
+
+ /** Throws an exception (XSLT is not supported). */
+ public Templates newTemplates (Source stylesheet)
+ throws TransformerConfigurationException
+ {
+ throw new TransformerConfigurationException (noXSLT);
+ }
+
+ /** Throws an exception (XSLT is not supported). */
+ public TemplatesHandler newTemplatesHandler ()
+ throws TransformerConfigurationException
+ {
+ throw new TransformerConfigurationException (noXSLT);
+ }
+
+ /** Throws an exception (XSLT is not supported). */
+ public TransformerHandler newTransformerHandler (Source stylesheet)
+ throws TransformerConfigurationException
+ {
+ throw new TransformerConfigurationException (noXSLT);
+ }
+
+ /** Throws an exception (XSLT is not supported). */
+ public TransformerHandler newTransformerHandler (Templates stylesheet)
+ throws TransformerConfigurationException
+ {
+ throw new TransformerConfigurationException (noXSLT);
+ }
+
+ /** Throws an exception (XSLT is not supported). */
+ public XMLFilter newXMLFilter (Source stylesheet)
+ throws TransformerConfigurationException
+ {
+ throw new TransformerConfigurationException (noXSLT);
+ }
+
+ /** Throws an exception (XSLT is not supported). */
+ public XMLFilter newXMLFilter (Templates stylesheet)
+ throws TransformerConfigurationException
+ {
+ throw new TransformerConfigurationException (noXSLT);
+ }
+
+ /** Returns the value assigned by {@link #setErrorListener}. */
+ public ErrorListener getErrorListener ()
+ {
+ return errListener;
+ }
+
+ /** Assigns a value that would be used when parsing stylesheets */
+ public void setErrorListener (ErrorListener e)
+ {
+ errListener = e;
+ }
+
+ /** Returns the value assigned by {@link #setURIResolver}. */
+ public URIResolver getURIResolver ()
+ {
+ return uriResolver;
+ }
+
+ /** Assigns a value that would be used when parsing stylesheets */
+ public void setURIResolver (URIResolver u)
+ {
+ uriResolver = u;
+ }
+
+
+ //
+ // Helper classes. These might in theory be subclassed
+ // by an XSLT implementation, if they were exported.
+ //
+
+ static class DomTerminus
+ extends DomConsumer
+ {
+
+ DomTerminus (DOMResult result)
+ throws SAXException
+ {
+ // won't really throw SAXException
+ super (DomDocument.class);
+ setHandler (new DomHandler (this, result));
+ }
+
+ }
+
+ static class DomHandler
+ extends Consumer.Backdoor
+ {
+
+ private DOMResult result;
+
+ DomHandler (DomConsumer c, DOMResult r)
+ throws SAXException
+ {
+ // won't really throw SAXException
+ super (c);
+ result = r;
+ }
+
+ public void endDocument ()
+ throws SAXException
+ {
+ super.endDocument ();
+ result.setNode (getDocument ());
+ }
+
+ }
+
+ private static OutputStream getOutputStream (String uri)
+ throws IOException
+ {
+ // JDK stupidity: file "protocol does not support output" ...
+ if (uri.startsWith ("file:"))
+ return new FileOutputStream (uri.substring (5));
+
+ // Otherwise ...
+ URL url = new URL (uri);
+ URLConnection conn = url.openConnection ();
+
+ conn.setDoOutput (true);
+ return conn.getOutputStream ();
+ }
+
+
+ static class NullHandler
+ extends EventFilter
+ implements TransformerHandler
+ {
+
+ private String systemId;
+ private Transformer transformer;
+
+ NullHandler (Transformer t)
+ {
+ transformer = t;
+ }
+
+ public Transformer getTransformer ()
+ {
+ return transformer;
+ }
+
+ public String getSystemId ()
+ {
+ return systemId;
+ }
+
+ public void setSystemId (String id)
+ {
+ systemId = id;
+ }
+
+ public void setResult (Result result)
+ {
+ if (result.getSystemId () != null)
+ systemId = result.getSystemId ();
+
+ try
+ {
+
+ // output to partial SAX event stream?
+ if (result instanceof SAXResult)
+ {
+ SAXResult r = (SAXResult) result;
+
+ setContentHandler (r.getHandler ());
+ setProperty (LEXICAL_HANDLER, r.getLexicalHandler ());
+ // DTD info is filtered out by javax.transform
+
+ // output to DOM tree?
+ }
+ else if (result instanceof DOMResult)
+ {
+ DomTerminus out = new DomTerminus ((DOMResult) result);
+
+ setContentHandler (out.getContentHandler ());
+ setProperty (LEXICAL_HANDLER,
+ out.getProperty (LEXICAL_HANDLER));
+ // save DTD-derived info, if any.
+ setDTDHandler (out.getDTDHandler ());
+ setProperty (DECL_HANDLER,
+ out.getProperty (DECL_HANDLER));
+
+ // node is saved into result on endDocument()
+
+ // output to (XML) text?
+ }
+ else if (result instanceof StreamResult)
+ {
+ StreamResult r = (StreamResult) result;
+ XMLWriter out;
+
+ // FIXME: when do output properties take effect?
+ // encoding, standalone decl, xml/xhtml/... ...
+
+ // FIXME: maybe put nsfix filter up front
+
+ try
+ {
+ if (r.getWriter () != null)
+ out = new XMLWriter (r.getWriter ());
+ else if (r.getOutputStream () != null)
+ out = new XMLWriter (r.getOutputStream ());
+ else if (r.getSystemId () != null)
+ out = new XMLWriter (
+ getOutputStream (r.getSystemId ()));
+ else
+ throw new IllegalArgumentException (
+ "bad StreamResult");
+ }
+ catch (IOException e)
+ {
+ e.printStackTrace ();
+ // on jdk 1.4, pass the root cause ...
+ throw new IllegalArgumentException (e.getMessage ());
+ }
+
+ // out.setExpandingEntities (true);
+ // out.setPrettyPrinting (true);
+ // out.setXhtml (true);
+
+ setContentHandler (out);
+ setProperty (LEXICAL_HANDLER, out);
+ // save DTD info, if any; why not?
+ setDTDHandler (out);
+ setProperty (DECL_HANDLER, out);
+ }
+
+ }
+ catch (SAXException e)
+ {
+ // SAXNotSupportedException or SAXNotRecognizedException:
+ // "can't happen" ... but SAXException for DOM build probs
+ // could happen, so ...
+ // on jdk 1.4, pass the root cause ...
+ throw new IllegalArgumentException (e.getMessage ());
+ }
+ }
+ }
+
+ // an interface that adds no value
+ static class LocatorAdapter
+ extends LocatorImpl
+ implements SourceLocator
+ {
+
+ LocatorAdapter (SAXParseException e)
+ {
+ setSystemId (e.getSystemId ());
+ setPublicId (e.getPublicId ());
+ setLineNumber (e.getLineNumber ());
+ setColumnNumber (e.getColumnNumber ());
+ }
+
+ }
+
+ // another interface that adds no value
+ static class ListenerAdapter
+ implements ErrorHandler
+ {
+
+ NullTransformer transformer;
+
+ ListenerAdapter (NullTransformer t)
+ {
+ transformer = t;
+ }
+
+ private TransformerException map (SAXParseException e)
+ {
+ return new TransformerException (
+ e.getMessage (),
+ new LocatorAdapter (e),
+ e);
+ }
+
+ public void error (SAXParseException e)
+ throws SAXParseException
+ {
+ try
+ {
+ if (transformer.errListener != null)
+ transformer.errListener.error (map (e));
+ }
+ catch (TransformerException ex)
+ {
+ transformer.ex = ex;
+ throw e;
+ }
+ }
+
+ public void fatalError (SAXParseException e)
+ throws SAXParseException
+ {
+ try
+ {
+ if (transformer.errListener != null)
+ transformer.errListener.fatalError (map (e));
+ else
+ throw map (e);
+ } catch (TransformerException ex) {
+ transformer.ex = ex;
+ throw e;
+ }
+ }
+
+ public void warning (SAXParseException e)
+ throws SAXParseException
+ {
+ try
+ {
+ if (transformer.errListener != null)
+ transformer.errListener.warning (map (e));
+ }
+ catch (TransformerException ex)
+ {
+ transformer.ex = ex;
+ throw e;
+ }
+ }
+ }
+
+ static class NullTransformer
+ extends Transformer
+ {
+
+ private URIResolver uriResolver;
+ private Properties props = new Properties ();
+ private Hashtable params = new Hashtable (7);
+
+ ErrorListener errListener = null;
+ TransformerException ex = null;
+ NullHandler handler;
+
+ NullTransformer ()
+ {
+ super ();
+ handler = new NullHandler (this);
+ }
+
+ public ErrorListener getErrorListener ()
+ {
+ return errListener;
+ }
+
+ public void setErrorListener (ErrorListener e)
+ {
+ errListener = e;
+ }
+
+ public URIResolver getURIResolver ()
+ {
+ return uriResolver;
+ }
+
+ public void setURIResolver (URIResolver u)
+ {
+ uriResolver = u;
+ }
+
+ public void setOutputProperties (Properties p)
+ {
+ props = (Properties) p.clone ();
+ }
+
+ public Properties getOutputProperties ()
+ {
+ return (Properties) props.clone ();
+ }
+
+ public void setOutputProperty (String name, String value)
+ {
+ props.setProperty (name, value);
+ }
+
+ public String getOutputProperty (String name)
+ {
+ return props.getProperty (name);
+ }
+
+ public void clearParameters ()
+ {
+ params.clear ();
+ }
+
+ public void setParameter (String name, Object value)
+ {
+ props.put (name, value);
+ }
+
+ public Object getParameter (String name)
+ {
+ return props.get (name);
+ }
+
+ public void transform (Source in, Result out)
+ throws TransformerException
+ {
+ try
+ {
+ XMLReader producer;
+ InputSource input;
+
+ // Input from DOM?
+ if (in instanceof DOMSource)
+ {
+ DOMSource source = (DOMSource) in;
+
+ if (source.getNode () == null)
+ throw new IllegalArgumentException ("no DOM node");
+ producer = new DomParser (source.getNode ());
+ input = null;
+
+ // Input from SAX?
+ }
+ else if (in instanceof SAXSource)
+ {
+ SAXSource source = (SAXSource) in;
+
+ producer = source.getXMLReader ();
+ if (producer == null)
+ producer = XMLReaderFactory.createXMLReader ();
+
+ input = source.getInputSource ();
+ if (input == null)
+ {
+ if (source.getSystemId () != null)
+ input = new InputSource (source.getSystemId ());
+ else
+ throw new IllegalArgumentException (
+ "missing SAX input");
+ }
+
+ // Input from a stream or something?
+ }
+ else
+ {
+ producer = XMLReaderFactory.createXMLReader ();
+ input = SAXSource.sourceToInputSource (in);
+ if (input == null)
+ throw new IllegalArgumentException ("missing input");
+ }
+
+ // preserve original namespace prefixes
+ try
+ {
+ producer.setFeature(handler.FEATURE_URI + "namespace-prefixes",
+ true);
+ }
+ catch (Exception e)
+ {
+ /* ignore */
+ // FIXME if we couldn't, "NsFix" stage before the output ..
+ }
+
+ // arrange the output
+ handler.setResult (out);
+ handler.bind (producer, handler);
+
+ // then parse ... single element pipeline
+ producer.parse (input);
+
+ }
+ catch (IOException e)
+ {
+ throw new TransformerException ("transform failed", e);
+
+ }
+ catch (SAXException e)
+ {
+ if (ex == null && ex.getCause () == e)
+ throw ex;
+ else
+ throw new TransformerException ("transform failed", e);
+
+ }
+ finally
+ {
+ ex = null;
+ }
+ }
+ }
+
+}
diff --git a/libjava/gnu/xml/util/XCat.java b/libjava/gnu/xml/util/XCat.java
new file mode 100644
index 0000000..269c97b
--- /dev/null
+++ b/libjava/gnu/xml/util/XCat.java
@@ -0,0 +1,1609 @@
+/* XCat.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.util;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.net.URL;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.StringTokenizer;
+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.SAXNotRecognizedException;
+import org.xml.sax.SAXParseException;
+import org.xml.sax.XMLReader;
+
+import org.xml.sax.ext.DefaultHandler2;
+import org.xml.sax.ext.EntityResolver2;
+
+import org.xml.sax.helpers.XMLReaderFactory;
+
+/**
+ * Packages <a href=
+ "http://www.oasis-open.org/committees/entity/spec-2001-08-06.html"
+ >OASIS XML Catalogs</a>,
+ * primarily for entity resolution by parsers.
+ * That specification defines an XML syntax for mappings between
+ * identifiers declared in DTDs (particularly PUBLIC identifiers) and
+ * locations. SAX has always supported such mappings, but conventions for
+ * an XML file syntax to maintain them have previously been lacking.
+ *
+ * <p> This has three main operational modes. The primary intended mode is
+ * to create a resolver, then preloading it with one or more site-standard
+ * catalogs before using it with one or more SAX parsers: <pre>
+ * XCat catalog = new XCat ();
+ * catalog.setErrorHandler (diagnosticErrorHandler);
+ * catalog.loadCatalog ("file:/local/catalogs/catalog.cat");
+ * catalog.loadCatalog ("http://shared/catalog.cat");
+ * ...
+ * catalog.disableLoading ();
+ * parser1.setEntityResolver (catalog);
+ * parser2.setEntityResolver (catalog);
+ * ...</pre>
+ *
+ * <p>A second mode is to arrange that your application uses instances of
+ * this class as its entity resolver, and automatically loads catalogs
+ * referenced by <em>&lt;?oasis-xml-catalog...?&gt;</em> processing
+ * instructions found before the DTD in documents it parses.
+ * It would then discard the resolver after each parse.
+ *
+ * <p> A third mode applies catalogs in contexts other than entity
+ * resolution for parsers.
+ * The {@link #resolveURI resolveURI()} method supports resolving URIs
+ * stored in XML application data, rather than inside DTDs.
+ * Catalogs would be loaded as shown above, and the catalog could
+ * be used concurrently for parser entity resolution and for
+ * application URI resolution.
+ * </p>
+ *
+ * <center><hr width='70%'></center>
+ *
+ * <p>Errors in catalogs implicitly loaded (during resolution) are ignored
+ * beyond being reported through any <em>ErrorHandler</em> assigned using
+ * {@link #setErrorHandler setErrorHandler()}. SAX exceptions
+ * thrown from such a handler won't abort resolution, although throwing a
+ * <em>RuntimeException</em> or <em>Error</em> will normally abort both
+ * resolution and parsing. Useful diagnostic information is available to
+ * any <em>ErrorHandler</em> used to report problems, or from any exception
+ * thrown from an explicit {@link #loadCatalog loadCatalog()} invocation.
+ * Applications can use that information as troubleshooting aids.
+ *
+ * <p>While this class requires <em>SAX2 Extensions 1.1</em> classes in
+ * its class path, basic functionality does not require using a SAX2
+ * parser that supports the extended entity resolution functionality.
+ * See the original SAX1
+ * {@link #resolveEntity(java.lang.String,java.lang.String) resolveEntity()}
+ * method for a list of restrictions which apply when it is used with
+ * older SAX parsers.
+ *
+ * @see EntityResolver2
+ *
+ * @author David Brownell
+ */
+public class XCat implements EntityResolver2
+{
+ private Catalog catalogs [];
+ private boolean usingPublic = true;
+ private boolean loadingPermitted = true;
+ private boolean unified = true;
+ private String parserClass;
+ private ErrorHandler errorHandler;
+
+ // private EntityResolver next; // chain to next if we fail...
+
+ //
+ // NOTE: This is a straightforward implementation, and if
+ // there are lots of "nextCatalog" or "delegate*" entries
+ // in use, two tweaks would be worth considering:
+ //
+ // - Centralize some sort of cache (key by URI) for individual
+ // resolvers. That'd avoid multiple copies of a given catalog.
+ //
+ // - Have resolution track what catalogs (+modes) have been
+ // searched. This would support loop detection.
+ //
+
+
+ /**
+ * Initializes without preloading a catalog.
+ * This API is convenient when you may want to arrange that catalogs
+ * are automatically loaded when explicitly referenced in documents,
+ * using the <em>oasis-xml-catalog</em> processing instruction.
+ * In such cases you won't usually be able to preload catalogs.
+ */
+ public XCat () { }
+
+ /**
+ * Initializes, and preloads a catalog using the default SAX parser.
+ * This API is convenient when you operate with one or more standard
+ * catalogs.
+ *
+ * <p> This just delegates to {@link #loadCatalog loadCatalog()};
+ * see it for exception information.
+ *
+ * @param uri absolute URI for the catalog file.
+ */
+ public XCat (String uri)
+ throws SAXException, IOException
+ { loadCatalog (uri); }
+
+
+ /**
+ * Loads an OASIS XML Catalog.
+ * It is appended to the list of currently active catalogs, or
+ * reloaded if a catalog with the same URI was already loaded.
+ * Callers have control over what parser is used, how catalog parsing
+ * errors are reported, and whether URIs will be resolved consistently.
+ *
+ * <p> The OASIS specification says that errors detected when loading
+ * catalogs "must recover by ignoring the catalog entry file that
+ * failed, and proceeding." In this API, that action can be the
+ * responsibility of applications, when they explicitly load any
+ * catalog using this method.
+ *
+ * <p>Note that catalogs referenced by this one will not be loaded
+ * at this time. Catalogs referenced through <em>nextCatalog</em>
+ * or <em>delegate*</em> elements are normally loaded only if needed.
+ *
+ * @see #setErrorHandler
+ * @see #setParserClass
+ * @see #setUnified
+ *
+ * @param uri absolute URI for the catalog file.
+ *
+ * @exception IOException As thrown by the parser, typically to
+ * indicate problems reading data from that URI.
+ * @exception SAXException As thrown by the parser, typically to
+ * indicate problems parsing data from that URI. It may also
+ * be thrown if the parser doesn't support necessary handlers.
+ * @exception IllegalStateException When attempting to load a
+ * catalog after loading has been {@link #disableLoading disabled},
+ * such as after any entity or URI lookup has been performed.
+ */
+ public synchronized void loadCatalog (String uri)
+ throws SAXException, IOException
+ {
+ Catalog catalog;
+ int index = -1;
+
+ if (!loadingPermitted)
+ throw new IllegalStateException ();
+
+ uri = normalizeURI (uri);
+ if (catalogs != null) {
+ // maybe just reload
+ for (index = 0; index < catalogs.length; index++)
+ if (uri.equals (catalogs [index].catalogURI))
+ break;
+ }
+ catalog = loadCatalog (parserClass, errorHandler, uri, unified);
+
+ // add to list of catalogs
+ if (catalogs == null) {
+ index = 0;
+ catalogs = new Catalog [1];
+ } else if (index == catalogs.length) {
+ Catalog tmp [];
+
+ tmp = new Catalog [index + 1];
+ System.arraycopy (catalogs, 0, tmp, 0, index);
+ catalogs = tmp;
+ }
+ catalogs [index] = catalog;
+ }
+
+
+ /**
+ * "New Style" external entity resolution for parsers.
+ * Calls to this method prevent explicit loading of additional catalogs
+ * using {@link #loadCatalog loadCatalog()}.
+ *
+ * <p>This supports the full core catalog functionality for locating
+ * (and relocating) parsed entities that have been declared in a
+ * document's DTD.
+ *
+ * @param name Entity name, such as "dudley", "%nell", or "[dtd]".
+ * @param publicId Either a normalized public ID, or null.
+ * @param baseURI Absolute base URI associated with systemId.
+ * @param systemId URI found in entity declaration (may be
+ * relative to baseURI).
+ *
+ * @return Input source for accessing the external entity, or null
+ * if no mapping was found. The input source may have opened
+ * the stream, and will have a fully resolved URI.
+ *
+ * @see #getExternalSubset
+ */
+ public InputSource resolveEntity (
+ String name, // UNUSED ... systemId is always non-null
+ String publicId,
+ String baseURI, // UNUSED ... it just lets sysId be relative
+ String systemId
+ ) throws SAXException, IOException
+ {
+ if (loadingPermitted)
+ disableLoading ();
+
+ try {
+ // steps as found in OASIS XML catalog spec 7.1.2
+ // steps 1, 8 involve looping over the list of catalogs
+ for (int i = 0; i < catalogs.length; i++) {
+ InputSource retval;
+ retval = catalogs [i].resolve (usingPublic, publicId, systemId);
+ if (retval != null)
+ return retval;;
+ }
+ } catch (DoneDelegation x) {
+ // done!
+ }
+ // step 9 involves returning "no match"
+ return null;
+ }
+
+
+ /**
+ * "New Style" parser callback to add an external subset.
+ * For documents that don't include an external subset, this may
+ * return one according to <em>doctype</em> catalog entries.
+ * (This functionality is not a core part of the OASIS XML Catalog
+ * specification, though it's presented in an appendix.)
+ * If no such entry is defined, this returns null to indicate that
+ * this document will not be modified to include such a subset.
+ * Calls to this method prevent explicit loading of additional catalogs
+ * using {@link #loadCatalog loadCatalog()}.
+ *
+ * <p><em>Warning:</em> That catalog functionality can be dangerous.
+ * It can provide definitions of general entities, and thereby mask
+ * certain well formedess errors.
+ *
+ * @param name Name of the document element, either as declared in
+ * a DOCTYPE declaration or as observed in the text.
+ * @param baseURI Document's base URI (absolute).
+ *
+ * @return Input source for accessing the external subset, or null
+ * if no mapping was found. The input source may have opened
+ * the stream, and will have a fully resolved URI.
+ */
+ public InputSource getExternalSubset (String name, String baseURI)
+ throws SAXException, IOException
+ {
+ if (loadingPermitted)
+ disableLoading ();
+ try {
+ for (int i = 0; i < catalogs.length; i++) {
+ InputSource retval = catalogs [i].getExternalSubset (name);
+ if (retval != null)
+ return retval;
+ }
+ } catch (DoneDelegation x) {
+ // done!
+ }
+ return null;
+ }
+
+
+ /**
+ * "Old Style" external entity resolution for parsers.
+ * This API provides only core functionality.
+ * Calls to this method prevent explicit loading of additional catalogs
+ * using {@link #loadCatalog loadCatalog()}.
+ *
+ * <p>The functional limitations of this interface include:</p><ul>
+ *
+ * <li>Since system IDs will be absolutized before the resolver
+ * sees them, matching against relative URIs won't work.
+ * This may affect <em>system</em>, <em>rewriteSystem</em>,
+ * and <em>delegateSystem</em> catalog entries.
+ *
+ * <li>Because of that absolutization, documents declaring entities
+ * with system IDs using URI schemes that the JVM does not recognize
+ * may be unparsable. URI schemes such as <em>file:/</em>,
+ * <em>http://</em>, <em>https://</em>, and <em>ftp://</em>
+ * will usually work reliably.
+ *
+ * <li>Because missing external subsets can't be provided, the
+ * <em>doctype</em> catalog entries will be ignored.
+ * (The {@link #getExternalSubset getExternalSubset()} method is
+ * a "New Style" resolution option.)
+ *
+ * </ul>
+ *
+ * <p>Applications can tell whether this limited functionality will be
+ * used: if the feature flag associated with the {@link EntityResolver2}
+ * interface is not <em>true</em>, the limitations apply. Applications
+ * can't usually know whether a given document and catalog will trigger
+ * those limitations. The issue can only be bypassed by operational
+ * procedures such as not using catalogs or documents which involve
+ * those features.
+ *
+ * @param publicId Either a normalized public ID, or null
+ * @param systemId Always an absolute URI.
+ *
+ * @return Input source for accessing the external entity, or null
+ * if no mapping was found. The input source may have opened
+ * the stream, and will have a fully resolved URI.
+ */
+ final public InputSource resolveEntity (String publicId, String systemId)
+ throws SAXException, IOException
+ {
+ return resolveEntity (null, publicId, null, systemId);
+ }
+
+
+ /**
+ * Resolves a URI reference that's not defined to the DTD.
+ * This is intended for use with URIs found in document text, such as
+ * <em>xml-stylesheet</em> processing instructions and in attribute
+ * values, where they are not recognized as URIs by XML parsers.
+ * Calls to this method prevent explicit loading of additional catalogs
+ * using {@link #loadCatalog loadCatalog()}.
+ *
+ * <p>This functionality is supported by the OASIS XML Catalog
+ * specification, but will never be invoked by an XML parser.
+ * It corresponds closely to functionality for mapping system
+ * identifiers for entities declared in DTDs; closely enough that
+ * this implementation's default behavior is that they be
+ * identical, to minimize potential confusion.
+ *
+ * <p>This method could be useful when implementing the
+ * {@link javax.xml.transform.URIResolver} interface, wrapping the
+ * input source in a {@link javax.xml.transform.sax.SAXSource}.
+ *
+ * @see #isUnified
+ * @see #setUnified
+ *
+ * @param baseURI The relevant base URI as specified by the XML Base
+ * specification. This recognizes <em>xml:base</em> attributes
+ * as overriding the actual (physical) base URI.
+ * @param uri Either an absolute URI, or one relative to baseURI
+ *
+ * @return Input source for accessing the mapped URI, or null
+ * if no mapping was found. The input source may have opened
+ * the stream, and will have a fully resolved URI.
+ */
+ public InputSource resolveURI (String baseURI, String uri)
+ throws SAXException, IOException
+ {
+ if (loadingPermitted)
+ disableLoading ();
+
+ // NOTE: baseURI isn't used here, but caller MUST have it,
+ // and heuristics _might_ use it in the future ... plus,
+ // it's symmetric with resolveEntity ().
+
+ // steps 1, 6 involve looping
+ try {
+ for (int i = 0; i < catalogs.length; i++) {
+ InputSource tmp = catalogs [i].resolveURI (uri);
+ if (tmp != null)
+ return tmp;
+ }
+ } catch (DoneDelegation x) {
+ // done
+ }
+ // step 7 reports no match
+ return null;
+ }
+
+
+ /**
+ * Records that catalog loading is no longer permitted.
+ * Loading is automatically disabled when lookups are performed,
+ * and should be manually disabled when <em>startDTD()</em> (or
+ * any other DTD declaration callback) is invoked, or at the latest
+ * when the document root element is seen.
+ */
+ public synchronized void disableLoading ()
+ {
+ // NOTE: this method and loadCatalog() are synchronized
+ // so that it's impossible to load (top level) catalogs
+ // after lookups start. Likewise, deferred loading is also
+ // synchronized (for "next" and delegated catalogs) to
+ // ensure that parsers can share resolvers.
+ loadingPermitted = false;
+ }
+
+
+ /**
+ * Returns the error handler used to report catalog errors.
+ * Null is returned if the parser's default error handling
+ * will be used.
+ *
+ * @see #setErrorHandler
+ */
+ public ErrorHandler getErrorHandler ()
+ { return errorHandler; }
+
+ /**
+ * Assigns the error handler used to report catalog errors.
+ * These errors may come either from the SAX2 parser or
+ * from the catalog parsing code driven by the parser.
+ *
+ * <p> If you're sharing the resolver between parsers, don't
+ * change this once lookups have begun.
+ *
+ * @see #getErrorHandler
+ *
+ * @param parser The error handler, or null saying to use the default
+ * (no diagnostics, and only fatal errors terminate loading).
+ */
+ public void setErrorHandler (ErrorHandler handler)
+ { errorHandler = handler; }
+
+
+ /**
+ * Returns the name of the SAX2 parser class used to parse catalogs.
+ * Null is returned if the system default is used.
+ * @see #setParserClass
+ */
+ public String getParserClass ()
+ { return parserClass; }
+
+ /**
+ * Names the SAX2 parser class used to parse catalogs.
+ *
+ * <p> If you're sharing the resolver between parsers, don't change
+ * this once lookups have begun.
+ *
+ * <p> Note that in order to properly support the <em>xml:base</em>
+ * attribute and relative URI resolution, the SAX parser used to parse
+ * the catalog must provide a {@link Locator} and support the optional
+ * declaration and lexical handlers.
+ *
+ * @see #getParserClass
+ *
+ * @param parser The parser class name, or null saying to use the
+ * system default SAX2 parser.
+ */
+ public void setParserClass (String parser)
+ { parserClass = parser; }
+
+
+ /**
+ * Returns true (the default) if all methods resolve
+ * a given URI in the same way.
+ * Returns false if calls resolving URIs as entities (such as
+ * {@link #resolveEntity resolveEntity()}) use different catalog entries
+ * than those resolving them as URIs ({@link #resolveURI resolveURI()}),
+ * which will generally produce different results.
+ *
+ * <p>The OASIS XML Catalog specification defines two related schemes
+ * to map URIs "as URIs" or "as system IDs".
+ * URIs use <em>uri</em>, <em>rewriteURI</em>, and <em>delegateURI</em>
+ * elements. System IDs do the same things with <em>systemId</em>,
+ * <em>rewriteSystemId</em>, and <em>delegateSystemId</em>.
+ * It's confusing and error prone to maintain two parallel copies of
+ * such data. Accordingly, this class makes that behavior optional.
+ * The <em>unified</em> interpretation of URI mappings is preferred,
+ * since it prevents surprises where one URI gets mapped to different
+ * contents depending on whether the reference happens to have come
+ * from a DTD (or not).
+ *
+ * @see #setUnified
+ */
+ public boolean isUnified ()
+ { return unified; }
+
+ /**
+ * Assigns the value of the flag returned by {@link #isUnified}.
+ * Set it to false to be strictly conformant with the OASIS XML Catalog
+ * specification. Set it to true to make all mappings for a given URI
+ * give the same result, regardless of the reason for the mapping.
+ *
+ * <p>Don't change this once you've loaded the first catalog.
+ *
+ * @param value new flag setting
+ */
+ public void setUnified (boolean value)
+ { unified = value; }
+
+
+ /**
+ * Returns true (the default) if a catalog's public identifier
+ * mappings will be used.
+ * When false is returned, such mappings are ignored except when
+ * system IDs are discarded, such as for
+ * entities using the <em>urn:publicid:</em> URI scheme in their
+ * system identifiers. (See RFC 3151 for information about that
+ * URI scheme. Using it in system identifiers may not work well
+ * with many SAX parsers unless the <em>resolve-dtd-uris</em>
+ * feature flag is set to false.)
+ * @see #setUsingPublic
+ */
+ public boolean isUsingPublic ()
+ { return usingPublic; }
+
+ /**
+ * Specifies which catalog search mode is used.
+ * By default, public identifier mappings are able to override system
+ * identifiers when both are available.
+ * Applications may choose to ignore public
+ * identifier mappings in such cases, so that system identifiers
+ * declared in DTDs will only be overridden by an explicit catalog
+ * match for that system ID.
+ *
+ * <p> If you're sharing the resolver between parsers, don't
+ * change this once lookups have begun.
+ * @see #isUsingPublic
+ *
+ * @param value true to always use public identifier mappings,
+ * false to only use them for system ids using the <em>urn:publicid:</em>
+ * URI scheme.
+ */
+ public void setUsingPublic (boolean value)
+ { usingPublic = value; }
+
+
+
+ // hmm, what's this do? :)
+ private static Catalog loadCatalog (
+ String parserClass,
+ ErrorHandler eh,
+ String uri,
+ boolean unified
+ ) throws SAXException, IOException
+ {
+ XMLReader parser;
+ Loader loader;
+ boolean doesIntern = false;
+
+ if (parserClass == null)
+ parser = XMLReaderFactory.createXMLReader ();
+ else
+ parser = XMLReaderFactory.createXMLReader (parserClass);
+ if (eh != null)
+ parser.setErrorHandler (eh);
+ // resolve-dtd-entities is at default value (unrecognized == true)
+
+ try {
+ doesIntern = parser.getFeature (
+ "http://xml.org/sax/features/string-interning");
+ } catch (SAXNotRecognizedException e) { }
+
+ loader = new Loader (doesIntern, eh, unified);
+ loader.cat.parserClass = parserClass;
+ loader.cat.catalogURI = uri;
+
+ parser.setContentHandler (loader);
+ parser.setProperty (
+ "http://xml.org/sax/properties/declaration-handler",
+ loader);
+ parser.setProperty (
+ "http://xml.org/sax/properties/lexical-handler",
+ loader);
+ parser.parse (uri);
+
+ return loader.cat;
+ }
+
+ // perform one or both the normalizations for public ids
+ private static String normalizePublicId (boolean full, String publicId)
+ {
+ if (publicId.startsWith ("urn:publicid:")) {
+ StringBuffer buf = new StringBuffer ();
+ char chars [] = publicId.toCharArray ();
+boolean hasbug = false;
+
+ for (int i = 13; i < chars.length; i++) {
+ switch (chars [i]) {
+ case '+': buf.append (' '); continue;
+ case ':': buf.append ("//"); continue;
+ case ';': buf.append ("::"); continue;
+ case '%':
+// FIXME unhex that char! meanwhile, warn and fallthrough ...
+ hasbug = true;
+ default: buf.append (chars [i]); continue;
+ }
+ }
+ publicId = buf.toString ();
+if (hasbug)
+System.err.println ("nyet unhexing public id: " + publicId);
+ full = true;
+ }
+
+ // SAX parsers do everything except that URN mapping, but
+ // we can't trust other sources to normalize correctly
+ if (full) {
+ StringTokenizer tokens;
+ String token;
+
+ tokens = new StringTokenizer (publicId, " \r\n");
+ publicId = null;
+ while (tokens.hasMoreTokens ()) {
+ if (publicId == null)
+ publicId = tokens.nextToken ();
+ else
+ publicId += " " + tokens.nextToken ();
+ }
+ }
+ return publicId;
+ }
+
+ private static boolean isUriExcluded (int c)
+ { return c <= 0x20 || c >= 0x7f || "\"<>^`{|}".indexOf (c) != -1; }
+
+ private static int hexNibble (int c)
+ {
+ if (c < 10)
+ return c + '0';
+ return ('a' - 10) + c;
+ }
+
+ // handles URIs with "excluded" characters
+ private static String normalizeURI (String systemId)
+ {
+ int length = systemId.length ();
+
+ for (int i = 0; i < length; i++) {
+ char c = systemId.charAt (i);
+
+ // escape non-ASCII plus "excluded" characters
+ if (isUriExcluded (c)) {
+ byte buf [];
+ ByteArrayOutputStream out;
+ int b;
+
+ // a JVM that doesn't know UTF8 and 8859_1 is unusable!
+ try {
+ buf = systemId.getBytes ("UTF8");
+ out = new ByteArrayOutputStream (buf.length + 10);
+
+ for (i = 0; i < buf.length; i++) {
+ b = buf [i] & 0x0ff;
+ if (isUriExcluded (b)) {
+ out.write ((int) '%');
+ out.write (hexNibble (b >> 4));
+ out.write (hexNibble (b & 0x0f));
+ } else
+ out.write (b);
+ }
+ return out.toString ("8859_1");
+ } catch (IOException e) {
+ throw new RuntimeException (
+ "can't normalize URI: " + e.getMessage ());
+ }
+ }
+ }
+ return systemId;
+ }
+
+ // thrown to mark authoritative end of a search
+ private static class DoneDelegation extends SAXException
+ {
+ DoneDelegation () { }
+ }
+
+
+ /**
+ * Represents a OASIS XML Catalog, and encapsulates much of
+ * the catalog functionality.
+ */
+ private static class Catalog
+ {
+ // loading infrastructure
+ String catalogURI;
+ ErrorHandler eh;
+ boolean unified;
+ String parserClass;
+
+ // catalog data
+ boolean hasPreference;
+ boolean usingPublic;
+
+ Hashtable publicIds;
+ Hashtable publicDelegations;
+
+ Hashtable systemIds;
+ Hashtable systemRewrites;
+ Hashtable systemDelegations;
+
+ Hashtable uris;
+ Hashtable uriRewrites;
+ Hashtable uriDelegations;
+
+ Hashtable doctypes;
+
+ Vector next;
+
+ // nonpublic!
+ Catalog () { }
+
+
+ // steps as found in OASIS XML catalog spec 7.1.2
+ private InputSource locatePublicId (String publicId)
+ throws SAXException, IOException
+ {
+ // 5. return (first) 'public' entry
+ if (publicIds != null) {
+ String retval = (String) publicIds.get (publicId);
+ if (retval != null) {
+ // IF the URI is accessible ...
+ return new InputSource (retval);
+ }
+ }
+
+ // 6. return delegatePublic catalog match [complex]
+ if (publicDelegations != null)
+ return checkDelegations (publicDelegations, publicId,
+ publicId, null);
+
+ return null;
+ }
+
+ // steps as found in OASIS XML catalog spec 7.1.2 or 7.2.2
+ private InputSource mapURI (
+ String uri,
+ Hashtable ids,
+ Hashtable rewrites,
+ Hashtable delegations
+ ) throws SAXException, IOException
+ {
+ // 7.1.2: 2. return (first) 'system' entry
+ // 7.2.2: 2. return (first) 'uri' entry
+ if (ids != null) {
+ String retval = (String) ids.get (uri);
+ if (retval != null) {
+ // IF the URI is accessible ...
+ return new InputSource (retval);
+ }
+ }
+
+ // 7.1.2: 3. return 'rewriteSystem' entries
+ // 7.2.2: 3. return 'rewriteURI' entries
+ if (rewrites != null) {
+ String prefix = null;
+ String replace = null;
+ int prefixLen = -1;
+
+ for (Enumeration e = rewrites.keys ();
+ e.hasMoreElements ();
+ /* NOP */) {
+ String temp = (String) e.nextElement ();
+ int len = -1;
+
+ if (!uri.startsWith (temp))
+ continue;
+ if (prefix != null
+ && (len = temp.length ()) < prefixLen)
+ continue;
+ prefix = temp;
+ prefixLen = len;
+ replace = (String) rewrites.get (temp);
+ }
+ if (prefix != null) {
+ StringBuffer buf = new StringBuffer (replace);
+ buf.append (uri.substring (prefixLen));
+ // IF the URI is accessible ...
+ return new InputSource (buf.toString ());
+ }
+ }
+
+ // 7.1.2: 4. return 'delegateSystem' catalog match [complex]
+ // 7.2.2: 4. return 'delegateURI' catalog match [complex]
+ if (delegations != null)
+ return checkDelegations (delegations, uri, null, uri);
+
+ return null;
+ }
+
+
+ /**
+ * Returns a URI for an external entity.
+ */
+ public InputSource resolve (
+ boolean usingPublic,
+ String publicId,
+ String systemId
+ ) throws SAXException, IOException
+ {
+ boolean preferSystem;
+ InputSource retval;
+
+ if (hasPreference)
+ preferSystem = !this.usingPublic;
+ else
+ preferSystem = !usingPublic;
+
+ if (publicId != null)
+ publicId = normalizePublicId (false, publicId);
+
+ // behavior here matches section 7.1.1 of the oasis spec
+ if (systemId != null) {
+ if (systemId.startsWith ("urn:publicid:")) {
+ String temp = normalizePublicId (true, systemId);
+ if (publicId == null) {
+ publicId = temp;
+ systemId = null;
+ } else if (!publicId.equals (temp)) {
+ // error; ok to recover by:
+ systemId = null;
+ }
+ } else
+ systemId = normalizeURI (systemId);
+ }
+
+ if (systemId == null && publicId == null)
+ return null;
+
+ if (systemId != null) {
+ retval = mapURI (systemId, systemIds, systemRewrites,
+ systemDelegations);
+ if (retval != null) {
+ retval.setPublicId (publicId);
+ return retval;
+ }
+ }
+
+ if (publicId != null
+ && !(systemId != null && preferSystem)) {
+ retval = locatePublicId (publicId);
+ if (retval != null) {
+ retval.setPublicId (publicId);
+ return retval;
+ }
+ }
+
+ // 7. apply nextCatalog entries
+ if (next != null) {
+ int length = next.size ();
+ for (int i = 0; i < length; i++) {
+ Catalog n = getNext (i);
+ retval = n.resolve (usingPublic, publicId, systemId);
+ if (retval != null)
+ return retval;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Maps one URI into another, for resources that are not defined
+ * using XML external entity or notation syntax.
+ */
+ public InputSource resolveURI (String uri)
+ throws SAXException, IOException
+ {
+ if (uri.startsWith ("urn:publicid:"))
+ return resolve (true, normalizePublicId (true, uri), null);
+
+ InputSource retval;
+
+ uri = normalizeURI (uri);
+
+ // 7.2.2 steps 2-4
+ retval = mapURI (uri, uris, uriRewrites, uriDelegations);
+ if (retval != null)
+ return retval;
+
+ // 7.2.2 step 5. apply nextCatalog entries
+ if (next != null) {
+ int length = next.size ();
+ for (int i = 0; i < length; i++) {
+ Catalog n = getNext (i);
+ retval = n.resolveURI (uri);
+ if (retval != null)
+ return retval;
+ }
+ }
+
+ return null;
+ }
+
+
+ /**
+ * Finds the external subset associated with a given root element.
+ */
+ public InputSource getExternalSubset (String name)
+ throws SAXException, IOException
+ {
+ if (doctypes != null) {
+ String value = (String) doctypes.get (name);
+ if (value != null) {
+ // IF the URI is accessible ...
+ return new InputSource (value);
+ }
+ }
+ if (next != null) {
+ int length = next.size ();
+ for (int i = 0; i < length; i++) {
+ Catalog n = getNext (i);
+ if (n == null)
+ continue;
+ InputSource retval = n.getExternalSubset (name);
+ if (retval != null)
+ return retval;
+ }
+ }
+ return null;
+ }
+
+ private synchronized Catalog getNext (int i)
+ throws SAXException, IOException
+ {
+ Object obj;
+
+ if (next == null || i < 0 || i >= next.size ())
+ return null;
+ obj = next.elementAt (i);
+ if (obj instanceof Catalog)
+ return (Catalog) obj;
+
+ // ok, we deferred reading that catalog till now.
+ // load and cache it.
+ Catalog cat = null;
+
+ try {
+ cat = loadCatalog (parserClass, eh, (String) obj, unified);
+ next.setElementAt (cat, i);
+ } catch (SAXException e) {
+ // must fail quietly, says the OASIS spec
+ } catch (IOException e) {
+ // same applies here
+ }
+ return cat;
+ }
+
+ private InputSource checkDelegations (
+ Hashtable delegations,
+ String id,
+ String publicId, // only one of public/system
+ String systemId // will be non-null...
+ ) throws SAXException, IOException
+ {
+ Vector matches = null;
+ int length = 0;
+
+ // first, see if any prefixes match.
+ for (Enumeration e = delegations.keys ();
+ e.hasMoreElements ();
+ /* NOP */) {
+ String prefix = (String) e.nextElement ();
+
+ if (!id.startsWith (prefix))
+ continue;
+ if (matches == null)
+ matches = new Vector ();
+
+ // maintain in longer->shorter sorted order
+ // NOTE: assumes not many matches will fire!
+ int index;
+
+ for (index = 0; index < length; index++) {
+ String temp = (String) matches.elementAt (index);
+ if (prefix.length () > temp.length ()) {
+ matches.insertElementAt (prefix, index);
+ break;
+ }
+ }
+ if (index == length)
+ matches.addElement (prefix);
+ length++;
+ }
+ if (matches == null)
+ return null;
+
+ // now we know the list of catalogs to replace our "top level"
+ // list ... we use it here, rather than somehow going back and
+ // restarting, since this helps avoid reading most catalogs.
+ // this assumes stackspace won't be a problem.
+ for (int i = 0; i < length; i++) {
+ Catalog catalog = null;
+ InputSource result;
+
+ // get this catalog. we may not have read it yet.
+ synchronized (delegations) {
+ Object prefix = matches.elementAt (i);
+ Object cat = delegations.get (prefix);
+
+ if (cat instanceof Catalog)
+ catalog = (Catalog) cat;
+ else {
+ try {
+ // load and cache that catalog
+ catalog = loadCatalog (parserClass, eh,
+ (String) cat, unified);
+ delegations.put (prefix, catalog);
+ } catch (SAXException e) {
+ // must ignore, says the OASIS spec
+ } catch (IOException e) {
+ // same applies here
+ }
+ }
+ }
+
+ // ignore failed loads, and proceed
+ if (catalog == null)
+ continue;
+
+ // we have a catalog ... resolve!
+ // usingPublic value can't matter, there's no choice
+ result = catalog.resolve (true, publicId, systemId);
+ if (result != null)
+ return result;
+ }
+
+ // if there were no successes, the entire
+ // lookup failed (all the way to top level)
+ throw new DoneDelegation ();
+ }
+ }
+
+
+ /** This is the namespace URI used for OASIS XML Catalogs. */
+ private static final String catalogNamespace =
+ "urn:oasis:names:tc:entity:xmlns:xml:catalog";
+
+
+ /**
+ * Loads/unmarshals one catalog.
+ */
+ private static class Loader extends DefaultHandler2
+ {
+ private boolean preInterned;
+ private ErrorHandler handler;
+ private boolean unified;
+ private int ignoreDepth;
+ private Locator locator;
+ private boolean started;
+ private Hashtable externals;
+ private Stack bases;
+
+ Catalog cat = new Catalog ();
+
+
+ /**
+ * Constructor.
+ * @param flag true iff the parser already interns strings.
+ * @param eh Errors and warnings are delegated to this.
+ * @param unified true keeps one table for URI mappings;
+ * false matches OASIS spec, storing mappings
+ * for URIs and SYSTEM ids in parallel tables.
+ */
+ Loader (boolean flag, ErrorHandler eh, boolean unified)
+ {
+ preInterned = flag;
+ handler = eh;
+ this.unified = unified;
+ cat.unified = unified;
+ cat.eh = eh;
+ }
+
+
+ // strips out fragments
+ private String nofrag (String uri)
+ throws SAXException
+ {
+ if (uri.indexOf ('#') != -1) {
+ warn ("URI with fragment: " + uri);
+ uri = uri.substring (0, uri.indexOf ('#'));
+ }
+ return uri;
+ }
+
+ // absolutizes relative URIs
+ private String absolutize (String uri)
+ throws SAXException
+ {
+ // avoid creating URLs if they're already absolutized,
+ // or if the URI is already using a known scheme
+ if (uri.startsWith ("file:/")
+ || uri.startsWith ("http:/")
+ || uri.startsWith ("https:/")
+ || uri.startsWith ("ftp:/")
+ || uri.startsWith ("urn:")
+ )
+ return uri;
+
+ // otherwise, let's hope the JDK handles this URI scheme.
+ try {
+ URL base = (URL) bases.peek ();
+ return new URL (base, uri).toString ();
+ } catch (Exception e) {
+ fatal ("can't absolutize URI: " + uri);
+ return null;
+ }
+ }
+
+ // recoverable error
+ private void error (String message)
+ throws SAXException
+ {
+ if (handler == null)
+ return;
+ handler.error (new SAXParseException (message, locator));
+ }
+
+ // nonrecoverable error
+ private void fatal (String message)
+ throws SAXException
+ {
+ SAXParseException spe;
+
+ spe = new SAXParseException (message, locator);
+ if (handler != null)
+ handler.fatalError (spe);
+ throw spe;
+ }
+
+ // low severity problem
+ private void warn (String message)
+ throws SAXException
+ {
+ if (handler == null)
+ return;
+ handler.warning (new SAXParseException (message, locator));
+ }
+
+ // callbacks:
+
+ public void setDocumentLocator (Locator l)
+ { locator = l; }
+
+ public void startDocument ()
+ throws SAXException
+ {
+ if (locator == null)
+ error ("no locator!");
+ bases = new Stack ();
+ String uri = locator.getSystemId ();
+ try {
+ bases.push (new URL (uri));
+ } catch (IOException e) {
+ fatal ("bad document base URI: " + uri);
+ }
+ }
+
+ public void endDocument ()
+ throws SAXException
+ {
+ try {
+ if (!started)
+ error ("not a catalog!");
+ } finally {
+ locator = null;
+ handler = null;
+ externals = null;
+ bases = null;
+ }
+ }
+
+ // XML Base support for external entities.
+
+ // NOTE: expects parser is in default "resolve-dtd-uris" mode.
+ public void externalEntityDecl (String name, String pub, String sys)
+ throws SAXException
+ {
+ if (externals == null)
+ externals = new Hashtable ();
+ if (externals.get (name) == null)
+ externals.put (name, pub);
+ }
+
+ public void startEntity (String name)
+ throws SAXException
+ {
+ if (externals == null)
+ return;
+ String uri = (String) externals.get (name);
+
+ // NOTE: breaks if an EntityResolver substitutes these URIs.
+ // If toplevel loader supports one, must intercept calls...
+ if (uri != null) {
+ try {
+ bases.push (new URL (uri));
+ } catch (IOException e) {
+ fatal ("entity '" + name + "', bad URI: " + uri);
+ }
+ }
+ }
+
+ public void endEntity (String name)
+ {
+ if (externals == null)
+ return;
+ String value = (String) externals.get (name);
+
+ if (value != null)
+ bases.pop ();
+ }
+
+ /**
+ * Processes catalog elements, saving their data.
+ */
+ public void startElement (String namespace, String local,
+ String qName, Attributes atts)
+ throws SAXException
+ {
+ // must ignore non-catalog elements, and their contents
+ if (ignoreDepth != 0 || !catalogNamespace.equals (namespace)) {
+ ignoreDepth++;
+ return;
+ }
+
+ // basic sanity checks
+ if (!preInterned)
+ local = local.intern ();
+ if (!started) {
+ started = true;
+ if ("catalog" != local)
+ fatal ("root element not 'catalog': " + local);
+ }
+
+ // Handle any xml:base attribute
+ String xmlbase = atts.getValue ("xml:base");
+
+ if (xmlbase != null) {
+ URL base = (URL) bases.peek ();
+ try {
+ base = new URL (base, xmlbase);
+ } catch (IOException e) {
+ fatal ("can't resolve xml:base attribute: " + xmlbase);
+ }
+ bases.push (base);
+ } else
+ bases.push (bases.peek ());
+
+ // fetch multi-element attributes, apply standard tweaks
+ // values (uri, catalog, rewritePrefix) get normalized too,
+ // as a precaution and since we may compare the values
+ String catalog = atts.getValue ("catalog");
+ if (catalog != null)
+ catalog = normalizeURI (absolutize (catalog));
+
+ String rewritePrefix = atts.getValue ("rewritePrefix");
+ if (rewritePrefix != null)
+ rewritePrefix = normalizeURI (absolutize (rewritePrefix));
+
+ String systemIdStartString;
+ systemIdStartString = atts.getValue ("systemIdStartString");
+ if (systemIdStartString != null) {
+ systemIdStartString = normalizeURI (systemIdStartString);
+ // unmatchable <rewriteSystemId>, <delegateSystemId> elements
+ if (systemIdStartString.startsWith ("urn:publicid:")) {
+ error ("systemIdStartString is really a publicId!!");
+ return;
+ }
+ }
+
+ String uri = atts.getValue ("uri");
+ if (uri != null)
+ uri = normalizeURI (absolutize (uri));
+
+ String uriStartString;
+ uriStartString = atts.getValue ("uriStartString");
+ if (uriStartString != null) {
+ uriStartString = normalizeURI (uriStartString);
+ // unmatchable <rewriteURI>, <delegateURI> elements
+ if (uriStartString.startsWith ("urn:publicid:")) {
+ error ("uriStartString is really a publicId!!");
+ return;
+ }
+ }
+
+ // strictly speaking "group" and "catalog" shouldn't nest
+ // ... arbitrary restriction, no evident motivation
+
+// FIXME stack "prefer" settings (two elements only!) and use
+// them to populate different public mapping/delegation tables
+
+ if ("catalog" == local || "group" == local) {
+ String prefer = atts.getValue ("prefer");
+
+ if (prefer != null && !"public".equals (prefer)) {
+ if (!"system".equals (prefer)) {
+ error ("in <" + local + " ... prefer='...'>, "
+ + "assuming 'public'");
+ prefer = "public";
+ }
+ }
+ if (prefer != null) {
+ if ("catalog" == local) {
+ cat.hasPreference = true;
+ cat.usingPublic = "public".equals (prefer);
+ } else {
+ if (!cat.hasPreference || cat.usingPublic
+ != "public".equals (prefer)) {
+fatal ("<group prefer=...> case not handled");
+ }
+ }
+ } else if ("group" == local && cat.hasPreference) {
+fatal ("<group prefer=...> case not handled");
+ }
+
+ //
+ // PUBLIC ids: cleanly set up for id substitution
+ //
+ } else if ("public" == local) {
+ String publicId = atts.getValue ("publicId");
+ String value = null;
+
+ if (publicId == null || uri == null) {
+ error ("expecting <public publicId=... uri=.../>");
+ return;
+ }
+ publicId = normalizePublicId (true, publicId);
+ uri = nofrag (uri);
+ if (cat.publicIds == null)
+ cat.publicIds = new Hashtable ();
+ else
+ value = (String) cat.publicIds.get (publicId);
+ if (value != null) {
+ if (!value.equals (uri))
+ warn ("ignoring <public...> entry for " + publicId);
+ } else
+ cat.publicIds.put (publicId, uri);
+
+ } else if ("delegatePublic" == local) {
+ String publicIdStartString;
+ Object value = null;
+
+ publicIdStartString = atts.getValue ("publicIdStartString");
+ if (publicIdStartString == null || catalog == null) {
+ error ("expecting <delegatePublic "
+ + "publicIdStartString=... catalog=.../>");
+ return;
+ }
+ publicIdStartString = normalizePublicId (true,
+ publicIdStartString);
+ if (cat.publicDelegations == null)
+ cat.publicDelegations = new Hashtable ();
+ else
+ value = cat.publicDelegations.get (publicIdStartString);
+ if (value != null) {
+ if (!value.equals (catalog))
+ warn ("ignoring <delegatePublic...> entry for "
+ + uriStartString);
+ } else
+ cat.publicDelegations.put (publicIdStartString, catalog);
+
+
+ //
+ // SYSTEM ids: need substitution due to operational issues
+ //
+ } else if ("system" == local) {
+ String systemId = atts.getValue ("systemId");
+ String value = null;
+
+ if (systemId == null || uri == null) {
+ error ("expecting <system systemId=... uri=.../>");
+ return;
+ }
+ systemId = normalizeURI (systemId);
+ uri = nofrag (uri);
+ if (systemId.startsWith ("urn:publicid:")) {
+ error ("systemId is really a publicId!!");
+ return;
+ }
+ if (cat.systemIds == null) {
+ cat.systemIds = new Hashtable ();
+ if (unified)
+ cat.uris = cat.systemIds;
+ } else
+ value = (String) cat.systemIds.get (systemId);
+ if (value != null) {
+ if (!value.equals (uri))
+ warn ("ignoring <system...> entry for " + systemId);
+ } else
+ cat.systemIds.put (systemId, uri);
+
+ } else if ("rewriteSystem" == local) {
+ String value = null;
+
+ if (systemIdStartString == null || rewritePrefix == null
+ || systemIdStartString.length () == 0
+ || rewritePrefix.length () == 0
+ ) {
+ error ("expecting <rewriteSystem "
+ + "systemIdStartString=... rewritePrefix=.../>");
+ return;
+ }
+ if (cat.systemRewrites == null) {
+ cat.systemRewrites = new Hashtable ();
+ if (unified)
+ cat.uriRewrites = cat.systemRewrites;
+ } else
+ value = (String) cat.systemRewrites.get (
+ systemIdStartString);
+ if (value != null) {
+ if (!value.equals (rewritePrefix))
+ warn ("ignoring <rewriteSystem...> entry for "
+ + systemIdStartString);
+ } else
+ cat.systemRewrites.put (systemIdStartString,
+ rewritePrefix);
+
+ } else if ("delegateSystem" == local) {
+ Object value = null;
+
+ if (systemIdStartString == null || catalog == null) {
+ error ("expecting <delegateSystem "
+ + "systemIdStartString=... catalog=.../>");
+ return;
+ }
+ if (cat.systemDelegations == null) {
+ cat.systemDelegations = new Hashtable ();
+ if (unified)
+ cat.uriDelegations = cat.systemDelegations;
+ } else
+ value = cat.systemDelegations.get (systemIdStartString);
+ if (value != null) {
+ if (!value.equals (catalog))
+ warn ("ignoring <delegateSystem...> entry for "
+ + uriStartString);
+ } else
+ cat.systemDelegations.put (systemIdStartString, catalog);
+
+
+ //
+ // URI: just like "system" ID support, except that
+ // fragment IDs are disallowed in "system" elements.
+ //
+ } else if ("uri" == local) {
+ String name = atts.getValue ("name");
+ String value = null;
+
+ if (name == null || uri == null) {
+ error ("expecting <uri name=... uri=.../>");
+ return;
+ }
+ if (name.startsWith ("urn:publicid:")) {
+ error ("name is really a publicId!!");
+ return;
+ }
+ name = normalizeURI (name);
+ if (cat.uris == null) {
+ cat.uris = new Hashtable ();
+ if (unified)
+ cat.systemIds = cat.uris;
+ } else
+ value = (String) cat.uris.get (name);
+ if (value != null) {
+ if (!value.equals (uri))
+ warn ("ignoring <uri...> entry for " + name);
+ } else
+ cat.uris.put (name, uri);
+
+ } else if ("rewriteURI" == local) {
+ String value = null;
+
+ if (uriStartString == null || rewritePrefix == null
+ || uriStartString.length () == 0
+ || rewritePrefix.length () == 0
+ ) {
+ error ("expecting <rewriteURI "
+ + "uriStartString=... rewritePrefix=.../>");
+ return;
+ }
+ if (cat.uriRewrites == null) {
+ cat.uriRewrites = new Hashtable ();
+ if (unified)
+ cat.systemRewrites = cat.uriRewrites;
+ } else
+ value = (String) cat.uriRewrites.get (uriStartString);
+ if (value != null) {
+ if (!value.equals (rewritePrefix))
+ warn ("ignoring <rewriteURI...> entry for "
+ + uriStartString);
+ } else
+ cat.uriRewrites.put (uriStartString, rewritePrefix);
+
+ } else if ("delegateURI" == local) {
+ Object value = null;
+
+ if (uriStartString == null || catalog == null) {
+ error ("expecting <delegateURI "
+ + "uriStartString=... catalog=.../>");
+ return;
+ }
+ if (cat.uriDelegations == null) {
+ cat.uriDelegations = new Hashtable ();
+ if (unified)
+ cat.systemDelegations = cat.uriDelegations;
+ } else
+ value = cat.uriDelegations.get (uriStartString);
+ if (value != null) {
+ if (!value.equals (catalog))
+ warn ("ignoring <delegateURI...> entry for "
+ + uriStartString);
+ } else
+ cat.uriDelegations.put (uriStartString, catalog);
+
+ //
+ // NON-DELEGATING approach to modularity
+ //
+ } else if ("nextCatalog" == local) {
+ if (catalog == null) {
+ error ("expecting <nextCatalog catalog=.../>");
+ return;
+ }
+ if (cat.next == null)
+ cat.next = new Vector ();
+ cat.next.addElement (catalog);
+
+ //
+ // EXTENSIONS from appendix E
+ //
+ } else if ("doctype" == local) {
+ String name = atts.getValue ("name");
+ String value = null;
+
+ if (name == null || uri == null) {
+ error ("expecting <doctype name=... uri=.../>");
+ return;
+ }
+ name = normalizeURI (name);
+ if (cat.doctypes == null)
+ cat.doctypes = new Hashtable ();
+ else
+ value = (String) cat.doctypes.get (name);
+ if (value != null) {
+ if (!value.equals (uri))
+ warn ("ignoring <doctype...> entry for "
+ + uriStartString);
+ } else
+ cat.doctypes.put (name, uri);
+
+
+ //
+ // RESERVED ... ignore (like reserved attributes) but warn
+ //
+ } else {
+ warn ("ignoring unknown catalog element: " + local);
+ ignoreDepth++;
+ }
+ }
+
+ public void endElement (String uri, String local, String qName)
+ throws SAXException
+ {
+ if (ignoreDepth != 0)
+ ignoreDepth--;
+ else
+ bases.pop ();
+ }
+ }
+}
diff --git a/libjava/gnu/xml/util/XHTMLWriter.java b/libjava/gnu/xml/util/XHTMLWriter.java
new file mode 100644
index 0000000..c1502b7
--- /dev/null
+++ b/libjava/gnu/xml/util/XHTMLWriter.java
@@ -0,0 +1,112 @@
+/* XHTMLWriter.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.util;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+
+
+/**
+ * This extends XMLWriter to create a class which defaults to writing
+ * XHTML text, preferring the US-ASCII encoding. It adds no unique
+ * functionality, only changing the defaults slightly to simplify writing
+ * XHTML processing components by providing a bean class whose properties
+ * have more convenient defaults. An artifact of using the US-ASCII
+ * encoding is that no XML declaration is written, so that HTML tools
+ * that can't accept them will not become confused. Components can treat
+ * the output as UTF-8, ISO-8859-1, or US-ASCII without incurring any
+ * data loss.
+ *
+ * @author David Brownell
+ */
+public class XHTMLWriter extends XMLWriter
+{
+ /**
+ * Constructs this handler with System.out used to write
+ * SAX events using the US-ASCII encoding, as XHTML.
+ */
+ public XHTMLWriter ()
+ throws IOException
+ {
+ this (System.out);
+ }
+
+ /**
+ * Constructs this handler such that the specified output stream
+ * is used to write SAX events in the US-ASCII encoding, as XHTML.
+ *
+ * @param out Where US-ASCII encoding of the stream of SAX
+ * events will be sent.
+ */
+ public XHTMLWriter (OutputStream out)
+ throws IOException
+ {
+ // not all JVMs understand "ASCII" as an encoding name, so
+ // we use 8859_1 (they all seem to handle that one) and
+ // make the echo handler filter out non-ASCII characters
+ this (new OutputStreamWriter (out, "8859_1"), "US-ASCII");
+ }
+
+ /**
+ * Constructs this handler such that the specified output stream
+ * is used to write SAX events as XHTML.
+ *
+ * @param out Where the stream of SAX events will be written.
+ */
+ public XHTMLWriter (Writer out)
+ {
+ this (out, null);
+ }
+
+ /**
+ * Constructs this handler such that the specified output stream
+ * is used to write SAX events as XHTML, labeled with the specified
+ * encoding.
+ *
+ * @param out Where the stream of SAX events will be written.
+ * @param encoding If non-null, this names the encoding to be
+ * placed in the encoding declaration.
+ */
+ public XHTMLWriter (Writer out, String encoding)
+ {
+ super (out, encoding);
+ setXhtml (true);
+ }
+}
diff --git a/libjava/gnu/xml/util/XMLWriter.java b/libjava/gnu/xml/util/XMLWriter.java
new file mode 100644
index 0000000..feb9e4f
--- /dev/null
+++ b/libjava/gnu/xml/util/XMLWriter.java
@@ -0,0 +1,1927 @@
+/* XMLWriter.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.util;
+
+import java.io.BufferedWriter;
+import java.io.CharConversionException;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+import java.util.Stack;
+
+import org.xml.sax.*;
+import org.xml.sax.ext.*;
+import org.xml.sax.helpers.*;
+
+
+/**
+ * This class is a SAX handler which writes all its input as a well formed
+ * XML or XHTML document. If driven using SAX2 events, this output may
+ * include a recreated document type declaration, subject to limitations
+ * of SAX (no internal subset exposed) or DOM (the important declarations,
+ * with their documentation, are discarded).
+ *
+ * <p> By default, text is generated "as-is", but some optional modes
+ * are supported. Pretty-printing is supported, to make life easier
+ * for people reading the output. XHTML (1.0) output has can be made
+ * particularly pretty; all the built-in character entities are known.
+ * Canonical XML can also be generated, assuming the input is properly
+ * formed.
+ *
+ * <hr>
+ *
+ * <p> Some of the methods on this class are intended for applications to
+ * use directly, rather than as pure SAX2 event callbacks. Some of those
+ * methods access the JavaBeans properties (used to tweak output formats,
+ * for example canonicalization and pretty printing). Subclasses
+ * are expected to add new behaviors, not to modify current behavior, so
+ * many such methods are final.</p>
+ *
+ * <p> The <em>write*()</em> methods may be slightly simpler for some
+ * applications to use than direct callbacks. For example, they support
+ * a simple policy for encoding data items as the content of a single element.
+ *
+ * <p> To reuse an XMLWriter you must provide it with a new Writer, since
+ * this handler closes the writer it was given as part of its endDocument()
+ * handling. (XML documents have an end of input, and the way to encode
+ * that on a stream is to close it.) </p>
+ *
+ * <hr>
+ *
+ * <p> Note that any relative URIs in the source document, as found in
+ * entity and notation declarations, ought to have been fully resolved by
+ * the parser providing events to this handler. This means that the
+ * output text should only have fully resolved URIs, which may not be
+ * the desired behavior in cases where later binding is desired. </p>
+ *
+ * <p> <em>Note that due to SAX2 defaults, you may need to manually
+ * ensure that the input events are XML-conformant with respect to namespace
+ * prefixes and declarations. {@link gnu.xml.pipeline.NSFilter} is
+ * one solution to this problem, in the context of processing pipelines.</em>
+ * Something as simple as connecting this handler to a parser might not
+ * generate the correct output. Another workaround is to ensure that the
+ * <em>namespace-prefixes</em> feature is always set to true, if you're
+ * hooking this directly up to some XMLReader implementation.
+ *
+ * @see gnu.xml.pipeline.TextConsumer
+ *
+ * @author David Brownell
+ */
+public class XMLWriter
+ implements ContentHandler, LexicalHandler, DTDHandler, DeclHandler
+{
+ // text prints/escapes differently depending on context
+ // CTX_ENTITY ... entity literal value
+ // CTX_ATTRIBUTE ... attribute literal value
+ // CTX_CONTENT ... content of an element
+ // CTX_UNPARSED ... CDATA, comment, PI, names, etc
+ // CTX_NAME ... name or nmtoken, no escapes possible
+ private static final int CTX_ENTITY = 1;
+ private static final int CTX_ATTRIBUTE = 2;
+ private static final int CTX_CONTENT = 3;
+ private static final int CTX_UNPARSED = 4;
+ private static final int CTX_NAME = 5;
+
+// FIXME: names (element, attribute, PI, notation, etc) are not
+// currently written out with range checks (escapeChars).
+// In non-XHTML, some names can't be directly written; panic!
+
+ private static String sysEOL;
+
+ static {
+ try {
+ sysEOL = System.getProperty ("line.separator", "\n");
+
+ // don't use the system's EOL if it's illegal XML.
+ if (!isLineEnd (sysEOL))
+ sysEOL = "\n";
+
+ } catch (SecurityException e) {
+ sysEOL = "\n";
+ }
+ }
+
+ private static boolean isLineEnd (String eol)
+ {
+ return "\n".equals (eol)
+ || "\r".equals (eol)
+ || "\r\n".equals (eol);
+ }
+
+ private Writer out;
+ private boolean inCDATA;
+ private int elementNestLevel;
+ private String eol = sysEOL;
+
+ private short dangerMask;
+ private StringBuffer stringBuf;
+ private Locator locator;
+ private ErrorHandler errHandler;
+
+ private boolean expandingEntities = false;
+ private int entityNestLevel;
+ private boolean xhtml;
+ private boolean startedDoctype;
+ private String encoding;
+
+ private boolean canonical;
+ private boolean inDoctype;
+ private boolean inEpilogue;
+
+ // pretty printing controls
+ private boolean prettyPrinting;
+ private int column;
+ private boolean noWrap;
+ private Stack space = new Stack ();
+
+ // this is not a hard'n'fast rule -- longer lines are OK,
+ // but are to be avoided. Here, prettyprinting is more to
+ // show structure "cleanly" than to be precise about it.
+ // better to have ragged layout than one line 24Kb long.
+ private static final int lineLength = 75;
+
+
+ /**
+ * Constructs this handler with System.out used to write SAX events
+ * using the UTF-8 encoding. Avoid using this except when you know
+ * it's safe to close System.out at the end of the document.
+ */
+ public XMLWriter () throws IOException
+ { this (System.out); }
+
+ /**
+ * Constructs a handler which writes all input to the output stream
+ * in the UTF-8 encoding, and closes it when endDocument is called.
+ * (Yes it's annoying that this throws an exception -- but there's
+ * really no way around it, since it's barely possible a JDK may
+ * exist somewhere that doesn't know how to emit UTF-8.)
+ */
+ public XMLWriter (OutputStream out) throws IOException
+ {
+ this (new OutputStreamWriter (out, "UTF8"));
+ }
+
+ /**
+ * Constructs a handler which writes all input to the writer, and then
+ * closes the writer when the document ends. If an XML declaration is
+ * written onto the output, and this class can determine the name of
+ * the character encoding for this writer, that encoding name will be
+ * included in the XML declaration.
+ *
+ * <P> See the description of the constructor which takes an encoding
+ * name for imporant information about selection of encodings.
+ *
+ * @param writer XML text is written to this writer.
+ */
+ public XMLWriter (Writer writer)
+ {
+ this (writer, null);
+ }
+
+ /**
+ * Constructs a handler which writes all input to the writer, and then
+ * closes the writer when the document ends. If an XML declaration is
+ * written onto the output, this class will use the specified encoding
+ * name in that declaration. If no encoding name is specified, no
+ * encoding name will be declared unless this class can otherwise
+ * determine the name of the character encoding for this writer.
+ *
+ * <P> At this time, only the UTF-8 ("UTF8") and UTF-16 ("Unicode")
+ * output encodings are fully lossless with respect to XML data. If you
+ * use any other encoding you risk having your data be silently mangled
+ * on output, as the standard Java character encoding subsystem silently
+ * maps non-encodable characters to a question mark ("?") and will not
+ * report such errors to applications.
+ *
+ * <p> For a few other encodings the risk can be reduced. If the writer is
+ * a java.io.OutputStreamWriter, and uses either the ISO-8859-1 ("8859_1",
+ * "ISO8859_1", etc) or US-ASCII ("ASCII") encodings, content which
+ * can't be encoded in those encodings will be written safely. Where
+ * relevant, the XHTML entity names will be used; otherwise, numeric
+ * character references will be emitted.
+ *
+ * <P> However, there remain a number of cases where substituting such
+ * entity or character references is not an option. Such references are
+ * not usable within a DTD, comment, PI, or CDATA section. Neither may
+ * they be used when element, attribute, entity, or notation names have
+ * the problematic characters.
+ *
+ * @param writer XML text is written to this writer.
+ * @param encoding if non-null, and an XML declaration is written,
+ * this is the name that will be used for the character encoding.
+ */
+ public XMLWriter (Writer writer, String encoding)
+ {
+ setWriter (writer, encoding);
+ }
+
+ private void setEncoding (String encoding)
+ {
+ if (encoding == null && out instanceof OutputStreamWriter)
+ encoding = ((OutputStreamWriter)out).getEncoding ();
+
+ if (encoding != null) {
+ encoding = encoding.toUpperCase ();
+
+ // Use official encoding names where we know them,
+ // avoiding the Java-only names. When using common
+ // encodings where we can easily tell if characters
+ // are out of range, we'll escape out-of-range
+ // characters using character refs for safety.
+
+ // I _think_ these are all the main synonyms for these!
+ if ("UTF8".equals (encoding)) {
+ encoding = "UTF-8";
+ } else if ("US-ASCII".equals (encoding)
+ || "ASCII".equals (encoding)) {
+ dangerMask = (short) 0xff80;
+ encoding = "US-ASCII";
+ } else if ("ISO-8859-1".equals (encoding)
+ || "8859_1".equals (encoding)
+ || "ISO8859_1".equals (encoding)) {
+ dangerMask = (short) 0xff00;
+ encoding = "ISO-8859-1";
+ } else if ("UNICODE".equals (encoding)
+ || "UNICODE-BIG".equals (encoding)
+ || "UNICODE-LITTLE".equals (encoding)) {
+ encoding = "UTF-16";
+
+ // TODO: UTF-16BE, UTF-16LE ... no BOM; what
+ // release of JDK supports those Unicode names?
+ }
+
+ if (dangerMask != 0)
+ stringBuf = new StringBuffer ();
+ }
+
+ this.encoding = encoding;
+ }
+
+
+ /**
+ * Resets the handler to write a new text document.
+ *
+ * @param writer XML text is written to this writer.
+ * @param encoding if non-null, and an XML declaration is written,
+ * this is the name that will be used for the character encoding.
+ *
+ * @exception IllegalStateException if the current
+ * document hasn't yet ended (with {@link #endDocument})
+ */
+ final public void setWriter (Writer writer, String encoding)
+ {
+ if (out != null)
+ throw new IllegalStateException (
+ "can't change stream in mid course");
+ out = writer;
+ if (out != null)
+ setEncoding (encoding);
+ if (!(out instanceof BufferedWriter))
+ out = new BufferedWriter (out);
+ space.push ("default");
+ }
+
+ /**
+ * Assigns the line ending style to be used on output.
+ * @param eolString null to use the system default; else
+ * "\n", "\r", or "\r\n".
+ */
+ final public void setEOL (String eolString)
+ {
+ if (eolString == null)
+ eol = sysEOL;
+ else if (!isLineEnd (eolString))
+ eol = eolString;
+ else
+ throw new IllegalArgumentException (eolString);
+ }
+
+ /**
+ * Assigns the error handler to be used to present most fatal
+ * errors.
+ */
+ public void setErrorHandler (ErrorHandler handler)
+ {
+ errHandler = handler;
+ }
+
+ /**
+ * Used internally and by subclasses, this encapsulates the logic
+ * involved in reporting fatal errors. It uses locator information
+ * for good diagnostics, if available, and gives the application's
+ * ErrorHandler the opportunity to handle the error before throwing
+ * an exception.
+ */
+ protected void fatal (String message, Exception e)
+ throws SAXException
+ {
+ SAXParseException x;
+
+ if (locator == null)
+ x = new SAXParseException (message, null, null, -1, -1, e);
+ else
+ x = new SAXParseException (message, locator, e);
+ if (errHandler != null)
+ errHandler.fatalError (x);
+ throw x;
+ }
+
+
+ // JavaBeans properties
+
+ /**
+ * Controls whether the output should attempt to follow the "transitional"
+ * XHTML rules so that it meets the "HTML Compatibility Guidelines"
+ * appendix in the XHTML specification. A "transitional" Document Type
+ * Declaration (DTD) is placed near the beginning of the output document,
+ * instead of whatever DTD would otherwise have been placed there, and
+ * XHTML empty elements are printed specially. When writing text in
+ * US-ASCII or ISO-8859-1 encodings, the predefined XHTML internal
+ * entity names are used (in preference to character references) when
+ * writing content characters which can't be expressed in those encodings.
+ *
+ * <p> When this option is enabled, it is the caller's responsibility
+ * to ensure that the input is otherwise valid as XHTML. Things to
+ * be careful of in all cases, as described in the appendix referenced
+ * above, include: <ul>
+ *
+ * <li> Element and attribute names must be in lower case, both
+ * in the document and in any CSS style sheet.
+ * <li> All XML constructs must be valid as defined by the XHTML
+ * "transitional" DTD (including all familiar constructs,
+ * even deprecated ones).
+ * <li> The root element must be "html".
+ * <li> Elements that must be empty (such as <em>&lt;br&gt;</em>
+ * must have no content.
+ * <li> Use both <em>lang</em> and <em>xml:lang</em> attributes
+ * when specifying language.
+ * <li> Similarly, use both <em>id</em> and <em>name</em> attributes
+ * when defining elements that may be referred to through
+ * URI fragment identifiers ... and make sure that the
+ * value is a legal NMTOKEN, since not all such HTML 4.0
+ * identifiers are valid in XML.
+ * <li> Be careful with character encodings; make sure you provide
+ * a <em>&lt;meta http-equiv="Content-type"
+ * content="text/xml;charset=..." /&gt;</em> element in
+ * the HTML "head" element, naming the same encoding
+ * used to create this handler. Also, if that encoding
+ * is anything other than US-ASCII, make sure that if
+ * the document is given a MIME content type, it has
+ * a <em>charset=...</em> attribute with that encoding.
+ * </ul>
+ *
+ * <p> Additionally, some of the oldest browsers have additional
+ * quirks, to address with guidelines such as: <ul>
+ *
+ * <li> Processing instructions may be rendered, so avoid them.
+ * (Similarly for an XML declaration.)
+ * <li> Embedded style sheets and scripts should not contain XML
+ * markup delimiters: &amp;, &lt;, and ]]&gt; are trouble.
+ * <li> Attribute values should not have line breaks or multiple
+ * consecutive white space characters.
+ * <li> Use no more than one of the deprecated (transitional)
+ * <em>&lt;isindex&gt;</em> elements.
+ * <li> Some boolean attributes (such as <em>compact, checked,
+ * disabled, readonly, selected,</em> and more) confuse
+ * some browsers, since they only understand minimized
+ * versions which are illegal in XML.
+ * </ul>
+ *
+ * <p> Also, some characteristics of the resulting output may be
+ * a function of whether the document is later given a MIME
+ * content type of <em>text/html</em> rather than one indicating
+ * XML (<em>application/xml</em> or <em>text/xml</em>). Worse,
+ * some browsers ignore MIME content types and prefer to rely URI
+ * name suffixes -- so an "index.xml" could always be XML, never
+ * XHTML, no matter its MIME type.
+ */
+ final public void setXhtml (boolean value)
+ {
+ if (locator != null)
+ throw new IllegalStateException ("started parsing");
+ xhtml = value;
+ if (xhtml)
+ canonical = false;
+ }
+
+ /**
+ * Returns true if the output attempts to echo the input following
+ * "transitional" XHTML rules and matching the "HTML Compatibility
+ * Guidelines" so that an HTML version 3 browser can read the output
+ * as HTML; returns false (the default) othewise.
+ */
+ final public boolean isXhtml ()
+ {
+ return xhtml;
+ }
+
+ /**
+ * Controls whether the output text contains references to
+ * entities (the default), or instead contains the expanded
+ * values of those entities.
+ */
+ final public void setExpandingEntities (boolean value)
+ {
+ if (locator != null)
+ throw new IllegalStateException ("started parsing");
+ expandingEntities = value;
+ if (!expandingEntities)
+ canonical = false;
+ }
+
+ /**
+ * Returns true if the output will have no entity references;
+ * returns false (the default) otherwise.
+ */
+ final public boolean isExpandingEntities ()
+ {
+ return expandingEntities;
+ }
+
+ /**
+ * Controls pretty-printing, which by default is not enabled
+ * (and currently is most useful for XHTML output).
+ * Pretty printing enables structural indentation, sorting of attributes
+ * by name, line wrapping, and potentially other mechanisms for making
+ * output more or less readable.
+ *
+ * <p> At this writing, structural indentation and line wrapping are
+ * enabled when pretty printing is enabled and the <em>xml:space</em>
+ * attribute has the value <em>default</em> (its other legal value is
+ * <em>preserve</em>, as defined in the XML specification). The three
+ * XHTML element types which use another value are recognized by their
+ * names (namespaces are ignored).
+ *
+ * <p> Also, for the record, the "pretty" aspect of printing here
+ * is more to provide basic structure on outputs that would otherwise
+ * risk being a single long line of text. For now, expect the
+ * structure to be ragged ... unless you'd like to submit a patch
+ * to make this be more strictly formatted!
+ *
+ * @exception IllegalStateException thrown if this method is invoked
+ * after output has begun.
+ */
+ final public void setPrettyPrinting (boolean value)
+ {
+ if (locator != null)
+ throw new IllegalStateException ("started parsing");
+ prettyPrinting = value;
+ if (prettyPrinting)
+ canonical = false;
+ }
+
+ /**
+ * Returns value of flag controlling pretty printing.
+ */
+ final public boolean isPrettyPrinting ()
+ {
+ return prettyPrinting;
+ }
+
+
+ /**
+ * Sets the output style to be canonicalized. Input events must
+ * meet requirements that are slightly more stringent than the
+ * basic well-formedness ones, and include: <ul>
+ *
+ * <li> Namespace prefixes must not have been changed from those
+ * in the original document. (This may only be ensured by setting
+ * the SAX2 XMLReader <em>namespace-prefixes</em> feature flag;
+ * by default, it is cleared.)
+ *
+ * <li> Redundant namespace declaration attributes have been
+ * removed. (If an ancestor element defines a namespace prefix
+ * and that declaration hasn't been overriden, an element must
+ * not redeclare it.)
+ *
+ * <li> If comments are not to be included in the canonical output,
+ * they must first be removed from the input event stream; this
+ * <em>Canonical XML with comments</em> by default.
+ *
+ * <li> If the input character encoding was not UCS-based, the
+ * character data must have been normalized using Unicode
+ * Normalization Form C. (UTF-8 and UTF-16 are UCS-based.)
+ *
+ * <li> Attribute values must have been normalized, as is done
+ * by any conformant XML processor which processes all external
+ * parameter entities.
+ *
+ * <li> Similarly, attribute value defaulting has been performed.
+ *
+ * </ul>
+ *
+ * <p> Note that fragments of XML documents, as specified by an XPath
+ * node set, may be canonicalized. In such cases, elements may need
+ * some fixup (for <em>xml:*</em> attributes and application-specific
+ * context).
+ *
+ * @exception IllegalArgumentException if the output encoding
+ * is anything other than UTF-8.
+ */
+ final public void setCanonical (boolean value)
+ {
+ if (value && !"UTF-8".equals (encoding))
+ throw new IllegalArgumentException ("encoding != UTF-8");
+ canonical = value;
+ if (canonical) {
+ prettyPrinting = xhtml = false;
+ expandingEntities = true;
+ eol = "\n";
+ }
+ }
+
+
+ /**
+ * Returns value of flag controlling canonical output.
+ */
+ final public boolean isCanonical ()
+ {
+ return canonical;
+ }
+
+
+ /**
+ * Flushes the output stream. When this handler is used in long lived
+ * pipelines, it can be important to flush buffered state, for example
+ * so that it can reach the disk as part of a state checkpoint.
+ */
+ final public void flush ()
+ throws IOException
+ {
+ if (out != null)
+ out.flush ();
+ }
+
+
+ // convenience routines
+
+// FIXME: probably want a subclass that holds a lot of these...
+// and maybe more!
+
+ /**
+ * Writes the string as if characters() had been called on the contents
+ * of the string. This is particularly useful when applications act as
+ * producers and write data directly to event consumers.
+ */
+ final public void write (String data)
+ throws SAXException
+ {
+ char buf [] = data.toCharArray ();
+ characters (buf, 0, buf.length);
+ }
+
+
+ /**
+ * Writes an element that has content consisting of a single string.
+ * @see #writeEmptyElement
+ * @see #startElement
+ */
+ public void writeElement (
+ String uri,
+ String localName,
+ String qName,
+ Attributes atts,
+ String content
+ ) throws SAXException
+ {
+ if (content == null || content.length () == 0) {
+ writeEmptyElement (uri, localName, qName, atts);
+ return;
+ }
+ startElement (uri, localName, qName, atts);
+ char chars [] = content.toCharArray ();
+ characters (chars, 0, chars.length);
+ endElement (uri, localName, qName);
+ }
+
+
+ /**
+ * Writes an element that has content consisting of a single integer,
+ * encoded as a decimal string.
+ * @see #writeEmptyElement
+ * @see #startElement
+ */
+ public void writeElement (
+ String uri,
+ String localName,
+ String qName,
+ Attributes atts,
+ int content
+ ) throws SAXException
+ {
+ writeElement (uri, localName, qName, atts, Integer.toString (content));
+ }
+
+
+ // SAX1 ContentHandler
+ /** <b>SAX1</b>: provides parser status information */
+ final public void setDocumentLocator (Locator l)
+ {
+ locator = l;
+ }
+
+
+ // URL for dtd that validates against all normal HTML constructs
+ private static final String xhtmlFullDTD =
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd";
+
+
+ /**
+ * <b>SAX1</b>: indicates the beginning of a document parse.
+ * If you're writing (well formed) fragments of XML, neither
+ * this nor endDocument should be called.
+ */
+ // NOT final
+ public void startDocument ()
+ throws SAXException
+ {
+ try {
+ if (out == null)
+ throw new IllegalStateException (
+ "null Writer given to XMLWriter");
+
+ // Not all parsers provide the locator we want; this also
+ // flags whether events are being sent to this object yet.
+ // We could only have this one call if we only printed whole
+ // documents ... but we also print fragments, so most of the
+ // callbacks here replicate this test.
+
+ if (locator == null)
+ locator = new LocatorImpl ();
+
+ // Unless the data is in US-ASCII or we're canonicalizing, write
+ // the XML declaration if we know the encoding. US-ASCII won't
+ // normally get mangled by web server confusion about the
+ // character encodings used. Plus, it's an easy way to
+ // ensure we can write ASCII that's unlikely to confuse
+ // elderly HTML parsers.
+
+ if (!canonical
+ && dangerMask != (short) 0xff80
+ && encoding != null) {
+ rawWrite ("<?xml version='1.0'");
+ rawWrite (" encoding='" + encoding + "'");
+ rawWrite ("?>");
+ newline ();
+ }
+
+ if (xhtml) {
+
+ rawWrite ("<!DOCTYPE html PUBLIC");
+ newline ();
+ rawWrite (" '-//W3C//DTD XHTML 1.0 Transitional//EN'");
+ newline ();
+ rawWrite (" '");
+ // NOTE: URL (above) matches the REC
+ rawWrite (xhtmlFullDTD);
+ rawWrite ("'>");
+ newline ();
+ newline ();
+
+ // fake the rest of the handler into ignoring
+ // everything until the root element, so any
+ // XHTML DTD comments, PIs, etc are ignored
+ startedDoctype = true;
+ }
+
+ entityNestLevel = 0;
+
+ } catch (IOException e) {
+ fatal ("can't write", e);
+ }
+ }
+
+ /**
+ * <b>SAX1</b>: indicates the completion of a parse.
+ * Note that all complete SAX event streams make this call, even
+ * if an error is reported during a parse.
+ */
+ // NOT final
+ public void endDocument ()
+ throws SAXException
+ {
+ try {
+ if (!canonical) {
+ newline ();
+ newline ();
+ }
+ out.close ();
+ out = null;
+ locator = null;
+ } catch (IOException e) {
+ fatal ("can't write", e);
+ }
+ }
+
+ // XHTML elements declared as EMPTY print differently
+ final private static boolean isEmptyElementTag (String tag)
+ {
+ switch (tag.charAt (0)) {
+ case 'a': return "area".equals (tag);
+ case 'b': return "base".equals (tag)
+ || "basefont".equals (tag)
+ || "br".equals (tag);
+ case 'c': return "col".equals (tag);
+ case 'f': return "frame".equals (tag);
+ case 'h': return "hr".equals (tag);
+ case 'i': return "img".equals (tag)
+ || "input".equals (tag)
+ || "isindex".equals (tag);
+ case 'l': return "link".equals (tag);
+ case 'm': return "meta".equals (tag);
+ case 'p': return "param".equals (tag);
+ }
+ return false;
+ }
+
+ private static boolean indentBefore (String tag)
+ {
+ // basically indent before block content
+ // and within structure like tables, lists
+ switch (tag.charAt (0)) {
+ case 'a': return "applet".equals (tag);
+ case 'b': return "body".equals (tag)
+ || "blockquote".equals (tag);
+ case 'c': return "center".equals (tag);
+ case 'f': return "frame".equals (tag)
+ || "frameset".equals (tag);
+ case 'h': return "head".equals (tag);
+ case 'm': return "meta".equals (tag);
+ case 'o': return "object".equals (tag);
+ case 'p': return "param".equals (tag)
+ || "pre".equals (tag);
+ case 's': return "style".equals (tag);
+ case 't': return "title".equals (tag)
+ || "td".equals (tag)
+ || "th".equals (tag);
+ }
+ // ... but not inline elements like "em", "b", "font"
+ return false;
+ }
+
+ private static boolean spaceBefore (String tag)
+ {
+ // blank line AND INDENT before certain structural content
+ switch (tag.charAt (0)) {
+ case 'h': return "h1".equals (tag)
+ || "h2".equals (tag)
+ || "h3".equals (tag)
+ || "h4".equals (tag)
+ || "h5".equals (tag)
+ || "h6".equals (tag)
+ || "hr".equals (tag);
+ case 'l': return "li".equals (tag);
+ case 'o': return "ol".equals (tag);
+ case 'p': return "p".equals (tag);
+ case 't': return "table".equals (tag)
+ || "tr".equals (tag);
+ case 'u': return "ul".equals (tag);
+ }
+ return false;
+ }
+
+ // XHTML DTDs say these three have xml:space="preserve"
+ private static boolean spacePreserve (String tag)
+ {
+ return "pre".equals (tag)
+ || "style".equals (tag)
+ || "script".equals (tag);
+ }
+
+ /**
+ * <b>SAX2</b>: ignored.
+ */
+ final public void startPrefixMapping (String prefix, String uri)
+ {}
+
+ /**
+ * <b>SAX2</b>: ignored.
+ */
+ final public void endPrefixMapping (String prefix)
+ {}
+
+ private void writeStartTag (
+ String name,
+ Attributes atts,
+ boolean isEmpty
+ ) throws SAXException, IOException
+ {
+ rawWrite ('<');
+ rawWrite (name);
+
+ // write out attributes ... sorting is particularly useful
+ // with output that's been heavily defaulted.
+ if (atts != null && atts.getLength () != 0) {
+
+ // Set up to write, with optional sorting
+ int indices [] = new int [atts.getLength ()];
+
+ for (int i= 0; i < indices.length; i++)
+ indices [i] = i;
+
+ // optionally sort
+
+// FIXME: canon xml demands xmlns nodes go first,
+// and sorting by URI first (empty first) then localname
+// it should maybe use a different sort
+
+ if (canonical || prettyPrinting) {
+
+ // insertion sort by attribute name
+ for (int i = 1; i < indices.length; i++) {
+ int n = indices [i], j;
+ String s = atts.getQName (n);
+
+ for (j = i - 1; j >= 0; j--) {
+ if (s.compareTo (atts.getQName (indices [j]))
+ >= 0)
+ break;
+ indices [j + 1] = indices [j];
+ }
+ indices [j + 1] = n;
+ }
+ }
+
+ // write, sorted or no
+ for (int i= 0; i < indices.length; i++) {
+ String s = atts.getQName (indices [i]);
+
+ if (s == null || "".equals (s))
+ throw new IllegalArgumentException ("no XML name");
+ rawWrite (" ");
+ rawWrite (s);
+ rawWrite ("=");
+ writeQuotedValue (atts.getValue (indices [i]),
+ CTX_ATTRIBUTE);
+ }
+ }
+ if (isEmpty)
+ rawWrite (" /");
+ rawWrite ('>');
+ }
+
+ /**
+ * <b>SAX2</b>: indicates the start of an element.
+ * When XHTML is in use, avoid attribute values with
+ * line breaks or multiple whitespace characters, since
+ * not all user agents handle them correctly.
+ */
+ final public void startElement (
+ String uri,
+ String localName,
+ String qName,
+ Attributes atts
+ ) throws SAXException
+ {
+ startedDoctype = false;
+
+ if (locator == null)
+ locator = new LocatorImpl ();
+
+ if (qName == null || "".equals (qName))
+ throw new IllegalArgumentException ("no XML name");
+
+ try {
+ if (entityNestLevel != 0)
+ return;
+ if (prettyPrinting) {
+ String whitespace = null;
+
+ if (xhtml && spacePreserve (qName))
+ whitespace = "preserve";
+ else if (atts != null)
+ whitespace = atts.getValue ("xml:space");
+ if (whitespace == null)
+ whitespace = (String) space.peek ();
+ space.push (whitespace);
+
+ if ("default".equals (whitespace)) {
+ if (xhtml) {
+ if (spaceBefore (qName)) {
+ newline ();
+ doIndent ();
+ } else if (indentBefore (qName))
+ doIndent ();
+ // else it's inlined, modulo line length
+ // FIXME: incrementing element nest level
+ // for inlined elements causes ugliness
+ } else
+ doIndent ();
+ }
+ }
+ elementNestLevel++;
+ writeStartTag (qName, atts, xhtml && isEmptyElementTag (qName));
+
+ if (xhtml) {
+// FIXME: if this is an XHTML "pre" element, turn
+// off automatic wrapping.
+ }
+
+ } catch (IOException e) {
+ fatal ("can't write", e);
+ }
+ }
+
+ /**
+ * Writes an empty element.
+ * @see #startElement
+ */
+ public void writeEmptyElement (
+ String uri,
+ String localName,
+ String qName,
+ Attributes atts
+ ) throws SAXException
+ {
+ if (canonical) {
+ startElement (uri, localName, qName, atts);
+ endElement (uri, localName, qName);
+ } else {
+ try {
+ writeStartTag (qName, atts, true);
+ } catch (IOException e) {
+ fatal ("can't write", e);
+ }
+ }
+ }
+
+
+ /** <b>SAX2</b>: indicates the end of an element */
+ final public void endElement (String uri, String localName, String qName)
+ throws SAXException
+ {
+ if (qName == null || "".equals (qName))
+ throw new IllegalArgumentException ("no XML name");
+
+ try {
+ elementNestLevel--;
+ if (entityNestLevel != 0)
+ return;
+ if (xhtml && isEmptyElementTag (qName))
+ return;
+ rawWrite ("</");
+ rawWrite (qName);
+ rawWrite ('>');
+
+ if (prettyPrinting) {
+ if (!space.empty ())
+ space.pop ();
+ else
+ fatal ("stack discipline", null);
+ }
+ if (elementNestLevel == 0)
+ inEpilogue = true;
+
+ } catch (IOException e) {
+ fatal ("can't write", e);
+ }
+ }
+
+ /** <b>SAX1</b>: reports content characters */
+ final public void characters (char ch [], int start, int length)
+ throws SAXException
+ {
+ if (locator == null)
+ locator = new LocatorImpl ();
+
+ try {
+ if (entityNestLevel != 0)
+ return;
+ if (inCDATA) {
+ escapeChars (ch, start, length, CTX_UNPARSED);
+ } else {
+ escapeChars (ch, start, length, CTX_CONTENT);
+ }
+ } catch (IOException e) {
+ fatal ("can't write", e);
+ }
+ }
+
+ /** <b>SAX1</b>: reports ignorable whitespace */
+ final public void ignorableWhitespace (char ch [], int start, int length)
+ throws SAXException
+ {
+ if (locator == null)
+ locator = new LocatorImpl ();
+
+ try {
+ if (entityNestLevel != 0)
+ return;
+ // don't forget to map NL to CRLF, CR, etc
+ escapeChars (ch, start, length, CTX_CONTENT);
+ } catch (IOException e) {
+ fatal ("can't write", e);
+ }
+ }
+
+ /**
+ * <b>SAX1</b>: reports a PI.
+ * This doesn't check for illegal target names, such as "xml" or "XML",
+ * or namespace-incompatible ones like "big:dog"; the caller is
+ * responsible for ensuring those names are legal.
+ */
+ final public void processingInstruction (String target, String data)
+ throws SAXException
+ {
+ if (locator == null)
+ locator = new LocatorImpl ();
+
+ // don't print internal subset for XHTML
+ if (xhtml && startedDoctype)
+ return;
+
+ // ancient HTML browsers might render these ... their loss.
+ // to prevent: "if (xhtml) return;".
+
+ try {
+ if (entityNestLevel != 0)
+ return;
+ if (canonical && inEpilogue)
+ newline ();
+ rawWrite ("<?");
+ rawWrite (target);
+ rawWrite (' ');
+ escapeChars (data.toCharArray (), -1, -1, CTX_UNPARSED);
+ rawWrite ("?>");
+ if (elementNestLevel == 0 && !(canonical && inEpilogue))
+ newline ();
+ } catch (IOException e) {
+ fatal ("can't write", e);
+ }
+ }
+
+ /** <b>SAX1</b>: indicates a non-expanded entity reference */
+ public void skippedEntity (String name)
+ throws SAXException
+ {
+ try {
+ rawWrite ("&");
+ rawWrite (name);
+ rawWrite (";");
+ } catch (IOException e) {
+ fatal ("can't write", e);
+ }
+ }
+
+ // SAX2 LexicalHandler
+
+ /** <b>SAX2</b>: called before parsing CDATA characters */
+ final public void startCDATA ()
+ throws SAXException
+ {
+ if (locator == null)
+ locator = new LocatorImpl ();
+
+ if (canonical)
+ return;
+
+ try {
+ inCDATA = true;
+ if (entityNestLevel == 0)
+ rawWrite ("<![CDATA[");
+ } catch (IOException e) {
+ fatal ("can't write", e);
+ }
+ }
+
+ /** <b>SAX2</b>: called after parsing CDATA characters */
+ final public void endCDATA ()
+ throws SAXException
+ {
+ if (canonical)
+ return;
+
+ try {
+ inCDATA = false;
+ if (entityNestLevel == 0)
+ rawWrite ("]]>");
+ } catch (IOException e) {
+ fatal ("can't write", e);
+ }
+ }
+
+ /**
+ * <b>SAX2</b>: called when the doctype is partially parsed
+ * Note that this, like other doctype related calls, is ignored
+ * when XHTML is in use.
+ */
+ final public void startDTD (String name, String publicId, String systemId)
+ throws SAXException
+ {
+ if (locator == null)
+ locator = new LocatorImpl ();
+ if (xhtml)
+ return;
+ try {
+ inDoctype = startedDoctype = true;
+ if (canonical)
+ return;
+ rawWrite ("<!DOCTYPE ");
+ rawWrite (name);
+ rawWrite (' ');
+
+ if (!expandingEntities) {
+ if (publicId != null)
+ rawWrite ("PUBLIC '" + publicId + "' '" + systemId + "' ");
+ else if (systemId != null)
+ rawWrite ("SYSTEM '" + systemId + "' ");
+ }
+
+ rawWrite ('[');
+ newline ();
+ } catch (IOException e) {
+ fatal ("can't write", e);
+ }
+ }
+
+ /** <b>SAX2</b>: called after the doctype is parsed */
+ final public void endDTD ()
+ throws SAXException
+ {
+ inDoctype = false;
+ if (canonical || xhtml)
+ return;
+ try {
+ rawWrite ("]>");
+ newline ();
+ } catch (IOException e) {
+ fatal ("can't write", e);
+ }
+ }
+
+ /**
+ * <b>SAX2</b>: called before parsing a general entity in content
+ */
+ final public void startEntity (String name)
+ throws SAXException
+ {
+ try {
+ boolean writeEOL = true;
+
+ // Predefined XHTML entities (for characters) will get
+ // mapped back later.
+ if (xhtml || expandingEntities)
+ return;
+
+ entityNestLevel++;
+ if (name.equals ("[dtd]"))
+ return;
+ if (entityNestLevel != 1)
+ return;
+ if (!name.startsWith ("%")) {
+ writeEOL = false;
+ rawWrite ('&');
+ }
+ rawWrite (name);
+ rawWrite (';');
+ if (writeEOL)
+ newline ();
+ } catch (IOException e) {
+ fatal ("can't write", e);
+ }
+ }
+
+ /**
+ * <b>SAX2</b>: called after parsing a general entity in content
+ */
+ final public void endEntity (String name)
+ throws SAXException
+ {
+ if (xhtml || expandingEntities)
+ return;
+ entityNestLevel--;
+ }
+
+ /**
+ * <b>SAX2</b>: called when comments are parsed.
+ * When XHTML is used, the old HTML tradition of using comments
+ * to for inline CSS, or for JavaScript code is discouraged.
+ * This is because XML processors are encouraged to discard, on
+ * the grounds that comments are for users (and perhaps text
+ * editors) not programs. Instead, use external scripts
+ */
+ final public void comment (char ch [], int start, int length)
+ throws SAXException
+ {
+ if (locator == null)
+ locator = new LocatorImpl ();
+
+ // don't print internal subset for XHTML
+ if (xhtml && startedDoctype)
+ return;
+ // don't print comment in doctype for canon xml
+ if (canonical && inDoctype)
+ return;
+
+ try {
+ boolean indent;
+
+ if (prettyPrinting && space.empty ())
+ fatal ("stack discipline", null);
+ indent = prettyPrinting && "default".equals (space.peek ());
+ if (entityNestLevel != 0)
+ return;
+ if (indent)
+ doIndent ();
+ if (canonical && inEpilogue)
+ newline ();
+ rawWrite ("<!--");
+ escapeChars (ch, start, length, CTX_UNPARSED);
+ rawWrite ("-->");
+ if (indent)
+ doIndent ();
+ if (elementNestLevel == 0 && !(canonical && inEpilogue))
+ newline ();
+ } catch (IOException e) {
+ fatal ("can't write", e);
+ }
+ }
+
+ // SAX1 DTDHandler
+
+ /** <b>SAX1</b>: called on notation declarations */
+ final public void notationDecl (String name,
+ String publicId, String systemId)
+ throws SAXException
+ {
+ if (xhtml)
+ return;
+ try {
+ // At this time, only SAX2 callbacks start these.
+ if (!startedDoctype)
+ return;
+
+ if (entityNestLevel != 0)
+ return;
+ rawWrite ("<!NOTATION " + name + " ");
+ if (publicId != null)
+ rawWrite ("PUBLIC \"" + publicId + '"');
+ else
+ rawWrite ("SYSTEM ");
+ if (systemId != null)
+ rawWrite ('"' + systemId + '"');
+ rawWrite (">");
+ newline ();
+ } catch (IOException e) {
+ fatal ("can't write", e);
+ }
+ }
+
+ /** <b>SAX1</b>: called on unparsed entity declarations */
+ final public void unparsedEntityDecl (String name,
+ String publicId, String systemId,
+ String notationName)
+ throws SAXException
+ {
+ if (xhtml)
+ return;
+ try {
+ // At this time, only SAX2 callbacks start these.
+ if (!startedDoctype) {
+ // FIXME: write to temporary buffer, and make the start
+ // of the root element write these declarations.
+ return;
+ }
+
+ if (entityNestLevel != 0)
+ return;
+ rawWrite ("<!ENTITY " + name + " ");
+ if (publicId != null)
+ rawWrite ("PUBLIC \"" + publicId + '"');
+ else
+ rawWrite ("SYSTEM ");
+ rawWrite ('"' + systemId + '"');
+ rawWrite (" NDATA " + notationName + ">");
+ newline ();
+ } catch (IOException e) {
+ fatal ("can't write", e);
+ }
+ }
+
+ // SAX2 DeclHandler
+
+ /** <b>SAX2</b>: called on attribute declarations */
+ final public void attributeDecl (String eName, String aName,
+ String type, String mode, String value)
+ throws SAXException
+ {
+ if (xhtml)
+ return;
+ try {
+ // At this time, only SAX2 callbacks start these.
+ if (!startedDoctype)
+ return;
+ if (entityNestLevel != 0)
+ return;
+ rawWrite ("<!ATTLIST " + eName + ' ' + aName + ' ');
+ rawWrite (type);
+ rawWrite (' ');
+ if (mode != null)
+ rawWrite (mode + ' ');
+ if (value != null)
+ writeQuotedValue (value, CTX_ATTRIBUTE);
+ rawWrite ('>');
+ newline ();
+ } catch (IOException e) {
+ fatal ("can't write", e);
+ }
+ }
+
+ /** <b>SAX2</b>: called on element declarations */
+ final public void elementDecl (String name, String model)
+ throws SAXException
+ {
+ if (xhtml)
+ return;
+ try {
+ // At this time, only SAX2 callbacks start these.
+ if (!startedDoctype)
+ return;
+ if (entityNestLevel != 0)
+ return;
+ rawWrite ("<!ELEMENT " + name + ' ' + model + '>');
+ newline ();
+ } catch (IOException e) {
+ fatal ("can't write", e);
+ }
+ }
+
+ /** <b>SAX2</b>: called on external entity declarations */
+ final public void externalEntityDecl (
+ String name,
+ String publicId,
+ String systemId)
+ throws SAXException
+ {
+ if (xhtml)
+ return;
+ try {
+ // At this time, only SAX2 callbacks start these.
+ if (!startedDoctype)
+ return;
+ if (entityNestLevel != 0)
+ return;
+ rawWrite ("<!ENTITY ");
+ if (name.startsWith ("%")) {
+ rawWrite ("% ");
+ rawWrite (name.substring (1));
+ } else
+ rawWrite (name);
+ if (publicId != null)
+ rawWrite (" PUBLIC \"" + publicId + '"');
+ else
+ rawWrite (" SYSTEM ");
+ rawWrite ('"' + systemId + "\">");
+ newline ();
+ } catch (IOException e) {
+ fatal ("can't write", e);
+ }
+ }
+
+ /** <b>SAX2</b>: called on internal entity declarations */
+ final public void internalEntityDecl (String name, String value)
+ throws SAXException
+ {
+ if (xhtml)
+ return;
+ try {
+ // At this time, only SAX2 callbacks start these.
+ if (!startedDoctype)
+ return;
+ if (entityNestLevel != 0)
+ return;
+ rawWrite ("<!ENTITY ");
+ if (name.startsWith ("%")) {
+ rawWrite ("% ");
+ rawWrite (name.substring (1));
+ } else
+ rawWrite (name);
+ rawWrite (' ');
+ writeQuotedValue (value, CTX_ENTITY);
+ rawWrite ('>');
+ newline ();
+ } catch (IOException e) {
+ fatal ("can't write", e);
+ }
+ }
+
+ private void writeQuotedValue (String value, int code)
+ throws SAXException, IOException
+ {
+ char buf [] = value.toCharArray ();
+ int off = 0, len = buf.length;
+
+ // we can't add line breaks to attribute/entity/... values
+ noWrap = true;
+ rawWrite ('"');
+ escapeChars (buf, off, len, code);
+ rawWrite ('"');
+ noWrap = false;
+ }
+
+ // From "HTMLlat1x.ent" ... names of entities for ISO-8859-1
+ // (Latin/1) characters, all codes: 160-255 (0xA0-0xFF).
+ // Codes 128-159 have no assigned values.
+ private static final String HTMLlat1x [] = {
+ // 160
+ "nbsp", "iexcl", "cent", "pound", "curren",
+ "yen", "brvbar", "sect", "uml", "copy",
+
+ // 170
+ "ordf", "laquo", "not", "shy", "reg",
+ "macr", "deg", "plusmn", "sup2", "sup3",
+
+ // 180
+ "acute", "micro", "para", "middot", "cedil",
+ "sup1", "ordm", "raquo", "frac14", "frac12",
+
+ // 190
+ "frac34", "iquest", "Agrave", "Aacute", "Acirc",
+ "Atilde", "Auml", "Aring", "AElig", "Ccedil",
+
+ // 200
+ "Egrave", "Eacute", "Ecirc", "Euml", "Igrave",
+ "Iacute", "Icirc", "Iuml", "ETH", "Ntilde",
+
+ // 210
+ "Ograve", "Oacute", "Ocirc", "Otilde", "Ouml",
+ "times", "Oslash", "Ugrave", "Uacute", "Ucirc",
+
+ // 220
+ "Uuml", "Yacute", "THORN", "szlig", "agrave",
+ "aacute", "acirc", "atilde", "auml", "aring",
+
+ // 230
+ "aelig", "ccedil", "egrave", "eacute", "ecirc",
+ "euml", "igrave", "iacute", "icirc", "iuml",
+
+ // 240
+ "eth", "ntilde", "ograve", "oacute", "ocirc",
+ "otilde", "ouml", "divide", "oslash", "ugrave",
+
+ // 250
+ "uacute", "ucirc", "uuml", "yacute", "thorn",
+ "yuml"
+ };
+
+ // From "HTMLsymbolx.ent" ... some of the symbols that
+ // we can conveniently handle. Entities for the Greek.
+ // alphabet (upper and lower cases) are compact.
+ private static final String HTMLsymbolx_GR [] = {
+ // 913
+ "Alpha", "Beta", "Gamma", "Delta", "Epsilon",
+ "Zeta", "Eta", "Theta", "Iota", "Kappa",
+
+ // 923
+ "Lambda", "Mu", "Nu", "Xi", "Omicron",
+ "Pi", "Rho", null, "Sigma", "Tau",
+
+ // 933
+ "Upsilon", "Phi", "Chi", "Psi", "Omega"
+ };
+
+ private static final String HTMLsymbolx_gr [] = {
+ // 945
+ "alpha", "beta", "gamma", "delta", "epsilon",
+ "zeta", "eta", "theta", "iota", "kappa",
+
+ // 955
+ "lambda", "mu", "nu", "xi", "omicron",
+ "pi", "rho", "sigmaf", "sigma", "tau",
+
+ // 965
+ "upsilon", "phi", "chi", "psi", "omega"
+ };
+
+
+ // General routine to write text and substitute predefined
+ // entities (XML, and a special case for XHTML) as needed.
+ private void escapeChars (char buf [], int off, int len, int code)
+ throws SAXException, IOException
+ {
+ int first = 0;
+
+ if (off < 0) {
+ off = 0;
+ len = buf.length;
+ }
+ for (int i = 0; i < len; i++) {
+ String esc;
+ char c = buf [off + i];
+
+ switch (c) {
+ // Note that CTX_ATTRIBUTE isn't explicitly tested here;
+ // all syntax delimiters are escaped in CTX_ATTRIBUTE,
+ // otherwise it's similar to CTX_CONTENT
+
+ // ampersand flags entity references; entity replacement
+ // text has unexpanded references, other text doesn't.
+ case '&':
+ if (code == CTX_ENTITY || code == CTX_UNPARSED)
+ continue;
+ esc = "amp";
+ break;
+
+ // attributes and text may NOT have literal '<', but
+ // entities may have markup constructs
+ case '<':
+ if (code == CTX_ENTITY || code == CTX_UNPARSED)
+ continue;
+ esc = "lt";
+ break;
+
+ // as above re markup constructs; but otherwise
+ // except when canonicalizing, this is for consistency
+ case '>':
+ if (code == CTX_ENTITY || code == CTX_UNPARSED)
+ continue;
+ esc = "gt";
+ break;
+ case '\'':
+ if (code == CTX_CONTENT || code == CTX_UNPARSED)
+ continue;
+ if (canonical)
+ continue;
+ esc = "apos";
+ break;
+
+ // needed when printing quoted attribute/entity values
+ case '"':
+ if (code == CTX_CONTENT || code == CTX_UNPARSED)
+ continue;
+ esc = "quot";
+ break;
+
+ // make line ends work per host OS convention
+ case '\n':
+ esc = eol;
+ break;
+
+ //
+ // No other characters NEED special treatment ... except
+ // for encoding-specific issues, like whether the character
+ // can really be represented in that encoding.
+ //
+ default:
+ //
+ // There are characters we can never write safely; getting
+ // them is an error.
+ //
+ // (a) They're never legal in XML ... detected by range
+ // checks, and (eventually) by remerging surrogate
+ // pairs on output. (Easy error for apps to prevent.)
+ //
+ // (b) This encoding can't represent them, and we
+ // can't make reference substitution (e.g. inside
+ // CDATA sections, names, PI data, etc). (Hard for
+ // apps to prevent, except by using UTF-8 or UTF-16
+ // as their output encoding.)
+ //
+ // We know a very little bit about what characters
+ // the US-ASCII and ISO-8859-1 encodings support. For
+ // other encodings we can't detect the second type of
+ // error at all. (Never an issue for UTF-8 or UTF-16.)
+ //
+
+// FIXME: CR in CDATA is an error; in text, turn to a char ref
+
+// FIXME: CR/LF/TAB in attributes should become char refs
+
+ if ((c > 0xfffd)
+ || ((c < 0x0020) && !((c == 0x0009)
+ || (c == 0x000A) || (c == 0x000D)))
+ || (((c & dangerMask) != 0)
+ && (code == CTX_UNPARSED))) {
+
+ // if case (b) in CDATA, we might end the section,
+ // write a reference, then restart ... possible
+ // in one DOM L3 draft.
+
+ throw new CharConversionException (
+ "Illegal or non-writable character: U+"
+ + Integer.toHexString (c));
+ }
+
+ //
+ // If the output encoding represents the character
+ // directly, let it do so! Else we'll escape it.
+ //
+ if ((c & dangerMask) == 0)
+ continue;
+ esc = null;
+
+ // Avoid numeric refs where symbolic ones exist, as
+ // symbolic ones make more sense to humans reading!
+ if (xhtml) {
+ // all the HTMLlat1x.ent entities
+ // (all the "ISO-8859-1" characters)
+ if (c >= 160 && c <= 255)
+ esc = HTMLlat1x [c - 160];
+
+ // not quite half the HTMLsymbolx.ent entities
+ else if (c >= 913 && c <= 937)
+ esc = HTMLsymbolx_GR [c - 913];
+ else if (c >= 945 && c <= 969)
+ esc = HTMLsymbolx_gr [c - 945];
+
+ else switch (c) {
+ // all of the HTMLspecialx.ent entities
+ case 338: esc = "OElig"; break;
+ case 339: esc = "oelig"; break;
+ case 352: esc = "Scaron"; break;
+ case 353: esc = "scaron"; break;
+ case 376: esc = "Yuml"; break;
+ case 710: esc = "circ"; break;
+ case 732: esc = "tilde"; break;
+ case 8194: esc = "ensp"; break;
+ case 8195: esc = "emsp"; break;
+ case 8201: esc = "thinsp"; break;
+ case 8204: esc = "zwnj"; break;
+ case 8205: esc = "zwj"; break;
+ case 8206: esc = "lrm"; break;
+ case 8207: esc = "rlm"; break;
+ case 8211: esc = "ndash"; break;
+ case 8212: esc = "mdash"; break;
+ case 8216: esc = "lsquo"; break;
+ case 8217: esc = "rsquo"; break;
+ case 8218: esc = "sbquo"; break;
+ case 8220: esc = "ldquo"; break;
+ case 8221: esc = "rdquo"; break;
+ case 8222: esc = "bdquo"; break;
+ case 8224: esc = "dagger"; break;
+ case 8225: esc = "Dagger"; break;
+ case 8240: esc = "permil"; break;
+ case 8249: esc = "lsaquo"; break;
+ case 8250: esc = "rsaquo"; break;
+ case 8364: esc = "euro"; break;
+
+ // the other HTMLsymbox.ent entities
+ case 402: esc = "fnof"; break;
+ case 977: esc = "thetasym"; break;
+ case 978: esc = "upsih"; break;
+ case 982: esc = "piv"; break;
+ case 8226: esc = "bull"; break;
+ case 8230: esc = "hellip"; break;
+ case 8242: esc = "prime"; break;
+ case 8243: esc = "Prime"; break;
+ case 8254: esc = "oline"; break;
+ case 8260: esc = "frasl"; break;
+ case 8472: esc = "weierp"; break;
+ case 8465: esc = "image"; break;
+ case 8476: esc = "real"; break;
+ case 8482: esc = "trade"; break;
+ case 8501: esc = "alefsym"; break;
+ case 8592: esc = "larr"; break;
+ case 8593: esc = "uarr"; break;
+ case 8594: esc = "rarr"; break;
+ case 8595: esc = "darr"; break;
+ case 8596: esc = "harr"; break;
+ case 8629: esc = "crarr"; break;
+ case 8656: esc = "lArr"; break;
+ case 8657: esc = "uArr"; break;
+ case 8658: esc = "rArr"; break;
+ case 8659: esc = "dArr"; break;
+ case 8660: esc = "hArr"; break;
+ case 8704: esc = "forall"; break;
+ case 8706: esc = "part"; break;
+ case 8707: esc = "exist"; break;
+ case 8709: esc = "empty"; break;
+ case 8711: esc = "nabla"; break;
+ case 8712: esc = "isin"; break;
+ case 8713: esc = "notin"; break;
+ case 8715: esc = "ni"; break;
+ case 8719: esc = "prod"; break;
+ case 8721: esc = "sum"; break;
+ case 8722: esc = "minus"; break;
+ case 8727: esc = "lowast"; break;
+ case 8730: esc = "radic"; break;
+ case 8733: esc = "prop"; break;
+ case 8734: esc = "infin"; break;
+ case 8736: esc = "ang"; break;
+ case 8743: esc = "and"; break;
+ case 8744: esc = "or"; break;
+ case 8745: esc = "cap"; break;
+ case 8746: esc = "cup"; break;
+ case 8747: esc = "int"; break;
+ case 8756: esc = "there4"; break;
+ case 8764: esc = "sim"; break;
+ case 8773: esc = "cong"; break;
+ case 8776: esc = "asymp"; break;
+ case 8800: esc = "ne"; break;
+ case 8801: esc = "equiv"; break;
+ case 8804: esc = "le"; break;
+ case 8805: esc = "ge"; break;
+ case 8834: esc = "sub"; break;
+ case 8835: esc = "sup"; break;
+ case 8836: esc = "nsub"; break;
+ case 8838: esc = "sube"; break;
+ case 8839: esc = "supe"; break;
+ case 8853: esc = "oplus"; break;
+ case 8855: esc = "otimes"; break;
+ case 8869: esc = "perp"; break;
+ case 8901: esc = "sdot"; break;
+ case 8968: esc = "lceil"; break;
+ case 8969: esc = "rceil"; break;
+ case 8970: esc = "lfloor"; break;
+ case 8971: esc = "rfloor"; break;
+ case 9001: esc = "lang"; break;
+ case 9002: esc = "rang"; break;
+ case 9674: esc = "loz"; break;
+ case 9824: esc = "spades"; break;
+ case 9827: esc = "clubs"; break;
+ case 9829: esc = "hearts"; break;
+ case 9830: esc = "diams"; break;
+ }
+ }
+
+ // else escape with numeric char refs
+ if (esc == null) {
+ stringBuf.setLength (0);
+ stringBuf.append ("#x");
+ stringBuf.append (Integer.toHexString (c).toUpperCase ());
+ esc = stringBuf.toString ();
+
+ // FIXME: We don't write surrogate pairs correctly.
+ // They should work as one ref per character, since
+ // each pair is one character. For reading back into
+ // Unicode, it matters beginning in Unicode 3.1 ...
+ }
+ break;
+ }
+ if (i != first)
+ rawWrite (buf, off + first, i - first);
+ first = i + 1;
+ if (esc == eol)
+ newline ();
+ else {
+ rawWrite ('&');
+ rawWrite (esc);
+ rawWrite (';');
+ }
+ }
+ if (first < len)
+ rawWrite (buf, off + first, len - first);
+ }
+
+
+
+ private void newline ()
+ throws SAXException, IOException
+ {
+ out.write (eol);
+ column = 0;
+ }
+
+ private void doIndent ()
+ throws SAXException, IOException
+ {
+ int space = elementNestLevel * 2;
+
+ newline ();
+ column = space;
+ // track tabs only at line starts
+ while (space > 8) {
+ out.write ("\t");
+ space -= 8;
+ }
+ while (space > 0) {
+ out.write (" ");
+ space -= 2;
+ }
+ }
+
+ private void rawWrite (char c)
+ throws IOException
+ {
+ out.write (c);
+ column++;
+ }
+
+ private void rawWrite (String s)
+ throws SAXException, IOException
+ {
+ if (prettyPrinting && "default".equals (space.peek ())) {
+ char data [] = s.toCharArray ();
+ rawWrite (data, 0, data.length);
+ } else {
+ out.write (s);
+ column += s.length ();
+ }
+ }
+
+ // NOTE: if xhtml, the REC gives some rules about whitespace
+ // which we could follow ... notably, many places where conformant
+ // agents "must" consolidate/normalize whitespace. Line ends can
+ // be removed there, etc. This may not be the right place to do
+ // such mappings though.
+
+ // Line buffering may help clarify algorithms and improve results.
+
+ // It's likely xml:space needs more attention.
+
+ private void rawWrite (char buf [], int offset, int length)
+ throws SAXException, IOException
+ {
+ boolean wrap;
+
+ if (prettyPrinting && space.empty ())
+ fatal ("stack discipline", null);
+
+ wrap = prettyPrinting && "default".equals (space.peek ());
+ if (!wrap) {
+ out.write (buf, offset, length);
+ column += length;
+ return;
+ }
+
+ // we're pretty printing and want to fill lines out only
+ // to the desired line length.
+ while (length > 0) {
+ int target = lineLength - column;
+ boolean wrote = false;
+
+ // Do we even have a problem?
+ if (target > length || noWrap) {
+ out.write (buf, offset, length);
+ column += length;
+ return;
+ }
+
+ // break the line at a space character, trying to fill
+ // as much of the line as possible.
+ char c;
+
+ for (int i = target - 1; i >= 0; i--) {
+ if ((c = buf [offset + i]) == ' ' || c == '\t') {
+ i++;
+ out.write (buf, offset, i);
+ doIndent ();
+ offset += i;
+ length -= i;
+ wrote = true;
+ break;
+ }
+ }
+ if (wrote)
+ continue;
+
+ // no space character permitting break before target
+ // line length is filled. So, take the next one.
+ if (target < 0)
+ target = 0;
+ for (int i = target; i < length; i++)
+ if ((c = buf [offset + i]) == ' ' || c == '\t') {
+ i++;
+ out.write (buf, offset, i);
+ doIndent ();
+ offset += i;
+ length -= i;
+ wrote = true;
+ break;
+ }
+ if (wrote)
+ continue;
+
+ // no such luck.
+ out.write (buf, offset, length);
+ column += length;
+ break;
+ }
+ }
+}
diff --git a/libjava/gnu/xml/util/package.html b/libjava/gnu/xml/util/package.html
new file mode 100644
index 0000000..6e6c0d7
--- /dev/null
+++ b/libjava/gnu/xml/util/package.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html PUBLIC
+ "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/1999/PR-xhtml1-19991210/DTD/xhtml1-transitional.dtd">
+
+<html><head><title> org.brownell.xml package </title> </head>
+<!--
+/*
+ * Copyright (C) 1999,2000 The Free Software Foundation, Inc.
+ */
+-->
+<body>
+ <p> This package contains XML utilities, including SAX2 XML writers
+ and a parser of DOM trees, plus a command line driver.
+ That <a href="DoParse.html">driver</a>
+ connects parsers simple processing pipelines.
+ It can be handy for command line validation or
+ transformation tasks, possibly in batch mode,
+ or within Makefiles. </p>
+
+</body></html>
diff --git a/libjava/gnu/xml/xpath/AndExpr.java b/libjava/gnu/xml/xpath/AndExpr.java
new file mode 100644
index 0000000..91fb3bc
--- /dev/null
+++ b/libjava/gnu/xml/xpath/AndExpr.java
@@ -0,0 +1,81 @@
+/* AndExpr.java --
+ Copyright (C) 2004 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.xpath;
+
+import org.w3c.dom.Node;
+
+/**
+ * Logical and.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+public final class AndExpr
+ extends Expr
+{
+
+ final Expr lhs;
+ final Expr rhs;
+
+ public AndExpr(Expr lhs, Expr rhs)
+ {
+ this.lhs = lhs;
+ this.rhs = rhs;
+ }
+
+ public Object evaluate(Node context, int pos, int len)
+ {
+ Object left = lhs.evaluate(context, pos, len);
+ if (!_boolean(context, left))
+ {
+ return Boolean.FALSE;
+ }
+ Object right = rhs.evaluate(context, pos, len);
+ return _boolean(context, right) ? Boolean.TRUE : Boolean.FALSE;
+ }
+
+ public Expr clone(Object context)
+ {
+ return new AndExpr(lhs.clone(context), rhs.clone(context));
+ }
+
+ public String toString()
+ {
+ return lhs + " and " + rhs;
+ }
+
+}
diff --git a/libjava/gnu/xml/xpath/ArithmeticExpr.java b/libjava/gnu/xml/xpath/ArithmeticExpr.java
new file mode 100644
index 0000000..ad9da1e
--- /dev/null
+++ b/libjava/gnu/xml/xpath/ArithmeticExpr.java
@@ -0,0 +1,148 @@
+/* ArithmeticExpr.java --
+ Copyright (C) 2004 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.xpath;
+
+import org.w3c.dom.Node;
+
+/**
+ * Binary arithmetic expression.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+final class ArithmeticExpr
+ extends Expr
+{
+
+ static final int ADD = 0;
+ static final int SUBTRACT = 1;
+ static final int MULTIPLY = 2;
+ static final int DIVIDE = 3;
+ static final int MODULO = 4;
+
+ final Expr lhs;
+ final Expr rhs;
+ final int op;
+
+ ArithmeticExpr(Expr lhs, Expr rhs, int op)
+ {
+ this.lhs = lhs;
+ this.rhs = rhs;
+ switch (op)
+ {
+ case ADD:
+ case SUBTRACT:
+ case MULTIPLY:
+ case DIVIDE:
+ case MODULO:
+ this.op = op;
+ break;
+ default:
+ throw new IllegalArgumentException();
+ }
+ }
+
+ public Object evaluate(Node context, int pos, int len)
+ {
+ Object left = lhs.evaluate(context, pos, len);
+ Object right = rhs.evaluate(context, pos, len);
+
+ double ln = _number(context, left);
+ double rn = _number(context, right);
+ switch (op)
+ {
+ case ADD:
+ return new Double(ln + rn);
+ case SUBTRACT:
+ return new Double(ln - rn);
+ case MULTIPLY:
+ return new Double(ln * rn);
+ case DIVIDE:
+ if (rn == 0.0d || rn == -0.0d)
+ {
+ return new Double(ln < 0.0d ?
+ Double.NEGATIVE_INFINITY :
+ Double.POSITIVE_INFINITY);
+ }
+ return new Double(ln / rn);
+ case MODULO:
+ if (rn == 0.0d || rn == -0.0d)
+ {
+ return new Double(ln < 0.0d ?
+ Double.NEGATIVE_INFINITY :
+ Double.POSITIVE_INFINITY);
+ }
+ return new Double(ln % rn);
+ default:
+ throw new IllegalStateException();
+ }
+ }
+
+ public Expr clone(Object context)
+ {
+ return new ArithmeticExpr(lhs.clone(context), rhs.clone(context), op);
+ }
+
+ public String toString()
+ {
+ StringBuffer buf = new StringBuffer();
+ buf.append(lhs);
+ buf.append(' ');
+ switch (op)
+ {
+ case ADD:
+ buf.append('+');
+ break;
+ case SUBTRACT:
+ buf.append('-');
+ break;
+ case MULTIPLY:
+ buf.append('*');
+ break;
+ case DIVIDE:
+ buf.append("div");
+ break;
+ case MODULO:
+ buf.append("mod");
+ break;
+ }
+ buf.append(' ');
+ buf.append(rhs);
+ return buf.toString();
+ }
+
+}
diff --git a/libjava/gnu/xml/xpath/BooleanFunction.java b/libjava/gnu/xml/xpath/BooleanFunction.java
new file mode 100644
index 0000000..df54ff5
--- /dev/null
+++ b/libjava/gnu/xml/xpath/BooleanFunction.java
@@ -0,0 +1,89 @@
+/* BooleanFunction.java --
+ Copyright (C) 2004 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.xpath;
+
+import java.util.List;
+import org.w3c.dom.Node;
+
+/**
+ * The <code>boolean</code> function converts its argument to a boolean as
+ * follows:
+ * <ul>
+ * <li>a number is true if and only if it is neither positive or negative
+ * zero nor NaN</li>
+ * <li>a node-set is true if and only if it is non-empty</li>
+ * <li>a string is true if and only if its length is non-zero</li>
+ * <li>an object of a type other than the four basic types is converted to a
+ * boolean in a way that is dependent on that type</li>
+ * </ul>
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+final class BooleanFunction
+ extends Expr
+{
+
+ final Expr arg;
+
+ BooleanFunction(List args)
+ {
+ this((Expr) args.get(0));
+ }
+
+ BooleanFunction(Expr arg)
+ {
+ this.arg = arg;
+ }
+
+ public Object evaluate(Node context, int pos, int len)
+ {
+ Object val = arg.evaluate(context, pos, len);
+ return _boolean(context, val) ? Boolean.TRUE : Boolean.FALSE;
+ }
+
+ public Expr clone(Object context)
+ {
+ return new BooleanFunction(arg.clone(context));
+ }
+
+ public String toString()
+ {
+ return "boolean(" + arg + ")";
+ }
+
+}
diff --git a/libjava/gnu/xml/xpath/CeilingFunction.java b/libjava/gnu/xml/xpath/CeilingFunction.java
new file mode 100644
index 0000000..8c0b8b1
--- /dev/null
+++ b/libjava/gnu/xml/xpath/CeilingFunction.java
@@ -0,0 +1,83 @@
+/* CeilingFunction.java --
+ Copyright (C) 2004 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.xpath;
+
+import java.util.List;
+import org.w3c.dom.Node;
+
+/**
+ * The <code>ceiling</code> function returns the smallest (closest to
+ * negative infinity) number that is not less than the argument and that
+ * is an integer.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+final class CeilingFunction
+ extends Expr
+{
+
+ final Expr arg;
+
+ CeilingFunction(List args)
+ {
+ this((Expr) args.get(0));
+ }
+
+ CeilingFunction(Expr arg)
+ {
+ this.arg = arg;
+ }
+
+ public Object evaluate(Node context, int pos, int len)
+ {
+ Object val = arg.evaluate(context, pos, len);
+ double n = _number(context, val);
+ return new Double(Math.ceil(n));
+ }
+
+ public Expr clone(Object context)
+ {
+ return new CeilingFunction(arg.clone(context));
+ }
+
+ public String toString()
+ {
+ return "ceiling(" + arg + ")";
+ }
+
+}
diff --git a/libjava/gnu/xml/xpath/ConcatFunction.java b/libjava/gnu/xml/xpath/ConcatFunction.java
new file mode 100644
index 0000000..7c51f9d
--- /dev/null
+++ b/libjava/gnu/xml/xpath/ConcatFunction.java
@@ -0,0 +1,100 @@
+/* ConcatFunction.java --
+ Copyright (C) 2004 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.xpath;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import org.w3c.dom.Node;
+
+/**
+ * The <code>concat</code> function returns the concatenation of its arguments.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+final class ConcatFunction
+ extends Expr
+{
+
+ final List args;
+
+ ConcatFunction(List args)
+ {
+ this.args = args;
+ }
+
+ public Object evaluate(Node context, int pos, int len)
+ {
+ StringBuffer buf = new StringBuffer();
+ for (Iterator i = args.iterator(); i.hasNext(); )
+ {
+ Expr arg = (Expr) i.next();
+ Object val = arg.evaluate(context, pos, len);
+ buf.append(_string(context, val));
+ }
+ return buf.toString();
+ }
+
+ public Expr clone(Object context)
+ {
+ int len = args.size();
+ List args2 = new ArrayList(len);
+ for (int i = 0; i < len; i++)
+ {
+ args2.add(((Expr) args.get(i)).clone(context));
+ }
+ return new ConcatFunction(args2);
+ }
+
+ public String toString()
+ {
+ StringBuffer buf = new StringBuffer("concat(");
+ int len = args.size();
+ for (int i = 0; i < len; i++)
+ {
+ if (i > 0)
+ {
+ buf.append(',');
+ }
+ buf.append(args.get(i));
+ }
+ buf.append(')');
+ return buf.toString();
+ }
+
+}
diff --git a/libjava/gnu/xml/xpath/Constant.java b/libjava/gnu/xml/xpath/Constant.java
new file mode 100644
index 0000000..d38f7ea
--- /dev/null
+++ b/libjava/gnu/xml/xpath/Constant.java
@@ -0,0 +1,92 @@
+/* Constant.java --
+ Copyright (C) 2004 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.xpath;
+
+import org.w3c.dom.Node;
+
+/**
+ * Constant value (string literal or number).
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+public final class Constant
+ extends Expr
+{
+
+ final Object value;
+
+ public Constant(Object value)
+ {
+ this.value = value;
+ }
+
+ public Object evaluate(Node context, int pos, int len)
+ {
+ return value;
+ }
+
+ public Expr clone(Object context)
+ {
+ return new Constant(value);
+ }
+
+ public String toString()
+ {
+ String ret = value.toString();
+ if (value instanceof String)
+ {
+ if (ret.indexOf('\'') == -1)
+ {
+ return '\'' + ret + '\'';
+ }
+ else
+ {
+ return '"' + ret + '"';
+ }
+ }
+ if (value instanceof Double)
+ {
+ if (ret.endsWith(".0"))
+ {
+ ret = ret.substring(0, ret.length() - 2);
+ }
+ }
+ return ret;
+ }
+
+}
diff --git a/libjava/gnu/xml/xpath/ContainsFunction.java b/libjava/gnu/xml/xpath/ContainsFunction.java
new file mode 100644
index 0000000..2a0d76d
--- /dev/null
+++ b/libjava/gnu/xml/xpath/ContainsFunction.java
@@ -0,0 +1,86 @@
+/* ContainsFunction.java --
+ Copyright (C) 2004 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.xpath;
+
+import java.util.List;
+import org.w3c.dom.Node;
+
+/**
+ * The <code>contains</code> function returns true if the first argument
+ * string contains the second argument string, and otherwise returns false.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+final class ContainsFunction
+ extends Expr
+{
+
+ final Expr arg1;
+ final Expr arg2;
+
+ ContainsFunction(List args)
+ {
+ this((Expr) args.get(0), (Expr) args.get(1));
+ }
+
+ ContainsFunction(Expr arg1, Expr arg2)
+ {
+ this.arg1 = arg1;
+ this.arg2 = arg2;
+ }
+
+ public Object evaluate(Node context, int pos, int len)
+ {
+ Object val1 = arg1.evaluate(context, pos, len);
+ Object val2 = arg2.evaluate(context, pos, len);
+ String s1 = _string(context, val1);
+ String s2 = _string(context, val2);
+ return (s1.indexOf(s2) != -1) ? Boolean.TRUE : Boolean.FALSE;
+ }
+
+ public Expr clone(Object context)
+ {
+ return new ContainsFunction(arg1.clone(context), arg2.clone(context));
+ }
+
+ public String toString()
+ {
+ return "contains(" + arg1 + "," + arg2 + ")";
+ }
+
+}
diff --git a/libjava/gnu/xml/xpath/CountFunction.java b/libjava/gnu/xml/xpath/CountFunction.java
new file mode 100644
index 0000000..33663c9
--- /dev/null
+++ b/libjava/gnu/xml/xpath/CountFunction.java
@@ -0,0 +1,82 @@
+/* CountFunction.java --
+ Copyright (C) 2004 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.xpath;
+
+import java.util.Collection;
+import java.util.List;
+import org.w3c.dom.Node;
+
+/**
+ * The <code>count</code> function returns the number of nodes in the
+ * argument node-set.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+final class CountFunction
+ extends Expr
+{
+
+ final Expr arg;
+
+ CountFunction(List args)
+ {
+ this((Expr) args.get(0));
+ }
+
+ CountFunction(Expr arg)
+ {
+ this.arg = arg;
+ }
+
+ public Object evaluate(Node context, int pos, int len)
+ {
+ Object val = arg.evaluate(context, pos, len);
+ return new Double((double) ((Collection) val).size());
+ }
+
+ public Expr clone(Object context)
+ {
+ return new CountFunction(arg.clone(context));
+ }
+
+ public String toString()
+ {
+ return "count(" + arg + ")";
+ }
+
+}
diff --git a/libjava/gnu/xml/xpath/DocumentOrderComparator.java b/libjava/gnu/xml/xpath/DocumentOrderComparator.java
new file mode 100644
index 0000000..28d97eb
--- /dev/null
+++ b/libjava/gnu/xml/xpath/DocumentOrderComparator.java
@@ -0,0 +1,63 @@
+/* DocumentOrderComparator.java --
+ Copyright (C) 2004 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.xpath;
+
+import java.util.Comparator;
+import org.w3c.dom.Node;
+
+/**
+ * Sorts nodes into document order.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+public class DocumentOrderComparator
+ implements Comparator
+{
+
+ public int compare(Object o1, Object o2)
+ {
+ if (o1 instanceof Node && o2 instanceof Node)
+ {
+ Node n1 = (Node)o1;
+ Node n2 = (Node)o2;
+ return (int) n1.compareDocumentPosition(n2);
+ }
+ return 0;
+ }
+
+}
diff --git a/libjava/gnu/xml/xpath/EqualityExpr.java b/libjava/gnu/xml/xpath/EqualityExpr.java
new file mode 100644
index 0000000..bedf1e9
--- /dev/null
+++ b/libjava/gnu/xml/xpath/EqualityExpr.java
@@ -0,0 +1,258 @@
+/* EqualityExpr.java --
+ Copyright (C) 2004 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.xpath;
+
+import java.util.Collection;
+import java.util.Iterator;
+import org.w3c.dom.Node;
+
+/**
+ * Boolean equality expression.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+final class EqualityExpr
+ extends Expr
+{
+
+ final Expr lhs;
+ final Expr rhs;
+ final boolean invert;
+
+ EqualityExpr(Expr lhs, Expr rhs, boolean invert)
+ {
+ this.lhs = lhs;
+ this.rhs = rhs;
+ this.invert = invert;
+ }
+
+ public Object evaluate(Node context, int pos, int len)
+ {
+ boolean val = evaluateImpl(context, pos, len);
+ if (invert)
+ {
+ return val ? Boolean.FALSE : Boolean.TRUE;
+ }
+ else
+ {
+ return val ? Boolean.TRUE : Boolean.FALSE;
+ }
+ }
+
+ private boolean evaluateImpl(Node context, int pos, int len)
+ {
+ Object left = lhs.evaluate(context, pos, len);
+ Object right = rhs.evaluate(context, pos, len);
+
+ /*
+ * If both objects to be compared are node-sets, then the comparison
+ * will be true if and only if there is a node in the first node-set and
+ * a node in the second node-set such that the result of performing the
+ * comparison on the string-values of the two nodes is true.
+ */
+ boolean flns = left instanceof Collection;
+ boolean frns = right instanceof Collection;
+ if (flns && frns)
+ {
+ Collection lns = (Collection) left;
+ Collection rns = (Collection) right;
+ boolean all = true;
+ for (Iterator i = lns.iterator(); i.hasNext(); )
+ {
+ Node ltest = (Node) i.next();
+ for (Iterator j = rns.iterator(); j.hasNext(); )
+ {
+ Node rtest = (Node) j.next();
+ if (ltest == rtest || ltest.equals(rtest))
+ {
+ // much shorter
+ if (!invert)
+ {
+ return true;
+ }
+ }
+ else if (stringValue(ltest).equals(stringValue(rtest)))
+ {
+ if (!invert)
+ {
+ return true;
+ }
+ }
+ else
+ {
+ all = false;
+ }
+ }
+ }
+ return false;
+ }
+ /*
+ * If one object to be compared is a node-set and the other is a number,
+ * then the comparison will be true if and only if there is a node in
+ * the node-set such that the result of performing the comparison on the
+ * number to be compared and on the result of converting the
+ * string-value of that node to a number using the number function is
+ * true.
+ */
+ boolean fln = left instanceof Double;
+ boolean frn = right instanceof Double;
+ if ((flns && frn) || (frns && fln))
+ {
+ Collection ns = flns ? (Collection) left : (Collection) right;
+ double n = fln ? ((Double) left).doubleValue() :
+ ((Double) right).doubleValue();
+ boolean all = true;
+ for (Iterator i = ns.iterator(); i.hasNext(); )
+ {
+ Node test = (Node) i.next();
+ double nn = _number(context, stringValue(test));
+ if (nn == n)
+ {
+ if (!invert)
+ {
+ return true;
+ }
+ }
+ else
+ {
+ all = false;
+ }
+ }
+ return invert ? all : false;
+ }
+ /*
+ * If one object to be compared is a node-set and the other is a
+ * string, then the comparison will be true if and only if there is a
+ * node in the node-set such that the result of performing the
+ * comparison on the string-value of the node and the other string is
+ * true.
+ */
+ boolean fls = left instanceof String;
+ boolean frs = right instanceof String;
+ if ((flns && frs) || (frns && fls))
+ {
+ Collection ns = flns ? (Collection) left : (Collection) right;
+ String s = fls ? (String) left : (String) right;
+ boolean all = true;
+ for (Iterator i = ns.iterator(); i.hasNext(); )
+ {
+ Node test = (Node) i.next();
+ if (stringValue(test).equals(s))
+ {
+ if (!invert)
+ {
+ return true;
+ }
+ }
+ else
+ {
+ all = false;
+ }
+ }
+ return invert ? all : false;
+ }
+ /*
+ * If one object to be compared is a node-set and the other is a
+ * boolean, then the comparison will be true if and only if the result
+ * of performing the comparison on the boolean and on the result of
+ * converting the node-set to a boolean using the boolean function is
+ * true.
+ */
+ boolean flb = left instanceof Boolean;
+ boolean frb = right instanceof Boolean;
+ if ((flns && frb) || (frns && flb))
+ {
+ Collection ns = flns ? (Collection) left : (Collection) right;
+ boolean b = flb ? ((Boolean) left).booleanValue() :
+ ((Boolean) right).booleanValue();
+ return _boolean(context, ns) == b;
+ }
+ /*
+ * If at least one object to be compared is a boolean, then each object
+ * to be compared is converted to a boolean as if by applying the
+ * boolean function.
+ */
+ if (flb || frb)
+ {
+ boolean lb = flb ? ((Boolean) left).booleanValue() :
+ _boolean(context, left);
+ boolean rb = frb ? ((Boolean) right).booleanValue() :
+ _boolean(context, right);
+ return lb == rb;
+ }
+ /*
+ * Otherwise, if at least one object to be compared is
+ * a number, then each object to be compared is converted to a number as
+ * if by applying the number function.
+ */
+ if (fln || frn)
+ {
+ double ln = fln ? ((Double) left).doubleValue() :
+ _number(context, left);
+ double rn = frn ? ((Double) right).doubleValue() :
+ _number(context, right);
+ return ln == rn;
+ }
+ /*
+ * Otherwise, both objects to be
+ * compared are converted to strings as if by applying the string
+ * function.
+ */
+ String ls = fls ? (String) left : _string(context, left);
+ String rs = frs ? (String) right : _string(context, right);
+ return ls.equals(rs);
+ }
+
+ public Expr clone(Object context)
+ {
+ return new EqualityExpr(lhs.clone(context), rhs.clone(context), invert);
+ }
+
+ public String toString()
+ {
+ if (invert)
+ {
+ return lhs + " != " + rhs;
+ }
+ else
+ {
+ return lhs + " = " + rhs;
+ }
+ }
+
+}
diff --git a/libjava/gnu/xml/xpath/Expr.java b/libjava/gnu/xml/xpath/Expr.java
new file mode 100644
index 0000000..6a933c8
--- /dev/null
+++ b/libjava/gnu/xml/xpath/Expr.java
@@ -0,0 +1,476 @@
+/* Expr.java --
+ Copyright (C) 2004 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.xpath;
+
+import java.io.IOException;
+import java.text.DecimalFormat;
+import java.text.DecimalFormatSymbols;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.Set;
+import java.util.StringTokenizer;
+import javax.xml.namespace.QName;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.xpath.XPathConstants;
+import javax.xml.xpath.XPathExpression;
+import javax.xml.xpath.XPathExpressionException;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+
+/**
+ * An XPath expression.
+ * This can be evaluated in the context of a node to produce a result.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+public abstract class Expr
+ implements XPathExpression
+{
+
+ protected static final Comparator documentOrderComparator =
+ new DocumentOrderComparator();
+
+ protected static final DecimalFormat decimalFormat =
+ new DecimalFormat("####################################################" +
+ ".####################################################",
+ new DecimalFormatSymbols(Locale.US));
+
+ public Object evaluate(Object item, QName returnType)
+ throws XPathExpressionException
+ {
+ Object ret = null;
+ Node context = null;
+ if (item instanceof Node)
+ {
+ context = (Node) item;
+ ret = evaluate(context, 1, 1);
+ if (XPathConstants.STRING == returnType &&
+ !(ret instanceof String))
+ {
+ ret = _string(context, ret);
+ }
+ else if (XPathConstants.NUMBER == returnType &&
+ !(ret instanceof Double))
+ {
+ ret = new Double(_number(context, ret));
+ }
+ else if (XPathConstants.BOOLEAN == returnType &&
+ !(ret instanceof Boolean))
+ {
+ ret = _boolean(context, ret) ? Boolean.TRUE : Boolean.FALSE;
+ }
+ else if (XPathConstants.NODE == returnType)
+ {
+ if (ret instanceof Collection)
+ {
+ Collection ns = (Collection) ret;
+ switch (ns.size())
+ {
+ case 0:
+ ret = null;
+ break;
+ case 1:
+ ret = (Node) ns.iterator().next();
+ break;
+ default:
+ throw new XPathExpressionException("multiple nodes in node-set");
+ }
+ }
+ else if (ret != null)
+ {
+ throw new XPathExpressionException("return value is not a node-set");
+ }
+ }
+ else if (XPathConstants.NODESET == returnType)
+ {
+ if (ret != null && !(ret instanceof Collection))
+ {
+ throw new XPathExpressionException("return value is not a node-set");
+ }
+ }
+ }
+ return ret;
+ }
+
+ public String evaluate(Object item)
+ throws XPathExpressionException
+ {
+ return (String) evaluate(item, XPathConstants.STRING);
+ }
+
+ public Object evaluate(InputSource source, QName returnType)
+ throws XPathExpressionException
+ {
+ try
+ {
+ DocumentBuilderFactory factory =
+ new gnu.xml.dom.JAXPFactory();
+ DocumentBuilder builder = factory.newDocumentBuilder();
+ Document doc = builder.parse(source);
+ return evaluate(doc, returnType);
+ }
+ catch (ParserConfigurationException e)
+ {
+ throw new XPathExpressionException(e);
+ }
+ catch (SAXException e)
+ {
+ throw new XPathExpressionException(e);
+ }
+ catch (IOException e)
+ {
+ throw new XPathExpressionException(e);
+ }
+ }
+
+ public String evaluate(InputSource source)
+ throws XPathExpressionException
+ {
+ return (String) evaluate(source, XPathConstants.STRING);
+ }
+
+ public abstract Object evaluate(Node context, int pos, int len);
+
+ public abstract Expr clone(Object context);
+
+ /* -- 4.1 Node Set Functions -- */
+
+ /**
+ * The id function selects elements by their unique ID.
+ * When the argument to id is of type node-set, then the result is
+ * the union of the result of applying id to the string-value of each of
+ * the nodes in the argument node-set. When the argument to id is of any
+ * other type, the argument is converted to a string as if by a call to
+ * the string function; the string is split into a whitespace-separated
+ * list of tokens (whitespace is any sequence of characters matching the
+ * production S); the result is a node-set containing the elements in the
+ * same document as the context node that have a unique ID equal to any of
+ * the tokens in the list.
+ */
+ public static Collection _id(Node context, Object object)
+ {
+ Set ret = new HashSet();
+ if (object instanceof Collection)
+ {
+ Collection nodeSet = (Collection) object;
+ for (Iterator i = nodeSet.iterator(); i.hasNext(); )
+ {
+ String string = stringValue((Node) i.next());
+ ret.addAll(_id (context, string));
+ }
+ }
+ else
+ {
+ Document doc = (context instanceof Document) ? (Document) context :
+ context.getOwnerDocument();
+ String string = _string(context, object);
+ StringTokenizer st = new StringTokenizer(string, " \t\r\n");
+ while (st.hasMoreTokens())
+ {
+ Node element = doc.getElementById(st.nextToken());
+ if (element != null)
+ {
+ ret.add(element);
+ }
+ }
+ }
+ return ret;
+ }
+
+ /**
+ * The local-name function returns the local part of the expanded-name of
+ * the node in the argument node-set that is first in document order. If
+ * the argument node-set is empty or the first node has no expanded-name,
+ * an empty string is returned. If the argument is omitted, it defaults to
+ * a node-set with the context node as its only member.
+ */
+ public static String _local_name(Node context, Collection nodeSet)
+ {
+ Node node = (nodeSet == null || nodeSet.size() == 0) ? context :
+ firstNode(nodeSet);
+ return node.getLocalName();
+ }
+
+ /**
+ * The namespace-uri function returns the namespace URI of the
+ * expanded-name of the node in the argument node-set that is first in
+ * document order. If the argument node-set is empty, the first node has
+ * no expanded-name, or the namespace URI of the expanded-name is null, an
+ * empty string is returned. If the argument is omitted, it defaults to a
+ * node-set with the context node as its only member.
+ */
+ public static String _namespace_uri(Node context, Collection nodeSet)
+ {
+ Node node = (nodeSet == null || nodeSet.size() == 0) ? context :
+ firstNode(nodeSet);
+ return node.getNamespaceURI();
+ }
+
+ /**
+ * The name function returns a string containing a QName representing the
+ * expanded-name of the node in the argument node-set that is first in
+ * document order. The QName must represent the expanded-name with respect
+ * to the namespace declarations in effect on the node whose expanded-name
+ * is being represented. Typically, this will be the QName that occurred
+ * in the XML source. This need not be the case if there are namespace
+ * declarations in effect on the node that associate multiple prefixes
+ * with the same namespace. However, an implementation may include
+ * information about the original prefix in its representation of nodes;
+ * in this case, an implementation can ensure that the returned string is
+ * always the same as the QName used in the XML source. If the argument
+ * node-set is empty or the first node has no expanded-name, an empty
+ * string is returned. If the argument it omitted, it defaults to a
+ * node-set with the context node as its only member.
+ */
+ public static String _name(Node context, Collection nodeSet)
+ {
+ Node node = (nodeSet == null || nodeSet.size() == 0) ? context :
+ firstNode(nodeSet);
+ switch (node.getNodeType())
+ {
+ case Node.ATTRIBUTE_NODE:
+ case Node.ELEMENT_NODE:
+ case Node.PROCESSING_INSTRUCTION_NODE:
+ return node.getNodeName();
+ default:
+ return "";
+ }
+ }
+
+ /**
+ * Returns the first node in the set in document order.
+ */
+ static Node firstNode(Collection nodeSet)
+ {
+ List list = new ArrayList(nodeSet);
+ Collections.sort(list, documentOrderComparator);
+ return (Node) list.get(0);
+ }
+
+ /* -- 4.2 String Functions -- */
+
+ /**
+ * Implementation of the XPath <code>string</code> function.
+ */
+ public static String _string(Node context, Object object)
+ {
+ if (object == null)
+ {
+ return stringValue(context);
+ }
+ if (object instanceof String)
+ {
+ return (String) object;
+ }
+ if (object instanceof Boolean)
+ {
+ return object.toString();
+ }
+ if (object instanceof Double)
+ {
+ double d = ((Double) object).doubleValue();
+ if (Double.isNaN(d))
+ {
+ return "NaN";
+ }
+ else if (d == 0.0d)
+ {
+ return "0";
+ }
+ else if (Double.isInfinite(d))
+ {
+ if (d < 0)
+ {
+ return "-Infinity";
+ }
+ else
+ {
+ return "Infinity";
+ }
+ }
+ else
+ {
+ String ret = decimalFormat.format(d);
+ if (ret.endsWith (".0"))
+ {
+ ret = ret.substring(0, ret.length() - 2);
+ }
+ return ret;
+ }
+ }
+ if (object instanceof Collection)
+ {
+ Collection nodeSet = (Collection) object;
+ if (nodeSet.isEmpty())
+ {
+ return "";
+ }
+ Node node = firstNode(nodeSet);
+ return stringValue(node);
+ }
+ throw new IllegalArgumentException(object.toString());
+ }
+
+ /* -- 4.3 Boolean Functions -- */
+
+ /**
+ * Implementation of the XPath <code>boolean</code> function.
+ */
+ public static boolean _boolean(Node context, Object object)
+ {
+ if (object instanceof Boolean)
+ {
+ return ((Boolean) object).booleanValue();
+ }
+ if (object instanceof Double)
+ {
+ return ((Double) object).doubleValue() != 0.0;
+ }
+ if (object instanceof String)
+ {
+ return ((String) object).length() != 0;
+ }
+ if (object instanceof Collection)
+ {
+ return ((Collection) object).size() != 0;
+ }
+ return false; // TODO user defined types
+ }
+
+ /* -- 4.4 Number Functions -- */
+
+ /**
+ * Implementation of the XPath <code>number</code> function.
+ */
+ public static double _number(Node context, Object object)
+ {
+ if (object == null)
+ {
+ object = Collections.singleton(context);
+ }
+ if (object instanceof Double)
+ {
+ return ((Double) object).doubleValue();
+ }
+ if (object instanceof Boolean)
+ {
+ return ((Boolean) object).booleanValue() ? 1.0 : 0.0;
+ }
+ if (object instanceof Collection)
+ {
+ // Convert node-set to string
+ object = stringValue((Collection) object);
+ }
+ if (object instanceof String)
+ {
+ String string = ((String) object).trim();
+ try
+ {
+ return Double.parseDouble(string);
+ }
+ catch (NumberFormatException e)
+ {
+ return Double.NaN;
+ }
+ }
+ return Double.NaN; // TODO user-defined types
+ }
+
+ /**
+ * Computes the XPath string-value of the specified node-set.
+ */
+ public static String stringValue(Collection nodeSet)
+ {
+ StringBuffer buf = new StringBuffer();
+ for (Iterator i = nodeSet.iterator(); i.hasNext(); )
+ {
+ buf.append(stringValue((Node) i.next()));
+ }
+ return buf.toString();
+ }
+
+ /**
+ * Computes the XPath string-value of the specified node.
+ */
+ public static String stringValue(Node node)
+ {
+ return stringValue(node, false);
+ }
+
+ static String stringValue(Node node, boolean elementMode)
+ {
+ switch (node.getNodeType())
+ {
+ case Node.DOCUMENT_NODE: // 5.1 Root Node
+ case Node.DOCUMENT_FRAGMENT_NODE:
+ case Node.ELEMENT_NODE: // 5.2 Element Nodes
+ StringBuffer buf = new StringBuffer();
+ for (Node ctx = node.getFirstChild(); ctx != null;
+ ctx = ctx.getNextSibling())
+ {
+ buf.append(stringValue(ctx, true));
+ }
+ return buf.toString();
+ case Node.TEXT_NODE: // 5.7 Text Nodes
+ case Node.CDATA_SECTION_NODE:
+ return node.getNodeValue();
+ case Node.ATTRIBUTE_NODE: // 5.3 Attribute Nodes
+ case Node.PROCESSING_INSTRUCTION_NODE: // 5.5 Processing Instruction
+ case Node.COMMENT_NODE: // 5.6 Comment Nodes
+ if (!elementMode)
+ {
+ return node.getNodeValue();
+ }
+ default:
+ return "";
+ }
+ }
+
+}
diff --git a/libjava/gnu/xml/xpath/FalseFunction.java b/libjava/gnu/xml/xpath/FalseFunction.java
new file mode 100644
index 0000000..85417a1
--- /dev/null
+++ b/libjava/gnu/xml/xpath/FalseFunction.java
@@ -0,0 +1,66 @@
+/* FalseFunction.java --
+ Copyright (C) 2004 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.xpath;
+
+import org.w3c.dom.Node;
+
+/**
+ * The <code>false</code> function returns false.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+final class FalseFunction
+ extends Expr
+{
+
+ public Object evaluate(Node context, int pos, int len)
+ {
+ return Boolean.FALSE;
+ }
+
+ public Expr clone(Object context)
+ {
+ return new FalseFunction();
+ }
+
+ public String toString()
+ {
+ return "false()";
+ }
+
+}
diff --git a/libjava/gnu/xml/xpath/FloorFunction.java b/libjava/gnu/xml/xpath/FloorFunction.java
new file mode 100644
index 0000000..dd9cd84
--- /dev/null
+++ b/libjava/gnu/xml/xpath/FloorFunction.java
@@ -0,0 +1,83 @@
+/* FloorFunction.java --
+ Copyright (C) 2004 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.xpath;
+
+import java.util.List;
+import org.w3c.dom.Node;
+
+/**
+ * The <code>floor</code> function returns the largest (closest to positive
+ * infinity) number that is not greater than the argument and that is an
+ * integer.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+final class FloorFunction
+ extends Expr
+{
+
+ final Expr arg;
+
+ FloorFunction(List args)
+ {
+ this((Expr) args.get(0));
+ }
+
+ FloorFunction(Expr arg)
+ {
+ this.arg = arg;
+ }
+
+ public Object evaluate(Node context, int pos, int len)
+ {
+ Object val = arg.evaluate(context, pos, len);
+ double n = _number(context, val);
+ return new Double(Math.floor(n));
+ }
+
+ public Expr clone(Object context)
+ {
+ return new FloorFunction(arg.clone(context));
+ }
+
+ public String toString()
+ {
+ return "floor(" + arg + ")";
+ }
+
+}
diff --git a/libjava/gnu/xml/xpath/Function.java b/libjava/gnu/xml/xpath/Function.java
new file mode 100644
index 0000000..d40dff6
--- /dev/null
+++ b/libjava/gnu/xml/xpath/Function.java
@@ -0,0 +1,57 @@
+/* Function.java --
+ Copyright (C) 2004 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.xpath;
+
+import java.util.List;
+
+/**
+ * Interface to be implemented by external functions that need to receive
+ * parameter values.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+public interface Function
+{
+
+ /**
+ * Sets the list of expressions to evaluate as parameter values.
+ */
+ void setArguments(List args);
+
+}
+
diff --git a/libjava/gnu/xml/xpath/FunctionCall.java b/libjava/gnu/xml/xpath/FunctionCall.java
new file mode 100644
index 0000000..2c77b58
--- /dev/null
+++ b/libjava/gnu/xml/xpath/FunctionCall.java
@@ -0,0 +1,151 @@
+/* FunctionCall.java --
+ Copyright (C) 2004 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.xpath;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import javax.xml.namespace.QName;
+import javax.xml.xpath.XPathFunction;
+import javax.xml.xpath.XPathFunctionException;
+import javax.xml.xpath.XPathFunctionResolver;
+import org.w3c.dom.Node;
+
+/**
+ * Executes an XPath core or extension function.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+public final class FunctionCall
+ extends Expr
+{
+
+ final XPathFunctionResolver resolver;
+ final String name;
+ final List args;
+
+ public FunctionCall(XPathFunctionResolver resolver, String name)
+ {
+ this(resolver, name, Collections.EMPTY_LIST);
+ }
+
+ public FunctionCall(XPathFunctionResolver resolver, String name, List args)
+ {
+ this.resolver = resolver;
+ this.name = name;
+ this.args = args;
+ }
+
+ public Object evaluate(Node context, int pos, int len)
+ {
+ if (resolver != null)
+ {
+ QName qname = QName.valueOf(name);
+ int arity = args.size();
+ XPathFunction function = resolver.resolveFunction(qname, arity);
+ if (function != null)
+ {
+ //System.err.println("Calling "+toString()+" with "+values);
+ if (function instanceof Expr)
+ {
+ if (function instanceof Function)
+ {
+ ((Function) function).setArguments(args);
+ }
+ return ((Expr) function).evaluate(context, pos, len);
+ }
+ else
+ {
+ List values = new ArrayList(arity);
+ for (int i = 0; i < arity; i++)
+ {
+ Expr arg = (Expr) args.get(i);
+ values.add(arg.evaluate(context, pos, len));
+ }
+ try
+ {
+ return function.evaluate(values);
+ }
+ catch (XPathFunctionException e)
+ {
+ e.printStackTrace(System.err); // FIXME
+ throw new RuntimeException(e.getMessage(), e);
+ }
+ }
+ }
+ }
+ throw new IllegalArgumentException("Invalid function call: " +
+ toString());
+ }
+
+ public Expr clone(Object context)
+ {
+ int len = args.size();
+ List args2 = new ArrayList(len);
+ for (int i = 0; i < len; i++)
+ {
+ args2.add(((Expr) args.get(i)).clone(context));
+ }
+ XPathFunctionResolver r = resolver;
+ if (context instanceof XPathFunctionResolver)
+ {
+ r = (XPathFunctionResolver) context;
+ }
+ return new FunctionCall(r, name, args2);
+ }
+
+ public String toString()
+ {
+ StringBuffer buf = new StringBuffer();
+ buf.append(name);
+ buf.append('(');
+ int len = args.size();
+ for (int i = 0; i < len; i++)
+ {
+ if (i > 0)
+ {
+ buf.append(',');
+ }
+ buf.append(args.get(i));
+ }
+ buf.append(')');
+ return buf.toString();
+ }
+
+}
diff --git a/libjava/gnu/xml/xpath/IdFunction.java b/libjava/gnu/xml/xpath/IdFunction.java
new file mode 100644
index 0000000..a32cb89
--- /dev/null
+++ b/libjava/gnu/xml/xpath/IdFunction.java
@@ -0,0 +1,96 @@
+/* IdFunction.java --
+ Copyright (C) 2004 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.xpath;
+
+import java.util.Collection;
+import java.util.List;
+import org.w3c.dom.Node;
+
+/**
+ * The <code>id</code> function selects elements by their unique ID.
+ * When the argument to id is of type node-set, then the result is
+ * the union of the result of applying id to the string-value of each of the
+ * nodes in the argument node-set. When the argument to id is of any other
+ * type, the argument is converted to a string as if by a call to the string
+ * function; the string is split into a whitespace-separated list of tokens
+ * (whitespace is any sequence of characters matching the production S); the
+ * result is a node-set containing the elements in the same document as the
+ * context node that have a unique ID equal to any of the tokens in the
+ * list.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+public final class IdFunction
+ extends Pattern
+{
+
+ final Expr arg;
+
+ IdFunction(List args)
+ {
+ this((Expr) args.get(0));
+ }
+
+ public IdFunction(Expr arg)
+ {
+ this.arg = arg;
+ }
+
+ public boolean matches(Node context)
+ {
+ Object ret = evaluate(context, 1, 1);
+ return !((Collection) ret).isEmpty();
+ }
+
+ public Object evaluate(Node context, int pos, int len)
+ {
+ Object val = arg.evaluate(context, pos, len);
+ return _id(context, val);
+ }
+
+ public Expr clone(Object context)
+ {
+ return new IdFunction(arg.clone(context));
+ }
+
+ public String toString()
+ {
+ return "id(" + arg + ")";
+ }
+
+}
diff --git a/libjava/gnu/xml/xpath/LangFunction.java b/libjava/gnu/xml/xpath/LangFunction.java
new file mode 100644
index 0000000..d44c72c
--- /dev/null
+++ b/libjava/gnu/xml/xpath/LangFunction.java
@@ -0,0 +1,110 @@
+/* LangFunction.java --
+ Copyright (C) 2004 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.xpath;
+
+import java.util.List;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+
+/**
+ * The <code>lang</code> function returns true or false depending on whether
+ * the language of the context node as specified by xml:lang attributes is
+ * the same as or is a sublanguage of the language specified by the argument
+ * string. The language of the context node is determined by the value of
+ * the xml:lang attribute on the context node, or, if the context node has
+ * no xml:lang attribute, by the value of the xml:lang attribute on the
+ * nearest ancestor of the context node that has an xml:lang attribute. If
+ * there is no such attribute, then lang returns false. If there is such an
+ * attribute, then lang returns true if the attribute value is equal to the
+ * argument ignoring case, or if there is some suffix starting with - such
+ * that the attribute value is equal to the argument ignoring that suffix of
+ * the attribute value and ignoring case.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+final class LangFunction
+ extends Expr
+{
+
+ final Expr arg;
+
+ LangFunction(List args)
+ {
+ this((Expr) args.get(0));
+ }
+
+ LangFunction(Expr arg)
+ {
+ this.arg = arg;
+ }
+
+ public Object evaluate(Node context, int pos, int len)
+ {
+ Object val = arg.evaluate(context, pos, len);
+ String lang = _string(context, val);
+ String clang = getLang(context);
+ while (clang == null && context != null)
+ {
+ context = context.getParentNode();
+ clang = getLang(context);
+ }
+ boolean ret = (clang == null) ? false :
+ clang.toLowerCase().startsWith(lang.toLowerCase());
+ return ret ? Boolean.TRUE : Boolean.FALSE;
+ }
+
+ String getLang(Node node)
+ {
+ if (node.getNodeType() == Node.ELEMENT_NODE)
+ {
+ return ((Element) node).getAttribute("xml:lang");
+ }
+ return null;
+ }
+
+ public Expr clone(Object context)
+ {
+ return new IdFunction(arg.clone(context));
+ }
+
+ public String toString()
+ {
+ return "lang(" + arg + ")";
+ }
+
+}
diff --git a/libjava/gnu/xml/xpath/LastFunction.java b/libjava/gnu/xml/xpath/LastFunction.java
new file mode 100644
index 0000000..a7458a3
--- /dev/null
+++ b/libjava/gnu/xml/xpath/LastFunction.java
@@ -0,0 +1,67 @@
+/* LastFunction.java --
+ Copyright (C) 2004 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.xpath;
+
+import org.w3c.dom.Node;
+
+/**
+ * The <code>last</code> function returns a number equal to the context
+ * size from the expression evaluation context.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+final class LastFunction
+ extends Expr
+{
+
+ public Object evaluate(Node context, int pos, int len)
+ {
+ return new Double((double) len);
+ }
+
+ public Expr clone(Object context)
+ {
+ return new LastFunction();
+ }
+
+ public String toString()
+ {
+ return "last()";
+ }
+
+}
diff --git a/libjava/gnu/xml/xpath/LocalNameFunction.java b/libjava/gnu/xml/xpath/LocalNameFunction.java
new file mode 100644
index 0000000..d91f973
--- /dev/null
+++ b/libjava/gnu/xml/xpath/LocalNameFunction.java
@@ -0,0 +1,87 @@
+/* LocalNameFunction.java --
+ Copyright (C) 2004 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.xpath;
+
+import java.util.Collection;
+import java.util.List;
+import org.w3c.dom.Node;
+
+/**
+ * The <code>local-name</code> function returns the local part of the
+ * expanded-name of the node in the argument node-set that is first in
+ * document order.
+ * If the argument node-set is empty or the first node has no expanded-name,
+ * an empty string is returned. If the argument is omitted, it defaults to a
+ * node-set with the context node as its only member.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+final class LocalNameFunction
+ extends Expr
+{
+
+ final Expr arg;
+
+ LocalNameFunction(List args)
+ {
+ this(args.size() > 0 ? (Expr) args.get(0) : null);
+ }
+
+ LocalNameFunction(Expr arg)
+ {
+ this.arg = arg;
+ }
+
+ public Object evaluate(Node context, int pos, int len)
+ {
+ Object val = (arg == null) ? null : arg.evaluate(context, pos, len);
+ return _local_name(context, (Collection) val);
+ }
+
+ public Expr clone(Object context)
+ {
+ return new LocalNameFunction((arg == null) ? null :
+ arg.clone(context));
+ }
+
+ public String toString()
+ {
+ return (arg == null) ? "local-name()" : "local-name(" + arg + ")";
+ }
+
+}
diff --git a/libjava/gnu/xml/xpath/NameFunction.java b/libjava/gnu/xml/xpath/NameFunction.java
new file mode 100644
index 0000000..a42681d
--- /dev/null
+++ b/libjava/gnu/xml/xpath/NameFunction.java
@@ -0,0 +1,95 @@
+/* NameFunction.java --
+ Copyright (C) 2004 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.xpath;
+
+import java.util.Collection;
+import java.util.List;
+import org.w3c.dom.Node;
+
+/**
+ * The <code>name</code> function returns a string containing a QName
+ * representing the expanded-name of the node in the argument node-set that
+ * is first in document order. The QName must represent the expanded-name
+ * with respect to the namespace declarations in effect on the node whose
+ * expanded-name is being represented. Typically, this will be the QName
+ * that occurred in the XML source. This need not be the case if there are
+ * namespace declarations in effect on the node that associate multiple
+ * prefixes with the same namespace. However, an implementation may include
+ * information about the original prefix in its representation of nodes; in
+ * this case, an implementation can ensure that the returned string is
+ * always the same as the QName used in the XML source. If the argument
+ * node-set is empty or the first node has no expanded-name, an empty string
+ * is returned. If the argument it omitted, it defaults to a node-set with
+ * the context node as its only member.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+final class NameFunction
+ extends Expr
+{
+
+ final Expr arg;
+
+ NameFunction(List args)
+ {
+ this(args.size() > 0 ? (Expr) args.get(0) : null);
+ }
+
+ NameFunction(Expr arg)
+ {
+ this.arg = arg;
+ }
+
+ public Object evaluate(Node context, int pos, int len)
+ {
+ Object val = (arg == null) ? null : arg.evaluate(context, pos, len);
+ return _name(context, (Collection) val);
+ }
+
+ public Expr clone(Object context)
+ {
+ return new NameFunction((arg == null) ? null :
+ arg.clone(context));
+ }
+
+ public String toString()
+ {
+ return (arg == null) ? "name()" : "name(" + arg + ")";
+ }
+
+}
diff --git a/libjava/gnu/xml/xpath/NameTest.java b/libjava/gnu/xml/xpath/NameTest.java
new file mode 100644
index 0000000..ececc92
--- /dev/null
+++ b/libjava/gnu/xml/xpath/NameTest.java
@@ -0,0 +1,131 @@
+/* NameTest.java --
+ Copyright (C) 2004 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.xpath;
+
+import javax.xml.XMLConstants;
+import javax.xml.namespace.QName;
+import org.w3c.dom.Node;
+
+/**
+ * Tests whether a node has the specified name.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+public final class NameTest
+ extends Test
+{
+
+ final QName qName;
+ final boolean anyLocalName;
+ final boolean any;
+
+ public NameTest(QName qName, boolean anyLocalName, boolean any)
+ {
+ this.anyLocalName = anyLocalName;
+ this.any = any;
+ this.qName = qName;
+ }
+
+ public boolean matchesAny()
+ {
+ return any;
+ }
+
+ public boolean matchesAnyLocalName()
+ {
+ return anyLocalName;
+ }
+
+ public boolean matches(Node node, int pos, int len)
+ {
+ switch (node.getNodeType())
+ {
+ case Node.ATTRIBUTE_NODE:
+ // Do not match namespace attributes
+ String uri = node.getNamespaceURI();
+ if (XMLConstants.XMLNS_ATTRIBUTE_NS_URI.equals(uri) ||
+ XMLConstants.XMLNS_ATTRIBUTE.equals(node.getPrefix()) ||
+ XMLConstants.XMLNS_ATTRIBUTE.equals(node.getNodeName()))
+ {
+ return false;
+ }
+ // Fall through
+ case Node.ELEMENT_NODE:
+ break;
+ default:
+ return false;
+ }
+ if (any)
+ {
+ return true;
+ }
+ String uri = qName.getNamespaceURI();
+ if (!equal(uri, node.getNamespaceURI()))
+ {
+ return false;
+ }
+ if (anyLocalName)
+ {
+ return true;
+ }
+ String localName = qName.getLocalPart();
+ return (localName.equals(node.getLocalName()));
+ }
+
+ final boolean equal(String s1, String s2)
+ {
+ return (((s1 == null || s1.length() == 0) &&
+ (s2 == null || s2.length() == 0)) ||
+ s1 != null && s1.equals(s2));
+ }
+
+ public Test clone(Object context)
+ {
+ return new NameTest(qName, anyLocalName, any);
+ }
+
+ public String toString ()
+ {
+ if (any)
+ {
+ return "*";
+ }
+ return qName.toString();
+ }
+
+}
diff --git a/libjava/gnu/xml/xpath/NamespaceTest.java b/libjava/gnu/xml/xpath/NamespaceTest.java
new file mode 100644
index 0000000..bdfe961
--- /dev/null
+++ b/libjava/gnu/xml/xpath/NamespaceTest.java
@@ -0,0 +1,118 @@
+/* NamespaceTest.java --
+ Copyright (C) 2004 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.xpath;
+
+import javax.xml.XMLConstants;
+import javax.xml.namespace.QName;
+import org.w3c.dom.Node;
+
+/**
+ * Tests whether a namespace attribute has the specified name.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+public final class NamespaceTest
+ extends Test
+{
+
+ final QName qName;
+ final boolean anyLocalName;
+ final boolean any;
+
+ public NamespaceTest(QName qName, boolean anyLocalName, boolean any)
+ {
+ this.anyLocalName = anyLocalName;
+ this.any = any;
+ this.qName = qName;
+ }
+
+ public boolean matchesAny()
+ {
+ return any;
+ }
+
+ public boolean matchesAnyLocalName()
+ {
+ return anyLocalName;
+ }
+
+ public boolean matches(Node node, int pos, int len)
+ {
+ switch (node.getNodeType())
+ {
+ case Node.ATTRIBUTE_NODE:
+ // Only match namespace attributes
+ String uri = node.getNamespaceURI();
+ if (XMLConstants.XMLNS_ATTRIBUTE_NS_URI.equals(uri) ||
+ XMLConstants.XMLNS_ATTRIBUTE.equals(node.getPrefix()) ||
+ XMLConstants.XMLNS_ATTRIBUTE.equals(node.getNodeName()))
+ {
+ break;
+ }
+ // Fall through
+ default:
+ // Only process namespace attributes
+ return false;
+ }
+ if (any)
+ {
+ return true;
+ }
+ if (anyLocalName)
+ {
+ return true;
+ }
+ String localName = qName.getLocalPart();
+ return (localName.equals(node.getLocalName()));
+ }
+
+ public Test clone(Object context)
+ {
+ return new NamespaceTest(qName, anyLocalName, any);
+ }
+
+ public String toString ()
+ {
+ if (any)
+ {
+ return "*";
+ }
+ return qName.toString();
+ }
+
+}
diff --git a/libjava/gnu/xml/xpath/NamespaceUriFunction.java b/libjava/gnu/xml/xpath/NamespaceUriFunction.java
new file mode 100644
index 0000000..ee8978e
--- /dev/null
+++ b/libjava/gnu/xml/xpath/NamespaceUriFunction.java
@@ -0,0 +1,87 @@
+/* NamespaceUriFunction.java --
+ Copyright (C) 2004 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.xpath;
+
+import java.util.Collection;
+import java.util.List;
+import org.w3c.dom.Node;
+
+/**
+ * The <code>namespace-uri</code> function returns the namespace URI of the
+ * expanded-name of the node in the argument node-set that is first in
+ * document order. If the argument node-set is empty, the first node has no
+ * expanded-name, or the namespace URI of the expanded-name is null, an
+ * empty string is returned. If the argument is omitted, it defaults to a
+ * node-set with the context node as its only member.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+final class NamespaceUriFunction
+ extends Expr
+{
+
+ final Expr arg;
+
+ NamespaceUriFunction(List args)
+ {
+ this(args.size() > 0 ? (Expr) args.get(0) : null);
+ }
+
+ NamespaceUriFunction(Expr arg)
+ {
+ this.arg = arg;
+ }
+
+ public Object evaluate(Node context, int pos, int len)
+ {
+ Object val = (arg == null) ? null : arg.evaluate(context, pos, len);
+ return _namespace_uri(context, (Collection) val);
+ }
+
+ public Expr clone(Object context)
+ {
+ return new NamespaceUriFunction((arg == null) ? null :
+ arg.clone(context));
+ }
+
+ public String toString()
+ {
+ return (arg == null) ? "namespace-uri()" : "namespace-uri(" + arg + ")";
+ }
+
+}
diff --git a/libjava/gnu/xml/xpath/NegativeExpr.java b/libjava/gnu/xml/xpath/NegativeExpr.java
new file mode 100644
index 0000000..5d7c00d
--- /dev/null
+++ b/libjava/gnu/xml/xpath/NegativeExpr.java
@@ -0,0 +1,75 @@
+/* NegativeExpr.java --
+ Copyright (C) 2004 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.xpath;
+
+import org.w3c.dom.Node;
+
+/**
+ * Unary negative.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+final class NegativeExpr
+ extends Expr
+{
+
+ final Expr expr;
+
+ NegativeExpr(Expr expr)
+ {
+ this.expr = expr;
+ }
+
+ public Object evaluate(Node context, int pos, int len)
+ {
+ Object val = expr.evaluate(context, pos, len);
+ double n = _number(context, val);
+ return new Double(-n);
+ }
+
+ public Expr clone(Object context)
+ {
+ return new NegativeExpr(expr.clone(context));
+ }
+
+ public String toString()
+ {
+ return "-" + expr;
+ }
+
+}
diff --git a/libjava/gnu/xml/xpath/NodeTypeTest.java b/libjava/gnu/xml/xpath/NodeTypeTest.java
new file mode 100644
index 0000000..d7798a2
--- /dev/null
+++ b/libjava/gnu/xml/xpath/NodeTypeTest.java
@@ -0,0 +1,130 @@
+/* NodeTypeTest.java --
+ Copyright (C) 2004 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.xpath;
+
+import org.w3c.dom.Node;
+
+/**
+ * Tests whether a node is of a given type.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+public final class NodeTypeTest
+ extends Test
+{
+
+ final short type;
+ final String data;
+
+ public NodeTypeTest(short type)
+ {
+ this(type, null);
+ }
+
+ public NodeTypeTest(short type, String data)
+ {
+ this.type = type;
+ this.data = data;
+ }
+
+ public short getNodeType()
+ {
+ return type;
+ }
+
+ public String getData()
+ {
+ return data;
+ }
+
+ public boolean matches(Node node, int pos, int len)
+ {
+ short nodeType = node.getNodeType();
+ switch (nodeType)
+ {
+ case Node.ELEMENT_NODE:
+ case Node.ATTRIBUTE_NODE:
+ case Node.TEXT_NODE:
+ case Node.CDATA_SECTION_NODE:
+ case Node.COMMENT_NODE:
+ case Node.PROCESSING_INSTRUCTION_NODE:
+ if (type > 0)
+ {
+ if (nodeType != type)
+ {
+ return false;
+ }
+ if (data != null && !data.equals(node.getNodeValue()))
+ {
+ return false;
+ }
+ }
+ return true;
+ default:
+ // Not part of XPath data model
+ return false;
+ }
+ }
+
+ public Test clone(Object context)
+ {
+ return new NodeTypeTest(type, data);
+ }
+
+ public String toString()
+ {
+ switch (type)
+ {
+ case 0:
+ return "node()";
+ case Node.TEXT_NODE:
+ return "text()";
+ case Node.COMMENT_NODE:
+ return "comment()";
+ case Node.PROCESSING_INSTRUCTION_NODE:
+ if (data != null)
+ {
+ return "processing-instruction('" + data + "')";
+ }
+ return "processing-instruction()";
+ default:
+ throw new IllegalStateException();
+ }
+ }
+
+}
diff --git a/libjava/gnu/xml/xpath/NormalizeSpaceFunction.java b/libjava/gnu/xml/xpath/NormalizeSpaceFunction.java
new file mode 100644
index 0000000..2dabe1e
--- /dev/null
+++ b/libjava/gnu/xml/xpath/NormalizeSpaceFunction.java
@@ -0,0 +1,99 @@
+/* NormalizeSpaceFunction.java --
+ Copyright (C) 2004 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.xpath;
+
+import java.util.List;
+import java.util.StringTokenizer;
+import org.w3c.dom.Node;
+
+/**
+ * The <code>normalize-space</code> function returns the argument string
+ * with whitespace normalized by stripping leading and trailing whitespace
+ * and replacing sequences of whitespace characters by a single space.
+ * Whitespace characters are the same as those allowed by the S production
+ * in XML. If the argument is omitted, it defaults to the context node
+ * converted to a string, in other words the string-value of the context
+ * node.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+final class NormalizeSpaceFunction
+ extends Expr
+{
+
+ final Expr arg;
+
+ NormalizeSpaceFunction(List args)
+ {
+ this((Expr) args.get(0));
+ }
+
+ NormalizeSpaceFunction(Expr arg)
+ {
+ this.arg = arg;
+ }
+
+ public Object evaluate(Node context, int pos, int len)
+ {
+ Object val = (arg == null) ? null : arg.evaluate(context, pos, len);
+ String s = _string(context, val);
+ StringTokenizer st = new StringTokenizer(s, " \t\r\n");
+ StringBuffer buf = new StringBuffer();
+ if (st.hasMoreTokens())
+ {
+ buf.append(st.nextToken());
+ while (st.hasMoreTokens())
+ {
+ buf.append(' ');
+ buf.append(st.nextToken());
+ }
+ }
+ return buf.toString();
+ }
+
+ public Expr clone(Object context)
+ {
+ return new NormalizeSpaceFunction(arg.clone(context));
+ }
+
+ public String toString()
+ {
+ return (arg == null) ? "normalize-space()" : "normalize-space(" + arg + ")";
+ }
+
+}
diff --git a/libjava/gnu/xml/xpath/NotFunction.java b/libjava/gnu/xml/xpath/NotFunction.java
new file mode 100644
index 0000000..d7d1ad1
--- /dev/null
+++ b/libjava/gnu/xml/xpath/NotFunction.java
@@ -0,0 +1,81 @@
+/* NotFunction.java --
+ Copyright (C) 2004 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.xpath;
+
+import java.util.List;
+import org.w3c.dom.Node;
+
+/**
+ * The <code>not</code> function returns true if its argument is false,
+ * and false otherwise.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+final class NotFunction
+ extends Expr
+{
+
+ final Expr arg;
+
+ NotFunction(List args)
+ {
+ this((Expr) args.get(0));
+ }
+
+ NotFunction(Expr arg)
+ {
+ this.arg = arg;
+ }
+
+ public Object evaluate(Node context, int pos, int len)
+ {
+ Object val = arg.evaluate(context, pos, len);
+ return _boolean(context, val) ? Boolean.FALSE : Boolean.TRUE;
+ }
+
+ public Expr clone(Object context)
+ {
+ return new NotFunction(arg.clone(context));
+ }
+
+ public String toString()
+ {
+ return "not(" + arg + ")";
+ }
+
+}
diff --git a/libjava/gnu/xml/xpath/NumberFunction.java b/libjava/gnu/xml/xpath/NumberFunction.java
new file mode 100644
index 0000000..9436533
--- /dev/null
+++ b/libjava/gnu/xml/xpath/NumberFunction.java
@@ -0,0 +1,96 @@
+/* NumberFunction.java --
+ Copyright (C) 2004 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.xpath;
+
+import java.util.List;
+import org.w3c.dom.Node;
+
+/**
+ * The <code>number</code> function converts its argument to a number as
+ * follows:
+ * <ul>
+ * <li>a string that consists of optional whitespace followed by an optional
+ * minus sign followed by a Number followed by whitespace is converted to
+ * the IEEE 754 number that is nearest (according to the IEEE 754
+ * round-to-nearest rule) to the mathematical value represented by the
+ * string; any other string is converted to NaN</li>
+ * <li>boolean true is converted to 1; boolean false is converted to 0</li>
+ * <li>a node-set is first converted to a string as if by a call to the
+ * string function and then converted in the same way as a string
+ * argument</li>
+ * <li>an object of a type other than the four basic types is converted to a
+ * number in a way that is dependent on that type</li>
+ * </ul>
+ * If the argument is omitted, it defaults to a node-set with the context
+ * node as its only member.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+final class NumberFunction
+ extends Expr
+{
+
+ final Expr arg;
+
+ NumberFunction(List args)
+ {
+ this(args.size() > 0 ? (Expr) args.get(0) : null);
+ }
+
+ NumberFunction(Expr arg)
+ {
+ this.arg = arg;
+ }
+
+ public Object evaluate(Node context, int pos, int len)
+ {
+ Object val = (arg == null) ? null : arg.evaluate(context, pos, len);
+ return new Double(_number(context, val));
+ }
+
+ public Expr clone(Object context)
+ {
+ return new NumberFunction(arg.clone(context));
+ }
+
+ public String toString()
+ {
+ return "number(" + arg + ")";
+ }
+
+}
diff --git a/libjava/gnu/xml/xpath/OrExpr.java b/libjava/gnu/xml/xpath/OrExpr.java
new file mode 100644
index 0000000..4cfda4a
--- /dev/null
+++ b/libjava/gnu/xml/xpath/OrExpr.java
@@ -0,0 +1,81 @@
+/* OrExpr.java --
+ Copyright (C) 2004 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.xpath;
+
+import org.w3c.dom.Node;
+
+/**
+ * Logical or.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+public final class OrExpr
+ extends Expr
+{
+
+ final Expr lhs;
+ final Expr rhs;
+
+ public OrExpr(Expr lhs, Expr rhs)
+ {
+ this.lhs = lhs;
+ this.rhs = rhs;
+ }
+
+ public Object evaluate(Node context, int pos, int len)
+ {
+ Object left = lhs.evaluate(context, pos, len);
+ if (_boolean(context, left))
+ {
+ return Boolean.TRUE;
+ }
+ Object right = rhs.evaluate(context, pos, len);
+ return _boolean(context, right) ? Boolean.TRUE : Boolean.FALSE;
+ }
+
+ public Expr clone(Object context)
+ {
+ return new OrExpr(lhs.clone(context), rhs.clone(context));
+ }
+
+ public String toString()
+ {
+ return lhs + " or " + rhs;
+ }
+
+}
diff --git a/libjava/gnu/xml/xpath/ParenthesizedExpr.java b/libjava/gnu/xml/xpath/ParenthesizedExpr.java
new file mode 100644
index 0000000..6c2d571
--- /dev/null
+++ b/libjava/gnu/xml/xpath/ParenthesizedExpr.java
@@ -0,0 +1,84 @@
+/* ParenthesizedExpr.java --
+ Copyright (C) 2004 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.xpath;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import org.w3c.dom.Node;
+
+/**
+ * Simple subexpression.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+final class ParenthesizedExpr
+ extends Expr
+{
+
+ final Expr expr;
+
+ ParenthesizedExpr(Expr expr)
+ {
+ this.expr = expr;
+ }
+
+ public Object evaluate(Node context, int pos, int len)
+ {
+ Object ret = expr.evaluate(context, pos, len);
+ if (ret instanceof Collection)
+ {
+ List list = new ArrayList((Collection) ret);
+ Collections.sort(list, documentOrderComparator);
+ ret = list;
+ }
+ return ret;
+ }
+
+ public Expr clone(Object context)
+ {
+ return new ParenthesizedExpr(expr.clone(context));
+ }
+
+ public String toString()
+ {
+ return "(" + expr + ")";
+ }
+
+}
diff --git a/libjava/gnu/xml/xpath/Path.java b/libjava/gnu/xml/xpath/Path.java
new file mode 100644
index 0000000..488215e
--- /dev/null
+++ b/libjava/gnu/xml/xpath/Path.java
@@ -0,0 +1,54 @@
+/* Path.java --
+ Copyright (C) 2004 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.xpath;
+
+import java.util.Collection;
+import org.w3c.dom.Node;
+
+/**
+ * An XPath path component expression.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+abstract class Path
+ extends Pattern
+{
+
+ abstract Collection evaluate(Node context, Collection nodeSet);
+
+}
diff --git a/libjava/gnu/xml/xpath/Pattern.java b/libjava/gnu/xml/xpath/Pattern.java
new file mode 100644
index 0000000..dcce68f
--- /dev/null
+++ b/libjava/gnu/xml/xpath/Pattern.java
@@ -0,0 +1,54 @@
+/* Pattern.java --
+ Copyright (C) 2004 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.xpath;
+
+import org.w3c.dom.Node;
+
+/**
+ * Interface implemented by expressions that can for part of XSL patterns.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+public abstract class Pattern
+ extends Expr
+{
+
+ public abstract boolean matches(Node context);
+
+}
+
diff --git a/libjava/gnu/xml/xpath/PositionFunction.java b/libjava/gnu/xml/xpath/PositionFunction.java
new file mode 100644
index 0000000..6d6f7ad
--- /dev/null
+++ b/libjava/gnu/xml/xpath/PositionFunction.java
@@ -0,0 +1,67 @@
+/* PositionFunction.java --
+ Copyright (C) 2004 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.xpath;
+
+import org.w3c.dom.Node;
+
+/**
+ * The <code>position</code> function returns a number equal to the context
+ * position from the expression evaluation context.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+final class PositionFunction
+ extends Expr
+{
+
+ public Object evaluate(Node context, int pos, int len)
+ {
+ return new Double((double) pos);
+ }
+
+ public Expr clone(Object context)
+ {
+ return new PositionFunction();
+ }
+
+ public String toString()
+ {
+ return "position()";
+ }
+
+}
diff --git a/libjava/gnu/xml/xpath/Predicate.java b/libjava/gnu/xml/xpath/Predicate.java
new file mode 100644
index 0000000..81e1755
--- /dev/null
+++ b/libjava/gnu/xml/xpath/Predicate.java
@@ -0,0 +1,79 @@
+/* Predicate.java --
+ Copyright (C) 2004 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.xpath;
+
+import org.w3c.dom.Node;
+
+/**
+ * Tests whether an expression matches against a given context node.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+class Predicate
+ extends Test
+{
+
+ final Expr expr;
+
+ Predicate(Expr expr)
+ {
+ this.expr = expr;
+ }
+
+ public boolean matches(Node node, int pos, int len)
+ {
+ Object ret = expr.evaluate(node, pos, len);
+ if (ret instanceof Double)
+ {
+ // Same as [position() = x]
+ return ((Double) ret).intValue() == pos;
+ }
+ return expr._boolean(node, expr.evaluate(node, pos, len));
+ }
+
+ public Test clone(Object context)
+ {
+ return new Predicate(expr.clone(context));
+ }
+
+ public String toString()
+ {
+ return "[" + expr + "]";
+ }
+
+}
diff --git a/libjava/gnu/xml/xpath/RelationalExpr.java b/libjava/gnu/xml/xpath/RelationalExpr.java
new file mode 100644
index 0000000..e039cc1
--- /dev/null
+++ b/libjava/gnu/xml/xpath/RelationalExpr.java
@@ -0,0 +1,102 @@
+/* RelationalExpr.java --
+ Copyright (C) 2004 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.xpath;
+
+import java.util.Collection;
+import org.w3c.dom.Node;
+
+/**
+ * Numerical comparison expression.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+final class RelationalExpr
+ extends Expr
+{
+
+ final Expr lhs;
+ final Expr rhs;
+ final boolean lt;
+ final boolean eq;
+
+ RelationalExpr(Expr lhs, Expr rhs, boolean lt, boolean eq)
+ {
+ this.lhs = lhs;
+ this.rhs = rhs;
+ this.lt = lt;
+ this.eq = eq;
+ }
+
+ public Object evaluate(Node context, int pos, int len)
+ {
+ Object left = lhs.evaluate(context, pos, len);
+ Object right = rhs.evaluate(context, pos, len);
+ double ln = _number(context, left);
+ double rn = _number(context, right);
+ if (eq && ln == rn)
+ {
+ return Boolean.TRUE;
+ }
+ if (lt)
+ {
+ if (ln < rn || Double.isInfinite(rn))
+ {
+ return Boolean.TRUE;
+ }
+ }
+ else
+ {
+ if (ln > rn || Double.isInfinite(ln))
+ {
+ return Boolean.TRUE;
+ }
+ }
+ return Boolean.FALSE;
+ }
+
+ public Expr clone(Object context)
+ {
+ return new RelationalExpr(lhs.clone(context), rhs.clone(context), lt, eq);
+ }
+
+ public String toString()
+ {
+ return lhs + " " + (lt ? "<" : ">") + (eq ? "=" : "") + " " + rhs;
+ }
+
+}
diff --git a/libjava/gnu/xml/xpath/Root.java b/libjava/gnu/xml/xpath/Root.java
new file mode 100644
index 0000000..b8fe5df
--- /dev/null
+++ b/libjava/gnu/xml/xpath/Root.java
@@ -0,0 +1,81 @@
+/* Root.java --
+ Copyright (C) 2004 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.xpath;
+
+import java.util.Collection;
+import java.util.Collections;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+
+/**
+ * Expression that evaluates to the document root.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+public final class Root
+ extends Path
+{
+
+ public boolean matches(Node node)
+ {
+ return (node.getNodeType() == Node.DOCUMENT_NODE);
+ }
+
+ public Object evaluate(Node context, int pos, int len)
+ {
+ return evaluate(context, Collections.EMPTY_SET);
+ }
+
+ Collection evaluate(Node context, Collection ns)
+ {
+ Document doc = (context instanceof Document) ? (Document) context :
+ context.getOwnerDocument();
+ return Collections.singleton(doc);
+ }
+
+ public Expr clone(Object context)
+ {
+ return new Root();
+ }
+
+ public String toString()
+ {
+ return "/";
+ }
+
+}
diff --git a/libjava/gnu/xml/xpath/RoundFunction.java b/libjava/gnu/xml/xpath/RoundFunction.java
new file mode 100644
index 0000000..76f5d8a
--- /dev/null
+++ b/libjava/gnu/xml/xpath/RoundFunction.java
@@ -0,0 +1,90 @@
+/* RoundFunction.java --
+ Copyright (C) 2004 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.xpath;
+
+import java.util.List;
+import org.w3c.dom.Node;
+
+/**
+ * The <code>round</code> function returns the number that is closest to the
+ * argument and that is an integer. If there are two such numbers, then the
+ * one that is closest to positive infinity is returned. If the argument is
+ * NaN, then NaN is returned. If the argument is positive infinity, then
+ * positive infinity is returned. If the argument is negative infinity, then
+ * negative infinity is returned. If the argument is positive zero, then
+ * positive zero is returned. If the argument is negative zero, then
+ * negative zero is returned. If the argument is less than zero, but greater
+ * than or equal to -0.5, then negative zero is returned.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+final class RoundFunction
+ extends Expr
+{
+
+ final Expr arg;
+
+ RoundFunction(List args)
+ {
+ this((Expr) args.get(0));
+ }
+
+ RoundFunction(Expr arg)
+ {
+ this.arg = arg;
+ }
+
+ public Object evaluate(Node context, int pos, int len)
+ {
+ Object val = arg.evaluate(context, pos, len);
+ double n = _number(context, val);
+ return (Double.isNaN(n) || Double.isInfinite(n)) ?
+ new Double(n) : new Double(Math.round(n));
+ }
+
+ public Expr clone(Object context)
+ {
+ return new RoundFunction(arg.clone(context));
+ }
+
+ public String toString()
+ {
+ return "round(" + arg + ")";
+ }
+
+}
diff --git a/libjava/gnu/xml/xpath/Selector.java b/libjava/gnu/xml/xpath/Selector.java
new file mode 100644
index 0000000..5431cbe
--- /dev/null
+++ b/libjava/gnu/xml/xpath/Selector.java
@@ -0,0 +1,500 @@
+/* Selector.java --
+ Copyright (C) 2004 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.xpath;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
+import javax.xml.XMLConstants;
+import org.w3c.dom.Attr;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+/**
+ * A single component of a location path.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+public final class Selector
+ extends Path
+{
+
+ public static final int ANCESTOR = 0;
+ public static final int ANCESTOR_OR_SELF = 1;
+ public static final int ATTRIBUTE = 2;
+ public static final int CHILD = 3;
+ public static final int DESCENDANT = 4;
+ public static final int DESCENDANT_OR_SELF = 5;
+ public static final int FOLLOWING = 6;
+ public static final int FOLLOWING_SIBLING = 7;
+ public static final int NAMESPACE = 8;
+ public static final int PARENT = 9;
+ public static final int PRECEDING = 10;
+ public static final int PRECEDING_SIBLING = 11;
+ public static final int SELF = 12;
+
+ /**
+ * Axis to select nodes in.
+ */
+ final int axis;
+
+ /**
+ * List of tests to perform on candidates.
+ */
+ final Test[] tests;
+
+ public Selector(int axis, List tests)
+ {
+ this.axis = axis;
+ this.tests = new Test[tests.size()];
+ tests.toArray(this.tests);
+ if (axis == NAMESPACE &&
+ this.tests.length > 0 &&
+ this.tests[0] instanceof NameTest)
+ {
+ NameTest nt = (NameTest) this.tests[0];
+ this.tests[0] = new NamespaceTest(nt.qName, nt.anyLocalName, nt.any);
+ }
+ }
+
+ /**
+ * Returns the list of tests to perform on candidates.
+ */
+ public Test[] getTests()
+ {
+ return tests;
+ }
+
+ public boolean matches(Node context)
+ {
+ short nodeType = context.getNodeType();
+ switch (axis)
+ {
+ case CHILD:
+ if (nodeType == Node.ATTRIBUTE_NODE)
+ {
+ return false;
+ }
+ break;
+ case ATTRIBUTE:
+ case NAMESPACE:
+ if (nodeType != Node.ATTRIBUTE_NODE)
+ {
+ return false;
+ }
+ break;
+ case DESCENDANT_OR_SELF:
+ return true;
+ default:
+ return false;
+ }
+ int tlen = tests.length;
+ if (tlen > 0)
+ {
+ int pos = getContextPosition(context);
+ int len = getContextSize(context);
+ for (int j = 0; j < tlen && len > 0; j++)
+ {
+ Test test = tests[j];
+ if (!test.matches(context, pos, len))
+ {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ private int getContextPosition(Node ctx)
+ {
+ int pos = 1;
+ for (ctx = ctx.getPreviousSibling(); ctx != null;
+ ctx = ctx.getPreviousSibling())
+ {
+ pos++;
+ }
+ return pos;
+ }
+
+ private int getContextSize(Node ctx)
+ {
+ if (ctx.getNodeType() == Node.ATTRIBUTE_NODE)
+ {
+ Node parent = ((Attr) ctx).getOwnerElement();
+ return parent.getAttributes().getLength();
+ }
+ Node parent = ctx.getParentNode();
+ if (parent != null)
+ {
+ return parent.getChildNodes().getLength();
+ }
+ return 1;
+ }
+
+ public Object evaluate(Node context, int pos, int len)
+ {
+ Set acc = new LinkedHashSet();
+ addCandidates(context, acc);
+ List candidates = new ArrayList(acc);
+ //Collections.sort(candidates, documentOrderComparator);
+ List ret = filterCandidates(candidates, false);
+ return ret;
+ }
+
+ Collection evaluate(Node context, Collection ns)
+ {
+ Set acc = new LinkedHashSet();
+ for (Iterator i = ns.iterator(); i.hasNext(); )
+ {
+ addCandidates((Node) i.next(), acc);
+ }
+ List candidates = new ArrayList(acc);
+ //Collections.sort(candidates, documentOrderComparator);
+ List ret = filterCandidates(candidates, true);
+ return ret;
+ }
+
+ /**
+ * Filter the given list of candidates according to the node tests.
+ */
+ List filterCandidates(List candidates, boolean cascade)
+ {
+ int len = candidates.size();
+ int tlen = tests.length;
+ if (tlen > 0 && len > 0)
+ {
+ // Present the result of each successful generation to the next test
+ for (int j = 0; j < tlen && len > 0; j++)
+ {
+ Test test = tests[j];
+ List successful = new ArrayList(len);
+ for (int i = 0; i < len; i++)
+ {
+ Node node = (Node) candidates.get(i);
+ if (cascade)
+ {
+ // Documents and DocumentFragments should be considered
+ // if part of a location path where the axis involves
+ // the SELF concept
+ short nodeType = node.getNodeType();
+ if ((nodeType == Node.DOCUMENT_NODE ||
+ nodeType == Node.DOCUMENT_FRAGMENT_NODE) &&
+ (axis == DESCENDANT_OR_SELF ||
+ axis == ANCESTOR_OR_SELF ||
+ axis == SELF) &&
+ (tests.length == 1 &&
+ tests[0] instanceof NodeTypeTest &&
+ ((NodeTypeTest) tests[0]).type == (short) 0))
+ {
+ successful.add(node);
+ continue;
+ }
+ }
+ if (test.matches(node, i + 1, len))
+ {
+ successful.add(node);
+ }
+ /*
+ System.err.println("Testing "+node);
+ int p = getContextPosition(node);
+ int l = getContextSize(node);
+ if (test.matches(node, p, l))
+ {
+ successful.add(node);
+ }*/
+ }
+ candidates = successful;
+ len = candidates.size();
+ }
+ }
+ return candidates;
+ }
+
+ void addCandidates(Node context, Collection candidates)
+ {
+ // Build list of candidates
+ switch (axis)
+ {
+ case CHILD:
+ addChildNodes(context, candidates, false);
+ break;
+ case DESCENDANT:
+ addChildNodes(context, candidates, true);
+ break;
+ case DESCENDANT_OR_SELF:
+ candidates.add (context);
+ addChildNodes(context, candidates, true);
+ break;
+ case PARENT:
+ addParentNode(context, candidates, false);
+ break;
+ case ANCESTOR:
+ addParentNode(context, candidates, true);
+ break;
+ case ANCESTOR_OR_SELF:
+ candidates.add(context);
+ addParentNode(context, candidates, true);
+ break;
+ case FOLLOWING_SIBLING:
+ addFollowingNodes(context, candidates, false);
+ break;
+ case PRECEDING_SIBLING:
+ addPrecedingNodes(context, candidates, false);
+ break;
+ case FOLLOWING:
+ addFollowingNodes(context, candidates, true);
+ break;
+ case PRECEDING:
+ addPrecedingNodes(context, candidates, true);
+ break;
+ case ATTRIBUTE:
+ addAttributes(context, candidates);
+ break;
+ case NAMESPACE:
+ addNamespaceAttributes(context, candidates);
+ break;
+ case SELF:
+ candidates.add(context);
+ break;
+ }
+ }
+
+ void addChildNodes(Node context, Collection acc, boolean recurse)
+ {
+ Node child = context.getFirstChild();
+ while (child != null)
+ {
+ acc.add(child);
+ if (recurse)
+ {
+ addChildNodes(child, acc, recurse);
+ }
+ child = child.getNextSibling();
+ }
+ }
+
+ void addParentNode(Node context, Collection acc, boolean recurse)
+ {
+ Node parent = (context.getNodeType() == Node.ATTRIBUTE_NODE) ?
+ ((Attr) context).getOwnerElement() : context.getParentNode();
+ if (parent != null)
+ {
+ acc.add(parent);
+ if (recurse)
+ {
+ addParentNode(parent, acc, recurse);
+ }
+ }
+ }
+
+ void addFollowingNodes(Node context, Collection acc, boolean recurse)
+ {
+ Node cur = context.getNextSibling();
+ while (cur != null)
+ {
+ acc.add(cur);
+ if (recurse)
+ {
+ addChildNodes(cur, acc, true);
+ }
+ cur = cur.getNextSibling();
+ }
+ if (recurse)
+ {
+ context = (context.getNodeType() == Node.ATTRIBUTE_NODE) ?
+ ((Attr) context).getOwnerElement() : context.getParentNode();
+ if (context != null)
+ {
+ addFollowingNodes(context, acc, recurse);
+ }
+ }
+ }
+
+ void addPrecedingNodes(Node context, Collection acc, boolean recurse)
+ {
+ Node cur = context.getPreviousSibling();
+ while (cur != null)
+ {
+ acc.add(cur);
+ if (recurse)
+ {
+ addChildNodes(cur, acc, true);
+ }
+ cur = cur.getPreviousSibling();
+ }
+ if (recurse)
+ {
+ context = (context.getNodeType() == Node.ATTRIBUTE_NODE) ?
+ ((Attr) context).getOwnerElement() : context.getParentNode();
+ if (context != null)
+ {
+ addPrecedingNodes(context, acc, recurse);
+ }
+ }
+ }
+
+ void addAttributes(Node context, Collection acc)
+ {
+ NamedNodeMap attrs = context.getAttributes();
+ if (attrs != null)
+ {
+ int attrLen = attrs.getLength();
+ for (int i = 0; i < attrLen; i++)
+ {
+ Node attr = attrs.item(i);
+ if (!isNamespaceAttribute(attr))
+ {
+ acc.add(attr);
+ }
+ }
+ }
+ }
+
+ void addNamespaceAttributes(Node context, Collection acc)
+ {
+ NamedNodeMap attrs = context.getAttributes();
+ if (attrs != null)
+ {
+ int attrLen = attrs.getLength();
+ for (int i = 0; i < attrLen; i++)
+ {
+ Node attr = attrs.item(i);
+ if (isNamespaceAttribute(attr))
+ {
+ acc.add(attr);
+ }
+ }
+ }
+ }
+
+ final boolean isNamespaceAttribute(Node node)
+ {
+ String uri = node.getNamespaceURI();
+ return (XMLConstants.XMLNS_ATTRIBUTE_NS_URI.equals(uri) ||
+ XMLConstants.XMLNS_ATTRIBUTE.equals(node.getPrefix()) ||
+ XMLConstants.XMLNS_ATTRIBUTE.equals(node.getNodeName()));
+ }
+
+ public Expr clone(Object context)
+ {
+ int len = tests.length;
+ List tests2 = new ArrayList(len);
+ for (int i = 0; i < len; i++)
+ {
+ tests2.add(tests[i].clone(context));
+ }
+ return new Selector(axis, tests2);
+ }
+
+ public String toString()
+ {
+ StringBuffer buf = new StringBuffer();
+ switch (axis)
+ {
+ case ANCESTOR:
+ buf.append("ancestor::");
+ break;
+ case ANCESTOR_OR_SELF:
+ buf.append("ancestor-or-self::");
+ break;
+ case ATTRIBUTE:
+ buf.append("attribute::");
+ break;
+ case CHILD:
+ //buf.append("child::");
+ break;
+ case DESCENDANT:
+ buf.append("descendant::");
+ break;
+ case DESCENDANT_OR_SELF:
+ buf.append("descendant-or-self::");
+ break;
+ case FOLLOWING:
+ buf.append("following::");
+ break;
+ case FOLLOWING_SIBLING:
+ buf.append("following-sibling::");
+ break;
+ case NAMESPACE:
+ buf.append("namespace::");
+ break;
+ case PARENT:
+ if (tests.length == 0 ||
+ (tests[0] instanceof NodeTypeTest &&
+ ((NodeTypeTest) tests[0]).type == 0))
+ {
+ return "..";
+ }
+ buf.append("parent::");
+ break;
+ case PRECEDING:
+ buf.append("preceding::");
+ break;
+ case PRECEDING_SIBLING:
+ buf.append("preceding-sibling::");
+ break;
+ case SELF:
+ if (tests.length == 0 ||
+ (tests[0] instanceof NodeTypeTest &&
+ ((NodeTypeTest) tests[0]).type == 0))
+ {
+ return ".";
+ }
+ buf.append("self::");
+ break;
+ }
+ if (tests.length == 0)
+ {
+ buf.append('*');
+ }
+ else
+ {
+ for (int i = 0; i < tests.length; i++)
+ {
+ buf.append(tests[i]);
+ }
+ }
+ return buf.toString();
+ }
+
+}
diff --git a/libjava/gnu/xml/xpath/StartsWithFunction.java b/libjava/gnu/xml/xpath/StartsWithFunction.java
new file mode 100644
index 0000000..c748295
--- /dev/null
+++ b/libjava/gnu/xml/xpath/StartsWithFunction.java
@@ -0,0 +1,86 @@
+/* StartsWithFunction.java --
+ Copyright (C) 2004 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.xpath;
+
+import java.util.List;
+import org.w3c.dom.Node;
+
+/**
+ * The <code>starts-with</code> function returns true if the first argument
+ * string starts with the second argument string, and otherwise returns false.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+final class StartsWithFunction
+ extends Expr
+{
+
+ final Expr arg1;
+ final Expr arg2;
+
+ StartsWithFunction(List args)
+ {
+ this((Expr) args.get(0), (Expr) args.get(1));
+ }
+
+ StartsWithFunction(Expr arg1, Expr arg2)
+ {
+ this.arg1 = arg1;
+ this.arg2 = arg2;
+ }
+
+ public Object evaluate(Node context, int pos, int len)
+ {
+ Object val1 = arg1.evaluate(context, pos, len);
+ Object val2 = arg2.evaluate(context, pos, len);
+ String s1 = _string(context, val1);
+ String s2 = _string(context, val2);
+ return s1.startsWith(s2) ? Boolean.TRUE : Boolean.FALSE;
+ }
+
+ public Expr clone(Object context)
+ {
+ return new StartsWithFunction(arg1.clone(context), arg2.clone(context));
+ }
+
+ public String toString()
+ {
+ return "starts-with(" + arg1 + "," + arg2 + ")";
+ }
+
+}
diff --git a/libjava/gnu/xml/xpath/Steps.java b/libjava/gnu/xml/xpath/Steps.java
new file mode 100644
index 0000000..abd30e4
--- /dev/null
+++ b/libjava/gnu/xml/xpath/Steps.java
@@ -0,0 +1,240 @@
+/* Steps.java --
+ Copyright (C) 2004 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.xpath;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.LinkedList;
+import java.util.Set;
+import org.w3c.dom.Attr;
+import org.w3c.dom.Node;
+
+/**
+ * A list of transitions between components in a location path.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+public final class Steps
+ extends Path
+{
+
+ final LinkedList path;
+
+ public Steps()
+ {
+ this(new LinkedList());
+ }
+
+ Steps(LinkedList path)
+ {
+ this.path = path;
+ }
+
+ public boolean matches(Node context)
+ {
+ // Right to left
+ return matches(context, path.size() - 1);
+ }
+
+ boolean matches(Node context, int pos)
+ {
+ Pattern right = (Pattern) path.get(pos);
+ if (!right.matches(context))
+ {
+ return false;
+ }
+ if (pos > 0)
+ {
+ Pattern left = (Pattern) path.get(pos - 1);
+ Iterator j = possibleContexts(right, context).iterator();
+ while (j.hasNext())
+ {
+ Node candidate = (Node) j.next();
+ if (left.matches(candidate) &&
+ matches(candidate, pos - 1))
+ {
+ return true;
+ }
+ // keep going, there may be another candidate
+ }
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Essentially the reverse of Selector.addCandidates.
+ * The idea is to determine possible context nodes for a match.
+ */
+ Collection possibleContexts(Pattern pattern, Node context)
+ {
+ if (pattern instanceof Selector)
+ {
+ Selector s = (Selector) pattern;
+ Collection candidates = new LinkedHashSet();
+ switch (s.axis)
+ {
+ case Selector.PARENT:
+ s.addChildNodes(context, candidates, false);
+ break;
+ case Selector.ANCESTOR:
+ s.addChildNodes(context, candidates, true);
+ break;
+ case Selector.ANCESTOR_OR_SELF:
+ candidates.add (context);
+ s.addChildNodes(context, candidates, true);
+ break;
+ case Selector.CHILD:
+ s.addParentNode(context, candidates, false);
+ break;
+ case Selector.DESCENDANT:
+ s.addParentNode(context, candidates, true);
+ break;
+ case Selector.DESCENDANT_OR_SELF:
+ candidates.add(context);
+ s.addParentNode(context, candidates, true);
+ break;
+ case Selector.PRECEDING_SIBLING:
+ s.addFollowingNodes(context, candidates, false);
+ break;
+ case Selector.FOLLOWING_SIBLING:
+ s.addPrecedingNodes(context, candidates, false);
+ break;
+ case Selector.PRECEDING:
+ s.addFollowingNodes(context, candidates, true);
+ break;
+ case Selector.FOLLOWING:
+ s.addPrecedingNodes(context, candidates, true);
+ break;
+ case Selector.ATTRIBUTE:
+ case Selector.NAMESPACE:
+ if (context.getNodeType() == Node.ATTRIBUTE_NODE)
+ {
+ candidates.add(((Attr) context).getOwnerElement());
+ }
+ break;
+ case Selector.SELF:
+ candidates.add(context);
+ break;
+ }
+ return candidates;
+ }
+ return Collections.EMPTY_SET;
+ }
+
+ public Object evaluate(Node context, int pos, int len)
+ {
+ //System.err.println(toString()+" evaluate");
+ // Left to right
+ Iterator i = path.iterator();
+ Expr lhs = (Expr) i.next();
+ Object val = lhs.evaluate(context, pos, len);
+ //System.err.println("\tevaluate "+lhs+" = "+val);
+ while (val instanceof Collection && i.hasNext())
+ {
+ Path rhs = (Path) i.next();
+ val = rhs.evaluate(context, (Collection) val);
+ //System.err.println("\tevaluate "+rhs+" = "+val);
+ }
+ return val;
+ }
+
+ Collection evaluate(Node context, Collection ns)
+ {
+ // Left to right
+ Iterator i = path.iterator();
+ Expr lhs = (Expr) i.next();
+ if (lhs instanceof Path)
+ {
+ ns = ((Path) lhs).evaluate(context, ns);
+ }
+ else
+ {
+ Set acc = new LinkedHashSet();
+ int pos = 1, len = ns.size();
+ for (Iterator j = ns.iterator(); j.hasNext(); )
+ {
+ Node node = (Node) j.next();
+ Object ret = lhs.evaluate(node, pos++, len);
+ if (ret instanceof Collection)
+ {
+ acc.addAll((Collection) ret);
+ }
+ }
+ ns = acc;
+ }
+ while (i.hasNext())
+ {
+ Path rhs = (Path) i.next();
+ ns = rhs.evaluate(context, ns);
+ }
+ return ns;
+ }
+
+ public Expr clone(Object context)
+ {
+ int len = path.size();
+ LinkedList path2 = new LinkedList();
+ for (int i = 0; i < len; i++)
+ {
+ path2.add(((Expr) path.get(i)).clone(context));
+ }
+ return new Steps(path2);
+ }
+
+ public String toString()
+ {
+ StringBuffer buf = new StringBuffer();
+ Iterator i = path.iterator();
+ Expr expr = (Expr) i.next();
+ if (!(expr instanceof Root))
+ {
+ buf.append(expr);
+ }
+ while (i.hasNext())
+ {
+ expr = (Expr) i.next();
+ buf.append('/');
+ buf.append(expr);
+ }
+ return buf.toString();
+ }
+
+}
diff --git a/libjava/gnu/xml/xpath/StringFunction.java b/libjava/gnu/xml/xpath/StringFunction.java
new file mode 100644
index 0000000..658995c
--- /dev/null
+++ b/libjava/gnu/xml/xpath/StringFunction.java
@@ -0,0 +1,113 @@
+/* StringFunction.java --
+ Copyright (C) 2004 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.xpath;
+
+import java.util.List;
+import org.w3c.dom.Node;
+
+/**
+ * The <code>string function converts an object to a string as follows:
+ * <ul>
+ * <li>A node-set is converted to a string by returning the string-value of
+ * the node in the node-set that is first in document order. If the node-set
+ * is empty, an empty string is returned.</li>
+ * <li>A number is converted to a string as follows
+ * <ul>
+ * <li>NaN is converted to the string NaN</li>
+ * <li>positive zero is converted to the string 0</li>
+ * <li>negative zero is converted to the string 0</li>
+ * <li>positive infinity is converted to the string Infinity</li>
+ * <li>negative infinity is converted to the string -Infinity</li>
+ * <li>if the number is an integer, the number is represented in decimal
+ * form as a Number with no decimal point and no leading zeros, preceded by
+ * a minus sign (-) if the number is negative</li>
+ * <li>otherwise, the number is represented in decimal form as a Number
+ * including a decimal point with at least one digit before the decimal
+ * point and at least one digit after the decimal point, preceded by a minus
+ * sign (-) if the number is negative; there must be no leading zeros before
+ * the decimal point apart possibly from the one required digit immediately
+ * before the decimal point; beyond the one required digit after the decimal
+ * point there must be as many, but only as many, more digits as are needed
+ * to uniquely distinguish the number from all other IEEE 754 numeric
+ * values.</li>
+ * </ul>
+ * </li>
+ * <li>The boolean false value is converted to the string false. The boolean
+ * true value is converted to the string true.</li>
+ * <li>An object of a type other than the four basic types is converted to a
+ * string in a way that is dependent on that type.</li>
+ * </ul>
+ * If the argument is omitted, it defaults to a node-set with the context
+ * node as its only member.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+final class StringFunction
+ extends Expr
+{
+
+ final Expr arg;
+
+ StringFunction(List args)
+ {
+ this(args.size() > 0 ? (Expr) args.get(0) : null);
+ }
+
+ StringFunction(Expr arg)
+ {
+ this.arg = arg;
+ }
+
+ public Object evaluate(Node context, int pos, int len)
+ {
+ Object val = (arg == null) ? null : arg.evaluate(context, pos, len);
+ return _string(context, val);
+ }
+
+ public Expr clone(Object context)
+ {
+ return new StringFunction((arg == null) ? null :
+ arg.clone(context));
+ }
+
+ public String toString()
+ {
+ return (arg == null) ? "string()" : "string(" + arg + ")";
+ }
+
+}
diff --git a/libjava/gnu/xml/xpath/StringLengthFunction.java b/libjava/gnu/xml/xpath/StringLengthFunction.java
new file mode 100644
index 0000000..7ed0463
--- /dev/null
+++ b/libjava/gnu/xml/xpath/StringLengthFunction.java
@@ -0,0 +1,85 @@
+/* StringLengthFunction.java --
+ Copyright (C) 2004 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.xpath;
+
+import java.util.List;
+import org.w3c.dom.Node;
+
+/**
+ * The <code>string-length</code> function returns the number of characters
+ * in the string.
+ * If the argument is omitted, it defaults to the context node converted to
+ * a string, in other words the string-value of the context node.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+final class StringLengthFunction
+ extends Expr
+{
+
+ final Expr arg;
+
+ StringLengthFunction(List args)
+ {
+ this(args.isEmpty() ? null : (Expr) args.get(0));
+ }
+
+ StringLengthFunction(Expr arg)
+ {
+ this.arg = arg;
+ }
+
+ public Object evaluate(Node context, int pos, int len)
+ {
+ Object val = (arg == null) ? null : arg.evaluate(context, pos, len);
+ String s = _string(context, val);
+ return new Double((double) s.length());
+ }
+
+ public Expr clone(Object context)
+ {
+ return new StringLengthFunction((arg == null) ? null :
+ arg.clone(context));
+ }
+
+ public String toString()
+ {
+ return (arg == null) ? "string-length()" : "string-length(" + arg + ")";
+ }
+
+}
diff --git a/libjava/gnu/xml/xpath/SubstringAfterFunction.java b/libjava/gnu/xml/xpath/SubstringAfterFunction.java
new file mode 100644
index 0000000..90b6060
--- /dev/null
+++ b/libjava/gnu/xml/xpath/SubstringAfterFunction.java
@@ -0,0 +1,92 @@
+/* SubstringAfterFunction.java --
+ Copyright (C) 2004 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.xpath;
+
+import java.util.List;
+import org.w3c.dom.Node;
+
+/**
+ * The <code>substring-after</code> function returns the substring of the
+ * first argument string that follows the first occurrence of the second
+ * argument string in the first argument string, or the empty string if the
+ * first argument string does not contain the second argument string. For
+ * example, substring-after("1999/04/01","/") returns 04/01, and
+ * substring-after("1999/04/01","19") returns 99/04/01.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+final class SubstringAfterFunction
+ extends Expr
+{
+
+ final Expr arg1;
+ final Expr arg2;
+
+ SubstringAfterFunction(List args)
+ {
+ this((Expr) args.get(0), (Expr) args.get(1));
+ }
+
+ SubstringAfterFunction(Expr arg1, Expr arg2)
+ {
+ this.arg1 = arg1;
+ this.arg2 = arg2;
+ }
+
+ public Object evaluate(Node context, int pos, int len)
+ {
+ Object val1 = arg1.evaluate(context, pos, len);
+ Object val2 = arg2.evaluate(context, pos, len);
+ String s1 = _string(context, val1);
+ String s2 = _string(context, val2);
+ int index = s1.indexOf(s2);
+ return (index == -1) ? "" : s1.substring(index + s2.length());
+ }
+
+ public Expr clone(Object context)
+ {
+ return new SubstringAfterFunction(arg1.clone(context),
+ arg2.clone(context));
+ }
+
+ public String toString()
+ {
+ return "substring-after(" + arg1 + "," + arg2 + ")";
+ }
+
+}
diff --git a/libjava/gnu/xml/xpath/SubstringBeforeFunction.java b/libjava/gnu/xml/xpath/SubstringBeforeFunction.java
new file mode 100644
index 0000000..cadb77c
--- /dev/null
+++ b/libjava/gnu/xml/xpath/SubstringBeforeFunction.java
@@ -0,0 +1,91 @@
+/* SubstringBeforeFunction.java --
+ Copyright (C) 2004 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.xpath;
+
+import java.util.List;
+import org.w3c.dom.Node;
+
+/**
+ * The <code>substring-before</code> function returns the substring of the
+ * first argument string that precedes the first occurrence of the second
+ * argument string in the first argument string, or the empty string if the
+ * first argument string does not contain the second argument string. For
+ * example, substring-before("1999/04/01","/") returns 1999.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+final class SubstringBeforeFunction
+ extends Expr
+{
+
+ final Expr arg1;
+ final Expr arg2;
+
+ SubstringBeforeFunction(List args)
+ {
+ this((Expr) args.get(0), (Expr) args.get(1));
+ }
+
+ SubstringBeforeFunction(Expr arg1, Expr arg2)
+ {
+ this.arg1 = arg1;
+ this.arg2 = arg2;
+ }
+
+ public Object evaluate(Node context, int pos, int len)
+ {
+ Object val1 = arg1.evaluate(context, pos, len);
+ Object val2 = arg2.evaluate(context, pos, len);
+ String s1 = _string(context, val1);
+ String s2 = _string(context, val2);
+ int index = s1.indexOf(s2);
+ return (index == -1) ? "" : s1.substring(0, index);
+ }
+
+ public Expr clone(Object context)
+ {
+ return new SubstringBeforeFunction(arg1.clone(context),
+ arg2.clone(context));
+ }
+
+ public String toString()
+ {
+ return "substring-before(" + arg1 + "," + arg2 + ")";
+ }
+
+}
diff --git a/libjava/gnu/xml/xpath/SubstringFunction.java b/libjava/gnu/xml/xpath/SubstringFunction.java
new file mode 100644
index 0000000..fdbbd06
--- /dev/null
+++ b/libjava/gnu/xml/xpath/SubstringFunction.java
@@ -0,0 +1,122 @@
+/* SubstringFunction.java --
+ Copyright (C) 2004 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.xpath;
+
+import java.util.List;
+import org.w3c.dom.Node;
+
+/**
+ * The substring function returns the substring of the first argument
+ * starting at the position specified in the second argument with length
+ * specified in the third argument. For example, substring("12345",2,3)
+ * returns "234". If the third argument is not specified, it returns the
+ * substring starting at the position specified in the second argument and
+ * continuing to the end of the string. For example, substring("12345",2)
+ * returns "2345".
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+final class SubstringFunction
+ extends Expr
+{
+
+ final Expr arg1;
+ final Expr arg2;
+ final Expr arg3;
+
+ SubstringFunction(List args)
+ {
+ this((Expr) args.get(0), (Expr) args.get(1),
+ (args.size() > 2) ? (Expr) args.get(2) : null);
+ }
+
+ SubstringFunction(Expr arg1, Expr arg2, Expr arg3)
+ {
+ this.arg1 = arg1;
+ this.arg2 = arg2;
+ this.arg3 = arg3;
+ }
+
+ public Object evaluate(Node context, int pos, int len)
+ {
+ Object val1 = arg1.evaluate(context, pos, len);
+ Object val2 = arg2.evaluate(context, pos, len);
+ String s = _string(context, val1);
+ int p = (val2 instanceof Double) ?
+ ((Double) val2).intValue() :
+ (int) Math.round(_number(context, val2));
+ p--;
+ if (p < 0)
+ {
+ p = 0;
+ }
+
+ int l = s.length() - p;
+ if (l <= 0)
+ {
+ return "";
+ }
+
+ if (arg3 != null)
+ {
+ Object val3 = arg3.evaluate(context, pos, len);
+ int v3 = (val3 instanceof Double) ?
+ ((Double) val3).intValue() :
+ (int) Math.round(_number(context, val3));
+ if (v3 < l)
+ {
+ l = v3;
+ }
+ }
+
+ return s.substring(p, p + l);
+ }
+
+ public Expr clone(Object context)
+ {
+ return new SubstringFunction(arg1.clone(context), arg2.clone(context),
+ (arg3 == null) ? null : arg3.clone(context));
+ }
+
+ public String toString()
+ {
+ return (arg3 == null) ? "substring(" + arg1 + "," + arg2 + ")" :
+ "substring(" + arg1 + "," + arg2 + "," + arg3 + ")";
+ }
+
+}
diff --git a/libjava/gnu/xml/xpath/SumFunction.java b/libjava/gnu/xml/xpath/SumFunction.java
new file mode 100644
index 0000000..1501b46
--- /dev/null
+++ b/libjava/gnu/xml/xpath/SumFunction.java
@@ -0,0 +1,94 @@
+/* SumFunction.java --
+ Copyright (C) 2004 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.xpath;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import org.w3c.dom.Node;
+
+/**
+ * The <code>sum</code> function returns the sum, for each node in the
+ * argument node-set, of the result of converting the string-values of the
+ * node to a number.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+final class SumFunction
+ extends Expr
+{
+
+ final Expr arg;
+
+ SumFunction(List args)
+ {
+ this((Expr) args.get(0));
+ }
+
+ SumFunction(Expr arg)
+ {
+ this.arg = arg;
+ }
+
+ public Object evaluate(Node context, int pos, int len)
+ {
+ Object val = arg.evaluate(context, pos, len);
+ double sum = 0.0d;
+ if (val instanceof Collection)
+ {
+ for (Iterator i = ((Collection) val).iterator(); i.hasNext(); )
+ {
+ Node node = (Node) i.next();
+ String s = stringValue(node);
+ sum += _number(context, s);
+ }
+ }
+ return new Double(sum);
+ }
+
+ public Expr clone(Object context)
+ {
+ return new SumFunction(arg.clone(context));
+ }
+
+ public String toString()
+ {
+ return "sum(" + arg + ")";
+ }
+
+}
diff --git a/libjava/gnu/xml/xpath/Test.java b/libjava/gnu/xml/xpath/Test.java
new file mode 100644
index 0000000..865461c
--- /dev/null
+++ b/libjava/gnu/xml/xpath/Test.java
@@ -0,0 +1,55 @@
+/* Test.java --
+ Copyright (C) 2004 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.xpath;
+
+import org.w3c.dom.Node;
+
+/**
+ * A test that can be performed on a node to determine whether to include it
+ * in a selection.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+public abstract class Test
+{
+
+ public abstract boolean matches(Node node, int pos, int len);
+
+ public abstract Test clone(Object context);
+
+}
diff --git a/libjava/gnu/xml/xpath/TranslateFunction.java b/libjava/gnu/xml/xpath/TranslateFunction.java
new file mode 100644
index 0000000..b92ec31
--- /dev/null
+++ b/libjava/gnu/xml/xpath/TranslateFunction.java
@@ -0,0 +1,126 @@
+/* TranslateFunction.java --
+ Copyright (C) 2004 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.xpath;
+
+import java.util.List;
+import org.w3c.dom.Node;
+
+/**
+ * The <code>translate</code> function returns the first argument string
+ * with occurrences of characters in the second argument string replaced by
+ * the character at the corresponding position in the third argument string.
+ * For example, translate("bar","abc","ABC") returns the string BAr. If
+ * there is a character in the second argument string with no character at a
+ * corresponding position in the third argument string (because the second
+ * argument string is longer than the third argument string), then
+ * occurrences of that character in the first argument string are removed.
+ * For example, translate("--aaa--","abc-","ABC") returns "AAA". If a
+ * character occurs more than once in the second argument string, then the
+ * first occurrence determines the replacement character. If the third
+ * argument string is longer than the second argument string, then excess
+ * characters are ignored.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+final class TranslateFunction
+ extends Expr
+{
+
+ final Expr arg1;
+ final Expr arg2;
+ final Expr arg3;
+
+ TranslateFunction(List args)
+ {
+ this((Expr) args.get(0), (Expr) args.get(1), (Expr) args.get(2));
+ }
+
+ TranslateFunction(Expr arg1, Expr arg2, Expr arg3)
+ {
+ this.arg1 = arg1;
+ this.arg2 = arg2;
+ this.arg3 = arg3;
+ }
+
+ public Object evaluate(Node context, int pos, int len)
+ {
+ Object val1 = arg1.evaluate(context, pos, len);
+ Object val2 = arg2.evaluate(context, pos, len);
+ Object val3 = arg3.evaluate(context, pos, len);
+ String string = _string(context, val1);
+ String search = _string(context, val2);
+ String replace = _string(context, val3);
+ StringBuffer buf = new StringBuffer();
+ int l1 = string.length();
+ int l2 = search.length();
+ int l3 = replace.length();
+ for (int i = 0; i < l1; i++)
+ {
+ char c = string.charAt(i);
+ boolean replaced = false;
+ for (int j = 0; j < l2; j++)
+ {
+ if (c == search.charAt(j))
+ {
+ if (j < l3)
+ {
+ buf.append(replace.charAt(j));
+ }
+ replaced = true;
+ }
+ }
+ if (!replaced)
+ {
+ buf.append(c);
+ }
+ }
+ return new String(buf);
+ }
+
+ public Expr clone(Object context)
+ {
+ return new TranslateFunction(arg1.clone(context), arg2.clone(context),
+ arg3.clone(context));
+ }
+
+ public String toString()
+ {
+ return "translate(" + arg1 + "," + arg2 + "," + arg3 + ")";
+ }
+
+}
diff --git a/libjava/gnu/xml/xpath/TrueFunction.java b/libjava/gnu/xml/xpath/TrueFunction.java
new file mode 100644
index 0000000..2d47ed3
--- /dev/null
+++ b/libjava/gnu/xml/xpath/TrueFunction.java
@@ -0,0 +1,66 @@
+/* TrueFunction.java --
+ Copyright (C) 2004 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.xpath;
+
+import org.w3c.dom.Node;
+
+/**
+ * The <code>true</code> function returns true.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+final class TrueFunction
+ extends Expr
+{
+
+ public Object evaluate(Node context, int pos, int len)
+ {
+ return Boolean.TRUE;
+ }
+
+ public Expr clone(Object context)
+ {
+ return new TrueFunction();
+ }
+
+ public String toString()
+ {
+ return "true()";
+ }
+
+}
diff --git a/libjava/gnu/xml/xpath/UnionExpr.java b/libjava/gnu/xml/xpath/UnionExpr.java
new file mode 100644
index 0000000..1b1344e
--- /dev/null
+++ b/libjava/gnu/xml/xpath/UnionExpr.java
@@ -0,0 +1,102 @@
+/* UnionExpr.java --
+ Copyright (C) 2004 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.xpath;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import org.w3c.dom.Node;
+
+/**
+ * The union of two node-sets.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+public final class UnionExpr
+ extends Pattern
+{
+
+ final Expr lhs;
+ final Expr rhs;
+
+ public UnionExpr(Expr lhs, Expr rhs)
+ {
+ this.lhs = lhs;
+ this.rhs = rhs;
+ }
+
+ public boolean matches(Node context)
+ {
+ if (lhs instanceof Pattern && rhs instanceof Pattern)
+ {
+ return ((Pattern) lhs).matches(context) ||
+ ((Pattern) rhs).matches(context);
+ }
+ return false;
+ }
+
+ public Object evaluate(Node context, int pos, int len)
+ {
+ Object left = lhs.evaluate(context, pos, len);
+ Object right = rhs.evaluate(context, pos, len);
+ if (left instanceof Collection && right instanceof Collection)
+ {
+ Set set = new HashSet();
+ set.addAll ((Collection) left);
+ set.addAll ((Collection) right);
+ List list = new ArrayList(set);
+ Collections.sort(list, documentOrderComparator);
+ return list;
+ }
+ return Collections.EMPTY_SET;
+ }
+
+ public Expr clone(Object context)
+ {
+ return new UnionExpr(lhs.clone(context), rhs.clone(context));
+ }
+
+ public String toString()
+ {
+ return lhs + " | " + rhs;
+ }
+
+}
diff --git a/libjava/gnu/xml/xpath/VariableReference.java b/libjava/gnu/xml/xpath/VariableReference.java
new file mode 100644
index 0000000..faa5bfa
--- /dev/null
+++ b/libjava/gnu/xml/xpath/VariableReference.java
@@ -0,0 +1,88 @@
+/* VariableReference.java --
+ Copyright (C) 2004 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.xpath;
+
+import javax.xml.namespace.QName;
+import javax.xml.xpath.XPathVariableResolver;
+import org.w3c.dom.Node;
+import gnu.xml.transform.Bindings;
+
+public class VariableReference
+ extends Expr
+{
+
+ final XPathVariableResolver resolver;
+ final String name;
+
+ public VariableReference(XPathVariableResolver resolver, String name)
+ {
+ this.resolver = resolver;
+ this.name = name;
+ }
+
+ public Object evaluate(Node context, int pos, int len)
+ {
+ if (resolver != null)
+ {
+ if (resolver instanceof Bindings)
+ {
+ // Needs context to operate properly
+ return ((Bindings) resolver).get(name, context, pos, len);
+ }
+ QName qname = QName.valueOf(name);
+ return resolver.resolveVariable(qname);
+ }
+ throw new IllegalStateException("no variable resolver");
+ }
+
+ public Expr clone(Object context)
+ {
+ XPathVariableResolver r = resolver;
+ if (context instanceof XPathVariableResolver)
+ {
+ r = (XPathVariableResolver) context;
+ }
+ return new VariableReference(r, name);
+ }
+
+ public String toString()
+ {
+ return "$" + name;
+ }
+
+}
diff --git a/libjava/gnu/xml/xpath/XPathFactoryImpl.java b/libjava/gnu/xml/xpath/XPathFactoryImpl.java
new file mode 100644
index 0000000..f9f70a3
--- /dev/null
+++ b/libjava/gnu/xml/xpath/XPathFactoryImpl.java
@@ -0,0 +1,90 @@
+/* XPathFactoryImpl.java --
+ Copyright (C) 2004 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.xpath;
+
+import javax.xml.xpath.XPath;
+import javax.xml.xpath.XPathFactory;
+import javax.xml.xpath.XPathFactoryConfigurationException;
+import javax.xml.xpath.XPathFunctionResolver;
+import javax.xml.xpath.XPathVariableResolver;
+
+/**
+ * GNU XPath factory implementation.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+public class XPathFactoryImpl
+ extends XPathFactory
+{
+
+ XPathVariableResolver variableResolver;
+ XPathFunctionResolver functionResolver;
+
+ public boolean isObjectModelSupported(String objectModel)
+ {
+ return XPathFactory.DEFAULT_OBJECT_MODEL_URI.equals(objectModel);
+ }
+
+ public void setFeature(String name, boolean value)
+ throws XPathFactoryConfigurationException
+ {
+ throw new XPathFactoryConfigurationException(name);
+ }
+
+ public boolean getFeature(String name)
+ throws XPathFactoryConfigurationException
+ {
+ throw new XPathFactoryConfigurationException(name);
+ }
+
+ public void setXPathVariableResolver(XPathVariableResolver resolver)
+ {
+ variableResolver = resolver;
+ }
+
+ public void setXPathFunctionResolver(XPathFunctionResolver resolver)
+ {
+ functionResolver = resolver;
+ }
+
+ public XPath newXPath()
+ {
+ return new XPathImpl(null, variableResolver, functionResolver);
+ }
+
+}
diff --git a/libjava/gnu/xml/xpath/XPathImpl.java b/libjava/gnu/xml/xpath/XPathImpl.java
new file mode 100644
index 0000000..2189364
--- /dev/null
+++ b/libjava/gnu/xml/xpath/XPathImpl.java
@@ -0,0 +1,164 @@
+/* XPathImpl.java --
+ Copyright (C) 2004 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.xpath;
+
+import java.io.IOException;
+import javax.xml.namespace.NamespaceContext;
+import javax.xml.namespace.QName;
+import javax.xml.xpath.XPath;
+import javax.xml.xpath.XPathExpression;
+import javax.xml.xpath.XPathExpressionException;
+import javax.xml.xpath.XPathFunctionResolver;
+import javax.xml.xpath.XPathVariableResolver;
+import org.xml.sax.InputSource;
+
+/**
+ * JAXP XPath implementation.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+public class XPathImpl
+ implements XPath
+{
+
+ XPathParser parser;
+ NamespaceContext namespaceContext;
+ XPathVariableResolver variableResolver;
+ XPathFunctionResolver functionResolver;
+
+ XPathImpl(NamespaceContext namespaceContext,
+ XPathVariableResolver variableResolver,
+ XPathFunctionResolver functionResolver)
+ {
+ parser = new XPathParser();
+ this.namespaceContext = namespaceContext;
+ this.variableResolver = variableResolver;
+ this.functionResolver = functionResolver;
+ reset();
+ }
+
+ public void reset()
+ {
+ parser.namespaceContext = namespaceContext;
+ parser.variableResolver = variableResolver;
+ parser.functionResolver = functionResolver;
+ }
+
+ public void setXPathVariableResolver(XPathVariableResolver resolver)
+ {
+ parser.variableResolver = resolver;
+ }
+
+ public XPathVariableResolver getXPathVariableResolver()
+ {
+ return parser.variableResolver;
+ }
+
+ public void setXPathFunctionResolver(XPathFunctionResolver resolver)
+ {
+ parser.functionResolver = resolver;
+ }
+
+ public XPathFunctionResolver getXPathFunctionResolver()
+ {
+ return parser.functionResolver;
+ }
+
+ public void setNamespaceContext(NamespaceContext nsContext)
+ {
+ parser.namespaceContext = nsContext;
+ }
+
+ public NamespaceContext getNamespaceContext()
+ {
+ return parser.namespaceContext;
+ }
+
+ public XPathExpression compile(String expression)
+ throws XPathExpressionException
+ {
+ XPathTokenizer tokenizer = new XPathTokenizer(expression);
+ try
+ {
+ return (Expr) parser.yyparse(tokenizer);
+ }
+ catch (IOException e)
+ {
+ throw new XPathExpressionException(e);
+ }
+ catch (XPathParser.yyException e)
+ {
+ throw new XPathExpressionException(expression);
+ }
+ }
+
+ public Object evaluate(String expression,
+ Object item,
+ QName returnType)
+ throws XPathExpressionException
+ {
+ XPathExpression expr = compile(expression);
+ return expr.evaluate(item, returnType);
+ }
+
+ public String evaluate(String expression,
+ Object item)
+ throws XPathExpressionException
+ {
+ XPathExpression expr = compile(expression);
+ return expr.evaluate(item);
+ }
+
+ public Object evaluate(String expression,
+ InputSource source,
+ QName returnType)
+ throws XPathExpressionException
+ {
+ XPathExpression expr = compile(expression);
+ return expr.evaluate(source, returnType);
+ }
+
+ public String evaluate(String expression,
+ InputSource source)
+ throws XPathExpressionException
+ {
+ XPathExpression expr = compile(expression);
+ return expr.evaluate(source);
+ }
+
+}
diff --git a/libjava/gnu/xml/xpath/XPathParser.java b/libjava/gnu/xml/xpath/XPathParser.java
new file mode 100644
index 0000000..973919b
--- /dev/null
+++ b/libjava/gnu/xml/xpath/XPathParser.java
@@ -0,0 +1,1462 @@
+// created by jay 0.8 (c) 1998 Axel.Schreiner@informatik.uni-osnabrueck.de
+
+ // line 2 "XPathParser.y"
+/* XPathParser.java -- An XPath 1.0 parser.
+ Copyright (C) 2004 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.xpath;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import javax.xml.namespace.NamespaceContext;
+import javax.xml.namespace.QName;
+import javax.xml.xpath.XPathFunctionResolver;
+import javax.xml.xpath.XPathVariableResolver;
+import org.w3c.dom.Node;
+
+/**
+ * An XPath 1.0 parser.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+public class XPathParser
+{
+
+ NamespaceContext namespaceContext;
+ XPathVariableResolver variableResolver;
+ XPathFunctionResolver functionResolver;
+
+ QName getQName(String name)
+ {
+ QName qName = QName.valueOf(name);
+ if (namespaceContext != null)
+ {
+ String prefix = qName.getPrefix();
+ String uri = qName.getNamespaceURI();
+ if (prefix != null && (uri == null || uri.length() == 0))
+ {
+ uri = namespaceContext.getNamespaceURI(prefix);
+ String localName = qName.getLocalPart();
+ qName = new QName(uri, localName, prefix);
+ }
+ }
+ return qName;
+ }
+
+ Expr lookupFunction(String name, List args)
+ {
+ int arity = args.size();
+ if ("position".equals(name) && arity == 0)
+ {
+ return new PositionFunction();
+ }
+ else if ("last".equals(name) && arity == 0)
+ {
+ return new LastFunction();
+ }
+ else if ("string".equals(name) && (arity == 1 || arity == 0))
+ {
+ return new StringFunction(args);
+ }
+ else if ("number".equals(name) && (arity == 1 || arity == 0))
+ {
+ return new NumberFunction(args);
+ }
+ else if ("boolean".equals(name) && arity == 1)
+ {
+ return new BooleanFunction(args);
+ }
+ else if ("count".equals(name) && arity == 1)
+ {
+ return new CountFunction(args);
+ }
+ else if ("not".equals(name) && arity == 1)
+ {
+ return new NotFunction(args);
+ }
+ else if ("id".equals(name) && arity == 1)
+ {
+ return new IdFunction(args);
+ }
+ else if ("concat".equals(name) && arity > 1)
+ {
+ return new ConcatFunction(args);
+ }
+ else if ("true".equals(name) && arity == 0)
+ {
+ return new TrueFunction();
+ }
+ else if ("false".equals(name) && arity == 0)
+ {
+ return new FalseFunction();
+ }
+ else if ("name".equals(name) && (arity == 1 || arity == 0))
+ {
+ return new NameFunction(args);
+ }
+ else if ("local-name".equals(name) && (arity == 1 || arity == 0))
+ {
+ return new LocalNameFunction(args);
+ }
+ else if ("namespace-uri".equals(name) && (arity == 1 || arity == 0))
+ {
+ return new NamespaceUriFunction(args);
+ }
+ else if ("starts-with".equals(name) && arity == 2)
+ {
+ return new StartsWithFunction(args);
+ }
+ else if ("contains".equals(name) && arity == 2)
+ {
+ return new ContainsFunction(args);
+ }
+ else if ("string-length".equals(name) && (arity == 1 || arity == 0))
+ {
+ return new StringLengthFunction(args);
+ }
+ else if ("translate".equals(name) && arity == 3)
+ {
+ return new TranslateFunction(args);
+ }
+ else if ("normalize-space".equals(name) && (arity == 1 || arity == 0))
+ {
+ return new NormalizeSpaceFunction(args);
+ }
+ else if ("substring".equals(name) && (arity == 2 || arity == 3))
+ {
+ return new SubstringFunction(args);
+ }
+ else if ("substring-before".equals(name) && arity == 2)
+ {
+ return new SubstringBeforeFunction(args);
+ }
+ else if ("substring-after".equals(name) && arity == 2)
+ {
+ return new SubstringAfterFunction(args);
+ }
+ else if ("lang".equals(name) && arity == 1)
+ {
+ return new LangFunction(args);
+ }
+ else if ("sum".equals(name) && arity == 1)
+ {
+ return new SumFunction(args);
+ }
+ else if ("floor".equals(name) && arity == 1)
+ {
+ return new FloorFunction(args);
+ }
+ else if ("ceiling".equals(name) && arity == 1)
+ {
+ return new CeilingFunction(args);
+ }
+ else if ("round".equals(name) && arity == 1)
+ {
+ return new RoundFunction(args);
+ }
+ else if (functionResolver != null)
+ {
+ QName qName = QName.valueOf(name);
+ Object function = functionResolver.resolveFunction(qName, arity);
+ if (function != null &&
+ function instanceof Function &&
+ function instanceof Expr)
+ {
+ Function f = (Function) function;
+ f.setArguments(args);
+ return (Expr) function;
+ }
+ }
+ return new FunctionCall(functionResolver, name, args);
+ }
+
+ // line 210 "-"
+// %token constants
+
+ public static final int LITERAL = 257;
+ public static final int DIGITS = 258;
+ public static final int NAME = 259;
+ public static final int LP = 260;
+ public static final int RP = 261;
+ public static final int LB = 262;
+ public static final int RB = 263;
+ public static final int COMMA = 264;
+ public static final int PIPE = 265;
+ public static final int SLASH = 266;
+ public static final int DOUBLE_SLASH = 267;
+ public static final int EQ = 268;
+ public static final int NE = 269;
+ public static final int GT = 270;
+ public static final int LT = 271;
+ public static final int GTE = 272;
+ public static final int LTE = 273;
+ public static final int PLUS = 274;
+ public static final int MINUS = 275;
+ public static final int AT = 276;
+ public static final int STAR = 277;
+ public static final int DOLLAR = 278;
+ public static final int COLON = 279;
+ public static final int DOUBLE_COLON = 280;
+ public static final int DOT = 281;
+ public static final int DOUBLE_DOT = 282;
+ public static final int ANCESTOR = 283;
+ public static final int ANCESTOR_OR_SELF = 284;
+ public static final int ATTRIBUTE = 285;
+ public static final int CHILD = 286;
+ public static final int DESCENDANT = 287;
+ public static final int DESCENDANT_OR_SELF = 288;
+ public static final int FOLLOWING = 289;
+ public static final int FOLLOWING_SIBLING = 290;
+ public static final int NAMESPACE = 291;
+ public static final int PARENT = 292;
+ public static final int PRECEDING = 293;
+ public static final int PRECEDING_SIBLING = 294;
+ public static final int SELF = 295;
+ public static final int DIV = 296;
+ public static final int MOD = 297;
+ public static final int OR = 298;
+ public static final int AND = 299;
+ public static final int COMMENT = 300;
+ public static final int PROCESSING_INSTRUCTION = 301;
+ public static final int TEXT = 302;
+ public static final int NODE = 303;
+ public static final int UNARY = 304;
+ public static final int yyErrorCode = 256;
+
+ /** thrown for irrecoverable syntax errors and stack overflow.
+ */
+ public static class yyException extends java.lang.Exception {
+ public yyException (String message) {
+ super(message);
+ }
+ }
+
+ /** must be implemented by a scanner object to supply input to the parser.
+ */
+ public interface yyInput {
+ /** move on to next token.
+ @return false if positioned beyond tokens.
+ @throws IOException on input error.
+ */
+ boolean advance () throws java.io.IOException;
+ /** classifies current token.
+ Should not be called if advance() returned false.
+ @return current %token or single character.
+ */
+ int token ();
+ /** associated with current token.
+ Should not be called if advance() returned false.
+ @return value for token().
+ */
+ Object value ();
+ }
+
+ /** simplified error message.
+ @see <a href="#yyerror(java.lang.String, java.lang.String[])">yyerror</a>
+ */
+ public void yyerror (String message) {
+ yyerror(message, null);
+ }
+
+ /** (syntax) error message.
+ Can be overwritten to control message format.
+ @param message text to be displayed.
+ @param expected vector of acceptable tokens, if available.
+ */
+ public void yyerror (String message, String[] expected) {
+ if (expected != null && expected.length > 0) {
+ System.err.print(message+", expecting");
+ for (int n = 0; n < expected.length; ++ n)
+ System.err.print(" "+expected[n]);
+ System.err.println();
+ } else
+ System.err.println(message);
+ }
+
+ /** debugging support, requires the package jay.yydebug.
+ Set to null to suppress debugging messages.
+ */
+//t protected jay.yydebug.yyDebug yydebug;
+
+ protected static final int yyFinal = 30;
+
+ /** index-checked interface to yyName[].
+ @param token single character or %token value.
+ @return token name or [illegal] or [unknown].
+ */
+//t public static final String yyname (int token) {
+//t if (token < 0 || token > YyNameClass.yyName.length) return "[illegal]";
+//t String name;
+//t if ((name = YyNameClass.yyName[token]) != null) return name;
+//t return "[unknown]";
+//t }
+
+ /** computes list of expected tokens on error by tracing the tables.
+ @param state for which to compute the list.
+ @return list of token names.
+ */
+ protected String[] yyExpecting (int state) {
+ int token, n, len = 0;
+ boolean[] ok = new boolean[YyNameClass.yyName.length];
+
+ if ((n = YySindexClass.yySindex[state]) != 0)
+ for (token = n < 0 ? -n : 0;
+ token < YyNameClass.yyName.length && n+token < YyTableClass.yyTable.length; ++ token)
+ if (YyCheckClass.yyCheck[n+token] == token && !ok[token] && YyNameClass.yyName[token] != null) {
+ ++ len;
+ ok[token] = true;
+ }
+ if ((n = YyRindexClass.yyRindex[state]) != 0)
+ for (token = n < 0 ? -n : 0;
+ token < YyNameClass.yyName.length && n+token < YyTableClass.yyTable.length; ++ token)
+ if (YyCheckClass.yyCheck[n+token] == token && !ok[token] && YyNameClass.yyName[token] != null) {
+ ++ len;
+ ok[token] = true;
+ }
+
+ String result[] = new String[len];
+ for (n = token = 0; n < len; ++ token)
+ if (ok[token]) result[n++] = YyNameClass.yyName[token];
+ return result;
+ }
+
+ /** the generated parser, with debugging messages.
+ Maintains a state and a value stack, currently with fixed maximum size.
+ @param yyLex scanner.
+ @param yydebug debug message writer implementing yyDebug, or null.
+ @return result of the last reduction, if any.
+ @throws yyException on irrecoverable parse error.
+ */
+ public Object yyparse (yyInput yyLex, Object yydebug)
+ throws java.io.IOException, yyException {
+//t this.yydebug = (jay.yydebug.yyDebug)yydebug;
+ return yyparse(yyLex);
+ }
+
+ /** initial size and increment of the state/value stack [default 256].
+ This is not final so that it can be overwritten outside of invocations
+ of yyparse().
+ */
+ protected int yyMax;
+
+ /** executed at the beginning of a reduce action.
+ Used as $$ = yyDefault($1), prior to the user-specified action, if any.
+ Can be overwritten to provide deep copy, etc.
+ @param first value for $1, or null.
+ @return first.
+ */
+ protected Object yyDefault (Object first) {
+ return first;
+ }
+
+ /** the generated parser.
+ Maintains a state and a value stack, currently with fixed maximum size.
+ @param yyLex scanner.
+ @return result of the last reduction, if any.
+ @throws yyException on irrecoverable parse error.
+ */
+ public Object yyparse (yyInput yyLex)
+ throws java.io.IOException, yyException {
+ if (yyMax <= 0) yyMax = 256; // initial size
+ int yyState = 0, yyStates[] = new int[yyMax]; // state stack
+ Object yyVal = null, yyVals[] = new Object[yyMax]; // value stack
+ int yyToken = -1; // current input
+ int yyErrorFlag = 0; // #tks to shift
+
+ yyLoop: for (int yyTop = 0;; ++ yyTop) {
+ if (yyTop >= yyStates.length) { // dynamically increase
+ int[] i = new int[yyStates.length+yyMax];
+ System.arraycopy(yyStates, 0, i, 0, yyStates.length);
+ yyStates = i;
+ Object[] o = new Object[yyVals.length+yyMax];
+ System.arraycopy(yyVals, 0, o, 0, yyVals.length);
+ yyVals = o;
+ }
+ yyStates[yyTop] = yyState;
+ yyVals[yyTop] = yyVal;
+//t if (yydebug != null) yydebug.push(yyState, yyVal);
+
+ yyDiscarded: for (;;) { // discarding a token does not change stack
+ int yyN;
+ if ((yyN = YyDefRedClass.yyDefRed[yyState]) == 0) { // else [default] reduce (yyN)
+ if (yyToken < 0) {
+ yyToken = yyLex.advance() ? yyLex.token() : 0;
+//t if (yydebug != null)
+//t yydebug.lex(yyState, yyToken, yyname(yyToken), yyLex.value());
+ }
+ if ((yyN = YySindexClass.yySindex[yyState]) != 0 && (yyN += yyToken) >= 0
+ && yyN < YyTableClass.yyTable.length && YyCheckClass.yyCheck[yyN] == yyToken) {
+//t if (yydebug != null)
+//t yydebug.shift(yyState, YyTableClass.yyTable[yyN], yyErrorFlag-1);
+ yyState = YyTableClass.yyTable[yyN]; // shift to yyN
+ yyVal = yyLex.value();
+ yyToken = -1;
+ if (yyErrorFlag > 0) -- yyErrorFlag;
+ continue yyLoop;
+ }
+ if ((yyN = YyRindexClass.yyRindex[yyState]) != 0 && (yyN += yyToken) >= 0
+ && yyN < YyTableClass.yyTable.length && YyCheckClass.yyCheck[yyN] == yyToken)
+ yyN = YyTableClass.yyTable[yyN]; // reduce (yyN)
+ else
+ switch (yyErrorFlag) {
+
+ case 0:
+ yyerror("syntax error", yyExpecting(yyState));
+//t if (yydebug != null) yydebug.error("syntax error");
+
+ case 1: case 2:
+ yyErrorFlag = 3;
+ do {
+ if ((yyN = YySindexClass.yySindex[yyStates[yyTop]]) != 0
+ && (yyN += yyErrorCode) >= 0 && yyN < YyTableClass.yyTable.length
+ && YyCheckClass.yyCheck[yyN] == yyErrorCode) {
+//t if (yydebug != null)
+//t yydebug.shift(yyStates[yyTop], YyTableClass.yyTable[yyN], 3);
+ yyState = YyTableClass.yyTable[yyN];
+ yyVal = yyLex.value();
+ continue yyLoop;
+ }
+//t if (yydebug != null) yydebug.pop(yyStates[yyTop]);
+ } while (-- yyTop >= 0);
+//t if (yydebug != null) yydebug.reject();
+ throw new yyException("irrecoverable syntax error");
+
+ case 3:
+ if (yyToken == 0) {
+//t if (yydebug != null) yydebug.reject();
+ throw new yyException("irrecoverable syntax error at end-of-file");
+ }
+//t if (yydebug != null)
+//t yydebug.discard(yyState, yyToken, yyname(yyToken),
+//t yyLex.value());
+ yyToken = -1;
+ continue yyDiscarded; // leave stack alone
+ }
+ }
+ int yyV = yyTop + 1-YyLenClass.yyLen[yyN];
+//t if (yydebug != null)
+//t yydebug.reduce(yyState, yyStates[yyV-1], yyN, YyRuleClass.yyRule[yyN], YyLenClass.yyLen[yyN]);
+ yyVal = yyDefault(yyV > yyTop ? null : yyVals[yyV]);
+ switch (yyN) {
+case 4:
+ // line 276 "XPathParser.y"
+ {
+ yyVal = new Root();
+ }
+ break;
+case 5:
+ // line 280 "XPathParser.y"
+ {
+ Steps steps;
+ if (yyVals[0+yyTop] instanceof Steps)
+ {
+ steps = (Steps) yyVals[0+yyTop];
+ }
+ else
+ {
+ steps = new Steps();
+ steps.path.addFirst(yyVals[0+yyTop]);
+ }
+ steps.path.addFirst(new Root());
+ yyVal = steps;
+ /*$$ = new Step(new Root(), (Path) $2);*/
+ }
+ break;
+case 6:
+ // line 296 "XPathParser.y"
+ {
+ Test nt = new NodeTypeTest((short) 0);
+ Selector s = new Selector(Selector.DESCENDANT_OR_SELF,
+ Collections.singletonList (nt));
+ Steps steps;
+ if (yyVals[0+yyTop] instanceof Steps)
+ {
+ steps = (Steps) yyVals[0+yyTop];
+ }
+ else
+ {
+ steps = new Steps();
+ steps.path.addFirst(yyVals[0+yyTop]);
+ }
+ steps.path.addFirst(s);
+ steps.path.addFirst(new Root());
+ yyVal = steps;
+ /*Step step = new Step(s, (Path) $2);*/
+ /*$$ = new Step(new Root(), step);*/
+ }
+ break;
+case 8:
+ // line 321 "XPathParser.y"
+ {
+ Steps steps;
+ if (yyVals[-2+yyTop] instanceof Steps)
+ {
+ steps = (Steps) yyVals[-2+yyTop];
+ }
+ else
+ {
+ steps = new Steps();
+ steps.path.addFirst(yyVals[-2+yyTop]);
+ }
+ steps.path.addLast(yyVals[0+yyTop]);
+ yyVal = steps;
+ /*$$ = new Step((Expr) $1, (Path) $3);*/
+ }
+ break;
+case 9:
+ // line 337 "XPathParser.y"
+ {
+ Test nt = new NodeTypeTest((short) 0);
+ Selector s = new Selector(Selector.DESCENDANT_OR_SELF,
+ Collections.singletonList (nt));
+ Steps steps;
+ if (yyVals[-2+yyTop] instanceof Steps)
+ {
+ steps = (Steps) yyVals[-2+yyTop];
+ }
+ else
+ {
+ steps = new Steps();
+ steps.path.addFirst(yyVals[-2+yyTop]);
+ }
+ steps.path.addLast(s);
+ steps.path.addLast(yyVals[0+yyTop]);
+ yyVal = steps;
+ /*Step step = new Step(s, (Path) $3);*/
+ /*$$ = new Step((Expr) $1, step);*/
+ }
+ break;
+case 10:
+ // line 361 "XPathParser.y"
+ {
+ yyVal = new Selector (Selector.CHILD, (List) yyVals[0+yyTop]);
+ }
+ break;
+case 11:
+ // line 365 "XPathParser.y"
+ {
+ yyVal = new Selector (Selector.ATTRIBUTE, (List) yyVals[0+yyTop]);
+ }
+ break;
+case 12:
+ // line 369 "XPathParser.y"
+ {
+ yyVal = new Selector (((Integer) yyVals[-2+yyTop]).intValue (), (List) yyVals[0+yyTop]);
+ }
+ break;
+case 13:
+ // line 373 "XPathParser.y"
+ {
+ yyVal = new Selector (Selector.SELF, Collections.EMPTY_LIST);
+ }
+ break;
+case 14:
+ // line 377 "XPathParser.y"
+ {
+ yyVal = new Selector (Selector.PARENT, Collections.EMPTY_LIST);
+ }
+ break;
+case 15:
+ // line 384 "XPathParser.y"
+ {
+ List list = new ArrayList();
+ list.add(yyVals[0+yyTop]);
+ yyVal = list;
+ }
+ break;
+case 16:
+ // line 390 "XPathParser.y"
+ {
+ List list = (List)yyVals[-1+yyTop];
+ list.add(yyVals[0+yyTop]);
+ yyVal = list;
+ }
+ break;
+case 17:
+ // line 414 "XPathParser.y"
+ {
+ yyVal = new Integer(Selector.ANCESTOR);
+ }
+ break;
+case 18:
+ // line 418 "XPathParser.y"
+ {
+ yyVal = new Integer(Selector.ANCESTOR_OR_SELF);
+ }
+ break;
+case 19:
+ // line 422 "XPathParser.y"
+ {
+ yyVal = new Integer(Selector.ATTRIBUTE);
+ }
+ break;
+case 20:
+ // line 426 "XPathParser.y"
+ {
+ yyVal = new Integer(Selector.CHILD);
+ }
+ break;
+case 21:
+ // line 430 "XPathParser.y"
+ {
+ yyVal = new Integer(Selector.DESCENDANT);
+ }
+ break;
+case 22:
+ // line 434 "XPathParser.y"
+ {
+ yyVal = new Integer(Selector.DESCENDANT_OR_SELF);
+ }
+ break;
+case 23:
+ // line 438 "XPathParser.y"
+ {
+ yyVal = new Integer(Selector.FOLLOWING);
+ }
+ break;
+case 24:
+ // line 442 "XPathParser.y"
+ {
+ yyVal = new Integer(Selector.FOLLOWING_SIBLING);
+ }
+ break;
+case 25:
+ // line 446 "XPathParser.y"
+ {
+ yyVal = new Integer(Selector.NAMESPACE);
+ }
+ break;
+case 26:
+ // line 450 "XPathParser.y"
+ {
+ yyVal = new Integer(Selector.PARENT);
+ }
+ break;
+case 27:
+ // line 454 "XPathParser.y"
+ {
+ yyVal = new Integer(Selector.PRECEDING);
+ }
+ break;
+case 28:
+ // line 458 "XPathParser.y"
+ {
+ yyVal = new Integer(Selector.PRECEDING_SIBLING);
+ }
+ break;
+case 29:
+ // line 462 "XPathParser.y"
+ {
+ yyVal = new Integer(Selector.SELF);
+ }
+ break;
+case 31:
+ // line 471 "XPathParser.y"
+ {
+ yyVal = new NodeTypeTest(Node.PROCESSING_INSTRUCTION_NODE, (String) yyVals[-1+yyTop]);
+ }
+ break;
+case 32:
+ // line 476 "XPathParser.y"
+ {
+ yyVal = new NodeTypeTest(((Short) yyVals[-1+yyTop]).shortValue());
+ }
+ break;
+case 33:
+ // line 483 "XPathParser.y"
+ {
+ yyVal = new Predicate((Expr) yyVals[-1+yyTop]);
+ }
+ break;
+case 35:
+ // line 491 "XPathParser.y"
+ {
+ yyVal = new ParenthesizedExpr((Expr) yyVals[-1+yyTop]);
+ }
+ break;
+case 36:
+ // line 495 "XPathParser.y"
+ {
+ yyVal = new Constant(yyVals[0+yyTop]);
+ }
+ break;
+case 37:
+ // line 499 "XPathParser.y"
+ {
+ yyVal = new Constant(yyVals[0+yyTop]);
+ }
+ break;
+case 39:
+ // line 507 "XPathParser.y"
+ {
+ yyVal = lookupFunction((String) yyVals[-2+yyTop], Collections.EMPTY_LIST);
+ }
+ break;
+case 40:
+ // line 511 "XPathParser.y"
+ {
+ yyVal = lookupFunction((String) yyVals[-3+yyTop], (List) yyVals[-1+yyTop]);
+ }
+ break;
+case 41:
+ // line 518 "XPathParser.y"
+ {
+ List list = new ArrayList();
+ list.add(yyVals[0+yyTop]);
+ yyVal = list;
+ }
+ break;
+case 42:
+ // line 524 "XPathParser.y"
+ {
+ List list = (List) yyVals[0+yyTop];
+ list.add(0, yyVals[-2+yyTop]);
+ yyVal = list;
+ }
+ break;
+case 44:
+ // line 534 "XPathParser.y"
+ {
+ yyVal = new UnionExpr((Expr) yyVals[-2+yyTop], (Expr) yyVals[0+yyTop]);
+ }
+ break;
+case 47:
+ // line 543 "XPathParser.y"
+ {
+ Steps steps;
+ if (yyVals[0+yyTop] instanceof Steps)
+ {
+ steps = (Steps) yyVals[0+yyTop];
+ }
+ else
+ {
+ steps = new Steps();
+ steps.path.addFirst(yyVals[0+yyTop]);
+ }
+ steps.path.addFirst(yyVals[-2+yyTop]);
+ yyVal = steps;
+ /*$$ = new Step ((Expr) $1, (Path) $3);*/
+ }
+ break;
+case 48:
+ // line 559 "XPathParser.y"
+ {
+ Test nt = new NodeTypeTest((short) 0);
+ Selector s = new Selector(Selector.DESCENDANT_OR_SELF,
+ Collections.singletonList(nt));
+ Steps steps;
+ if (yyVals[0+yyTop] instanceof Steps)
+ {
+ steps = (Steps) yyVals[0+yyTop];
+ }
+ else
+ {
+ steps = new Steps();
+ steps.path.addFirst(yyVals[0+yyTop]);
+ }
+ steps.path.addFirst(s);
+ steps.path.addFirst(yyVals[-2+yyTop]);
+ yyVal = steps;
+ /*Step step = new Step (s, (Path) $3);*/
+ /*$$ = new Step ((Expr) $1, step);*/
+ }
+ break;
+case 50:
+ // line 584 "XPathParser.y"
+ {
+ Predicate filter = (Predicate) yyVals[0+yyTop];
+ Selector s = new Selector(Selector.SELF,
+ Collections.singletonList(filter));
+ Steps steps;
+ if (yyVals[-1+yyTop] instanceof Steps)
+ {
+ steps = (Steps) yyVals[-1+yyTop];
+ }
+ else
+ {
+ steps = new Steps();
+ steps.path.addFirst(yyVals[-1+yyTop]);
+ }
+ steps.path.addLast(s);
+ yyVal = steps;
+ /*$$ = new Step ((Expr) $1, s);*/
+ }
+ break;
+case 52:
+ // line 607 "XPathParser.y"
+ {
+ yyVal = new OrExpr((Expr) yyVals[-2+yyTop], (Expr) yyVals[0+yyTop]);
+ }
+ break;
+case 54:
+ // line 615 "XPathParser.y"
+ {
+ yyVal = new AndExpr((Expr) yyVals[-2+yyTop], (Expr) yyVals[0+yyTop]);
+ }
+ break;
+case 56:
+ // line 623 "XPathParser.y"
+ {
+ yyVal = new EqualityExpr((Expr) yyVals[-2+yyTop], (Expr) yyVals[0+yyTop], false);
+ }
+ break;
+case 57:
+ // line 627 "XPathParser.y"
+ {
+ yyVal = new EqualityExpr((Expr) yyVals[-2+yyTop], (Expr) yyVals[0+yyTop], true);
+ }
+ break;
+case 59:
+ // line 635 "XPathParser.y"
+ {
+ yyVal = new RelationalExpr((Expr) yyVals[-2+yyTop], (Expr) yyVals[0+yyTop], true, false);
+ }
+ break;
+case 60:
+ // line 639 "XPathParser.y"
+ {
+ yyVal = new RelationalExpr((Expr) yyVals[-2+yyTop], (Expr) yyVals[0+yyTop], false, false);
+ }
+ break;
+case 61:
+ // line 643 "XPathParser.y"
+ {
+ yyVal = new RelationalExpr((Expr) yyVals[-2+yyTop], (Expr) yyVals[0+yyTop], true, true);
+ }
+ break;
+case 62:
+ // line 647 "XPathParser.y"
+ {
+ yyVal = new RelationalExpr((Expr) yyVals[-2+yyTop], (Expr) yyVals[0+yyTop], false, true);
+ }
+ break;
+case 64:
+ // line 655 "XPathParser.y"
+ {
+ yyVal = new ArithmeticExpr((Expr) yyVals[-2+yyTop], (Expr) yyVals[0+yyTop], ArithmeticExpr.ADD);
+ }
+ break;
+case 65:
+ // line 659 "XPathParser.y"
+ {
+ yyVal = new ArithmeticExpr((Expr) yyVals[-2+yyTop], (Expr) yyVals[0+yyTop], ArithmeticExpr.SUBTRACT);
+ }
+ break;
+case 67:
+ // line 667 "XPathParser.y"
+ {
+ yyVal = new ArithmeticExpr((Expr) yyVals[-2+yyTop], (Expr) yyVals[0+yyTop], ArithmeticExpr.MULTIPLY);
+ }
+ break;
+case 68:
+ // line 671 "XPathParser.y"
+ {
+ yyVal = new ArithmeticExpr((Expr) yyVals[-2+yyTop], (Expr) yyVals[0+yyTop], ArithmeticExpr.DIVIDE);
+ }
+ break;
+case 69:
+ // line 675 "XPathParser.y"
+ {
+ yyVal = new ArithmeticExpr((Expr) yyVals[-2+yyTop], (Expr) yyVals[0+yyTop], ArithmeticExpr.MODULO);
+ }
+ break;
+case 71:
+ // line 683 "XPathParser.y"
+ {
+ yyVal = new NegativeExpr((Expr) yyVals[0+yyTop]);
+ }
+ break;
+case 72:
+ // line 690 "XPathParser.y"
+ {
+ yyVal = new Double((String) yyVals[0+yyTop] + ".0");
+ }
+ break;
+case 73:
+ // line 694 "XPathParser.y"
+ {
+ yyVal = new Double((String) yyVals[-1+yyTop] + ".0");
+ }
+ break;
+case 74:
+ // line 698 "XPathParser.y"
+ {
+ yyVal = new Double((String) yyVals[-2+yyTop] + "." + (String) yyVals[0+yyTop]);
+ }
+ break;
+case 75:
+ // line 702 "XPathParser.y"
+ {
+ yyVal = new Double("0." + (String) yyVals[0+yyTop]);
+ }
+ break;
+case 77:
+ // line 731 "XPathParser.y"
+ {
+ yyVal = new VariableReference(variableResolver, (String) yyVals[0+yyTop]);
+ }
+ break;
+case 78:
+ // line 738 "XPathParser.y"
+ {
+ yyVal = new NameTest(null, true, true);
+ }
+ break;
+case 79:
+ // line 742 "XPathParser.y"
+ {
+ QName qName = getQName((String) yyVals[-2+yyTop]);
+ yyVal = new NameTest(qName, true, false);
+ }
+ break;
+case 80:
+ // line 747 "XPathParser.y"
+ {
+ QName qName = getQName((String) yyVals[0+yyTop]);
+ yyVal = new NameTest(qName, false, false);
+ }
+ break;
+case 82:
+ // line 756 "XPathParser.y"
+ {
+ yyVal = (String) yyVals[-2+yyTop] + ':' + (String) yyVals[0+yyTop];
+ }
+ break;
+case 83:
+ // line 763 "XPathParser.y"
+ {
+ yyVal = new Short(Node.COMMENT_NODE);
+ }
+ break;
+case 84:
+ // line 767 "XPathParser.y"
+ {
+ yyVal = new Short(Node.TEXT_NODE);
+ }
+ break;
+case 85:
+ // line 771 "XPathParser.y"
+ {
+ yyVal = new Short(Node.PROCESSING_INSTRUCTION_NODE);
+ }
+ break;
+case 86:
+ // line 775 "XPathParser.y"
+ {
+ yyVal = new Short((short) 0);
+ }
+ break;
+ // line 986 "-"
+ }
+ yyTop -= YyLenClass.yyLen[yyN];
+ yyState = yyStates[yyTop];
+ int yyM = YyLhsClass.yyLhs[yyN];
+ if (yyState == 0 && yyM == 0) {
+//t if (yydebug != null) yydebug.shift(0, yyFinal);
+ yyState = yyFinal;
+ if (yyToken < 0) {
+ yyToken = yyLex.advance() ? yyLex.token() : 0;
+//t if (yydebug != null)
+//t yydebug.lex(yyState, yyToken,yyname(yyToken), yyLex.value());
+ }
+ if (yyToken == 0) {
+//t if (yydebug != null) yydebug.accept(yyVal);
+ return yyVal;
+ }
+ continue yyLoop;
+ }
+ if ((yyN = YyGindexClass.yyGindex[yyM]) != 0 && (yyN += yyState) >= 0
+ && yyN < YyTableClass.yyTable.length && YyCheckClass.yyCheck[yyN] == yyState)
+ yyState = YyTableClass.yyTable[yyN];
+ else
+ yyState = YyDgotoClass.yyDgoto[yyM];
+//t if (yydebug != null) yydebug.shift(yyStates[yyTop], yyState);
+ continue yyLoop;
+ }
+ }
+ }
+
+ protected static final class YyLhsClass {
+
+ public static final short yyLhs [] = { -1,
+ 0, 2, 2, 4, 4, 4, 3, 3, 3, 5,
+ 5, 5, 5, 5, 6, 6, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 8,
+ 8, 8, 9, 12, 12, 12, 12, 12, 15, 15,
+ 17, 17, 18, 18, 19, 19, 19, 19, 20, 20,
+ 1, 1, 21, 21, 22, 22, 22, 23, 23, 23,
+ 23, 23, 24, 24, 24, 25, 25, 25, 25, 26,
+ 26, 14, 14, 14, 14, 16, 13, 10, 10, 10,
+ 27, 27, 11, 11, 11, 11,
+ };
+ } /* End of class YyLhsClass */
+
+ protected static final class YyLenClass {
+
+ public static final short yyLen [] = { 2,
+ 1, 1, 1, 1, 2, 2, 1, 3, 3, 1,
+ 2, 3, 1, 1, 1, 2, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 3, 2, 3, 1, 3, 1, 1, 1, 3, 4,
+ 1, 3, 1, 3, 1, 1, 3, 3, 1, 2,
+ 1, 3, 1, 3, 1, 3, 3, 1, 3, 3,
+ 3, 3, 1, 3, 3, 1, 3, 3, 3, 1,
+ 2, 1, 2, 3, 2, 1, 2, 1, 3, 1,
+ 1, 3, 1, 1, 1, 1,
+ };
+ } /* End class YyLenClass */
+
+ protected static final class YyDefRedClass {
+
+ public static final short yyDefRed [] = { 0,
+ 36, 0, 0, 0, 0, 0, 0, 0, 78, 0,
+ 0, 14, 17, 18, 19, 20, 21, 22, 23, 24,
+ 25, 26, 27, 28, 29, 83, 0, 84, 86, 0,
+ 0, 45, 0, 3, 7, 0, 0, 15, 30, 0,
+ 49, 34, 37, 38, 0, 0, 43, 0, 0, 0,
+ 0, 0, 0, 66, 0, 0, 0, 0, 13, 0,
+ 80, 0, 71, 0, 0, 77, 75, 0, 0, 0,
+ 0, 0, 16, 0, 32, 0, 0, 0, 0, 50,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 74, 82, 79, 35, 0, 31, 0, 8,
+ 9, 0, 0, 39, 0, 0, 44, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 67, 68,
+ 69, 33, 0, 40, 42,
+ };
+ } /* End of class YyDefRedClass */
+
+ protected static final class YyDgotoClass {
+
+ public static final short yyDgoto [] = { 105,
+ 31, 32, 33, 34, 35, 36, 37, 38, 73, 39,
+ 40, 41, 42, 43, 44, 45, 106, 46, 47, 48,
+ 49, 50, 51, 52, 53, 54, 55,
+ };
+ } /* End of class YyDgotoClass */
+
+ protected static final class YySindexClass {
+
+ public static final short yySindex [] = { -97,
+ 0, -271, -267, -97, -239, -239, -97, -199, 0, -236,
+ -222, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, -218, 0, 0, 0,
+ -257, 0, -241, 0, 0, -205, -221, 0, 0, -194,
+ 0, 0, 0, 0, -190, -185, 0, -238, -211, -234,
+ -255, -209, -275, 0, 0, -169, -250, -168, 0, -241,
+ 0, -241, 0, -205, -187, 0, 0, -167, -97, -239,
+ -239, -97, 0, -199, 0, -151, -43, -239, -239, 0,
+ -97, -97, -97, -97, -97, -97, -97, -97, -97, -97,
+ -97, -97, 0, 0, 0, 0, -164, 0, -211, 0,
+ 0, -166, -205, 0, -165, -163, 0, -241, -241, -234,
+ -255, -255, -209, -209, -209, -209, -275, -275, 0, 0,
+ 0, 0, -97, 0, 0,
+ };
+ } /* End of class YySindexClass */
+
+ protected static final class YyRindexClass {
+
+ public static final short yyRindex [] = { 0,
+ 0, 58, 1, 0, 420, 0, 0, 0, 0, 0,
+ 129, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, -161, 0, 0, 0,
+ 40, 0, 237, 0, 0, 168, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 459, 0, 277, 557, 544,
+ 656, 561, 474, 0, 19, 75, 0, 0, 0, 295,
+ 0, 334, 0, 183, 114, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 686, 0,
+ 0, 0, 222, 0, -156, 0, 0, 351, 405, 553,
+ 665, 697, 577, 600, 617, 639, 513, 528, 0, 0,
+ 0, 0, 0, 0, 0,
+ };
+ } /* End of class YyRindexClass */
+
+ protected static final class YyGindexClass {
+
+ public static final short yyGindex [] = { 7,
+ 0, 0, 8, 0, 3, -3, 0, 0, 48, 0,
+ 0, 0, 0, 0, 0, 0, -12, 0, 35, 0,
+ 44, 36, -1, -54, 2, -7, -2,
+ };
+ } /* End of class YyGindexClass */
+
+ protected static final class YyTableClass {
+
+ public static final short yyTable [] = { 63,
+ 81, 90, 61, 61, 64, 61, 30, 66, 94, 56,
+ 58, 57, 60, 62, 84, 85, 86, 87, 80, 3,
+ 91, 92, 65, 72, 70, 71, 95, 78, 79, 113,
+ 114, 115, 116, 82, 83, 67, 8, 9, 68, 1,
+ 69, 59, 12, 13, 14, 15, 16, 17, 18, 19,
+ 20, 21, 22, 23, 24, 25, 72, 72, 74, 3,
+ 26, 27, 28, 29, 88, 89, 75, 61, 61, 76,
+ 103, 61, 100, 101, 73, 61, 61, 9, 102, 77,
+ 111, 112, 119, 120, 121, 108, 109, 81, 93, 117,
+ 118, 97, 96, 98, 94, 80, 122, 124, 123, 85,
+ 26, 27, 28, 29, 41, 1, 2, 3, 4, 104,
+ 125, 107, 99, 81, 5, 6, 110, 0, 0, 0,
+ 0, 0, 0, 7, 8, 9, 10, 0, 13, 11,
+ 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
+ 22, 23, 24, 25, 0, 0, 0, 0, 26, 27,
+ 28, 29, 0, 0, 0, 0, 0, 0, 0, 1,
+ 2, 3, 4, 0, 0, 0, 0, 10, 5, 6,
+ 0, 0, 0, 0, 0, 0, 0, 7, 8, 9,
+ 10, 0, 11, 11, 12, 13, 14, 15, 16, 17,
+ 18, 19, 20, 21, 22, 23, 24, 25, 0, 0,
+ 0, 0, 26, 27, 28, 29, 0, 0, 0, 0,
+ 0, 0, 0, 1, 2, 3, 4, 0, 0, 0,
+ 0, 12, 5, 6, 0, 0, 0, 0, 0, 0,
+ 0, 0, 8, 9, 10, 0, 2, 11, 12, 13,
+ 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
+ 24, 25, 0, 0, 0, 0, 26, 27, 28, 29,
+ 81, 81, 81, 81, 81, 81, 81, 81, 81, 81,
+ 81, 81, 81, 81, 81, 81, 46, 81, 76, 80,
+ 80, 80, 80, 80, 80, 80, 80, 80, 80, 80,
+ 80, 80, 80, 80, 5, 80, 81, 81, 81, 81,
+ 1, 0, 1, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 80, 80, 80, 80, 72, 72,
+ 72, 72, 72, 72, 72, 72, 72, 72, 72, 72,
+ 72, 72, 72, 6, 72, 73, 73, 73, 73, 73,
+ 73, 73, 73, 73, 73, 73, 73, 73, 73, 73,
+ 47, 73, 0, 72, 72, 72, 72, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 73, 73, 73, 73, 81, 81, 81, 81, 81, 81,
+ 81, 81, 81, 81, 81, 81, 81, 81, 81, 13,
+ 81, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 13, 13, 13, 13, 48, 13, 0, 0, 0, 81,
+ 81, 81, 81, 0, 0, 0, 0, 0, 0, 4,
+ 0, 0, 0, 0, 13, 13, 13, 13, 10, 0,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 11, 10, 11, 11, 11, 11, 11,
+ 11, 11, 11, 11, 11, 11, 11, 11, 70, 11,
+ 0, 0, 0, 10, 10, 10, 10, 0, 0, 0,
+ 0, 0, 0, 63, 0, 0, 0, 0, 11, 11,
+ 11, 11, 12, 0, 12, 12, 12, 12, 12, 12,
+ 12, 12, 12, 12, 12, 12, 12, 2, 12, 2,
+ 2, 2, 0, 0, 2, 2, 2, 2, 2, 2,
+ 2, 2, 64, 2, 0, 0, 0, 12, 12, 12,
+ 12, 0, 0, 0, 0, 0, 0, 65, 0, 0,
+ 0, 0, 2, 2, 2, 2, 0, 46, 0, 46,
+ 46, 46, 0, 53, 46, 46, 46, 46, 46, 46,
+ 46, 46, 54, 46, 0, 5, 51, 5, 5, 5,
+ 58, 0, 5, 5, 5, 5, 5, 5, 5, 5,
+ 0, 5, 46, 46, 46, 46, 60, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 5, 5, 5, 5, 6, 0, 6, 6, 6, 59,
+ 0, 6, 6, 6, 6, 6, 6, 6, 6, 0,
+ 6, 47, 0, 47, 47, 47, 62, 0, 47, 47,
+ 47, 47, 47, 47, 47, 47, 0, 47, 0, 6,
+ 6, 6, 6, 0, 0, 0, 0, 0, 61, 0,
+ 0, 0, 0, 0, 0, 0, 47, 47, 47, 47,
+ 0, 0, 0, 0, 0, 55, 0, 0, 0, 0,
+ 0, 0, 0, 0, 56, 48, 0, 48, 48, 48,
+ 0, 0, 48, 48, 48, 48, 48, 48, 48, 48,
+ 4, 48, 4, 4, 4, 52, 0, 4, 4, 4,
+ 4, 4, 4, 4, 4, 0, 57, 0, 0, 0,
+ 48, 48, 48, 48, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 4, 4, 4, 4, 70,
+ 0, 70, 70, 0, 0, 0, 70, 70, 70, 70,
+ 70, 70, 70, 70, 63, 70, 63, 63, 0, 0,
+ 0, 63, 63, 63, 63, 63, 63, 63, 63, 0,
+ 0, 0, 0, 0, 70, 70, 70, 70, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 63, 63, 64, 0, 64, 64, 0, 0, 0,
+ 64, 64, 64, 64, 64, 64, 64, 64, 65, 0,
+ 65, 65, 0, 0, 0, 65, 65, 65, 65, 65,
+ 65, 65, 65, 0, 53, 0, 53, 53, 0, 0,
+ 64, 64, 0, 54, 0, 54, 54, 51, 0, 51,
+ 51, 58, 0, 58, 58, 65, 65, 0, 58, 58,
+ 58, 58, 58, 58, 0, 0, 0, 60, 0, 60,
+ 60, 53, 53, 0, 60, 60, 60, 60, 60, 60,
+ 54, 54, 0, 0, 51, 0, 0, 0, 58, 58,
+ 59, 0, 59, 59, 0, 0, 0, 59, 59, 59,
+ 59, 59, 59, 0, 60, 60, 0, 62, 0, 62,
+ 62, 0, 0, 0, 62, 62, 62, 62, 62, 62,
+ 0, 0, 0, 0, 0, 0, 0, 59, 59, 61,
+ 0, 61, 61, 0, 0, 0, 61, 61, 61, 61,
+ 61, 61, 0, 0, 62, 62, 55, 0, 55, 55,
+ 0, 0, 0, 55, 55, 56, 0, 56, 56, 0,
+ 0, 0, 56, 56, 0, 0, 61, 61, 0, 0,
+ 0, 0, 0, 0, 0, 0, 52, 0, 52, 52,
+ 0, 0, 0, 55, 55, 0, 0, 57, 0, 57,
+ 57, 0, 56, 56, 57, 57, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 52, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 57, 57,
+ };
+ } /* End of class YyTableClass */
+
+ protected static final class YyCheckClass {
+
+ public static final short yyCheck [] = { 7,
+ 0, 277, 5, 6, 8, 8, 0, 10, 259, 281,
+ 4, 279, 5, 6, 270, 271, 272, 273, 0, 259,
+ 296, 297, 259, 262, 266, 267, 277, 266, 267, 84,
+ 85, 86, 87, 268, 269, 258, 276, 277, 257, 0,
+ 298, 281, 282, 283, 284, 285, 286, 287, 288, 289,
+ 290, 291, 292, 293, 294, 295, 262, 0, 280, 259,
+ 300, 301, 302, 303, 274, 275, 261, 70, 71, 260,
+ 74, 74, 70, 71, 0, 78, 79, 277, 72, 265,
+ 82, 83, 90, 91, 92, 78, 79, 299, 258, 88,
+ 89, 279, 261, 261, 259, 48, 263, 261, 264, 261,
+ 300, 301, 302, 303, 261, 257, 258, 259, 260, 261,
+ 123, 77, 69, 0, 266, 267, 81, -1, -1, -1,
+ -1, -1, -1, 275, 276, 277, 278, -1, 0, 281,
+ 282, 283, 284, 285, 286, 287, 288, 289, 290, 291,
+ 292, 293, 294, 295, -1, -1, -1, -1, 300, 301,
+ 302, 303, -1, -1, -1, -1, -1, -1, -1, 257,
+ 258, 259, 260, -1, -1, -1, -1, 0, 266, 267,
+ -1, -1, -1, -1, -1, -1, -1, 275, 276, 277,
+ 278, -1, 0, 281, 282, 283, 284, 285, 286, 287,
+ 288, 289, 290, 291, 292, 293, 294, 295, -1, -1,
+ -1, -1, 300, 301, 302, 303, -1, -1, -1, -1,
+ -1, -1, -1, 257, 258, 259, 260, -1, -1, -1,
+ -1, 0, 266, 267, -1, -1, -1, -1, -1, -1,
+ -1, -1, 276, 277, 278, -1, 0, 281, 282, 283,
+ 284, 285, 286, 287, 288, 289, 290, 291, 292, 293,
+ 294, 295, -1, -1, -1, -1, 300, 301, 302, 303,
+ 260, 261, 262, 263, 264, 265, 266, 267, 268, 269,
+ 270, 271, 272, 273, 274, 275, 0, 277, 260, 261,
+ 262, 263, 264, 265, 266, 267, 268, 269, 270, 271,
+ 272, 273, 274, 275, 0, 277, 296, 297, 298, 299,
+ 261, -1, 263, 264, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 296, 297, 298, 299, 261, 262,
+ 263, 264, 265, 266, 267, 268, 269, 270, 271, 272,
+ 273, 274, 275, 0, 277, 261, 262, 263, 264, 265,
+ 266, 267, 268, 269, 270, 271, 272, 273, 274, 275,
+ 0, 277, -1, 296, 297, 298, 299, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 296, 297, 298, 299, 261, 262, 263, 264, 265, 266,
+ 267, 268, 269, 270, 271, 272, 273, 274, 275, 261,
+ 277, 263, 264, 265, 266, 267, 268, 269, 270, 271,
+ 272, 273, 274, 275, 0, 277, -1, -1, -1, 296,
+ 297, 298, 299, -1, -1, -1, -1, -1, -1, 0,
+ -1, -1, -1, -1, 296, 297, 298, 299, 261, -1,
+ 263, 264, 265, 266, 267, 268, 269, 270, 271, 272,
+ 273, 274, 275, 261, 277, 263, 264, 265, 266, 267,
+ 268, 269, 270, 271, 272, 273, 274, 275, 0, 277,
+ -1, -1, -1, 296, 297, 298, 299, -1, -1, -1,
+ -1, -1, -1, 0, -1, -1, -1, -1, 296, 297,
+ 298, 299, 261, -1, 263, 264, 265, 266, 267, 268,
+ 269, 270, 271, 272, 273, 274, 275, 261, 277, 263,
+ 264, 265, -1, -1, 268, 269, 270, 271, 272, 273,
+ 274, 275, 0, 277, -1, -1, -1, 296, 297, 298,
+ 299, -1, -1, -1, -1, -1, -1, 0, -1, -1,
+ -1, -1, 296, 297, 298, 299, -1, 261, -1, 263,
+ 264, 265, -1, 0, 268, 269, 270, 271, 272, 273,
+ 274, 275, 0, 277, -1, 261, 0, 263, 264, 265,
+ 0, -1, 268, 269, 270, 271, 272, 273, 274, 275,
+ -1, 277, 296, 297, 298, 299, 0, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 296, 297, 298, 299, 261, -1, 263, 264, 265, 0,
+ -1, 268, 269, 270, 271, 272, 273, 274, 275, -1,
+ 277, 261, -1, 263, 264, 265, 0, -1, 268, 269,
+ 270, 271, 272, 273, 274, 275, -1, 277, -1, 296,
+ 297, 298, 299, -1, -1, -1, -1, -1, 0, -1,
+ -1, -1, -1, -1, -1, -1, 296, 297, 298, 299,
+ -1, -1, -1, -1, -1, 0, -1, -1, -1, -1,
+ -1, -1, -1, -1, 0, 261, -1, 263, 264, 265,
+ -1, -1, 268, 269, 270, 271, 272, 273, 274, 275,
+ 261, 277, 263, 264, 265, 0, -1, 268, 269, 270,
+ 271, 272, 273, 274, 275, -1, 0, -1, -1, -1,
+ 296, 297, 298, 299, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 296, 297, 298, 299, 261,
+ -1, 263, 264, -1, -1, -1, 268, 269, 270, 271,
+ 272, 273, 274, 275, 261, 277, 263, 264, -1, -1,
+ -1, 268, 269, 270, 271, 272, 273, 274, 275, -1,
+ -1, -1, -1, -1, 296, 297, 298, 299, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 298, 299, 261, -1, 263, 264, -1, -1, -1,
+ 268, 269, 270, 271, 272, 273, 274, 275, 261, -1,
+ 263, 264, -1, -1, -1, 268, 269, 270, 271, 272,
+ 273, 274, 275, -1, 261, -1, 263, 264, -1, -1,
+ 298, 299, -1, 261, -1, 263, 264, 261, -1, 263,
+ 264, 261, -1, 263, 264, 298, 299, -1, 268, 269,
+ 270, 271, 272, 273, -1, -1, -1, 261, -1, 263,
+ 264, 298, 299, -1, 268, 269, 270, 271, 272, 273,
+ 298, 299, -1, -1, 298, -1, -1, -1, 298, 299,
+ 261, -1, 263, 264, -1, -1, -1, 268, 269, 270,
+ 271, 272, 273, -1, 298, 299, -1, 261, -1, 263,
+ 264, -1, -1, -1, 268, 269, 270, 271, 272, 273,
+ -1, -1, -1, -1, -1, -1, -1, 298, 299, 261,
+ -1, 263, 264, -1, -1, -1, 268, 269, 270, 271,
+ 272, 273, -1, -1, 298, 299, 261, -1, 263, 264,
+ -1, -1, -1, 268, 269, 261, -1, 263, 264, -1,
+ -1, -1, 268, 269, -1, -1, 298, 299, -1, -1,
+ -1, -1, -1, -1, -1, -1, 261, -1, 263, 264,
+ -1, -1, -1, 298, 299, -1, -1, 261, -1, 263,
+ 264, -1, 298, 299, 268, 269, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 298, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 298, 299,
+ };
+ } /* End of class YyCheckClass */
+
+
+//t protected static final class YyRuleClass {
+
+//t public static final String yyRule [] = {
+//t "$accept : expr",
+//t "expr : or_expr",
+//t "location_path : relative_location_path",
+//t "location_path : absolute_location_path",
+//t "absolute_location_path : SLASH",
+//t "absolute_location_path : SLASH relative_location_path",
+//t "absolute_location_path : DOUBLE_SLASH relative_location_path",
+//t "relative_location_path : step",
+//t "relative_location_path : relative_location_path SLASH step",
+//t "relative_location_path : relative_location_path DOUBLE_SLASH step",
+//t "step : step_node_test",
+//t "step : AT step_node_test",
+//t "step : axis_name DOUBLE_COLON step_node_test",
+//t "step : DOT",
+//t "step : DOUBLE_DOT",
+//t "step_node_test : node_test",
+//t "step_node_test : step_node_test predicate",
+//t "axis_name : ANCESTOR",
+//t "axis_name : ANCESTOR_OR_SELF",
+//t "axis_name : ATTRIBUTE",
+//t "axis_name : CHILD",
+//t "axis_name : DESCENDANT",
+//t "axis_name : DESCENDANT_OR_SELF",
+//t "axis_name : FOLLOWING",
+//t "axis_name : FOLLOWING_SIBLING",
+//t "axis_name : NAMESPACE",
+//t "axis_name : PARENT",
+//t "axis_name : PRECEDING",
+//t "axis_name : PRECEDING_SIBLING",
+//t "axis_name : SELF",
+//t "node_test : name_test",
+//t "node_test : PROCESSING_INSTRUCTION LITERAL RP",
+//t "node_test : node_type RP",
+//t "predicate : LB expr RB",
+//t "primary_expr : variable_reference",
+//t "primary_expr : LP expr RP",
+//t "primary_expr : LITERAL",
+//t "primary_expr : number",
+//t "primary_expr : function_call",
+//t "function_call : function_name LP RP",
+//t "function_call : function_name LP argument_list RP",
+//t "argument_list : expr",
+//t "argument_list : expr COMMA argument_list",
+//t "union_expr : path_expr",
+//t "union_expr : union_expr PIPE path_expr",
+//t "path_expr : location_path",
+//t "path_expr : filter_expr",
+//t "path_expr : filter_expr SLASH relative_location_path",
+//t "path_expr : filter_expr DOUBLE_SLASH relative_location_path",
+//t "filter_expr : primary_expr",
+//t "filter_expr : filter_expr predicate",
+//t "or_expr : and_expr",
+//t "or_expr : or_expr OR and_expr",
+//t "and_expr : equality_expr",
+//t "and_expr : and_expr AND equality_expr",
+//t "equality_expr : relational_expr",
+//t "equality_expr : equality_expr EQ relational_expr",
+//t "equality_expr : equality_expr NE relational_expr",
+//t "relational_expr : additive_expr",
+//t "relational_expr : relational_expr LT additive_expr",
+//t "relational_expr : relational_expr GT additive_expr",
+//t "relational_expr : relational_expr LTE additive_expr",
+//t "relational_expr : relational_expr GTE additive_expr",
+//t "additive_expr : multiplicative_expr",
+//t "additive_expr : additive_expr PLUS multiplicative_expr",
+//t "additive_expr : additive_expr MINUS multiplicative_expr",
+//t "multiplicative_expr : unary_expr",
+//t "multiplicative_expr : multiplicative_expr STAR unary_expr",
+//t "multiplicative_expr : multiplicative_expr DIV unary_expr",
+//t "multiplicative_expr : multiplicative_expr MOD unary_expr",
+//t "unary_expr : union_expr",
+//t "unary_expr : MINUS unary_expr",
+//t "number : DIGITS",
+//t "number : DIGITS DOT",
+//t "number : DIGITS DOT DIGITS",
+//t "number : DOT DIGITS",
+//t "function_name : qname",
+//t "variable_reference : DOLLAR qname",
+//t "name_test : STAR",
+//t "name_test : NAME COLON STAR",
+//t "name_test : qname",
+//t "qname : NAME",
+//t "qname : NAME COLON NAME",
+//t "node_type : COMMENT",
+//t "node_type : TEXT",
+//t "node_type : PROCESSING_INSTRUCTION",
+//t "node_type : NODE",
+//t };
+//t } /* End of class YyRuleClass */
+
+ protected static final class YyNameClass {
+
+ public static final String yyName [] = {
+ "end-of-file",null,null,null,null,null,null,null,null,null,null,null,
+ null,null,null,null,null,null,null,null,null,null,null,null,null,null,
+ null,null,null,null,null,null,null,null,null,null,null,null,null,null,
+ null,null,null,null,null,null,null,null,null,null,null,null,null,null,
+ null,null,null,null,null,null,null,null,null,null,null,null,null,null,
+ null,null,null,null,null,null,null,null,null,null,null,null,null,null,
+ null,null,null,null,null,null,null,null,null,null,null,null,null,null,
+ null,null,null,null,null,null,null,null,null,null,null,null,null,null,
+ null,null,null,null,null,null,null,null,null,null,null,null,null,null,
+ null,null,null,null,null,null,null,null,null,null,null,null,null,null,
+ null,null,null,null,null,null,null,null,null,null,null,null,null,null,
+ null,null,null,null,null,null,null,null,null,null,null,null,null,null,
+ null,null,null,null,null,null,null,null,null,null,null,null,null,null,
+ null,null,null,null,null,null,null,null,null,null,null,null,null,null,
+ null,null,null,null,null,null,null,null,null,null,null,null,null,null,
+ null,null,null,null,null,null,null,null,null,null,null,null,null,null,
+ null,null,null,null,null,null,null,null,null,null,null,null,null,null,
+ null,null,null,null,null,null,null,null,null,null,null,null,null,null,
+ null,null,null,null,null,null,null,"LITERAL","DIGITS","NAME","LP",
+ "RP","LB","RB","COMMA","PIPE","SLASH","DOUBLE_SLASH","EQ","NE","GT",
+ "LT","GTE","LTE","PLUS","MINUS","AT","STAR","DOLLAR","COLON",
+ "DOUBLE_COLON","DOT","DOUBLE_DOT","ANCESTOR","ANCESTOR_OR_SELF",
+ "ATTRIBUTE","CHILD","DESCENDANT","DESCENDANT_OR_SELF","FOLLOWING",
+ "FOLLOWING_SIBLING","NAMESPACE","PARENT","PRECEDING",
+ "PRECEDING_SIBLING","SELF","DIV","MOD","OR","AND","COMMENT",
+ "PROCESSING_INSTRUCTION","TEXT","NODE","UNARY",
+ };
+ } /* End of class YyNameClass */
+
+
+ // line 781 "XPathParser.y"
+
+}
+ // line 1461 "-"
diff --git a/libjava/gnu/xml/xpath/XPathParser.y b/libjava/gnu/xml/xpath/XPathParser.y
new file mode 100644
index 0000000..8af7cb8
--- /dev/null
+++ b/libjava/gnu/xml/xpath/XPathParser.y
@@ -0,0 +1,782 @@
+%{
+/* XPathParser.java -- An XPath 1.0 parser.
+ Copyright (C) 2004 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.xpath;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import javax.xml.namespace.NamespaceContext;
+import javax.xml.namespace.QName;
+import javax.xml.xpath.XPathFunctionResolver;
+import javax.xml.xpath.XPathVariableResolver;
+import org.w3c.dom.Node;
+
+/**
+ * An XPath 1.0 parser.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+public class XPathParser
+{
+
+ NamespaceContext namespaceContext;
+ XPathVariableResolver variableResolver;
+ XPathFunctionResolver functionResolver;
+
+ QName getQName(String name)
+ {
+ QName qName = QName.valueOf(name);
+ if (namespaceContext != null)
+ {
+ String prefix = qName.getPrefix();
+ String uri = qName.getNamespaceURI();
+ if (prefix != null && (uri == null || uri.length() == 0))
+ {
+ uri = namespaceContext.getNamespaceURI(prefix);
+ String localName = qName.getLocalPart();
+ qName = new QName(uri, localName, prefix);
+ }
+ }
+ return qName;
+ }
+
+ Expr lookupFunction(String name, List args)
+ {
+ int arity = args.size();
+ if ("position".equals(name) && arity == 0)
+ {
+ return new PositionFunction();
+ }
+ else if ("last".equals(name) && arity == 0)
+ {
+ return new LastFunction();
+ }
+ else if ("string".equals(name) && (arity == 1 || arity == 0))
+ {
+ return new StringFunction(args);
+ }
+ else if ("number".equals(name) && (arity == 1 || arity == 0))
+ {
+ return new NumberFunction(args);
+ }
+ else if ("boolean".equals(name) && arity == 1)
+ {
+ return new BooleanFunction(args);
+ }
+ else if ("count".equals(name) && arity == 1)
+ {
+ return new CountFunction(args);
+ }
+ else if ("not".equals(name) && arity == 1)
+ {
+ return new NotFunction(args);
+ }
+ else if ("id".equals(name) && arity == 1)
+ {
+ return new IdFunction(args);
+ }
+ else if ("concat".equals(name) && arity > 1)
+ {
+ return new ConcatFunction(args);
+ }
+ else if ("true".equals(name) && arity == 0)
+ {
+ return new TrueFunction();
+ }
+ else if ("false".equals(name) && arity == 0)
+ {
+ return new FalseFunction();
+ }
+ else if ("name".equals(name) && (arity == 1 || arity == 0))
+ {
+ return new NameFunction(args);
+ }
+ else if ("local-name".equals(name) && (arity == 1 || arity == 0))
+ {
+ return new LocalNameFunction(args);
+ }
+ else if ("namespace-uri".equals(name) && (arity == 1 || arity == 0))
+ {
+ return new NamespaceUriFunction(args);
+ }
+ else if ("starts-with".equals(name) && arity == 2)
+ {
+ return new StartsWithFunction(args);
+ }
+ else if ("contains".equals(name) && arity == 2)
+ {
+ return new ContainsFunction(args);
+ }
+ else if ("string-length".equals(name) && (arity == 1 || arity == 0))
+ {
+ return new StringLengthFunction(args);
+ }
+ else if ("translate".equals(name) && arity == 3)
+ {
+ return new TranslateFunction(args);
+ }
+ else if ("normalize-space".equals(name) && (arity == 1 || arity == 0))
+ {
+ return new NormalizeSpaceFunction(args);
+ }
+ else if ("substring".equals(name) && (arity == 2 || arity == 3))
+ {
+ return new SubstringFunction(args);
+ }
+ else if ("substring-before".equals(name) && arity == 2)
+ {
+ return new SubstringBeforeFunction(args);
+ }
+ else if ("substring-after".equals(name) && arity == 2)
+ {
+ return new SubstringAfterFunction(args);
+ }
+ else if ("lang".equals(name) && arity == 1)
+ {
+ return new LangFunction(args);
+ }
+ else if ("sum".equals(name) && arity == 1)
+ {
+ return new SumFunction(args);
+ }
+ else if ("floor".equals(name) && arity == 1)
+ {
+ return new FloorFunction(args);
+ }
+ else if ("ceiling".equals(name) && arity == 1)
+ {
+ return new CeilingFunction(args);
+ }
+ else if ("round".equals(name) && arity == 1)
+ {
+ return new RoundFunction(args);
+ }
+ else if (functionResolver != null)
+ {
+ QName qName = QName.valueOf(name);
+ Object function = functionResolver.resolveFunction(qName, arity);
+ if (function != null &&
+ function instanceof Function &&
+ function instanceof Expr)
+ {
+ Function f = (Function) function;
+ f.setArguments(args);
+ return (Expr) function;
+ }
+ }
+ return new FunctionCall(functionResolver, name, args);
+ }
+
+%}
+
+%token LITERAL
+%token DIGITS
+%token NAME
+
+%token LP // '('
+%token RP // ')'
+%token LB // '['
+%token RB // ']'
+%token COMMA // ','
+%token PIPE // '|'
+%token SLASH // '/'
+%token DOUBLE_SLASH // '//'
+%token EQ // '='
+%token NE // '!='
+%token GT // '>'
+%token LT // '<'
+%token GTE // '>='
+%token LTE // '<='
+%token PLUS // '+'
+%token MINUS // '-'
+%token AT // '@'
+%token STAR // '*'
+%token DOLLAR // '$'
+%token COLON // ':'
+%token DOUBLE_COLON // '::'
+%token DOT // '.'
+%token DOUBLE_DOT // '..'
+
+%token ANCESTOR
+%token ANCESTOR_OR_SELF
+%token ATTRIBUTE
+%token CHILD
+%token DESCENDANT
+%token DESCENDANT_OR_SELF
+%token FOLLOWING
+%token FOLLOWING_SIBLING
+%token NAMESPACE
+%token PARENT
+%token PRECEDING
+%token PRECEDING_SIBLING
+%token SELF
+%token DIV
+%token MOD
+%token OR
+%token AND
+%token COMMENT
+%token PROCESSING_INSTRUCTION
+%token TEXT
+%token NODE
+
+%right UNARY
+
+%start expr
+
+%%
+
+expr:
+ or_expr
+ ;
+
+location_path:
+ relative_location_path
+ | absolute_location_path
+ ;
+
+absolute_location_path:
+ SLASH
+ {
+ $$ = new Root();
+ }
+ | SLASH relative_location_path
+ {
+ Steps steps;
+ if ($2 instanceof Steps)
+ {
+ steps = (Steps) $2;
+ }
+ else
+ {
+ steps = new Steps();
+ steps.path.addFirst($2);
+ }
+ steps.path.addFirst(new Root());
+ $$ = steps;
+ //$$ = new Step(new Root(), (Path) $2);
+ }
+ | DOUBLE_SLASH relative_location_path
+ {
+ Test nt = new NodeTypeTest((short) 0);
+ Selector s = new Selector(Selector.DESCENDANT_OR_SELF,
+ Collections.singletonList (nt));
+ Steps steps;
+ if ($2 instanceof Steps)
+ {
+ steps = (Steps) $2;
+ }
+ else
+ {
+ steps = new Steps();
+ steps.path.addFirst($2);
+ }
+ steps.path.addFirst(s);
+ steps.path.addFirst(new Root());
+ $$ = steps;
+ //Step step = new Step(s, (Path) $2);
+ //$$ = new Step(new Root(), step);
+ }
+ ;
+
+relative_location_path:
+ step
+ | relative_location_path SLASH step
+ {
+ Steps steps;
+ if ($1 instanceof Steps)
+ {
+ steps = (Steps) $1;
+ }
+ else
+ {
+ steps = new Steps();
+ steps.path.addFirst($1);
+ }
+ steps.path.addLast($3);
+ $$ = steps;
+ //$$ = new Step((Expr) $1, (Path) $3);
+ }
+ | relative_location_path DOUBLE_SLASH step
+ {
+ Test nt = new NodeTypeTest((short) 0);
+ Selector s = new Selector(Selector.DESCENDANT_OR_SELF,
+ Collections.singletonList (nt));
+ Steps steps;
+ if ($1 instanceof Steps)
+ {
+ steps = (Steps) $1;
+ }
+ else
+ {
+ steps = new Steps();
+ steps.path.addFirst($1);
+ }
+ steps.path.addLast(s);
+ steps.path.addLast($3);
+ $$ = steps;
+ //Step step = new Step(s, (Path) $3);
+ //$$ = new Step((Expr) $1, step);
+ }
+ ;
+
+step:
+ step_node_test
+ {
+ $$ = new Selector (Selector.CHILD, (List) $1);
+ }
+ | AT step_node_test
+ {
+ $$ = new Selector (Selector.ATTRIBUTE, (List) $2);
+ }
+ | axis_name DOUBLE_COLON step_node_test
+ {
+ $$ = new Selector (((Integer) $1).intValue (), (List) $3);
+ }
+ | DOT
+ {
+ $$ = new Selector (Selector.SELF, Collections.EMPTY_LIST);
+ }
+ | DOUBLE_DOT
+ {
+ $$ = new Selector (Selector.PARENT, Collections.EMPTY_LIST);
+ }
+ ;
+
+step_node_test:
+ node_test
+ {
+ List list = new ArrayList();
+ list.add($1);
+ $$ = list;
+ }
+ | step_node_test predicate
+ {
+ List list = (List)$1;
+ list.add($2);
+ $$ = list;
+ }
+ ;
+
+/*predicate_list:
+ predicate
+ {
+ List list = new ArrayList ();
+ list.add ($1);
+ $$ = list;
+ }
+ | predicate predicate_list
+ {
+ List list = (List) $3;
+ list.add (0, $1);
+ $$ = list;
+ }
+ ;*/
+
+axis_name:
+ ANCESTOR
+ {
+ $$ = new Integer(Selector.ANCESTOR);
+ }
+ | ANCESTOR_OR_SELF
+ {
+ $$ = new Integer(Selector.ANCESTOR_OR_SELF);
+ }
+ | ATTRIBUTE
+ {
+ $$ = new Integer(Selector.ATTRIBUTE);
+ }
+ | CHILD
+ {
+ $$ = new Integer(Selector.CHILD);
+ }
+ | DESCENDANT
+ {
+ $$ = new Integer(Selector.DESCENDANT);
+ }
+ | DESCENDANT_OR_SELF
+ {
+ $$ = new Integer(Selector.DESCENDANT_OR_SELF);
+ }
+ | FOLLOWING
+ {
+ $$ = new Integer(Selector.FOLLOWING);
+ }
+ | FOLLOWING_SIBLING
+ {
+ $$ = new Integer(Selector.FOLLOWING_SIBLING);
+ }
+ | NAMESPACE
+ {
+ $$ = new Integer(Selector.NAMESPACE);
+ }
+ | PARENT
+ {
+ $$ = new Integer(Selector.PARENT);
+ }
+ | PRECEDING
+ {
+ $$ = new Integer(Selector.PRECEDING);
+ }
+ | PRECEDING_SIBLING
+ {
+ $$ = new Integer(Selector.PRECEDING_SIBLING);
+ }
+ | SELF
+ {
+ $$ = new Integer(Selector.SELF);
+ }
+ ;
+
+node_test:
+ name_test
+ /*| PROCESSING_INSTRUCTION LP LITERAL RP*/
+ | PROCESSING_INSTRUCTION LITERAL RP
+ {
+ $$ = new NodeTypeTest(Node.PROCESSING_INSTRUCTION_NODE, (String) $2);
+ }
+ /*| node_type LP RP*/
+ | node_type RP
+ {
+ $$ = new NodeTypeTest(((Short) $1).shortValue());
+ }
+ ;
+
+predicate:
+ LB expr RB
+ {
+ $$ = new Predicate((Expr) $2);
+ }
+ ;
+
+primary_expr:
+ variable_reference
+ | LP expr RP
+ {
+ $$ = new ParenthesizedExpr((Expr) $2);
+ }
+ | LITERAL
+ {
+ $$ = new Constant($1);
+ }
+ | number
+ {
+ $$ = new Constant($1);
+ }
+ | function_call
+ ;
+
+function_call:
+ function_name LP RP
+ {
+ $$ = lookupFunction((String) $1, Collections.EMPTY_LIST);
+ }
+ | function_name LP argument_list RP
+ {
+ $$ = lookupFunction((String) $1, (List) $3);
+ }
+ ;
+
+argument_list:
+ expr
+ {
+ List list = new ArrayList();
+ list.add($1);
+ $$ = list;
+ }
+ | expr COMMA argument_list
+ {
+ List list = (List) $3;
+ list.add(0, $1);
+ $$ = list;
+ }
+ ;
+
+union_expr:
+ path_expr
+ | union_expr PIPE path_expr
+ {
+ $$ = new UnionExpr((Expr) $1, (Expr) $3);
+ }
+ ;
+
+path_expr:
+ location_path
+ | filter_expr
+ | filter_expr SLASH relative_location_path
+ {
+ Steps steps;
+ if ($3 instanceof Steps)
+ {
+ steps = (Steps) $3;
+ }
+ else
+ {
+ steps = new Steps();
+ steps.path.addFirst($3);
+ }
+ steps.path.addFirst($1);
+ $$ = steps;
+ //$$ = new Step ((Expr) $1, (Path) $3);
+ }
+ | filter_expr DOUBLE_SLASH relative_location_path
+ {
+ Test nt = new NodeTypeTest((short) 0);
+ Selector s = new Selector(Selector.DESCENDANT_OR_SELF,
+ Collections.singletonList(nt));
+ Steps steps;
+ if ($3 instanceof Steps)
+ {
+ steps = (Steps) $3;
+ }
+ else
+ {
+ steps = new Steps();
+ steps.path.addFirst($3);
+ }
+ steps.path.addFirst(s);
+ steps.path.addFirst($1);
+ $$ = steps;
+ //Step step = new Step (s, (Path) $3);
+ //$$ = new Step ((Expr) $1, step);
+ }
+ ;
+
+filter_expr:
+ primary_expr
+ | filter_expr predicate
+ {
+ Predicate filter = (Predicate) $2;
+ Selector s = new Selector(Selector.SELF,
+ Collections.singletonList(filter));
+ Steps steps;
+ if ($1 instanceof Steps)
+ {
+ steps = (Steps) $1;
+ }
+ else
+ {
+ steps = new Steps();
+ steps.path.addFirst($1);
+ }
+ steps.path.addLast(s);
+ $$ = steps;
+ //$$ = new Step ((Expr) $1, s);
+ }
+ ;
+
+or_expr:
+ and_expr
+ | or_expr OR and_expr
+ {
+ $$ = new OrExpr((Expr) $1, (Expr) $3);
+ }
+ ;
+
+and_expr:
+ equality_expr
+ | and_expr AND equality_expr
+ {
+ $$ = new AndExpr((Expr) $1, (Expr) $3);
+ }
+ ;
+
+equality_expr:
+ relational_expr
+ | equality_expr EQ relational_expr
+ {
+ $$ = new EqualityExpr((Expr) $1, (Expr) $3, false);
+ }
+ | equality_expr NE relational_expr
+ {
+ $$ = new EqualityExpr((Expr) $1, (Expr) $3, true);
+ }
+ ;
+
+relational_expr:
+ additive_expr
+ | relational_expr LT additive_expr
+ {
+ $$ = new RelationalExpr((Expr) $1, (Expr) $3, true, false);
+ }
+ | relational_expr GT additive_expr
+ {
+ $$ = new RelationalExpr((Expr) $1, (Expr) $3, false, false);
+ }
+ | relational_expr LTE additive_expr
+ {
+ $$ = new RelationalExpr((Expr) $1, (Expr) $3, true, true);
+ }
+ | relational_expr GTE additive_expr
+ {
+ $$ = new RelationalExpr((Expr) $1, (Expr) $3, false, true);
+ }
+ ;
+
+additive_expr:
+ multiplicative_expr
+ | additive_expr PLUS multiplicative_expr
+ {
+ $$ = new ArithmeticExpr((Expr) $1, (Expr) $3, ArithmeticExpr.ADD);
+ }
+ | additive_expr MINUS multiplicative_expr
+ {
+ $$ = new ArithmeticExpr((Expr) $1, (Expr) $3, ArithmeticExpr.SUBTRACT);
+ }
+ ;
+
+multiplicative_expr:
+ unary_expr
+ | multiplicative_expr STAR unary_expr
+ {
+ $$ = new ArithmeticExpr((Expr) $1, (Expr) $3, ArithmeticExpr.MULTIPLY);
+ }
+ | multiplicative_expr DIV unary_expr
+ {
+ $$ = new ArithmeticExpr((Expr) $1, (Expr) $3, ArithmeticExpr.DIVIDE);
+ }
+ | multiplicative_expr MOD unary_expr
+ {
+ $$ = new ArithmeticExpr((Expr) $1, (Expr) $3, ArithmeticExpr.MODULO);
+ }
+ ;
+
+unary_expr:
+ union_expr
+ | MINUS unary_expr %prec UNARY
+ {
+ $$ = new NegativeExpr((Expr) $2);
+ }
+ ;
+
+number:
+ DIGITS
+ {
+ $$ = new Double((String) $1 + ".0");
+ }
+ | DIGITS DOT
+ {
+ $$ = new Double((String) $1 + ".0");
+ }
+ | DIGITS DOT DIGITS
+ {
+ $$ = new Double((String) $1 + "." + (String) $3);
+ }
+ | DOT DIGITS
+ {
+ $$ = new Double("0." + (String) $2);
+ }
+ ;
+
+function_name:
+ qname
+/* | node_type
+ {
+ switch (((Short) $1).shortValue ())
+ {
+ case Node.COMMENT_NODE:
+ $$ = "comment";
+ break;
+ case Node.TEXT_NODE:
+ $$ = "text";
+ break;
+ case Node.PROCESSING_INSTRUCTION_NODE:
+ $$ = "processing-instruction";
+ break;
+ default:
+ $$ = "node";
+ break;
+ }
+ }*/
+ ;
+
+variable_reference:
+ DOLLAR qname
+ {
+ $$ = new VariableReference(variableResolver, (String) $2);
+ }
+ ;
+
+name_test:
+ STAR
+ {
+ $$ = new NameTest(null, true, true);
+ }
+ | NAME COLON STAR
+ {
+ QName qName = getQName((String) $1);
+ $$ = new NameTest(qName, true, false);
+ }
+ | qname
+ {
+ QName qName = getQName((String) $1);
+ $$ = new NameTest(qName, false, false);
+ }
+ ;
+
+qname:
+ NAME
+ | NAME COLON NAME
+ {
+ $$ = (String) $1 + ':' + (String) $3;
+ }
+ ;
+
+node_type:
+ COMMENT
+ {
+ $$ = new Short(Node.COMMENT_NODE);
+ }
+ | TEXT
+ {
+ $$ = new Short(Node.TEXT_NODE);
+ }
+ | PROCESSING_INSTRUCTION
+ {
+ $$ = new Short(Node.PROCESSING_INSTRUCTION_NODE);
+ }
+ | NODE
+ {
+ $$ = new Short((short) 0);
+ }
+ ;
+
+%%
+
+}
diff --git a/libjava/gnu/xml/xpath/XPathTokenizer.java b/libjava/gnu/xml/xpath/XPathTokenizer.java
new file mode 100644
index 0000000..6bf31fd
--- /dev/null
+++ b/libjava/gnu/xml/xpath/XPathTokenizer.java
@@ -0,0 +1,592 @@
+/* XPathTokenizer.java --
+ Copyright (C) 2004 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.xpath;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.Reader;
+import java.io.StringReader;
+import java.util.Map;
+import java.util.TreeMap;
+
+/*import antlr.Token;
+import antlr.TokenStream;
+import antlr.TokenStreamException;
+import antlr.TokenStreamIOException;*/
+
+/**
+ * XPath 1.0 expression tokenizer.
+ *
+ * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
+ */
+public class XPathTokenizer
+implements XPathParser.yyInput
+//implements TokenStream
+{
+
+ static class XPathToken
+ //extends Token
+ {
+
+ int type;
+ String val;
+
+ XPathToken (int type)
+ {
+ this (type, null);
+ }
+
+ XPathToken (int type, String val)
+ {
+ //super (type);
+ this.type = type;
+ this.val = val;
+ }
+
+ public String getText ()
+ {
+ return val;
+ }
+
+ public String toString ()
+ {
+ return val;
+ }
+
+ }
+
+ static final Map keywords = new TreeMap ();
+ static
+ {
+ keywords.put ("ancestor", new Integer (XPathParser.ANCESTOR));
+ keywords.put ("ancestor-or-self", new Integer (XPathParser.ANCESTOR_OR_SELF));
+ keywords.put ("attribute", new Integer (XPathParser.ATTRIBUTE));
+ keywords.put ("child", new Integer (XPathParser.CHILD));
+ keywords.put ("descendant", new Integer (XPathParser.DESCENDANT));
+ keywords.put ("descendant-or-self", new Integer (XPathParser.DESCENDANT_OR_SELF));
+ keywords.put ("following", new Integer (XPathParser.FOLLOWING));
+ keywords.put ("following-sibling", new Integer (XPathParser.FOLLOWING_SIBLING));
+ keywords.put ("namespace", new Integer (XPathParser.NAMESPACE));
+ keywords.put ("parent", new Integer (XPathParser.PARENT));
+ keywords.put ("preceding", new Integer (XPathParser.PRECEDING));
+ keywords.put ("preceding-sibling", new Integer (XPathParser.PRECEDING_SIBLING));
+ keywords.put ("self", new Integer (XPathParser.SELF));
+ keywords.put ("div", new Integer (XPathParser.DIV));
+ keywords.put ("mod", new Integer (XPathParser.MOD));
+ keywords.put ("or", new Integer (XPathParser.OR));
+ keywords.put ("and", new Integer (XPathParser.AND));
+ keywords.put ("comment", new Integer (XPathParser.COMMENT));
+ keywords.put ("processing-instruction", new Integer (XPathParser.PROCESSING_INSTRUCTION));
+ keywords.put ("text", new Integer (XPathParser.TEXT));
+ keywords.put ("node", new Integer (XPathParser.NODE));
+ }
+
+ Reader in;
+ XPathToken token;
+ XPathToken lastToken;
+
+ public XPathTokenizer (String expr)
+ {
+ this (new StringReader (expr));
+ }
+
+ XPathTokenizer (Reader in)
+ {
+ this.in = in.markSupported () ? in : new BufferedReader (in);
+ }
+
+ /* Begin ANTLR specific *
+
+ public Token nextToken ()
+ throws TokenStreamException
+ {
+ try
+ {
+ if (!advance ())
+ {
+ throw new TokenStreamException ("eof");
+ }
+ token ();
+ return token;
+ }
+ catch (IOException e)
+ {
+ throw new TokenStreamIOException (e);
+ }
+ }
+
+ * End ANTLR specific */
+
+ public boolean advance ()
+ throws IOException
+ {
+ lastToken = token;
+ int c = in.read ();
+ switch (c)
+ {
+ case -1: // eof
+ return false;
+ case 0x20:
+ case 0x09:
+ case 0x0d:
+ case 0x0a: // skip whitespace
+ return advance ();
+ case 0x22: // "
+ case 0x27: // '
+ token = consume_literal (c);
+ break;
+ case 0x28: // (
+ token = new XPathToken (XPathParser.LP);
+ break;
+ case 0x29: // )
+ token = new XPathToken (XPathParser.RP);
+ break;
+ case 0x5b: // [
+ token = new XPathToken (XPathParser.LB);
+ break;
+ case 0x5d: // ]
+ token = new XPathToken (XPathParser.RB);
+ break;
+ case 0x2c: // ,
+ token = new XPathToken (XPathParser.COMMA);
+ break;
+ case 0x7c: // |
+ token = new XPathToken (XPathParser.PIPE);
+ break;
+ case 0x2f: // /
+ in.mark (1);
+ int d1 = in.read ();
+ if (d1 == 0x2f)
+ {
+ token = new XPathToken (XPathParser.DOUBLE_SLASH);
+ }
+ else
+ {
+ in.reset ();
+ token = new XPathToken (XPathParser.SLASH);
+ }
+ break;
+ case 0x3d: // =
+ token = new XPathToken (XPathParser.EQ);
+ break;
+ case 0x21: // !
+ in.mark (1);
+ int d2 = in.read ();
+ if (d2 == 0x3d) // =
+ {
+ token = new XPathToken (XPathParser.NE);
+ }
+ else
+ {
+ in.reset ();
+ token = new XPathToken (XPathParser.yyErrorCode);
+ }
+ break;
+ case 0x3e: // >
+ in.mark (1);
+ int d3 = in.read ();
+ if (d3 == 0x3d) // =
+ {
+ token = new XPathToken (XPathParser.GTE);
+ }
+ else
+ {
+ in.reset ();
+ token = new XPathToken (XPathParser.GT);
+ }
+ break;
+ case 0x3c: // <
+ in.mark (1);
+ int d4 = in.read ();
+ if (d4 == 0x3d) // =
+ {
+ token = new XPathToken (XPathParser.LTE);
+ }
+ else
+ {
+ in.reset ();
+ token = new XPathToken (XPathParser.LT);
+ }
+ break;
+ case 0x2b: // +
+ token = new XPathToken (XPathParser.PLUS);
+ break;
+ case 0x2d: // -
+ token = new XPathToken (XPathParser.MINUS);
+ break;
+ case 0x40: // @
+ token = new XPathToken (XPathParser.AT);
+ break;
+ case 0x2a: // *
+ token = new XPathToken (XPathParser.STAR);
+ break;
+ case 0x24: // $
+ token = new XPathToken (XPathParser.DOLLAR);
+ break;
+ case 0x3a: // :
+ in.mark (1);
+ int d5 = in.read ();
+ if (d5 == 0x3a)
+ {
+ token = new XPathToken (XPathParser.DOUBLE_COLON);
+ }
+ else
+ {
+ in.reset ();
+ token = new XPathToken (XPathParser.COLON);
+ }
+ break;
+ case 0x2e: // .
+ in.mark (1);
+ int d6 = in.read ();
+ if (d6 == 0x2e)
+ {
+ token = new XPathToken (XPathParser.DOUBLE_DOT);
+ }
+ else
+ {
+ in.reset ();
+ token = new XPathToken (XPathParser.DOT);
+ }
+ break;
+ default:
+ if (c >= 0x30 && c <= 0x39)
+ {
+ token = consume_digits (c);
+ }
+ else if (c == 0x5f || Character.isLetter ((char) c))
+ {
+ token = consume_name (c);
+ }
+ else
+ {
+ token = new XPathToken (XPathParser.yyErrorCode);
+ }
+ }
+ return true;
+ }
+
+ public int token ()
+ {
+ return token.type;
+ }
+
+ public Object value ()
+ {
+ return token.val;
+ }
+
+ XPathToken consume_literal (int delimiter)
+ throws IOException
+ {
+ StringBuffer buf = new StringBuffer ();
+ while (true)
+ {
+ int c = in.read ();
+ if (c == -1)
+ {
+ return new XPathToken (XPathParser.yyErrorCode);
+ }
+ else if (c == delimiter)
+ {
+ return new XPathToken (XPathParser.LITERAL, buf.toString ());
+ }
+ else
+ {
+ buf.append ((char) c);
+ }
+ }
+ }
+
+ XPathToken consume_digits (int c)
+ throws IOException
+ {
+ StringBuffer buf = new StringBuffer ();
+ buf.append ((char) c);
+ while (true)
+ {
+ in.mark (1);
+ c = in.read ();
+ if (c >= 0x30 && c <= 0x39)
+ {
+ buf.append ((char) c);
+ }
+ else
+ {
+ in.reset ();
+ return new XPathToken (XPathParser.DIGITS, buf.toString ());
+ }
+ }
+ }
+
+ XPathToken consume_name (int c)
+ throws IOException
+ {
+ StringBuffer buf = new StringBuffer ();
+ buf.append ((char) c);
+ while (true)
+ {
+ in.mark (1);
+ c = in.read ();
+ if (isNameChar (c))
+ {
+ buf.append ((char) c);
+ }
+ else
+ {
+ in.reset ();
+ String name = buf.toString ();
+ Integer keyword = (Integer) keywords.get (name);
+ if (keyword == null)
+ {
+ return new XPathToken (XPathParser.NAME, name);
+ }
+ else
+ {
+ int val = keyword.intValue ();
+ switch (val)
+ {
+ case XPathParser.NODE:
+ case XPathParser.COMMENT:
+ case XPathParser.TEXT:
+ case XPathParser.PROCESSING_INSTRUCTION:
+ // Consume subsequent (
+ in.mark (1);
+ do
+ {
+ c = in.read ();
+ }
+ while (c == 0x20 || c == 0x09);
+ if (c != 0x28)
+ {
+ in.reset ();
+ return new XPathToken (XPathParser.NAME, name);
+ }
+ break;
+ case XPathParser.CHILD:
+ case XPathParser.PARENT:
+ case XPathParser.SELF:
+ case XPathParser.DESCENDANT:
+ case XPathParser.ANCESTOR:
+ case XPathParser.DESCENDANT_OR_SELF:
+ case XPathParser.ANCESTOR_OR_SELF:
+ case XPathParser.ATTRIBUTE:
+ case XPathParser.NAMESPACE:
+ case XPathParser.FOLLOWING:
+ case XPathParser.FOLLOWING_SIBLING:
+ case XPathParser.PRECEDING:
+ case XPathParser.PRECEDING_SIBLING:
+ // Check that this is an axis specifier
+ in.mark(1);
+ do
+ {
+ c = in.read();
+ }
+ while (c == 0x20 || c == 0x09);
+ if (c == 0x3a)
+ {
+ c = in.read();
+ if (c == 0x3a)
+ {
+ in.reset();
+ return new XPathToken(val);
+ }
+ }
+ in.reset();
+ return new XPathToken(XPathParser.NAME, name);
+ case XPathParser.DIV:
+ case XPathParser.MOD:
+ // May be a name
+ if (lastToken == null)
+ {
+ return new XPathToken(XPathParser.NAME, name);
+ }
+ switch (lastToken.type)
+ {
+ case XPathParser.LP:
+ case XPathParser.LB:
+ case XPathParser.COMMA:
+ case XPathParser.PIPE:
+ case XPathParser.EQ:
+ case XPathParser.NE:
+ case XPathParser.GT:
+ case XPathParser.LT:
+ case XPathParser.GTE:
+ case XPathParser.LTE:
+ case XPathParser.PLUS:
+ case XPathParser.MINUS:
+ case XPathParser.STAR:
+ case XPathParser.AT:
+ case XPathParser.DOLLAR:
+ case XPathParser.COLON:
+ case XPathParser.DOUBLE_COLON:
+ case XPathParser.DIV:
+ case XPathParser.MOD:
+ case XPathParser.OR:
+ case XPathParser.AND:
+ case XPathParser.SLASH:
+ return new XPathToken(XPathParser.NAME, name);
+ }
+ break;
+ }
+ return new XPathToken (val);
+ }
+ }
+ }
+ }
+
+ boolean isNameChar (int c)
+ {
+ /* Name */
+ return (c == 0x5f
+ || c == 0x2d
+ || c == 0x2e
+ || (c >= 0x30 && c <= 0x39)
+ /* CombiningChar */
+ || (c >= 0x0300 && c <= 0x0345)
+ || (c >= 0x0360 && c <= 0x0361)
+ || (c >= 0x0483 && c <= 0x0486)
+ || (c >= 0x0591 && c <= 0x05A1)
+ || (c >= 0x05A3 && c <= 0x05B9)
+ || (c >= 0x05BB && c <= 0x05BD)
+ || c == 0x05BF
+ || (c >= 0x05C1 && c <= 0x05C2)
+ || c == 0x05C4
+ || (c >= 0x064B && c <= 0x0652)
+ || c == 0x0670
+ || (c >= 0x06D6 && c <= 0x06DC)
+ || (c >= 0x06DD && c <= 0x06DF)
+ || (c >= 0x06E0 && c <= 0x06E4)
+ || (c >= 0x06E7 && c <= 0x06E8)
+ || (c >= 0x06EA && c <= 0x06ED)
+ || (c >= 0x0901 && c <= 0x0903)
+ || c == 0x093C
+ || (c >= 0x093E && c <= 0x094C)
+ || c == 0x094D
+ || (c >= 0x0951 && c <= 0x0954)
+ || (c >= 0x0962 && c <= 0x0963)
+ || (c >= 0x0981 && c <= 0x0983)
+ || c == 0x09BC
+ || c == 0x09BE
+ || c == 0x09BF
+ || (c >= 0x09C0 && c <= 0x09C4)
+ || (c >= 0x09C7 && c <= 0x09C8)
+ || (c >= 0x09CB && c <= 0x09CD)
+ || c == 0x09D7
+ || (c >= 0x09E2 && c <= 0x09E3)
+ || c == 0x0A02
+ || c == 0x0A3C
+ || c == 0x0A3E
+ || c == 0x0A3F
+ || (c >= 0x0A40 && c <= 0x0A42)
+ || (c >= 0x0A47 && c <= 0x0A48)
+ || (c >= 0x0A4B && c <= 0x0A4D)
+ || (c >= 0x0A70 && c <= 0x0A71)
+ || (c >= 0x0A81 && c <= 0x0A83)
+ || c == 0x0ABC
+ || (c >= 0x0ABE && c <= 0x0AC5)
+ || (c >= 0x0AC7 && c <= 0x0AC9)
+ || (c >= 0x0ACB && c <= 0x0ACD)
+ || (c >= 0x0B01 && c <= 0x0B03)
+ || c == 0x0B3C
+ || (c >= 0x0B3E && c <= 0x0B43)
+ || (c >= 0x0B47 && c <= 0x0B48)
+ || (c >= 0x0B4B && c <= 0x0B4D)
+ || (c >= 0x0B56 && c <= 0x0B57)
+ || (c >= 0x0B82 && c <= 0x0B83)
+ || (c >= 0x0BBE && c <= 0x0BC2)
+ || (c >= 0x0BC6 && c <= 0x0BC8)
+ || (c >= 0x0BCA && c <= 0x0BCD)
+ || c == 0x0BD7
+ || (c >= 0x0C01 && c <= 0x0C03)
+ || (c >= 0x0C3E && c <= 0x0C44)
+ || (c >= 0x0C46 && c <= 0x0C48)
+ || (c >= 0x0C4A && c <= 0x0C4D)
+ || (c >= 0x0C55 && c <= 0x0C56)
+ || (c >= 0x0C82 && c <= 0x0C83)
+ || (c >= 0x0CBE && c <= 0x0CC4)
+ || (c >= 0x0CC6 && c <= 0x0CC8)
+ || (c >= 0x0CCA && c <= 0x0CCD)
+ || (c >= 0x0CD5 && c <= 0x0CD6)
+ || (c >= 0x0D02 && c <= 0x0D03)
+ || (c >= 0x0D3E && c <= 0x0D43)
+ || (c >= 0x0D46 && c <= 0x0D48)
+ || (c >= 0x0D4A && c <= 0x0D4D)
+ || c == 0x0D57
+ || c == 0x0E31
+ || (c >= 0x0E34 && c <= 0x0E3A)
+ || (c >= 0x0E47 && c <= 0x0E4E)
+ || c == 0x0EB1
+ || (c >= 0x0EB4 && c <= 0x0EB9)
+ || (c >= 0x0EBB && c <= 0x0EBC)
+ || (c >= 0x0EC8 && c <= 0x0ECD)
+ || (c >= 0x0F18 && c <= 0x0F19)
+ || c == 0x0F35
+ || c == 0x0F37
+ || c == 0x0F39
+ || c == 0x0F3E
+ || c == 0x0F3F
+ || (c >= 0x0F71 && c <= 0x0F84)
+ || (c >= 0x0F86 && c <= 0x0F8B)
+ || (c >= 0x0F90 && c <= 0x0F95)
+ || c == 0x0F97
+ || (c >= 0x0F99 && c <= 0x0FAD)
+ || (c >= 0x0FB1 && c <= 0x0FB7)
+ || c == 0x0FB9
+ || (c >= 0x20D0 && c <= 0x20DC)
+ || c == 0x20E1
+ || (c >= 0x302A && c <= 0x302F)
+ || c == 0x3099
+ || c == 0x309A
+ /* Extender */
+ || c == 0x00B7
+ || c == 0x02D0
+ || c == 0x02D1
+ || c == 0x0387
+ || c == 0x0640
+ || c == 0x0E46
+ || c == 0x0EC6
+ || c == 0x3005
+ || (c >= 0x3031 && c <= 0x3035)
+ || (c >= 0x309D && c <= 0x309E)
+ || (c >= 0x30FC && c <= 0x30FE)
+ /* Name */
+ || Character.isLetter ((char) c));
+ }
+
+}