From b828a436e6abb17b0d86ad2c8210f731bf12cb37 Mon Sep 17 00:00:00 2001 From: Sergio Fabian Date: Thu, 14 Nov 2019 10:51:12 -0300 Subject: [PATCH] Initial commit --- .gitattributes | 2 + LICENSE | 504 ++ build.xml | 73 + build/built-jar.properties | 4 + build/classes/nu/xom/ASCIIWriter.class | Bin 0 -> 526 bytes build/classes/nu/xom/Attribute$Type.class | Bin 0 -> 2272 bytes build/classes/nu/xom/Attribute.class | Bin 0 -> 8908 bytes build/classes/nu/xom/Builder$1.class | Bin 0 -> 183 bytes ...lder$NamespaceWellformednessRequired.class | Bin 0 -> 1177 bytes .../nu/xom/Builder$ValidityRequired.class | Bin 0 -> 1517 bytes build/classes/nu/xom/Builder.class | Bin 0 -> 16371 bytes build/classes/nu/xom/CDATASection.class | Bin 0 -> 1100 bytes build/classes/nu/xom/Comment.class | Bin 0 -> 3064 bytes build/classes/nu/xom/CycleException.class | Bin 0 -> 593 bytes build/classes/nu/xom/DocType.class | Bin 0 -> 6411 bytes build/classes/nu/xom/Document.class | Bin 0 -> 5291 bytes build/classes/nu/xom/DocumentFragment.class | Bin 0 -> 1001 bytes build/classes/nu/xom/EBCDICWriter.class | Bin 0 -> 813 bytes build/classes/nu/xom/Element$1.class | Bin 0 -> 183 bytes .../nu/xom/Element$AttributeIterator.class | Bin 0 -> 1531 bytes build/classes/nu/xom/Element.class | Bin 0 -> 21916 bytes build/classes/nu/xom/Elements.class | Bin 0 -> 1238 bytes build/classes/nu/xom/GenericWriter.class | Bin 0 -> 1488 bytes build/classes/nu/xom/ISOArabicWriter.class | Bin 0 -> 1387 bytes build/classes/nu/xom/ISOCyrillicWriter.class | Bin 0 -> 1672 bytes build/classes/nu/xom/ISOGreekWriter.class | Bin 0 -> 1766 bytes build/classes/nu/xom/ISOHebrewWriter.class | Bin 0 -> 1253 bytes build/classes/nu/xom/ISOThaiWriter.class | Bin 0 -> 1535 bytes .../classes/nu/xom/IllegalAddException.class | Bin 0 -> 612 bytes .../xom/IllegalCharacterDataException.class | Bin 0 -> 639 bytes .../classes/nu/xom/IllegalDataException.class | Bin 0 -> 820 bytes .../classes/nu/xom/IllegalNameException.class | Bin 0 -> 612 bytes .../nu/xom/IllegalTargetException.class | Bin 0 -> 618 bytes build/classes/nu/xom/Info.class | Bin 0 -> 2644 bytes build/classes/nu/xom/JDK15XML1_0Parser.class | Bin 0 -> 886 bytes build/classes/nu/xom/JaxenConnector.class | Bin 0 -> 2241 bytes .../nu/xom/JaxenNavigator$ChildIterator.class | Bin 0 -> 1987 bytes .../JaxenNavigator$NamedChildIterator.class | Bin 0 -> 1954 bytes build/classes/nu/xom/JaxenNavigator.class | Bin 0 -> 10157 bytes build/classes/nu/xom/Latin10Writer.class | Bin 0 -> 3069 bytes build/classes/nu/xom/Latin1Writer.class | Bin 0 -> 530 bytes build/classes/nu/xom/Latin2Writer.class | Bin 0 -> 3219 bytes build/classes/nu/xom/Latin3Writer.class | Bin 0 -> 2954 bytes build/classes/nu/xom/Latin4Writer.class | Bin 0 -> 3206 bytes build/classes/nu/xom/Latin5Writer.class | Bin 0 -> 2136 bytes build/classes/nu/xom/Latin6Writer.class | Bin 0 -> 3008 bytes build/classes/nu/xom/Latin7Writer.class | Bin 0 -> 3081 bytes build/classes/nu/xom/Latin8Writer.class | Bin 0 -> 3538 bytes build/classes/nu/xom/Latin9Writer.class | Bin 0 -> 2054 bytes .../nu/xom/MalformedURIException.class | Bin 0 -> 615 bytes .../nu/xom/MultipleParentException.class | Bin 0 -> 620 bytes build/classes/nu/xom/Namespace.class | Bin 0 -> 2622 bytes .../nu/xom/NamespaceConflictException.class | Bin 0 -> 633 bytes build/classes/nu/xom/Namespaces.class | Bin 0 -> 1919 bytes .../nu/xom/NoSuchAttributeException.class | Bin 0 -> 684 bytes .../classes/nu/xom/NoSuchChildException.class | Bin 0 -> 672 bytes build/classes/nu/xom/Node$1.class | Bin 0 -> 174 bytes .../nu/xom/Node$EmptyNamespaceContext.class | Bin 0 -> 719 bytes build/classes/nu/xom/Node.class | Bin 0 -> 4527 bytes build/classes/nu/xom/NodeFactory.class | Bin 0 -> 3158 bytes build/classes/nu/xom/Nodes.class | Bin 0 -> 2034 bytes .../classes/nu/xom/NonVerifyingHandler.class | Bin 0 -> 5823 bytes build/classes/nu/xom/ParentNode.class | Bin 0 -> 4234 bytes build/classes/nu/xom/ParsingException.class | Bin 0 -> 2910 bytes .../nu/xom/ProcessingInstruction.class | Bin 0 -> 4045 bytes build/classes/nu/xom/Serializer.class | Bin 0 -> 13586 bytes build/classes/nu/xom/Text.class | Bin 0 -> 4793 bytes build/classes/nu/xom/TextWriter.class | Bin 0 -> 10249 bytes build/classes/nu/xom/TextWriterFactory.class | Bin 0 -> 2782 bytes build/classes/nu/xom/UCSWriter.class | Bin 0 -> 543 bytes build/classes/nu/xom/URIUtil$ParsedURI.class | Bin 0 -> 2218 bytes build/classes/nu/xom/URIUtil.class | Bin 0 -> 6747 bytes .../xom/UnavailableCharacterException.class | Bin 0 -> 1186 bytes .../nu/xom/UnicodeUtil$UnicodeString.class | Bin 0 -> 3452 bytes build/classes/nu/xom/UnicodeUtil.class | Bin 0 -> 69122 bytes build/classes/nu/xom/UnicodeWriter.class | Bin 0 -> 4532 bytes .../nu/xom/UnsynchronizedBufferedWriter.class | Bin 0 -> 1844 bytes build/classes/nu/xom/ValidityException.class | Bin 0 -> 2729 bytes build/classes/nu/xom/Verifier$1.class | Bin 0 -> 735 bytes .../classes/nu/xom/Verifier$EmptyReader.class | Bin 0 -> 761 bytes build/classes/nu/xom/Verifier$URICache.class | Bin 0 -> 1040 bytes build/classes/nu/xom/Verifier.class | Bin 0 -> 19248 bytes .../nu/xom/WellformednessException.class | Bin 0 -> 613 bytes build/classes/nu/xom/XML1_0Parser.class | Bin 0 -> 422 bytes build/classes/nu/xom/XMLException.class | Bin 0 -> 1130 bytes build/classes/nu/xom/XOMHandler.class | Bin 0 -> 16578 bytes build/classes/nu/xom/XPathContext$1.class | Bin 0 -> 198 bytes .../XPathContext$JaxenNamespaceContext.class | Bin 0 -> 926 bytes build/classes/nu/xom/XPathContext.class | Bin 0 -> 2455 bytes build/classes/nu/xom/XPathException.class | Bin 0 -> 1349 bytes build/classes/nu/xom/XPathTypeException.class | Bin 0 -> 1251 bytes build/classes/nu/xom/benchmarks/BigText.class | Bin 0 -> 2733 bytes .../nu/xom/benchmarks/DOMConverterBench.class | Bin 0 -> 4243 bytes .../nu/xom/benchmarks/DocumentModifier.class | Bin 0 -> 4170 bytes .../nu/xom/benchmarks/ElementCopyBench.class | Bin 0 -> 3344 bytes .../nu/xom/benchmarks/FastReproducer.class | Bin 0 -> 4635 bytes .../nu/xom/benchmarks/ManyNamespaces.class | Bin 0 -> 1664 bytes .../nu/xom/benchmarks/MemoryTest.class | Bin 0 -> 3054 bytes .../nu/xom/benchmarks/NamespaceLister.class | Bin 0 -> 3135 bytes .../nu/xom/benchmarks/Reproducer.class | Bin 0 -> 4796 bytes .../nu/xom/benchmarks/ToXMLBench.class | Bin 0 -> 3035 bytes .../nu/xom/benchmarks/TreeWalker.class | Bin 0 -> 4876 bytes build/classes/nu/xom/benchmarks/package.html | 44 + .../canonical/CanonicalizationException.class | Bin 0 -> 1029 bytes .../nu/xom/canonical/Canonicalizer$1.class | Bin 0 -> 221 bytes .../Canonicalizer$AttributeComparator.class | Bin 0 -> 1436 bytes ...Canonicalizer$CanonicalXMLSerializer.class | Bin 0 -> 14117 bytes .../nu/xom/canonical/Canonicalizer.class | Bin 0 -> 8922 bytes .../xom/canonical/URIJoiner$ParsedURI.class | Bin 0 -> 2264 bytes .../classes/nu/xom/canonical/URIJoiner.class | Bin 0 -> 2806 bytes build/classes/nu/xom/canonical/package.html | 47 + build/classes/nu/xom/characters.dat | Bin 0 -> 65536 bytes build/classes/nu/xom/compositions.dat | Bin 0 -> 23247 bytes .../nu/xom/converters/DOMConverter.class | Bin 0 -> 14584 bytes .../nu/xom/converters/SAXConverter.class | Bin 0 -> 6550 bytes build/classes/nu/xom/converters/package.html | 44 + build/classes/nu/xom/doc-files/xom.png | Bin 0 -> 34156 bytes .../integrationtests/OASISXSLTTest$1.class | Bin 0 -> 235 bytes .../OASISXSLTTest$StrippingFactory.class | Bin 0 -> 2029 bytes .../xom/integrationtests/OASISXSLTTest.class | Bin 0 -> 19138 bytes .../integrationtests/W3CXIncludeTest.class | Bin 0 -> 5471 bytes build/classes/nu/xom/package.html | 48 + build/classes/nu/xom/samples/Account.class | Bin 0 -> 2961 bytes build/classes/nu/xom/samples/Agency.class | Bin 0 -> 2607 bytes .../nu/xom/samples/AttributesToElements.class | Bin 0 -> 2680 bytes .../nu/xom/samples/AttributesXMLBudget.class | Bin 0 -> 4301 bytes build/classes/nu/xom/samples/Budget.class | Bin 0 -> 3979 bytes build/classes/nu/xom/samples/BudgetData.class | Bin 0 -> 3313 bytes build/classes/nu/xom/samples/Bureau.class | Bin 0 -> 2516 bytes .../nu/xom/samples/CanonicalizerDriver.class | Bin 0 -> 1251 bytes .../nu/xom/samples/CommentReader.class | Bin 0 -> 1720 bytes .../nu/xom/samples/CrimsonPrinter.class | Bin 0 -> 2134 bytes .../nu/xom/samples/DTDGenerator$1.class | Bin 0 -> 214 bytes .../DTDGenerator$NamingNodeFactory.class | Bin 0 -> 2242 bytes .../classes/nu/xom/samples/DTDGenerator.class | Bin 0 -> 1513 bytes .../nu/xom/samples/DatabaseBuilder.class | Bin 0 -> 8207 bytes .../nu/xom/samples/DatabaseConverter.class | Bin 0 -> 5899 bytes .../nu/xom/samples/DocumentMerger.class | Bin 0 -> 1104 bytes .../classes/nu/xom/samples/EZSerializer.class | Bin 0 -> 1107 bytes .../nu/xom/samples/ElementLister.class | Bin 0 -> 2119 bytes .../classes/nu/xom/samples/EncodingDemo.class | Bin 0 -> 1924 bytes .../nu/xom/samples/ExampleExtractor.class | Bin 0 -> 3643 bytes .../nu/xom/samples/ExampleLister.class | Bin 0 -> 2831 bytes .../nu/xom/samples/FibonacciAttributes.class | Bin 0 -> 1542 bytes .../nu/xom/samples/FibonacciEBCDIC.class | Bin 0 -> 1821 bytes .../nu/xom/samples/FibonacciFile.class | Bin 0 -> 1804 bytes .../nu/xom/samples/FibonacciLatin1.class | Bin 0 -> 1630 bytes .../nu/xom/samples/FibonacciSOAPClient.class | Bin 0 -> 5383 bytes .../nu/xom/samples/FibonacciServlet.class | Bin 0 -> 2968 bytes .../classes/nu/xom/samples/FibonacciXML.class | Bin 0 -> 1259 bytes .../xom/samples/FibonacciXMLRPCClient.class | Bin 0 -> 4997 bytes .../xom/samples/FibonacciXOMSOAPServlet.class | Bin 0 -> 5592 bytes .../samples/FibonacciXOMXMLRPCServlet.class | Bin 0 -> 5552 bytes .../classes/nu/xom/samples/FilterDriver.class | Bin 0 -> 1743 bytes .../nu/xom/samples/FlatXMLBudget.class | Bin 0 -> 3234 bytes .../nu/xom/samples/HelloNamespaces.class | Bin 0 -> 943 bytes build/classes/nu/xom/samples/HelloWorld.class | Bin 0 -> 872 bytes .../classes/nu/xom/samples/HelloXMLBase.class | Bin 0 -> 1960 bytes .../xom/samples/HierarchicalXMLBudget.class | Bin 0 -> 3012 bytes build/classes/nu/xom/samples/IDFilter.class | Bin 0 -> 1206 bytes build/classes/nu/xom/samples/IDTagger.class | Bin 0 -> 2147 bytes build/classes/nu/xom/samples/Linkset.class | Bin 0 -> 2175 bytes .../nu/xom/samples/MathMLFibonacci.class | Bin 0 -> 1818 bytes .../nu/xom/samples/MinimalNodeFactory.class | Bin 0 -> 1707 bytes build/classes/nu/xom/samples/NodeLister.class | Bin 0 -> 2233 bytes .../xom/samples/NonRecursiveSerializer.class | Bin 0 -> 2830 bytes .../nu/xom/samples/NormalizingFactory.class | Bin 0 -> 2813 bytes .../classes/nu/xom/samples/PoliteSpider.class | Bin 0 -> 4085 bytes .../nu/xom/samples/PrettyFibonacci.class | Bin 0 -> 2212 bytes .../nu/xom/samples/PrettyPrinter.class | Bin 0 -> 1690 bytes .../samples/ProcessingInstructionLister.class | Bin 0 -> 1791 bytes .../nu/xom/samples/PropertyPrinter.class | Bin 0 -> 2711 bytes .../nu/xom/samples/PseudoAttributes.class | Bin 0 -> 1862 bytes .../nu/xom/samples/PureValidator.class | Bin 0 -> 1847 bytes build/classes/nu/xom/samples/RDDLFilter.class | Bin 0 -> 2565 bytes .../classes/nu/xom/samples/RDDLStripper.class | Bin 0 -> 2564 bytes .../classes/nu/xom/samples/RDDLToTable.class | Bin 0 -> 4380 bytes build/classes/nu/xom/samples/ROT13File.class | Bin 0 -> 1592 bytes build/classes/nu/xom/samples/ROT13XML.class | Bin 0 -> 2371 bytes .../classes/nu/xom/samples/RSSHeadlines.class | Bin 0 -> 2348 bytes .../nu/xom/samples/ResourceToTable.class | Bin 0 -> 4331 bytes .../classes/nu/xom/samples/Restructurer.class | Bin 0 -> 2095 bytes build/classes/nu/xom/samples/SQLToXML.class | Bin 0 -> 6265 bytes .../nu/xom/samples/SchemaValidator.class | Bin 0 -> 2140 bytes build/classes/nu/xom/samples/SimpleSVG.class | Bin 0 -> 1933 bytes .../nu/xom/samples/SourceCodeGenerator.class | Bin 0 -> 5786 bytes .../nu/xom/samples/SourceCodeSerializer.class | Bin 0 -> 7230 bytes .../xom/samples/StreamingCommentReader.class | Bin 0 -> 2785 bytes .../xom/samples/StreamingElementLister.class | Bin 0 -> 3211 bytes .../samples/StreamingExampleExtractor.class | Bin 0 -> 4997 bytes .../nu/xom/samples/StreamingNodeLister.class | Bin 0 -> 2876 bytes ...StreamingProcessingInstructionLister.class | Bin 0 -> 3015 bytes .../nu/xom/samples/StreamingROT13.class | Bin 0 -> 3221 bytes .../xom/samples/StreamingTextExtractor.class | Bin 0 -> 3559 bytes .../xom/samples/StreamingTreeReporter.class | Bin 0 -> 2353 bytes .../nu/xom/samples/StreamingTypeCounter.class | Bin 0 -> 4077 bytes .../xom/samples/StreamingXHTMLPurifier.class | Bin 0 -> 4490 bytes .../xom/samples/StreamingXHTMLQualifier.class | Bin 0 -> 1985 bytes .../classes/nu/xom/samples/Subfunction.class | Bin 0 -> 946 bytes .../nu/xom/samples/TextExtractor.class | Bin 0 -> 1578 bytes build/classes/nu/xom/samples/TextMerger.class | Bin 0 -> 1262 bytes .../nu/xom/samples/TextSerializer.class | Bin 0 -> 2956 bytes .../classes/nu/xom/samples/Transformer.class | Bin 0 -> 1892 bytes .../classes/nu/xom/samples/TreePrinter.class | Bin 0 -> 3268 bytes .../classes/nu/xom/samples/TreeReporter.class | Bin 0 -> 1580 bytes build/classes/nu/xom/samples/TreeViewer.class | Bin 0 -> 3150 bytes .../nu/xom/samples/ValidFibonacci.class | Bin 0 -> 1683 bytes build/classes/nu/xom/samples/Validator.class | Bin 0 -> 1943 bytes .../nu/xom/samples/WrappingSerializer.class | Bin 0 -> 5369 bytes .../nu/xom/samples/XHTMLPurifier.class | Bin 0 -> 2805 bytes .../nu/xom/samples/XHTMLQualifier.class | Bin 0 -> 2081 bytes .../nu/xom/samples/XHTMLValidator.class | Bin 0 -> 2810 bytes .../nu/xom/samples/XIncludeDriver.class | Bin 0 -> 1199 bytes .../classes/nu/xom/samples/XLinkSpider.class | Bin 0 -> 3194 bytes build/classes/nu/xom/samples/XMLPrinter.class | Bin 0 -> 1378 bytes build/classes/nu/xom/samples/XOMChecker.class | Bin 0 -> 1527 bytes .../classes/nu/xom/samples/XPathDriver.class | Bin 0 -> 2040 bytes .../nu/xom/samples/XPathHeadlines.class | Bin 0 -> 1819 bytes .../nu/xom/samples/inner/InnerElement$1.class | Bin 0 -> 775 bytes .../nu/xom/samples/inner/InnerElement.class | Bin 0 -> 2740 bytes .../nu/xom/samples/inner/InnerFactory.class | Bin 0 -> 603 bytes .../nu/xom/samples/inner/InnerTest.class | Bin 0 -> 1613 bytes build/classes/nu/xom/samples/package.html | 60 + build/classes/nu/xom/tests/AComment.class | Bin 0 -> 338 bytes build/classes/nu/xom/tests/ADocType.class | Bin 0 -> 429 bytes build/classes/nu/xom/tests/ADocument.class | Bin 0 -> 337 bytes build/classes/nu/xom/tests/AElement.class | Bin 0 -> 373 bytes build/classes/nu/xom/tests/ANodeFactory.class | Bin 0 -> 2208 bytes .../nu/xom/tests/AProcessingInstruction.class | Bin 0 -> 418 bytes build/classes/nu/xom/tests/AText.class | Bin 0 -> 329 bytes build/classes/nu/xom/tests/AnAttribute.class | Bin 0 -> 551 bytes .../classes/nu/xom/tests/AttributeTest.class | Bin 0 -> 15786 bytes .../nu/xom/tests/AttributeTypeTest.class | Bin 0 -> 2756 bytes .../classes/nu/xom/tests/AttributesTest.class | Bin 0 -> 1606 bytes build/classes/nu/xom/tests/BaseURITest.class | Bin 0 -> 26377 bytes .../classes/nu/xom/tests/BuilderTest$1.class | Bin 0 -> 207 bytes .../tests/BuilderTest$BadDeclHandler.class | Bin 0 -> 1598 bytes .../xom/tests/BuilderTest$CustomReader.class | Bin 0 -> 1397 bytes .../tests/BuilderTest$DoNothingReader.class | Bin 0 -> 823 bytes .../xom/tests/BuilderTest$EndOnlyReader.class | Bin 0 -> 949 bytes .../xom/tests/BuilderTest$EntitySkipper.class | Bin 0 -> 745 bytes .../tests/BuilderTest$ExceptionTester.class | Bin 0 -> 837 bytes .../BuilderTest$MalformedDTDFilter.class | Bin 0 -> 1201 bytes .../nu/xom/tests/BuilderTest$NoLocator.class | Bin 0 -> 616 bytes ...BuilderTest$NonEntityResolvingFilter.class | Bin 0 -> 1452 bytes .../BuilderTest$NonValidatingFilter.class | Bin 0 -> 1258 bytes .../tests/BuilderTest$OrderingFilter.class | Bin 0 -> 1352 bytes ...arenthesizedEnumeratedAttributeTypes.class | Bin 0 -> 1259 bytes .../tests/BuilderTest$StartAndEndReader.class | Bin 0 -> 1000 bytes .../tests/BuilderTest$StartOnlyReader.class | Bin 0 -> 957 bytes .../xom/tests/BuilderTest$WarningFilter.class | Bin 0 -> 1282 bytes .../BuilderTest$WeirdAttributeTypes.class | Bin 0 -> 1191 bytes build/classes/nu/xom/tests/BuilderTest.class | Bin 0 -> 67324 bytes .../nu/xom/tests/CDATASectionTest.class | Bin 0 -> 5988 bytes .../tests/CanonicalizationExceptionTest.class | Bin 0 -> 2340 bytes .../nu/xom/tests/Canonicalizer11Test.class | Bin 0 -> 10805 bytes .../nu/xom/tests/CanonicalizerTest$1.class | Bin 0 -> 225 bytes ...icalizerTest$UnwriteableOutputStream.class | Bin 0 -> 801 bytes .../tests/CanonicalizerTest$XMLFilter.class | Bin 0 -> 934 bytes .../nu/xom/tests/CanonicalizerTest.class | Bin 0 -> 44795 bytes build/classes/nu/xom/tests/CommentTest.class | Bin 0 -> 6378 bytes .../nu/xom/tests/DOMConverterTest$1.class | Bin 0 -> 222 bytes .../DOMConverterTest$StrippingFactory.class | Bin 0 -> 950 bytes .../nu/xom/tests/DOMConverterTest.class | Bin 0 -> 19647 bytes build/classes/nu/xom/tests/DocTypeTest.class | Bin 0 -> 18591 bytes build/classes/nu/xom/tests/DocumentTest.class | Bin 0 -> 10862 bytes build/classes/nu/xom/tests/EBCDICTest.class | Bin 0 -> 2090 bytes .../xom/tests/ElementTest$TestElement.class | Bin 0 -> 544 bytes build/classes/nu/xom/tests/ElementTest.class | Bin 0 -> 31277 bytes build/classes/nu/xom/tests/EncodingTest.class | Bin 0 -> 7755 bytes build/classes/nu/xom/tests/FastTests.class | Bin 0 -> 1979 bytes build/classes/nu/xom/tests/IDTest.class | Bin 0 -> 9360 bytes build/classes/nu/xom/tests/InfoTest.class | Bin 0 -> 2290 bytes build/classes/nu/xom/tests/LeafNodeTest.class | Bin 0 -> 1034 bytes .../classes/nu/xom/tests/LocatorFilter.class | Bin 0 -> 591 bytes build/classes/nu/xom/tests/MegaTest$1.class | Bin 0 -> 198 bytes .../nu/xom/tests/MegaTest$Generator.class | Bin 0 -> 1438 bytes .../tests/MegaTest$MinimizingFactory.class | Bin 0 -> 2405 bytes build/classes/nu/xom/tests/MegaTest.class | Bin 0 -> 2253 bytes .../nu/xom/tests/NamespaceNodeTest.class | Bin 0 -> 5969 bytes .../classes/nu/xom/tests/NamespacesTest.class | Bin 0 -> 17944 bytes .../nu/xom/tests/NodeFactoryTest$1.class | Bin 0 -> 1084 bytes .../nu/xom/tests/NodeFactoryTest$10.class | Bin 0 -> 897 bytes .../nu/xom/tests/NodeFactoryTest$11.class | Bin 0 -> 998 bytes .../nu/xom/tests/NodeFactoryTest$12.class | Bin 0 -> 1029 bytes .../nu/xom/tests/NodeFactoryTest$13.class | Bin 0 -> 1043 bytes .../nu/xom/tests/NodeFactoryTest$14.class | Bin 0 -> 805 bytes .../nu/xom/tests/NodeFactoryTest$15.class | Bin 0 -> 1068 bytes .../nu/xom/tests/NodeFactoryTest$16.class | Bin 0 -> 1032 bytes .../nu/xom/tests/NodeFactoryTest$17.class | Bin 0 -> 1004 bytes .../nu/xom/tests/NodeFactoryTest$18.class | Bin 0 -> 736 bytes .../nu/xom/tests/NodeFactoryTest$19.class | Bin 0 -> 853 bytes .../nu/xom/tests/NodeFactoryTest$2.class | Bin 0 -> 1199 bytes .../nu/xom/tests/NodeFactoryTest$20.class | Bin 0 -> 918 bytes .../nu/xom/tests/NodeFactoryTest$21.class | Bin 0 -> 866 bytes .../nu/xom/tests/NodeFactoryTest$22.class | Bin 0 -> 931 bytes .../nu/xom/tests/NodeFactoryTest$23.class | Bin 0 -> 1619 bytes .../nu/xom/tests/NodeFactoryTest$24.class | Bin 0 -> 1231 bytes .../nu/xom/tests/NodeFactoryTest$25.class | Bin 0 -> 991 bytes .../nu/xom/tests/NodeFactoryTest$3.class | Bin 0 -> 1027 bytes .../nu/xom/tests/NodeFactoryTest$4.class | Bin 0 -> 973 bytes .../nu/xom/tests/NodeFactoryTest$5.class | Bin 0 -> 1120 bytes .../nu/xom/tests/NodeFactoryTest$6.class | Bin 0 -> 902 bytes .../nu/xom/tests/NodeFactoryTest$7.class | Bin 0 -> 903 bytes .../nu/xom/tests/NodeFactoryTest$8.class | Bin 0 -> 856 bytes .../nu/xom/tests/NodeFactoryTest$9.class | Bin 0 -> 857 bytes .../xom/tests/NodeFactoryTest$BFilter.class | Bin 0 -> 715 bytes .../xom/tests/NodeFactoryTest$CFactory.class | Bin 0 -> 657 bytes .../tests/NodeFactoryTest$CallsMakeRoot.class | Bin 0 -> 684 bytes .../tests/NodeFactoryTest$CommentFilter.class | Bin 0 -> 583 bytes .../xom/tests/NodeFactoryTest$Counter.class | Bin 0 -> 1163 bytes .../NodeFactoryTest$DocumentFilter.class | Bin 0 -> 820 bytes .../NodeFactoryTest$MinimizingFactory.class | Bin 0 -> 1779 bytes .../NodeFactoryTest$NullDocumentFactory.class | Bin 0 -> 527 bytes .../NodeFactoryTest$NullElementFactory.class | Bin 0 -> 621 bytes ...toryTest$ProcessingInstructionFilter.class | Bin 0 -> 676 bytes ...deFactoryTest$SingleAttributeFactory.class | Bin 0 -> 1176 bytes ...NodeFactoryTest$SingleElementFactory.class | Bin 0 -> 943 bytes .../NodeFactoryTest$TripleElementFilter.class | Bin 0 -> 890 bytes .../NodeFactoryTest$UncommentFilter.class | Bin 0 -> 714 bytes .../nu/xom/tests/NodeFactoryTest.class | Bin 0 -> 17423 bytes build/classes/nu/xom/tests/NodesTest.class | Bin 0 -> 4884 bytes build/classes/nu/xom/tests/NullLocator.class | Bin 0 -> 788 bytes .../classes/nu/xom/tests/ParentNodeTest.class | Bin 0 -> 8902 bytes .../nu/xom/tests/ParsingExceptionTest.class | Bin 0 -> 3658 bytes .../xom/tests/ProcessingInstructionTest.class | Bin 0 -> 8782 bytes .../classes/nu/xom/tests/RoundTripTest.class | Bin 0 -> 3548 bytes .../nu/xom/tests/SAXConverterTest$1.class | Bin 0 -> 222 bytes .../tests/SAXConverterTest$BaseChecker.class | Bin 0 -> 1980 bytes ...SAXConverterTest$XMLPrefixMapCounter.class | Bin 0 -> 1184 bytes .../SAXConverterTest$XMLPrefixTester.class | Bin 0 -> 1175 bytes .../SAXConverterTest$XMLPrefixTester2.class | Bin 0 -> 1217 bytes .../nu/xom/tests/SAXConverterTest.class | Bin 0 -> 9493 bytes .../SerializerTest$AvoidWriteElement.class | Bin 0 -> 984 bytes .../SerializerTest$ColumnSerializer.class | Bin 0 -> 1008 bytes .../SerializerTest$ElementSerializer.class | Bin 0 -> 788 bytes .../SerializerTest$ExposingSerializer.class | Bin 0 -> 1342 bytes .../tests/SerializerTest$TextSerializer.class | Bin 0 -> 657 bytes .../classes/nu/xom/tests/SerializerTest.class | Bin 0 -> 52344 bytes .../SubclassTest$AttributeSubclass.class | Bin 0 -> 916 bytes .../tests/SubclassTest$CommentSubclass.class | Bin 0 -> 819 bytes .../tests/SubclassTest$DocTypeSubclass.class | Bin 0 -> 828 bytes .../tests/SubclassTest$ElementSubclass.class | Bin 0 -> 744 bytes ...ssTest$ProcessingInstructionSubclass.class | Bin 0 -> 1093 bytes .../xom/tests/SubclassTest$TextSubclass.class | Bin 0 -> 804 bytes build/classes/nu/xom/tests/SubclassTest.class | Bin 0 -> 3101 bytes build/classes/nu/xom/tests/TextTest.class | Bin 0 -> 6843 bytes .../nu/xom/tests/ValidityExceptionTest.class | Bin 0 -> 2999 bytes build/classes/nu/xom/tests/VerifierTest.class | Bin 0 -> 22293 bytes .../nu/xom/tests/XIncludeExceptionTest.class | Bin 0 -> 3023 bytes .../classes/nu/xom/tests/XIncludeTest$1.class | Bin 0 -> 210 bytes .../xom/tests/XIncludeTest$TextFilter.class | Bin 0 -> 748 bytes .../tests/XIncludeTest$TextNodeFactory.class | Bin 0 -> 896 bytes .../xom/tests/XIncludeTest$TextSubclass.class | Bin 0 -> 677 bytes .../tests/XIncludeTest$TextToAttribute.class | Bin 0 -> 881 bytes .../tests/XIncludeTest$TextToComment.class | Bin 0 -> 843 bytes .../tests/XIncludeTest$TextToElements.class | Bin 0 -> 952 bytes build/classes/nu/xom/tests/XIncludeTest.class | Bin 0 -> 46291 bytes .../nu/xom/tests/XMLExceptionTest.class | Bin 0 -> 4776 bytes build/classes/nu/xom/tests/XMLWriter.class | Bin 0 -> 10450 bytes build/classes/nu/xom/tests/XOMTestCase.class | Bin 0 -> 8364 bytes .../nu/xom/tests/XOMTestCaseTest.class | Bin 0 -> 4606 bytes build/classes/nu/xom/tests/XOMTests.class | Bin 0 -> 2500 bytes .../nu/xom/tests/XPathExceptionTest.class | Bin 0 -> 3479 bytes build/classes/nu/xom/tests/XPathTest.class | Bin 0 -> 44033 bytes .../nu/xom/tests/XSLExceptionTest.class | Bin 0 -> 2330 bytes .../nu/xom/tests/XSLTransformTest$1.class | Bin 0 -> 839 bytes .../XSLTransformTest$AttributeFactory.class | Bin 0 -> 1267 bytes ...SLTransformTest$AttributesToElements.class | Bin 0 -> 1221 bytes .../nu/xom/tests/XSLTransformTest.class | Bin 0 -> 23853 bytes build/classes/nu/xom/tests/package.html | 83 + .../classes/nu/xom/tools/XHTMLJavaDoc$1.class | Bin 0 -> 210 bytes .../xom/tools/XHTMLJavaDoc$HTMLFilter.class | Bin 0 -> 950 bytes .../tools/XHTMLJavaDoc$HTMLFixFactory.class | Bin 0 -> 1301 bytes .../tools/XHTMLJavaDoc$HTMLSerializer.class | Bin 0 -> 807 bytes build/classes/nu/xom/tools/XHTMLJavaDoc.class | Bin 0 -> 3984 bytes build/classes/nu/xom/tools/package.html | 41 + .../BadEncodingAttributeException.class | Bin 0 -> 634 bytes .../xom/xinclude/BadHTTPHeaderException.class | Bin 0 -> 613 bytes .../xinclude/BadHrefAttributeException.class | Bin 0 -> 622 bytes .../xinclude/BadParseAttributeException.class | Bin 0 -> 625 bytes .../nu/xom/xinclude/EncodingHeuristics.class | Bin 0 -> 2932 bytes .../xom/xinclude/InclusionLoopException.class | Bin 0 -> 613 bytes .../xinclude/MisplacedFallbackException.class | Bin 0 -> 625 bytes .../xinclude/NoIncludeLocationException.class | Bin 0 -> 867 bytes .../nu/xom/xinclude/XIncludeException.class | Bin 0 -> 1419 bytes build/classes/nu/xom/xinclude/XIncluder.class | Bin 0 -> 18431 bytes build/classes/nu/xom/xinclude/XPointer.class | Bin 0 -> 6520 bytes .../nu/xom/xinclude/XPointerException.class | Bin 0 -> 1174 bytes .../xinclude/XPointerResourceException.class | Bin 0 -> 477 bytes .../xinclude/XPointerSyntaxException.class | Bin 0 -> 645 bytes build/classes/nu/xom/xinclude/package.html | 44 + .../classes/nu/xom/xslt/XOMInputSource.class | Bin 0 -> 475 bytes build/classes/nu/xom/xslt/XOMReader.class | Bin 0 -> 3662 bytes build/classes/nu/xom/xslt/XOMResult.class | Bin 0 -> 778 bytes build/classes/nu/xom/xslt/XOMSource.class | Bin 0 -> 1101 bytes build/classes/nu/xom/xslt/XSLException.class | Bin 0 -> 1133 bytes build/classes/nu/xom/xslt/XSLTHandler.class | Bin 0 -> 8598 bytes .../classes/nu/xom/xslt/XSLTransform$1.class | Bin 0 -> 208 bytes .../xom/xslt/XSLTransform$FatalListener.class | Bin 0 -> 1035 bytes build/classes/nu/xom/xslt/XSLTransform.class | Bin 0 -> 6565 bytes build/classes/nu/xom/xslt/package.html | 40 + dist/xom.jar | Bin 0 -> 1697619 bytes nbproject/build-impl.xml | 1420 +++++ nbproject/genfiles.properties | 8 + nbproject/private/config.properties | 0 nbproject/private/private.properties | 6 + nbproject/private/private.xml | 9 + nbproject/project.properties | 60 + nbproject/project.xml | 15 + src/nu/xom/ASCIIWriter.java | 58 + src/nu/xom/Attribute.java | 1079 ++++ src/nu/xom/Builder.java | 1332 +++++ src/nu/xom/CDATASection.java | 66 + src/nu/xom/Comment.java | 243 + src/nu/xom/CycleException.java | 67 + src/nu/xom/DocType.java | 613 ++ src/nu/xom/Document.java | 461 ++ src/nu/xom/DocumentFragment.java | 67 + src/nu/xom/EBCDICWriter.java | 57 + src/nu/xom/Element.java | 1873 ++++++ src/nu/xom/Elements.java | 92 + src/nu/xom/GenericWriter.java | 80 + src/nu/xom/ISOArabicWriter.java | 128 + src/nu/xom/ISOCyrillicWriter.java | 153 + src/nu/xom/ISOGreekWriter.java | 169 + src/nu/xom/ISOHebrewWriter.java | 138 + src/nu/xom/ISOThaiWriter.java | 160 + src/nu/xom/IllegalAddException.java | 67 + src/nu/xom/IllegalCharacterDataException.java | 69 + src/nu/xom/IllegalDataException.java | 97 + src/nu/xom/IllegalNameException.java | 71 + src/nu/xom/IllegalTargetException.java | 71 + src/nu/xom/Info.java | 81 + src/nu/xom/JDK15XML1_0Parser.java | 55 + src/nu/xom/JaxenConnector.java | 101 + src/nu/xom/JaxenNavigator.java | 586 ++ src/nu/xom/Latin10Writer.java | 283 + src/nu/xom/Latin1Writer.java | 55 + src/nu/xom/Latin2Writer.java | 293 + src/nu/xom/Latin3Writer.java | 268 + src/nu/xom/Latin4Writer.java | 293 + src/nu/xom/Latin5Writer.java | 199 + src/nu/xom/Latin6Writer.java | 278 + src/nu/xom/Latin7Writer.java | 283 + src/nu/xom/Latin8Writer.java | 326 ++ src/nu/xom/Latin9Writer.java | 207 + src/nu/xom/MalformedURIException.java | 70 + src/nu/xom/MultipleParentException.java | 65 + src/nu/xom/Namespace.java | 237 + src/nu/xom/NamespaceConflictException.java | 71 + src/nu/xom/Namespaces.java | 105 + src/nu/xom/NoSuchAttributeException.java | 71 + src/nu/xom/NoSuchChildException.java | 71 + src/nu/xom/Node.java | 527 ++ src/nu/xom/NodeFactory.java | 409 ++ src/nu/xom/Nodes.java | 198 + src/nu/xom/NonVerifyingHandler.java | 245 + src/nu/xom/ParentNode.java | 462 ++ src/nu/xom/ParsingException.java | 299 + src/nu/xom/ProcessingInstruction.java | 301 + src/nu/xom/Serializer.java | 1307 +++++ src/nu/xom/Text.java | 474 ++ src/nu/xom/TextWriter.java | 935 +++ src/nu/xom/TextWriterFactory.java | 130 + src/nu/xom/UCSWriter.java | 53 + src/nu/xom/URIUtil.java | 681 +++ src/nu/xom/UnavailableCharacterException.java | 91 + src/nu/xom/UnicodeUtil.java | 4997 +++++++++++++++++ src/nu/xom/UnicodeWriter.java | 445 ++ src/nu/xom/UnsynchronizedBufferedWriter.java | 89 + src/nu/xom/ValidityException.java | 277 + src/nu/xom/Verifier.java | 1386 +++++ src/nu/xom/WellformednessException.java | 67 + src/nu/xom/XML1_0Parser.java | 52 + src/nu/xom/XMLException.java | 132 + src/nu/xom/XOMHandler.java | 984 ++++ src/nu/xom/XPathContext.java | 175 + src/nu/xom/XPathException.java | 148 + src/nu/xom/XPathTypeException.java | 70 + src/nu/xom/benchmarks/BigText.java | 116 + src/nu/xom/benchmarks/DOMConverterBench.java | 122 + src/nu/xom/benchmarks/DocumentModifier.java | 179 + src/nu/xom/benchmarks/ElementCopyBench.java | 106 + src/nu/xom/benchmarks/FastReproducer.java | 185 + src/nu/xom/benchmarks/ManyNamespaces.java | 30 + src/nu/xom/benchmarks/MemoryTest.java | 123 + src/nu/xom/benchmarks/NamespaceLister.java | 118 + src/nu/xom/benchmarks/Reproducer.java | 189 + src/nu/xom/benchmarks/ToXMLBench.java | 94 + src/nu/xom/benchmarks/TreeWalker.java | 193 + src/nu/xom/benchmarks/package.html | 44 + .../canonical/CanonicalizationException.java | 106 + src/nu/xom/canonical/Canonicalizer.java | 1172 ++++ src/nu/xom/canonical/URIJoiner.java | 278 + src/nu/xom/canonical/package.html | 47 + src/nu/xom/characters.dat | Bin 0 -> 65536 bytes src/nu/xom/compositions.dat | Bin 0 -> 23247 bytes src/nu/xom/converters/DOMConverter.java | 798 +++ src/nu/xom/converters/SAXConverter.java | 350 ++ src/nu/xom/converters/package.html | 44 + src/nu/xom/doc-files/xom.png | Bin 0 -> 34156 bytes .../xom/integrationtests/OASISXSLTTest.java | 1021 ++++ .../xom/integrationtests/W3CXIncludeTest.java | 213 + src/nu/xom/package.html | 48 + src/nu/xom/samples/Account.java | 120 + src/nu/xom/samples/Agency.java | 111 + src/nu/xom/samples/AttributesToElements.java | 95 + src/nu/xom/samples/AttributesXMLBudget.java | 136 + src/nu/xom/samples/Budget.java | 116 + src/nu/xom/samples/BudgetData.java | 115 + src/nu/xom/samples/Bureau.java | 108 + src/nu/xom/samples/CanonicalizerDriver.java | 69 + src/nu/xom/samples/CommentReader.java | 83 + src/nu/xom/samples/CrimsonPrinter.java | 78 + src/nu/xom/samples/DTDGenerator.java | 103 + src/nu/xom/samples/DatabaseBuilder.java | 275 + src/nu/xom/samples/DatabaseConverter.java | 224 + src/nu/xom/samples/DocumentMerger.java | 59 + src/nu/xom/samples/EZSerializer.java | 108 + src/nu/xom/samples/ElementLister.java | 88 + src/nu/xom/samples/EncodingDemo.java | 73 + src/nu/xom/samples/ExampleExtractor.java | 142 + src/nu/xom/samples/ExampleLister.java | 131 + src/nu/xom/samples/FibonacciAttributes.java | 66 + src/nu/xom/samples/FibonacciEBCDIC.java | 81 + src/nu/xom/samples/FibonacciFile.java | 75 + src/nu/xom/samples/FibonacciLatin1.java | 73 + src/nu/xom/samples/FibonacciSOAPClient.java | 205 + src/nu/xom/samples/FibonacciServlet.java | 106 + src/nu/xom/samples/FibonacciXML.java | 62 + src/nu/xom/samples/FibonacciXMLRPCClient.java | 176 + .../xom/samples/FibonacciXOMSOAPServlet.java | 228 + .../samples/FibonacciXOMXMLRPCServlet.java | 202 + src/nu/xom/samples/FilterDriver.java | 72 + src/nu/xom/samples/FlatXMLBudget.java | 111 + src/nu/xom/samples/HelloNamespaces.java | 49 + src/nu/xom/samples/HelloWorld.java | 50 + src/nu/xom/samples/HelloXMLBase.java | 90 + src/nu/xom/samples/HierarchicalXMLBudget.java | 108 + src/nu/xom/samples/IDFilter.java | 64 + src/nu/xom/samples/IDTagger.java | 90 + src/nu/xom/samples/Linkset.java | 79 + src/nu/xom/samples/MathMLFibonacci.java | 78 + src/nu/xom/samples/MinimalNodeFactory.java | 73 + src/nu/xom/samples/NodeLister.java | 98 + .../xom/samples/NonRecursiveSerializer.java | 138 + src/nu/xom/samples/NormalizingFactory.java | 119 + src/nu/xom/samples/PoliteSpider.java | 160 + src/nu/xom/samples/PrettyFibonacci.java | 86 + src/nu/xom/samples/PrettyPrinter.java | 74 + .../samples/ProcessingInstructionLister.java | 86 + src/nu/xom/samples/PropertyPrinter.java | 128 + src/nu/xom/samples/PseudoAttributes.java | 75 + src/nu/xom/samples/PureValidator.java | 76 + src/nu/xom/samples/RDDLFilter.java | 94 + src/nu/xom/samples/RDDLStripper.java | 104 + src/nu/xom/samples/RDDLToTable.java | 206 + src/nu/xom/samples/ROT13File.java | 70 + src/nu/xom/samples/ROT13XML.java | 109 + src/nu/xom/samples/RSSHeadlines.java | 87 + src/nu/xom/samples/ResourceToTable.java | 200 + src/nu/xom/samples/Restructurer.java | 105 + src/nu/xom/samples/SQLToXML.java | 217 + src/nu/xom/samples/SchemaValidator.java | 86 + src/nu/xom/samples/SimpleSVG.java | 82 + src/nu/xom/samples/SourceCodeGenerator.java | 204 + src/nu/xom/samples/SourceCodeSerializer.java | 271 + .../xom/samples/StreamingCommentReader.java | 108 + .../xom/samples/StreamingElementLister.java | 121 + .../samples/StreamingExampleExtractor.java | 199 + src/nu/xom/samples/StreamingNodeLister.java | 118 + .../StreamingProcessingInstructionLister.java | 111 + src/nu/xom/samples/StreamingROT13.java | 108 + .../xom/samples/StreamingTextExtractor.java | 136 + src/nu/xom/samples/StreamingTreeReporter.java | 98 + src/nu/xom/samples/StreamingTypeCounter.java | 138 + .../xom/samples/StreamingXHTMLPurifier.java | 161 + .../xom/samples/StreamingXHTMLQualifier.java | 88 + src/nu/xom/samples/Subfunction.java | 69 + src/nu/xom/samples/TextExtractor.java | 72 + src/nu/xom/samples/TextMerger.java | 63 + src/nu/xom/samples/TextSerializer.java | 128 + src/nu/xom/samples/Transformer.java | 83 + src/nu/xom/samples/TreePrinter.java | 120 + src/nu/xom/samples/TreeReporter.java | 84 + src/nu/xom/samples/TreeViewer.java | 110 + src/nu/xom/samples/ValidFibonacci.java | 69 + src/nu/xom/samples/Validator.java | 77 + src/nu/xom/samples/WrappingSerializer.java | 366 ++ src/nu/xom/samples/XHTMLPurifier.java | 115 + src/nu/xom/samples/XHTMLQualifier.java | 79 + src/nu/xom/samples/XHTMLValidator.java | 126 + src/nu/xom/samples/XIncludeDriver.java | 60 + src/nu/xom/samples/XLinkSpider.java | 131 + src/nu/xom/samples/XMLPrinter.java | 68 + src/nu/xom/samples/XOMChecker.java | 69 + src/nu/xom/samples/XPathDriver.java | 84 + src/nu/xom/samples/XPathHeadlines.java | 69 + src/nu/xom/samples/inner/InnerElement.java | 91 + src/nu/xom/samples/inner/InnerFactory.java | 32 + src/nu/xom/samples/inner/InnerTest.java | 54 + src/nu/xom/samples/package.html | 60 + src/nu/xom/tests/ANodeFactory.java | 119 + src/nu/xom/tests/AttributeTest.java | 751 +++ src/nu/xom/tests/AttributeTypeTest.java | 134 + src/nu/xom/tests/AttributesTest.java | 84 + src/nu/xom/tests/BaseURITest.java | 1260 +++++ src/nu/xom/tests/BuilderTest.java | 3531 ++++++++++++ src/nu/xom/tests/CDATASectionTest.java | 213 + .../tests/CanonicalizationExceptionTest.java | 128 + src/nu/xom/tests/Canonicalizer11Test.java | 425 ++ src/nu/xom/tests/CanonicalizerTest.java | 2250 ++++++++ src/nu/xom/tests/CommentTest.java | 322 ++ src/nu/xom/tests/DOMConverterTest.java | 737 +++ src/nu/xom/tests/DocTypeTest.java | 922 +++ src/nu/xom/tests/DocumentTest.java | 585 ++ src/nu/xom/tests/EBCDICTest.java | 107 + src/nu/xom/tests/ElementTest.java | 1752 ++++++ src/nu/xom/tests/EncodingTest.java | 318 ++ src/nu/xom/tests/FastTests.java | 87 + src/nu/xom/tests/IDTest.java | 355 ++ src/nu/xom/tests/InfoTest.java | 75 + src/nu/xom/tests/LeafNodeTest.java | 74 + src/nu/xom/tests/LocatorFilter.java | 53 + src/nu/xom/tests/MegaTest.java | 152 + src/nu/xom/tests/NamespaceNodeTest.java | 287 + src/nu/xom/tests/NamespacesTest.java | 910 +++ src/nu/xom/tests/NodeFactoryTest.java | 1145 ++++ src/nu/xom/tests/NodesTest.java | 272 + src/nu/xom/tests/ParentNodeTest.java | 559 ++ src/nu/xom/tests/ParsingExceptionTest.java | 183 + .../xom/tests/ProcessingInstructionTest.java | 494 ++ src/nu/xom/tests/RoundTripTest.java | 146 + src/nu/xom/tests/SAXConverterTest.java | 468 ++ src/nu/xom/tests/SerializerTest.java | 2799 +++++++++ src/nu/xom/tests/SubclassTest.java | 207 + src/nu/xom/tests/TextTest.java | 363 ++ src/nu/xom/tests/ValidityExceptionTest.java | 162 + src/nu/xom/tests/VerifierTest.java | 1066 ++++ src/nu/xom/tests/XIncludeExceptionTest.java | 167 + src/nu/xom/tests/XIncludeTest.java | 2478 ++++++++ src/nu/xom/tests/XMLExceptionTest.java | 227 + src/nu/xom/tests/XMLWriter.java | 1177 ++++ src/nu/xom/tests/XOMTestCase.java | 750 +++ src/nu/xom/tests/XOMTestCaseTest.java | 239 + src/nu/xom/tests/XOMTests.java | 94 + src/nu/xom/tests/XPathExceptionTest.java | 170 + src/nu/xom/tests/XPathTest.java | 2461 ++++++++ src/nu/xom/tests/XSLExceptionTest.java | 128 + src/nu/xom/tests/XSLTransformTest.java | 1133 ++++ src/nu/xom/tests/package.html | 83 + src/nu/xom/tools/XHTMLJavaDoc.java | 170 + src/nu/xom/tools/package.html | 41 + .../BadEncodingAttributeException.java | 68 + .../xom/xinclude/BadHTTPHeaderException.java | 69 + .../xinclude/BadHrefAttributeException.java | 68 + .../xinclude/BadParseAttributeException.java | 67 + src/nu/xom/xinclude/EncodingHeuristics.java | 231 + .../xom/xinclude/InclusionLoopException.java | 67 + .../xinclude/MisplacedFallbackException.java | 67 + .../xinclude/NoIncludeLocationException.java | 84 + src/nu/xom/xinclude/XIncludeException.java | 159 + src/nu/xom/xinclude/XIncluder.java | 983 ++++ src/nu/xom/xinclude/XPointer.java | 332 ++ src/nu/xom/xinclude/XPointerException.java | 124 + .../xinclude/XPointerResourceException.java | 54 + .../xom/xinclude/XPointerSyntaxException.java | 67 + src/nu/xom/xinclude/package.html | 44 + src/nu/xom/xslt/XOMInputSource.java | 45 + src/nu/xom/xslt/XOMReader.java | 156 + src/nu/xom/xslt/XOMResult.java | 54 + src/nu/xom/xslt/XOMSource.java | 86 + src/nu/xom/xslt/XSLException.java | 122 + src/nu/xom/xslt/XSLTHandler.java | 373 ++ src/nu/xom/xslt/XSLTransform.java | 450 ++ src/nu/xom/xslt/package.html | 40 + 677 files changed, 81806 insertions(+) create mode 100644 .gitattributes create mode 100644 LICENSE create mode 100644 build.xml create mode 100644 build/built-jar.properties create mode 100644 build/classes/nu/xom/ASCIIWriter.class create mode 100644 build/classes/nu/xom/Attribute$Type.class create mode 100644 build/classes/nu/xom/Attribute.class create mode 100644 build/classes/nu/xom/Builder$1.class create mode 100644 build/classes/nu/xom/Builder$NamespaceWellformednessRequired.class create mode 100644 build/classes/nu/xom/Builder$ValidityRequired.class create mode 100644 build/classes/nu/xom/Builder.class create mode 100644 build/classes/nu/xom/CDATASection.class create mode 100644 build/classes/nu/xom/Comment.class create mode 100644 build/classes/nu/xom/CycleException.class create mode 100644 build/classes/nu/xom/DocType.class create mode 100644 build/classes/nu/xom/Document.class create mode 100644 build/classes/nu/xom/DocumentFragment.class create mode 100644 build/classes/nu/xom/EBCDICWriter.class create mode 100644 build/classes/nu/xom/Element$1.class create mode 100644 build/classes/nu/xom/Element$AttributeIterator.class create mode 100644 build/classes/nu/xom/Element.class create mode 100644 build/classes/nu/xom/Elements.class create mode 100644 build/classes/nu/xom/GenericWriter.class create mode 100644 build/classes/nu/xom/ISOArabicWriter.class create mode 100644 build/classes/nu/xom/ISOCyrillicWriter.class create mode 100644 build/classes/nu/xom/ISOGreekWriter.class create mode 100644 build/classes/nu/xom/ISOHebrewWriter.class create mode 100644 build/classes/nu/xom/ISOThaiWriter.class create mode 100644 build/classes/nu/xom/IllegalAddException.class create mode 100644 build/classes/nu/xom/IllegalCharacterDataException.class create mode 100644 build/classes/nu/xom/IllegalDataException.class create mode 100644 build/classes/nu/xom/IllegalNameException.class create mode 100644 build/classes/nu/xom/IllegalTargetException.class create mode 100644 build/classes/nu/xom/Info.class create mode 100644 build/classes/nu/xom/JDK15XML1_0Parser.class create mode 100644 build/classes/nu/xom/JaxenConnector.class create mode 100644 build/classes/nu/xom/JaxenNavigator$ChildIterator.class create mode 100644 build/classes/nu/xom/JaxenNavigator$NamedChildIterator.class create mode 100644 build/classes/nu/xom/JaxenNavigator.class create mode 100644 build/classes/nu/xom/Latin10Writer.class create mode 100644 build/classes/nu/xom/Latin1Writer.class create mode 100644 build/classes/nu/xom/Latin2Writer.class create mode 100644 build/classes/nu/xom/Latin3Writer.class create mode 100644 build/classes/nu/xom/Latin4Writer.class create mode 100644 build/classes/nu/xom/Latin5Writer.class create mode 100644 build/classes/nu/xom/Latin6Writer.class create mode 100644 build/classes/nu/xom/Latin7Writer.class create mode 100644 build/classes/nu/xom/Latin8Writer.class create mode 100644 build/classes/nu/xom/Latin9Writer.class create mode 100644 build/classes/nu/xom/MalformedURIException.class create mode 100644 build/classes/nu/xom/MultipleParentException.class create mode 100644 build/classes/nu/xom/Namespace.class create mode 100644 build/classes/nu/xom/NamespaceConflictException.class create mode 100644 build/classes/nu/xom/Namespaces.class create mode 100644 build/classes/nu/xom/NoSuchAttributeException.class create mode 100644 build/classes/nu/xom/NoSuchChildException.class create mode 100644 build/classes/nu/xom/Node$1.class create mode 100644 build/classes/nu/xom/Node$EmptyNamespaceContext.class create mode 100644 build/classes/nu/xom/Node.class create mode 100644 build/classes/nu/xom/NodeFactory.class create mode 100644 build/classes/nu/xom/Nodes.class create mode 100644 build/classes/nu/xom/NonVerifyingHandler.class create mode 100644 build/classes/nu/xom/ParentNode.class create mode 100644 build/classes/nu/xom/ParsingException.class create mode 100644 build/classes/nu/xom/ProcessingInstruction.class create mode 100644 build/classes/nu/xom/Serializer.class create mode 100644 build/classes/nu/xom/Text.class create mode 100644 build/classes/nu/xom/TextWriter.class create mode 100644 build/classes/nu/xom/TextWriterFactory.class create mode 100644 build/classes/nu/xom/UCSWriter.class create mode 100644 build/classes/nu/xom/URIUtil$ParsedURI.class create mode 100644 build/classes/nu/xom/URIUtil.class create mode 100644 build/classes/nu/xom/UnavailableCharacterException.class create mode 100644 build/classes/nu/xom/UnicodeUtil$UnicodeString.class create mode 100644 build/classes/nu/xom/UnicodeUtil.class create mode 100644 build/classes/nu/xom/UnicodeWriter.class create mode 100644 build/classes/nu/xom/UnsynchronizedBufferedWriter.class create mode 100644 build/classes/nu/xom/ValidityException.class create mode 100644 build/classes/nu/xom/Verifier$1.class create mode 100644 build/classes/nu/xom/Verifier$EmptyReader.class create mode 100644 build/classes/nu/xom/Verifier$URICache.class create mode 100644 build/classes/nu/xom/Verifier.class create mode 100644 build/classes/nu/xom/WellformednessException.class create mode 100644 build/classes/nu/xom/XML1_0Parser.class create mode 100644 build/classes/nu/xom/XMLException.class create mode 100644 build/classes/nu/xom/XOMHandler.class create mode 100644 build/classes/nu/xom/XPathContext$1.class create mode 100644 build/classes/nu/xom/XPathContext$JaxenNamespaceContext.class create mode 100644 build/classes/nu/xom/XPathContext.class create mode 100644 build/classes/nu/xom/XPathException.class create mode 100644 build/classes/nu/xom/XPathTypeException.class create mode 100644 build/classes/nu/xom/benchmarks/BigText.class create mode 100644 build/classes/nu/xom/benchmarks/DOMConverterBench.class create mode 100644 build/classes/nu/xom/benchmarks/DocumentModifier.class create mode 100644 build/classes/nu/xom/benchmarks/ElementCopyBench.class create mode 100644 build/classes/nu/xom/benchmarks/FastReproducer.class create mode 100644 build/classes/nu/xom/benchmarks/ManyNamespaces.class create mode 100644 build/classes/nu/xom/benchmarks/MemoryTest.class create mode 100644 build/classes/nu/xom/benchmarks/NamespaceLister.class create mode 100644 build/classes/nu/xom/benchmarks/Reproducer.class create mode 100644 build/classes/nu/xom/benchmarks/ToXMLBench.class create mode 100644 build/classes/nu/xom/benchmarks/TreeWalker.class create mode 100644 build/classes/nu/xom/benchmarks/package.html create mode 100644 build/classes/nu/xom/canonical/CanonicalizationException.class create mode 100644 build/classes/nu/xom/canonical/Canonicalizer$1.class create mode 100644 build/classes/nu/xom/canonical/Canonicalizer$AttributeComparator.class create mode 100644 build/classes/nu/xom/canonical/Canonicalizer$CanonicalXMLSerializer.class create mode 100644 build/classes/nu/xom/canonical/Canonicalizer.class create mode 100644 build/classes/nu/xom/canonical/URIJoiner$ParsedURI.class create mode 100644 build/classes/nu/xom/canonical/URIJoiner.class create mode 100644 build/classes/nu/xom/canonical/package.html create mode 100644 build/classes/nu/xom/characters.dat create mode 100644 build/classes/nu/xom/compositions.dat create mode 100644 build/classes/nu/xom/converters/DOMConverter.class create mode 100644 build/classes/nu/xom/converters/SAXConverter.class create mode 100644 build/classes/nu/xom/converters/package.html create mode 100644 build/classes/nu/xom/doc-files/xom.png create mode 100644 build/classes/nu/xom/integrationtests/OASISXSLTTest$1.class create mode 100644 build/classes/nu/xom/integrationtests/OASISXSLTTest$StrippingFactory.class create mode 100644 build/classes/nu/xom/integrationtests/OASISXSLTTest.class create mode 100644 build/classes/nu/xom/integrationtests/W3CXIncludeTest.class create mode 100644 build/classes/nu/xom/package.html create mode 100644 build/classes/nu/xom/samples/Account.class create mode 100644 build/classes/nu/xom/samples/Agency.class create mode 100644 build/classes/nu/xom/samples/AttributesToElements.class create mode 100644 build/classes/nu/xom/samples/AttributesXMLBudget.class create mode 100644 build/classes/nu/xom/samples/Budget.class create mode 100644 build/classes/nu/xom/samples/BudgetData.class create mode 100644 build/classes/nu/xom/samples/Bureau.class create mode 100644 build/classes/nu/xom/samples/CanonicalizerDriver.class create mode 100644 build/classes/nu/xom/samples/CommentReader.class create mode 100644 build/classes/nu/xom/samples/CrimsonPrinter.class create mode 100644 build/classes/nu/xom/samples/DTDGenerator$1.class create mode 100644 build/classes/nu/xom/samples/DTDGenerator$NamingNodeFactory.class create mode 100644 build/classes/nu/xom/samples/DTDGenerator.class create mode 100644 build/classes/nu/xom/samples/DatabaseBuilder.class create mode 100644 build/classes/nu/xom/samples/DatabaseConverter.class create mode 100644 build/classes/nu/xom/samples/DocumentMerger.class create mode 100644 build/classes/nu/xom/samples/EZSerializer.class create mode 100644 build/classes/nu/xom/samples/ElementLister.class create mode 100644 build/classes/nu/xom/samples/EncodingDemo.class create mode 100644 build/classes/nu/xom/samples/ExampleExtractor.class create mode 100644 build/classes/nu/xom/samples/ExampleLister.class create mode 100644 build/classes/nu/xom/samples/FibonacciAttributes.class create mode 100644 build/classes/nu/xom/samples/FibonacciEBCDIC.class create mode 100644 build/classes/nu/xom/samples/FibonacciFile.class create mode 100644 build/classes/nu/xom/samples/FibonacciLatin1.class create mode 100644 build/classes/nu/xom/samples/FibonacciSOAPClient.class create mode 100644 build/classes/nu/xom/samples/FibonacciServlet.class create mode 100644 build/classes/nu/xom/samples/FibonacciXML.class create mode 100644 build/classes/nu/xom/samples/FibonacciXMLRPCClient.class create mode 100644 build/classes/nu/xom/samples/FibonacciXOMSOAPServlet.class create mode 100644 build/classes/nu/xom/samples/FibonacciXOMXMLRPCServlet.class create mode 100644 build/classes/nu/xom/samples/FilterDriver.class create mode 100644 build/classes/nu/xom/samples/FlatXMLBudget.class create mode 100644 build/classes/nu/xom/samples/HelloNamespaces.class create mode 100644 build/classes/nu/xom/samples/HelloWorld.class create mode 100644 build/classes/nu/xom/samples/HelloXMLBase.class create mode 100644 build/classes/nu/xom/samples/HierarchicalXMLBudget.class create mode 100644 build/classes/nu/xom/samples/IDFilter.class create mode 100644 build/classes/nu/xom/samples/IDTagger.class create mode 100644 build/classes/nu/xom/samples/Linkset.class create mode 100644 build/classes/nu/xom/samples/MathMLFibonacci.class create mode 100644 build/classes/nu/xom/samples/MinimalNodeFactory.class create mode 100644 build/classes/nu/xom/samples/NodeLister.class create mode 100644 build/classes/nu/xom/samples/NonRecursiveSerializer.class create mode 100644 build/classes/nu/xom/samples/NormalizingFactory.class create mode 100644 build/classes/nu/xom/samples/PoliteSpider.class create mode 100644 build/classes/nu/xom/samples/PrettyFibonacci.class create mode 100644 build/classes/nu/xom/samples/PrettyPrinter.class create mode 100644 build/classes/nu/xom/samples/ProcessingInstructionLister.class create mode 100644 build/classes/nu/xom/samples/PropertyPrinter.class create mode 100644 build/classes/nu/xom/samples/PseudoAttributes.class create mode 100644 build/classes/nu/xom/samples/PureValidator.class create mode 100644 build/classes/nu/xom/samples/RDDLFilter.class create mode 100644 build/classes/nu/xom/samples/RDDLStripper.class create mode 100644 build/classes/nu/xom/samples/RDDLToTable.class create mode 100644 build/classes/nu/xom/samples/ROT13File.class create mode 100644 build/classes/nu/xom/samples/ROT13XML.class create mode 100644 build/classes/nu/xom/samples/RSSHeadlines.class create mode 100644 build/classes/nu/xom/samples/ResourceToTable.class create mode 100644 build/classes/nu/xom/samples/Restructurer.class create mode 100644 build/classes/nu/xom/samples/SQLToXML.class create mode 100644 build/classes/nu/xom/samples/SchemaValidator.class create mode 100644 build/classes/nu/xom/samples/SimpleSVG.class create mode 100644 build/classes/nu/xom/samples/SourceCodeGenerator.class create mode 100644 build/classes/nu/xom/samples/SourceCodeSerializer.class create mode 100644 build/classes/nu/xom/samples/StreamingCommentReader.class create mode 100644 build/classes/nu/xom/samples/StreamingElementLister.class create mode 100644 build/classes/nu/xom/samples/StreamingExampleExtractor.class create mode 100644 build/classes/nu/xom/samples/StreamingNodeLister.class create mode 100644 build/classes/nu/xom/samples/StreamingProcessingInstructionLister.class create mode 100644 build/classes/nu/xom/samples/StreamingROT13.class create mode 100644 build/classes/nu/xom/samples/StreamingTextExtractor.class create mode 100644 build/classes/nu/xom/samples/StreamingTreeReporter.class create mode 100644 build/classes/nu/xom/samples/StreamingTypeCounter.class create mode 100644 build/classes/nu/xom/samples/StreamingXHTMLPurifier.class create mode 100644 build/classes/nu/xom/samples/StreamingXHTMLQualifier.class create mode 100644 build/classes/nu/xom/samples/Subfunction.class create mode 100644 build/classes/nu/xom/samples/TextExtractor.class create mode 100644 build/classes/nu/xom/samples/TextMerger.class create mode 100644 build/classes/nu/xom/samples/TextSerializer.class create mode 100644 build/classes/nu/xom/samples/Transformer.class create mode 100644 build/classes/nu/xom/samples/TreePrinter.class create mode 100644 build/classes/nu/xom/samples/TreeReporter.class create mode 100644 build/classes/nu/xom/samples/TreeViewer.class create mode 100644 build/classes/nu/xom/samples/ValidFibonacci.class create mode 100644 build/classes/nu/xom/samples/Validator.class create mode 100644 build/classes/nu/xom/samples/WrappingSerializer.class create mode 100644 build/classes/nu/xom/samples/XHTMLPurifier.class create mode 100644 build/classes/nu/xom/samples/XHTMLQualifier.class create mode 100644 build/classes/nu/xom/samples/XHTMLValidator.class create mode 100644 build/classes/nu/xom/samples/XIncludeDriver.class create mode 100644 build/classes/nu/xom/samples/XLinkSpider.class create mode 100644 build/classes/nu/xom/samples/XMLPrinter.class create mode 100644 build/classes/nu/xom/samples/XOMChecker.class create mode 100644 build/classes/nu/xom/samples/XPathDriver.class create mode 100644 build/classes/nu/xom/samples/XPathHeadlines.class create mode 100644 build/classes/nu/xom/samples/inner/InnerElement$1.class create mode 100644 build/classes/nu/xom/samples/inner/InnerElement.class create mode 100644 build/classes/nu/xom/samples/inner/InnerFactory.class create mode 100644 build/classes/nu/xom/samples/inner/InnerTest.class create mode 100644 build/classes/nu/xom/samples/package.html create mode 100644 build/classes/nu/xom/tests/AComment.class create mode 100644 build/classes/nu/xom/tests/ADocType.class create mode 100644 build/classes/nu/xom/tests/ADocument.class create mode 100644 build/classes/nu/xom/tests/AElement.class create mode 100644 build/classes/nu/xom/tests/ANodeFactory.class create mode 100644 build/classes/nu/xom/tests/AProcessingInstruction.class create mode 100644 build/classes/nu/xom/tests/AText.class create mode 100644 build/classes/nu/xom/tests/AnAttribute.class create mode 100644 build/classes/nu/xom/tests/AttributeTest.class create mode 100644 build/classes/nu/xom/tests/AttributeTypeTest.class create mode 100644 build/classes/nu/xom/tests/AttributesTest.class create mode 100644 build/classes/nu/xom/tests/BaseURITest.class create mode 100644 build/classes/nu/xom/tests/BuilderTest$1.class create mode 100644 build/classes/nu/xom/tests/BuilderTest$BadDeclHandler.class create mode 100644 build/classes/nu/xom/tests/BuilderTest$CustomReader.class create mode 100644 build/classes/nu/xom/tests/BuilderTest$DoNothingReader.class create mode 100644 build/classes/nu/xom/tests/BuilderTest$EndOnlyReader.class create mode 100644 build/classes/nu/xom/tests/BuilderTest$EntitySkipper.class create mode 100644 build/classes/nu/xom/tests/BuilderTest$ExceptionTester.class create mode 100644 build/classes/nu/xom/tests/BuilderTest$MalformedDTDFilter.class create mode 100644 build/classes/nu/xom/tests/BuilderTest$NoLocator.class create mode 100644 build/classes/nu/xom/tests/BuilderTest$NonEntityResolvingFilter.class create mode 100644 build/classes/nu/xom/tests/BuilderTest$NonValidatingFilter.class create mode 100644 build/classes/nu/xom/tests/BuilderTest$OrderingFilter.class create mode 100644 build/classes/nu/xom/tests/BuilderTest$ParenthesizedEnumeratedAttributeTypes.class create mode 100644 build/classes/nu/xom/tests/BuilderTest$StartAndEndReader.class create mode 100644 build/classes/nu/xom/tests/BuilderTest$StartOnlyReader.class create mode 100644 build/classes/nu/xom/tests/BuilderTest$WarningFilter.class create mode 100644 build/classes/nu/xom/tests/BuilderTest$WeirdAttributeTypes.class create mode 100644 build/classes/nu/xom/tests/BuilderTest.class create mode 100644 build/classes/nu/xom/tests/CDATASectionTest.class create mode 100644 build/classes/nu/xom/tests/CanonicalizationExceptionTest.class create mode 100644 build/classes/nu/xom/tests/Canonicalizer11Test.class create mode 100644 build/classes/nu/xom/tests/CanonicalizerTest$1.class create mode 100644 build/classes/nu/xom/tests/CanonicalizerTest$UnwriteableOutputStream.class create mode 100644 build/classes/nu/xom/tests/CanonicalizerTest$XMLFilter.class create mode 100644 build/classes/nu/xom/tests/CanonicalizerTest.class create mode 100644 build/classes/nu/xom/tests/CommentTest.class create mode 100644 build/classes/nu/xom/tests/DOMConverterTest$1.class create mode 100644 build/classes/nu/xom/tests/DOMConverterTest$StrippingFactory.class create mode 100644 build/classes/nu/xom/tests/DOMConverterTest.class create mode 100644 build/classes/nu/xom/tests/DocTypeTest.class create mode 100644 build/classes/nu/xom/tests/DocumentTest.class create mode 100644 build/classes/nu/xom/tests/EBCDICTest.class create mode 100644 build/classes/nu/xom/tests/ElementTest$TestElement.class create mode 100644 build/classes/nu/xom/tests/ElementTest.class create mode 100644 build/classes/nu/xom/tests/EncodingTest.class create mode 100644 build/classes/nu/xom/tests/FastTests.class create mode 100644 build/classes/nu/xom/tests/IDTest.class create mode 100644 build/classes/nu/xom/tests/InfoTest.class create mode 100644 build/classes/nu/xom/tests/LeafNodeTest.class create mode 100644 build/classes/nu/xom/tests/LocatorFilter.class create mode 100644 build/classes/nu/xom/tests/MegaTest$1.class create mode 100644 build/classes/nu/xom/tests/MegaTest$Generator.class create mode 100644 build/classes/nu/xom/tests/MegaTest$MinimizingFactory.class create mode 100644 build/classes/nu/xom/tests/MegaTest.class create mode 100644 build/classes/nu/xom/tests/NamespaceNodeTest.class create mode 100644 build/classes/nu/xom/tests/NamespacesTest.class create mode 100644 build/classes/nu/xom/tests/NodeFactoryTest$1.class create mode 100644 build/classes/nu/xom/tests/NodeFactoryTest$10.class create mode 100644 build/classes/nu/xom/tests/NodeFactoryTest$11.class create mode 100644 build/classes/nu/xom/tests/NodeFactoryTest$12.class create mode 100644 build/classes/nu/xom/tests/NodeFactoryTest$13.class create mode 100644 build/classes/nu/xom/tests/NodeFactoryTest$14.class create mode 100644 build/classes/nu/xom/tests/NodeFactoryTest$15.class create mode 100644 build/classes/nu/xom/tests/NodeFactoryTest$16.class create mode 100644 build/classes/nu/xom/tests/NodeFactoryTest$17.class create mode 100644 build/classes/nu/xom/tests/NodeFactoryTest$18.class create mode 100644 build/classes/nu/xom/tests/NodeFactoryTest$19.class create mode 100644 build/classes/nu/xom/tests/NodeFactoryTest$2.class create mode 100644 build/classes/nu/xom/tests/NodeFactoryTest$20.class create mode 100644 build/classes/nu/xom/tests/NodeFactoryTest$21.class create mode 100644 build/classes/nu/xom/tests/NodeFactoryTest$22.class create mode 100644 build/classes/nu/xom/tests/NodeFactoryTest$23.class create mode 100644 build/classes/nu/xom/tests/NodeFactoryTest$24.class create mode 100644 build/classes/nu/xom/tests/NodeFactoryTest$25.class create mode 100644 build/classes/nu/xom/tests/NodeFactoryTest$3.class create mode 100644 build/classes/nu/xom/tests/NodeFactoryTest$4.class create mode 100644 build/classes/nu/xom/tests/NodeFactoryTest$5.class create mode 100644 build/classes/nu/xom/tests/NodeFactoryTest$6.class create mode 100644 build/classes/nu/xom/tests/NodeFactoryTest$7.class create mode 100644 build/classes/nu/xom/tests/NodeFactoryTest$8.class create mode 100644 build/classes/nu/xom/tests/NodeFactoryTest$9.class create mode 100644 build/classes/nu/xom/tests/NodeFactoryTest$BFilter.class create mode 100644 build/classes/nu/xom/tests/NodeFactoryTest$CFactory.class create mode 100644 build/classes/nu/xom/tests/NodeFactoryTest$CallsMakeRoot.class create mode 100644 build/classes/nu/xom/tests/NodeFactoryTest$CommentFilter.class create mode 100644 build/classes/nu/xom/tests/NodeFactoryTest$Counter.class create mode 100644 build/classes/nu/xom/tests/NodeFactoryTest$DocumentFilter.class create mode 100644 build/classes/nu/xom/tests/NodeFactoryTest$MinimizingFactory.class create mode 100644 build/classes/nu/xom/tests/NodeFactoryTest$NullDocumentFactory.class create mode 100644 build/classes/nu/xom/tests/NodeFactoryTest$NullElementFactory.class create mode 100644 build/classes/nu/xom/tests/NodeFactoryTest$ProcessingInstructionFilter.class create mode 100644 build/classes/nu/xom/tests/NodeFactoryTest$SingleAttributeFactory.class create mode 100644 build/classes/nu/xom/tests/NodeFactoryTest$SingleElementFactory.class create mode 100644 build/classes/nu/xom/tests/NodeFactoryTest$TripleElementFilter.class create mode 100644 build/classes/nu/xom/tests/NodeFactoryTest$UncommentFilter.class create mode 100644 build/classes/nu/xom/tests/NodeFactoryTest.class create mode 100644 build/classes/nu/xom/tests/NodesTest.class create mode 100644 build/classes/nu/xom/tests/NullLocator.class create mode 100644 build/classes/nu/xom/tests/ParentNodeTest.class create mode 100644 build/classes/nu/xom/tests/ParsingExceptionTest.class create mode 100644 build/classes/nu/xom/tests/ProcessingInstructionTest.class create mode 100644 build/classes/nu/xom/tests/RoundTripTest.class create mode 100644 build/classes/nu/xom/tests/SAXConverterTest$1.class create mode 100644 build/classes/nu/xom/tests/SAXConverterTest$BaseChecker.class create mode 100644 build/classes/nu/xom/tests/SAXConverterTest$XMLPrefixMapCounter.class create mode 100644 build/classes/nu/xom/tests/SAXConverterTest$XMLPrefixTester.class create mode 100644 build/classes/nu/xom/tests/SAXConverterTest$XMLPrefixTester2.class create mode 100644 build/classes/nu/xom/tests/SAXConverterTest.class create mode 100644 build/classes/nu/xom/tests/SerializerTest$AvoidWriteElement.class create mode 100644 build/classes/nu/xom/tests/SerializerTest$ColumnSerializer.class create mode 100644 build/classes/nu/xom/tests/SerializerTest$ElementSerializer.class create mode 100644 build/classes/nu/xom/tests/SerializerTest$ExposingSerializer.class create mode 100644 build/classes/nu/xom/tests/SerializerTest$TextSerializer.class create mode 100644 build/classes/nu/xom/tests/SerializerTest.class create mode 100644 build/classes/nu/xom/tests/SubclassTest$AttributeSubclass.class create mode 100644 build/classes/nu/xom/tests/SubclassTest$CommentSubclass.class create mode 100644 build/classes/nu/xom/tests/SubclassTest$DocTypeSubclass.class create mode 100644 build/classes/nu/xom/tests/SubclassTest$ElementSubclass.class create mode 100644 build/classes/nu/xom/tests/SubclassTest$ProcessingInstructionSubclass.class create mode 100644 build/classes/nu/xom/tests/SubclassTest$TextSubclass.class create mode 100644 build/classes/nu/xom/tests/SubclassTest.class create mode 100644 build/classes/nu/xom/tests/TextTest.class create mode 100644 build/classes/nu/xom/tests/ValidityExceptionTest.class create mode 100644 build/classes/nu/xom/tests/VerifierTest.class create mode 100644 build/classes/nu/xom/tests/XIncludeExceptionTest.class create mode 100644 build/classes/nu/xom/tests/XIncludeTest$1.class create mode 100644 build/classes/nu/xom/tests/XIncludeTest$TextFilter.class create mode 100644 build/classes/nu/xom/tests/XIncludeTest$TextNodeFactory.class create mode 100644 build/classes/nu/xom/tests/XIncludeTest$TextSubclass.class create mode 100644 build/classes/nu/xom/tests/XIncludeTest$TextToAttribute.class create mode 100644 build/classes/nu/xom/tests/XIncludeTest$TextToComment.class create mode 100644 build/classes/nu/xom/tests/XIncludeTest$TextToElements.class create mode 100644 build/classes/nu/xom/tests/XIncludeTest.class create mode 100644 build/classes/nu/xom/tests/XMLExceptionTest.class create mode 100644 build/classes/nu/xom/tests/XMLWriter.class create mode 100644 build/classes/nu/xom/tests/XOMTestCase.class create mode 100644 build/classes/nu/xom/tests/XOMTestCaseTest.class create mode 100644 build/classes/nu/xom/tests/XOMTests.class create mode 100644 build/classes/nu/xom/tests/XPathExceptionTest.class create mode 100644 build/classes/nu/xom/tests/XPathTest.class create mode 100644 build/classes/nu/xom/tests/XSLExceptionTest.class create mode 100644 build/classes/nu/xom/tests/XSLTransformTest$1.class create mode 100644 build/classes/nu/xom/tests/XSLTransformTest$AttributeFactory.class create mode 100644 build/classes/nu/xom/tests/XSLTransformTest$AttributesToElements.class create mode 100644 build/classes/nu/xom/tests/XSLTransformTest.class create mode 100644 build/classes/nu/xom/tests/package.html create mode 100644 build/classes/nu/xom/tools/XHTMLJavaDoc$1.class create mode 100644 build/classes/nu/xom/tools/XHTMLJavaDoc$HTMLFilter.class create mode 100644 build/classes/nu/xom/tools/XHTMLJavaDoc$HTMLFixFactory.class create mode 100644 build/classes/nu/xom/tools/XHTMLJavaDoc$HTMLSerializer.class create mode 100644 build/classes/nu/xom/tools/XHTMLJavaDoc.class create mode 100644 build/classes/nu/xom/tools/package.html create mode 100644 build/classes/nu/xom/xinclude/BadEncodingAttributeException.class create mode 100644 build/classes/nu/xom/xinclude/BadHTTPHeaderException.class create mode 100644 build/classes/nu/xom/xinclude/BadHrefAttributeException.class create mode 100644 build/classes/nu/xom/xinclude/BadParseAttributeException.class create mode 100644 build/classes/nu/xom/xinclude/EncodingHeuristics.class create mode 100644 build/classes/nu/xom/xinclude/InclusionLoopException.class create mode 100644 build/classes/nu/xom/xinclude/MisplacedFallbackException.class create mode 100644 build/classes/nu/xom/xinclude/NoIncludeLocationException.class create mode 100644 build/classes/nu/xom/xinclude/XIncludeException.class create mode 100644 build/classes/nu/xom/xinclude/XIncluder.class create mode 100644 build/classes/nu/xom/xinclude/XPointer.class create mode 100644 build/classes/nu/xom/xinclude/XPointerException.class create mode 100644 build/classes/nu/xom/xinclude/XPointerResourceException.class create mode 100644 build/classes/nu/xom/xinclude/XPointerSyntaxException.class create mode 100644 build/classes/nu/xom/xinclude/package.html create mode 100644 build/classes/nu/xom/xslt/XOMInputSource.class create mode 100644 build/classes/nu/xom/xslt/XOMReader.class create mode 100644 build/classes/nu/xom/xslt/XOMResult.class create mode 100644 build/classes/nu/xom/xslt/XOMSource.class create mode 100644 build/classes/nu/xom/xslt/XSLException.class create mode 100644 build/classes/nu/xom/xslt/XSLTHandler.class create mode 100644 build/classes/nu/xom/xslt/XSLTransform$1.class create mode 100644 build/classes/nu/xom/xslt/XSLTransform$FatalListener.class create mode 100644 build/classes/nu/xom/xslt/XSLTransform.class create mode 100644 build/classes/nu/xom/xslt/package.html create mode 100644 dist/xom.jar create mode 100644 nbproject/build-impl.xml create mode 100644 nbproject/genfiles.properties create mode 100644 nbproject/private/config.properties create mode 100644 nbproject/private/private.properties create mode 100644 nbproject/private/private.xml create mode 100644 nbproject/project.properties create mode 100644 nbproject/project.xml create mode 100644 src/nu/xom/ASCIIWriter.java create mode 100644 src/nu/xom/Attribute.java create mode 100644 src/nu/xom/Builder.java create mode 100644 src/nu/xom/CDATASection.java create mode 100644 src/nu/xom/Comment.java create mode 100644 src/nu/xom/CycleException.java create mode 100644 src/nu/xom/DocType.java create mode 100644 src/nu/xom/Document.java create mode 100644 src/nu/xom/DocumentFragment.java create mode 100644 src/nu/xom/EBCDICWriter.java create mode 100644 src/nu/xom/Element.java create mode 100644 src/nu/xom/Elements.java create mode 100644 src/nu/xom/GenericWriter.java create mode 100644 src/nu/xom/ISOArabicWriter.java create mode 100644 src/nu/xom/ISOCyrillicWriter.java create mode 100644 src/nu/xom/ISOGreekWriter.java create mode 100644 src/nu/xom/ISOHebrewWriter.java create mode 100644 src/nu/xom/ISOThaiWriter.java create mode 100644 src/nu/xom/IllegalAddException.java create mode 100644 src/nu/xom/IllegalCharacterDataException.java create mode 100644 src/nu/xom/IllegalDataException.java create mode 100644 src/nu/xom/IllegalNameException.java create mode 100644 src/nu/xom/IllegalTargetException.java create mode 100644 src/nu/xom/Info.java create mode 100644 src/nu/xom/JDK15XML1_0Parser.java create mode 100644 src/nu/xom/JaxenConnector.java create mode 100644 src/nu/xom/JaxenNavigator.java create mode 100644 src/nu/xom/Latin10Writer.java create mode 100644 src/nu/xom/Latin1Writer.java create mode 100644 src/nu/xom/Latin2Writer.java create mode 100644 src/nu/xom/Latin3Writer.java create mode 100644 src/nu/xom/Latin4Writer.java create mode 100644 src/nu/xom/Latin5Writer.java create mode 100644 src/nu/xom/Latin6Writer.java create mode 100644 src/nu/xom/Latin7Writer.java create mode 100644 src/nu/xom/Latin8Writer.java create mode 100644 src/nu/xom/Latin9Writer.java create mode 100644 src/nu/xom/MalformedURIException.java create mode 100644 src/nu/xom/MultipleParentException.java create mode 100644 src/nu/xom/Namespace.java create mode 100644 src/nu/xom/NamespaceConflictException.java create mode 100644 src/nu/xom/Namespaces.java create mode 100644 src/nu/xom/NoSuchAttributeException.java create mode 100644 src/nu/xom/NoSuchChildException.java create mode 100644 src/nu/xom/Node.java create mode 100644 src/nu/xom/NodeFactory.java create mode 100644 src/nu/xom/Nodes.java create mode 100644 src/nu/xom/NonVerifyingHandler.java create mode 100644 src/nu/xom/ParentNode.java create mode 100644 src/nu/xom/ParsingException.java create mode 100644 src/nu/xom/ProcessingInstruction.java create mode 100644 src/nu/xom/Serializer.java create mode 100644 src/nu/xom/Text.java create mode 100644 src/nu/xom/TextWriter.java create mode 100644 src/nu/xom/TextWriterFactory.java create mode 100644 src/nu/xom/UCSWriter.java create mode 100644 src/nu/xom/URIUtil.java create mode 100644 src/nu/xom/UnavailableCharacterException.java create mode 100644 src/nu/xom/UnicodeUtil.java create mode 100644 src/nu/xom/UnicodeWriter.java create mode 100644 src/nu/xom/UnsynchronizedBufferedWriter.java create mode 100644 src/nu/xom/ValidityException.java create mode 100644 src/nu/xom/Verifier.java create mode 100644 src/nu/xom/WellformednessException.java create mode 100644 src/nu/xom/XML1_0Parser.java create mode 100644 src/nu/xom/XMLException.java create mode 100644 src/nu/xom/XOMHandler.java create mode 100644 src/nu/xom/XPathContext.java create mode 100644 src/nu/xom/XPathException.java create mode 100644 src/nu/xom/XPathTypeException.java create mode 100644 src/nu/xom/benchmarks/BigText.java create mode 100644 src/nu/xom/benchmarks/DOMConverterBench.java create mode 100644 src/nu/xom/benchmarks/DocumentModifier.java create mode 100644 src/nu/xom/benchmarks/ElementCopyBench.java create mode 100644 src/nu/xom/benchmarks/FastReproducer.java create mode 100644 src/nu/xom/benchmarks/ManyNamespaces.java create mode 100644 src/nu/xom/benchmarks/MemoryTest.java create mode 100644 src/nu/xom/benchmarks/NamespaceLister.java create mode 100644 src/nu/xom/benchmarks/Reproducer.java create mode 100644 src/nu/xom/benchmarks/ToXMLBench.java create mode 100644 src/nu/xom/benchmarks/TreeWalker.java create mode 100644 src/nu/xom/benchmarks/package.html create mode 100644 src/nu/xom/canonical/CanonicalizationException.java create mode 100644 src/nu/xom/canonical/Canonicalizer.java create mode 100644 src/nu/xom/canonical/URIJoiner.java create mode 100644 src/nu/xom/canonical/package.html create mode 100644 src/nu/xom/characters.dat create mode 100644 src/nu/xom/compositions.dat create mode 100644 src/nu/xom/converters/DOMConverter.java create mode 100644 src/nu/xom/converters/SAXConverter.java create mode 100644 src/nu/xom/converters/package.html create mode 100644 src/nu/xom/doc-files/xom.png create mode 100644 src/nu/xom/integrationtests/OASISXSLTTest.java create mode 100644 src/nu/xom/integrationtests/W3CXIncludeTest.java create mode 100644 src/nu/xom/package.html create mode 100644 src/nu/xom/samples/Account.java create mode 100644 src/nu/xom/samples/Agency.java create mode 100644 src/nu/xom/samples/AttributesToElements.java create mode 100644 src/nu/xom/samples/AttributesXMLBudget.java create mode 100644 src/nu/xom/samples/Budget.java create mode 100644 src/nu/xom/samples/BudgetData.java create mode 100644 src/nu/xom/samples/Bureau.java create mode 100644 src/nu/xom/samples/CanonicalizerDriver.java create mode 100644 src/nu/xom/samples/CommentReader.java create mode 100644 src/nu/xom/samples/CrimsonPrinter.java create mode 100644 src/nu/xom/samples/DTDGenerator.java create mode 100644 src/nu/xom/samples/DatabaseBuilder.java create mode 100644 src/nu/xom/samples/DatabaseConverter.java create mode 100644 src/nu/xom/samples/DocumentMerger.java create mode 100644 src/nu/xom/samples/EZSerializer.java create mode 100644 src/nu/xom/samples/ElementLister.java create mode 100644 src/nu/xom/samples/EncodingDemo.java create mode 100644 src/nu/xom/samples/ExampleExtractor.java create mode 100644 src/nu/xom/samples/ExampleLister.java create mode 100644 src/nu/xom/samples/FibonacciAttributes.java create mode 100644 src/nu/xom/samples/FibonacciEBCDIC.java create mode 100644 src/nu/xom/samples/FibonacciFile.java create mode 100644 src/nu/xom/samples/FibonacciLatin1.java create mode 100644 src/nu/xom/samples/FibonacciSOAPClient.java create mode 100644 src/nu/xom/samples/FibonacciServlet.java create mode 100644 src/nu/xom/samples/FibonacciXML.java create mode 100644 src/nu/xom/samples/FibonacciXMLRPCClient.java create mode 100644 src/nu/xom/samples/FibonacciXOMSOAPServlet.java create mode 100644 src/nu/xom/samples/FibonacciXOMXMLRPCServlet.java create mode 100644 src/nu/xom/samples/FilterDriver.java create mode 100644 src/nu/xom/samples/FlatXMLBudget.java create mode 100644 src/nu/xom/samples/HelloNamespaces.java create mode 100644 src/nu/xom/samples/HelloWorld.java create mode 100644 src/nu/xom/samples/HelloXMLBase.java create mode 100644 src/nu/xom/samples/HierarchicalXMLBudget.java create mode 100644 src/nu/xom/samples/IDFilter.java create mode 100644 src/nu/xom/samples/IDTagger.java create mode 100644 src/nu/xom/samples/Linkset.java create mode 100644 src/nu/xom/samples/MathMLFibonacci.java create mode 100644 src/nu/xom/samples/MinimalNodeFactory.java create mode 100644 src/nu/xom/samples/NodeLister.java create mode 100644 src/nu/xom/samples/NonRecursiveSerializer.java create mode 100644 src/nu/xom/samples/NormalizingFactory.java create mode 100644 src/nu/xom/samples/PoliteSpider.java create mode 100644 src/nu/xom/samples/PrettyFibonacci.java create mode 100644 src/nu/xom/samples/PrettyPrinter.java create mode 100644 src/nu/xom/samples/ProcessingInstructionLister.java create mode 100644 src/nu/xom/samples/PropertyPrinter.java create mode 100644 src/nu/xom/samples/PseudoAttributes.java create mode 100644 src/nu/xom/samples/PureValidator.java create mode 100644 src/nu/xom/samples/RDDLFilter.java create mode 100644 src/nu/xom/samples/RDDLStripper.java create mode 100644 src/nu/xom/samples/RDDLToTable.java create mode 100644 src/nu/xom/samples/ROT13File.java create mode 100644 src/nu/xom/samples/ROT13XML.java create mode 100644 src/nu/xom/samples/RSSHeadlines.java create mode 100644 src/nu/xom/samples/ResourceToTable.java create mode 100644 src/nu/xom/samples/Restructurer.java create mode 100644 src/nu/xom/samples/SQLToXML.java create mode 100644 src/nu/xom/samples/SchemaValidator.java create mode 100644 src/nu/xom/samples/SimpleSVG.java create mode 100644 src/nu/xom/samples/SourceCodeGenerator.java create mode 100644 src/nu/xom/samples/SourceCodeSerializer.java create mode 100644 src/nu/xom/samples/StreamingCommentReader.java create mode 100644 src/nu/xom/samples/StreamingElementLister.java create mode 100644 src/nu/xom/samples/StreamingExampleExtractor.java create mode 100644 src/nu/xom/samples/StreamingNodeLister.java create mode 100644 src/nu/xom/samples/StreamingProcessingInstructionLister.java create mode 100644 src/nu/xom/samples/StreamingROT13.java create mode 100644 src/nu/xom/samples/StreamingTextExtractor.java create mode 100644 src/nu/xom/samples/StreamingTreeReporter.java create mode 100644 src/nu/xom/samples/StreamingTypeCounter.java create mode 100644 src/nu/xom/samples/StreamingXHTMLPurifier.java create mode 100644 src/nu/xom/samples/StreamingXHTMLQualifier.java create mode 100644 src/nu/xom/samples/Subfunction.java create mode 100644 src/nu/xom/samples/TextExtractor.java create mode 100644 src/nu/xom/samples/TextMerger.java create mode 100644 src/nu/xom/samples/TextSerializer.java create mode 100644 src/nu/xom/samples/Transformer.java create mode 100644 src/nu/xom/samples/TreePrinter.java create mode 100644 src/nu/xom/samples/TreeReporter.java create mode 100644 src/nu/xom/samples/TreeViewer.java create mode 100644 src/nu/xom/samples/ValidFibonacci.java create mode 100644 src/nu/xom/samples/Validator.java create mode 100644 src/nu/xom/samples/WrappingSerializer.java create mode 100644 src/nu/xom/samples/XHTMLPurifier.java create mode 100644 src/nu/xom/samples/XHTMLQualifier.java create mode 100644 src/nu/xom/samples/XHTMLValidator.java create mode 100644 src/nu/xom/samples/XIncludeDriver.java create mode 100644 src/nu/xom/samples/XLinkSpider.java create mode 100644 src/nu/xom/samples/XMLPrinter.java create mode 100644 src/nu/xom/samples/XOMChecker.java create mode 100644 src/nu/xom/samples/XPathDriver.java create mode 100644 src/nu/xom/samples/XPathHeadlines.java create mode 100644 src/nu/xom/samples/inner/InnerElement.java create mode 100644 src/nu/xom/samples/inner/InnerFactory.java create mode 100644 src/nu/xom/samples/inner/InnerTest.java create mode 100644 src/nu/xom/samples/package.html create mode 100644 src/nu/xom/tests/ANodeFactory.java create mode 100644 src/nu/xom/tests/AttributeTest.java create mode 100644 src/nu/xom/tests/AttributeTypeTest.java create mode 100644 src/nu/xom/tests/AttributesTest.java create mode 100644 src/nu/xom/tests/BaseURITest.java create mode 100644 src/nu/xom/tests/BuilderTest.java create mode 100644 src/nu/xom/tests/CDATASectionTest.java create mode 100644 src/nu/xom/tests/CanonicalizationExceptionTest.java create mode 100644 src/nu/xom/tests/Canonicalizer11Test.java create mode 100644 src/nu/xom/tests/CanonicalizerTest.java create mode 100644 src/nu/xom/tests/CommentTest.java create mode 100644 src/nu/xom/tests/DOMConverterTest.java create mode 100644 src/nu/xom/tests/DocTypeTest.java create mode 100644 src/nu/xom/tests/DocumentTest.java create mode 100644 src/nu/xom/tests/EBCDICTest.java create mode 100644 src/nu/xom/tests/ElementTest.java create mode 100644 src/nu/xom/tests/EncodingTest.java create mode 100644 src/nu/xom/tests/FastTests.java create mode 100644 src/nu/xom/tests/IDTest.java create mode 100644 src/nu/xom/tests/InfoTest.java create mode 100644 src/nu/xom/tests/LeafNodeTest.java create mode 100644 src/nu/xom/tests/LocatorFilter.java create mode 100644 src/nu/xom/tests/MegaTest.java create mode 100644 src/nu/xom/tests/NamespaceNodeTest.java create mode 100644 src/nu/xom/tests/NamespacesTest.java create mode 100644 src/nu/xom/tests/NodeFactoryTest.java create mode 100644 src/nu/xom/tests/NodesTest.java create mode 100644 src/nu/xom/tests/ParentNodeTest.java create mode 100644 src/nu/xom/tests/ParsingExceptionTest.java create mode 100644 src/nu/xom/tests/ProcessingInstructionTest.java create mode 100644 src/nu/xom/tests/RoundTripTest.java create mode 100644 src/nu/xom/tests/SAXConverterTest.java create mode 100644 src/nu/xom/tests/SerializerTest.java create mode 100644 src/nu/xom/tests/SubclassTest.java create mode 100644 src/nu/xom/tests/TextTest.java create mode 100644 src/nu/xom/tests/ValidityExceptionTest.java create mode 100644 src/nu/xom/tests/VerifierTest.java create mode 100644 src/nu/xom/tests/XIncludeExceptionTest.java create mode 100644 src/nu/xom/tests/XIncludeTest.java create mode 100644 src/nu/xom/tests/XMLExceptionTest.java create mode 100644 src/nu/xom/tests/XMLWriter.java create mode 100644 src/nu/xom/tests/XOMTestCase.java create mode 100644 src/nu/xom/tests/XOMTestCaseTest.java create mode 100644 src/nu/xom/tests/XOMTests.java create mode 100644 src/nu/xom/tests/XPathExceptionTest.java create mode 100644 src/nu/xom/tests/XPathTest.java create mode 100644 src/nu/xom/tests/XSLExceptionTest.java create mode 100644 src/nu/xom/tests/XSLTransformTest.java create mode 100644 src/nu/xom/tests/package.html create mode 100644 src/nu/xom/tools/XHTMLJavaDoc.java create mode 100644 src/nu/xom/tools/package.html create mode 100644 src/nu/xom/xinclude/BadEncodingAttributeException.java create mode 100644 src/nu/xom/xinclude/BadHTTPHeaderException.java create mode 100644 src/nu/xom/xinclude/BadHrefAttributeException.java create mode 100644 src/nu/xom/xinclude/BadParseAttributeException.java create mode 100644 src/nu/xom/xinclude/EncodingHeuristics.java create mode 100644 src/nu/xom/xinclude/InclusionLoopException.java create mode 100644 src/nu/xom/xinclude/MisplacedFallbackException.java create mode 100644 src/nu/xom/xinclude/NoIncludeLocationException.java create mode 100644 src/nu/xom/xinclude/XIncludeException.java create mode 100644 src/nu/xom/xinclude/XIncluder.java create mode 100644 src/nu/xom/xinclude/XPointer.java create mode 100644 src/nu/xom/xinclude/XPointerException.java create mode 100644 src/nu/xom/xinclude/XPointerResourceException.java create mode 100644 src/nu/xom/xinclude/XPointerSyntaxException.java create mode 100644 src/nu/xom/xinclude/package.html create mode 100644 src/nu/xom/xslt/XOMInputSource.java create mode 100644 src/nu/xom/xslt/XOMReader.java create mode 100644 src/nu/xom/xslt/XOMResult.java create mode 100644 src/nu/xom/xslt/XOMSource.java create mode 100644 src/nu/xom/xslt/XSLException.java create mode 100644 src/nu/xom/xslt/XSLTHandler.java create mode 100644 src/nu/xom/xslt/XSLTransform.java create mode 100644 src/nu/xom/xslt/package.html diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..dfe0770 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +# Auto detect text files and perform LF normalization +* text=auto diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..f868b42 --- /dev/null +++ b/LICENSE @@ -0,0 +1,504 @@ +GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random + Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/build.xml b/build.xml new file mode 100644 index 0000000..43329e8 --- /dev/null +++ b/build.xml @@ -0,0 +1,73 @@ + + + + + + + + + + + Builds, tests, and runs the project xom. + + + diff --git a/build/built-jar.properties b/build/built-jar.properties new file mode 100644 index 0000000..783a960 --- /dev/null +++ b/build/built-jar.properties @@ -0,0 +1,4 @@ +#Fri, 08 Nov 2019 12:08:57 -0300 + + +C\:\\Users\\sgarces\\Documents\\NetBeansProjects\\xom= diff --git a/build/classes/nu/xom/ASCIIWriter.class b/build/classes/nu/xom/ASCIIWriter.class new file mode 100644 index 0000000000000000000000000000000000000000..fd98b9ebc14cb2b59d552554ff9e7801eb01e5cd GIT binary patch literal 526 zcmZWlO-sW-6r62RQ)6q>TC3uv7oi_wE`o=OC_$kZy|h&DwoRA1+9oAUr8obK;Khq4 z!Gk}*A0@s`f)riYWoO>JdBgkq{`drNh#eCess^?UY%{DJOJ7DO410C=o}9LmH`U<5=AkDpq{?Y6WjIjYdFaT}p#KpWN-5bBiztbW8Zxv%bux6R6|_bA zb%<5G(QNI%gT2K9gI+ThHc8Y%j&LoJVU;TC7+6D|Ow_M{^;lhmmD*ksR)g}p*3&Z# o{RO)AN?0Q`$W!$l#lF=<_v@QS31zBpkRpqWc}mMcg+K<{9 literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/Attribute$Type.class b/build/classes/nu/xom/Attribute$Type.class new file mode 100644 index 0000000000000000000000000000000000000000..5aaaa59c0a2651a62207250a5f8a67d351488263 GIT binary patch literal 2272 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 pI~(VFuU^P!jQ<=`j*m;B{{d#QrQ84j literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/Builder$1.class b/build/classes/nu/xom/Builder$1.class new file mode 100644 index 0000000000000000000000000000000000000000..2428d1114a54c0e126c3b0da449776054fc315ac GIT binary patch literal 183 zcmX^0Z`VEs1_l!bZgvKCb_PyH2Cm@z(xT*4x6GVWMg|_I(#)Kc)FQpC#Ii(227cGP zwJrT7n^0_k6L}>l_NT&krSKX6iwc%LA@F9Vf|5^uY@mR`+;E zgk9bgZ-tacf$E5sFT(JR_|)^1XfZfDzAx04PEk+9UBo@BN=uh=&7c7;b~8uM1JL% zf94J*VLP~ky9|XLDMg#hLh4JQtHhDle_SY%?qbD4#es{eg&IR;q)rO@-)rP`Jy&wS z?Y=rZ7R`uZaoEyW;7jheq##pPhJT%Nn;}0^o;tGIBHE<^;B8VlTPoMj3N|7|uSiY| zH8IH1EYiGiN%KLzo*@P;ZkFFOO#NF51sZP)3==f%CJgN$MVBaYX=ggS{td_Hwv7Nf%Ai(2~b04F-I%IJW&`i#XjwtBDZmh zv6;*%%&*9tZ2Y9uSlr1J?J(!zm&S|g!N+43Ogt`dtD|0rS8&Yd%0;M~9fGbJ>!f@?8sRkuOULX706L4Myt MjvnO-#mGhn@Az?4CJiW|KF+p1%Yzg?SS_$eFN^>!z;> z1A9%3VxKa3W%esGX@cW`G6xMDGH}?y5eD5)Ox+%6 z?{K95oNz0Sq#g~mr`9x|&^qc(lE|tI&&wbXRT8-|GhSRRSPFd_Dm9{q&XkseP}U0c zP*UWrKDMaC1$uuJo%fu2&22tHhS|(othJFsp)GdhrtjTTuQi%Xm#9?Y`L6quICjv_ z%K~ws1vLk<1~fGmiu93plsuI)v(KPq*%Krlk*8sUiqQ`K7M#DsM)DPsiuPlGc3`lD zG8LmF|BCP)I{(mykc<)N2aK&Tk3)n<)xcKECsjHAF}k>^nQWD2*oGm>Gi+}`+(%b* zY9jj>iPsp;z5u_LdkURal9oZMEB6fDBvKtA*HqOKk=RkX4gLuR|BMv>g7y3>`uR5u z^6wbqKak@;+n7wWm`ub>h9e|4MwE6CB`xaiVe*tJE%!>xarV(PO&s-_Pb-eW3(%F qI?aAf-`}_H~FHcl5cWU-m{KKkmRKlV-ZIChbbbk}ciEiXy{ literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/Builder.class b/build/classes/nu/xom/Builder.class new file mode 100644 index 0000000000000000000000000000000000000000..9f85705b8945ca8a5558a66a6c9cdd5d86a1d105 GIT binary patch literal 16371 zcmcJ02Yggj_W!x%l}sif3<(fwD1nfWgpLF(bOK4}CD@oG6EZNF2{RJ}Sy|hvD63*e zT?;E4bx}YipeUlO*!!+)@2gl=*RmFb<@desy_q+Guq0_bKAM6zsWQI zd1wz2jo`oc(9>-owHrc}_d&FdWO!lbB_L}T5lRa*-eJ1;h$(}ITev>_E zvZqY;w8@?kM6VO7_l|hjv+OxQZyiw+Kd)%=g2`S~FE4r6%j^{ey{f9e8tiXsz1LKA zr8>mlJ?wS%hRNPkn}5q-Z+qw*`*`md>|GC?t6t7!@2Tp2RehkU4^{P%$v##~|HEXT znCw%v>;aQ~X0Xo{-M&!Omx`wURMl4o``Tdt^3YZ68&!R)s_#_wy@#%5|MrlfI;NN2+%$O_2*W+9N>`N^6yq5lp2<5HJj>u64em2} zCzEHJJjdjnP2R=iT}_^A@@^*Y?%_RnPm}jDd2fT~8N83d`!e#Dhr@yB#E?H03&a@p zuJShqVom>vTCcViYjRm5Nx>Q7> zOG{TZhDu}pRi%q6E9M0Jb%E$uMn=8AHXeztW|UnKZZ2IFX)LXZ)CH#4tw6|F6^Pab zVhaM%STGW1#3o^oJukM^>C}p4{+0gHkUzY%bZ$Hv3@=q<>Vu)c{5ch~qJjG0Dn`Ec zko0lEa4>!nBT+DD0V6pP%QEUv5ex^anj32Z(Ru!w5L$c{ky?Lffj=5l?@ptPHw3YK zR$|W+nu8(xK(b)5n$BFQsNs)e0=Ae@*Mh|f)YRiCOiz66w8f)+Yj0&8ogOO6ZFCGo}8^^XcI}bu?a_pTPh0ds~{ds=fz7541^XHVqqA=>? z&ICFJ+TDt1P@Kz_LFPh{vj9zGlvdl|k3!njjYtTLAN+ml1Q0J}59Q&yz^a+e@tO4# zBF*8tSd!&HJXVX@>LRE+wnN&?u{r%8Z6R~CNRvq8q0}WmPBWbX%uuKLY#HgQ33P;bicQ0AVttFM= z$YTZ`v#oaNI@%eRss(@DNE4K^*buA-4uT4UJk|Qck#G=7FL<`nqKpQ#GykAt7rfthOQ02#Tlqo8t|UC@cj;Wkqv9DSz1%i9*?AB$u873&SYS zUPR4R>36ISCQZG6++Pr89wwX7;I7BC#z1Pfb1=x`{CQI*f$P_wm!m+3c%bbeR!7@;|1V?p2f)ht^j9LH0u!KR!U4lts%^}>BHxP|RqRFvs zt&he1){+S)UG!csExp+Id7N8RLU!Y?Xp#>QVkg99J@q0 z6kd9R-uKeS^bZC7?4`faYX%?e<;A?jOaG*=pfXZjnpFGCsF&-LR7EToYEqznic(z` zWz;OCupiFtq;d}Q-o!|As4g!YiRUTDAkUu{YYxVh>yd{=hUM877`x&_7?m`{<4t9y z%Cjm_+fm+CeZU`Y#x<3O6CPV}ljR2maPEFyK9moGME|ORFn&3yr^HTe8|mf4d8wC= z;3Ew_%F9Rd6BrEyW{DpjVnd*$HX3Y)1{BN5Ntb!>C`$NS`1Mpw*gpCcxgUW{;{j0`d&mbHoNg4TH$>n%@mUr6w zs8by+LeNqFZ4)uegDnn*)fu3RU`tY{YJ0*CQDY?ubp)OsQ5$J2i8Y566D01y!M5k} zhuRx9>{#?={#8xx%<8`SPlwt|S!?32SnGe!GTb49!!X#06&@s+*B^q1P^{z#*H9a& z12J%eqyERJSRBroKU!A|qfiVbpyCvNIId*)D^U9d4}|Iwe+?^H)EKfI z3Fsdf%#v7r3Dg0E(7{l*Iw=~&w20=^9R_Pd$HVZ_tMpfdK57kMrYK`C9iqcl&(X0O z_T(^sRV2Is%C~+sw(M94Mf{)Oq9s&b(qs1W@qB`pPvmfZi`x;6rf8%o5LNP98VamZ zPDyct?SOmvWIlz_(EmQDE>Ig%P8)8+9XORf^s=E0daz4^@s#FF@fVc3ndW60152c8 z^X5=!R>Tsiq%B1hScT@i6hB;zF8A{3d|HCvV8xY{ISOY6yhksuP%PE?ShiIDr51SdN!h@tKNKXTfpJA2vjRvn>#3LBnvX9%n5m!7=mlu!ZS+P`JeKgXT z7gl7py;L=B333M35+JTE$~OP2>55^`tv<&M^>V-3SBL^zyLUf6r8&N+`}0cIn~>f$}9GA;}&ukEl&G zscHo-oJUdNoH13w!0G2mnV+_0;rP(qIq>e+mErYFcE2}(;!SB_=R4+QXTdzh#FtTS9|%{e2tg?hCz1D z;cLD8xAdmL&-L>2l!P#7-}CteUVfpfPUpY#@{9Nd2EW+LFX8JF+qXiXl)ZFtiDvm; zkXE-P;Xig&>aZr%9H{lj;+9sO6|Aj|gd(781i@)2U^P14%*tV|fy80e*kww|EVq!L zNL2T*q^ecAy)M!?Vp&NrlF-H03<(S@aV$ovw(h17pzE8!|0OGjDy2QFBr)2khhDKb z*NDVQl+UN+G8|lG@Jqe?GQJ)ui-m9`BWJ;Ws@w;V;d;0AQ`%5vX3erdE%+^~El&TT z$9jVagOh(0%U4C>bKrz84I_fAOSz5y$KuVkt!x{#uFlE{?_lD%XVygK-B>yG z+(~2vHo1s+j@MV%&P*v$W%6WWtt=u^R#R-xU&T==cs7%dwX%9?h`1n}R$|WtY{e#^ z7k1%Fh1H0?rHGD$XQ+V9;;`&CBb+6`UvP2!Auw7_>JDs%8mp3TXg`{(tnGKe_S;@S z&$h9AYNs_q_ct{K!U*?@QjrBDgx^`=#h+Md$(P;ZMU^D z@<8!P^Cl%n9F<6QyDV&#OPpmpG01ZaY-h76q)$)^w$iBkftt21F+ z5lF5)*Ul`0OdVZB<%w7r9dMuHolMv)D@lV~I1FVWIhZDqIt+xEv{g4tk&NI?UmEsYPdK9j(BrmW<8@G?O~+c zFpf20bzFsf?McEQm0s-VZn+!`uvB@)>G8<4z^VjuX7SqSb~Xs;iSraa&JXG*>cY(>h&2(MUW3OM}zlZCWG-qAS6iZKuU7WGge) zF&b!$tPD(w#ODTgVqN^Dyt_&gX;iSI@JrJ#&DxyzXucyqtHK=0svA|j~9u>Bnd@m4z= zcI?%Zm#4I~)sEwS)da^KKIm5sFE6(gF?K(}&Wq#J5dP$gI#;S1Q$%j7{Zo4t!v|xQ z1%zu_pp>TvSv}_)4``^gP%?R&oz$y|HtU)+M3MoplyWgj?NGba&I^e+Y%vq$tBrp#Dbj3xSWCBh z6L0MgefPCNUfKJ+v_gP#g$#8pHNh^wF+PmhX!ttaw+))QeE zo}G!QECTAc@HPVYaAZj@6g!D+2gK=Z)E+#E&cVEE=^a2`8;jnh_wc4>c;A^}B1sE3 zy|DW(;@#O!5=T$70n=S&P1D&%b*3Rorw`~uOr$3K$eFMb6XG*EITH?UXTlatc&9aC zo{f6UglcL<2TrESfI3ruhp8o|9;cny<35smHc>A;dJNk|T18Q}U8EOnt7;)xbs=Xh zN8aB>X^Ga#-Q=nErDHGNN3o)Za;X~`*0UE@$fr^of_GujZWLu;jenx|YwAS*qOSA} zu)m}3^b_@>AE-C_@{xlWM2D$_{zHgVX((gXc}9TZoiLNSOXPW}AALff;yEbUHWVj2 zK%ZHJ9A}Y_=yR;sh4jN{WD5=YVxmD`($Az#2aIhX*NYxpH+v7q*MR&=p)W%|NST5P zeH|R2a`0o6wUKfM@1?8<3903(;wMROA+xx$<;W+4Tglf-ovOOuNW+SK*{zhbk+KVS zQ|D^jc$WvME2kDJE%fCkrtM|}E!3^h*WGE{O+6HPPYb=!*DHadmC0gXZ_Ifk^(yw| zSwK#)ua5=kh`OLYNb(Tn8}wav=NY(Q z*3YV~ioOTBjtTw^K2t*Vqr>?pV=5)lMqv>IJ^v^PWl_>uEJZ?%q(CKDQBC%fGnp&$ z0wOPpj+Ib$A%6bxyOasjlM~u$ZyyDM;Gz^3?V#eq;vH0i6-u{bS&19=CV8#|!b)s6Nwx=k_Y8U2Udm*Tf&U(- zk!(LLV$aj*>;4Rj*QWHlOq+aRiG?gARgC zm_fe~sxJ;v0qTR)7pef6PlqNE8@Bu2>PdVyg)4?~|Izk~@ zig(co8!1oqRStfV(s$Dsuu>W9#Mo+BiE$Mz)JJtbNtsZRRv(yTH=S6mT6fY(6>5^A z2kB%^4s?nQ`NpGlf`B%am8!=#(SBF3vkhDOnniL4O;Tvvaq=h(^?BgC^Jz0oV?yW6 zp=|KqzbT#lKt0)ypz1*?V874^b_hD|FrC7V&>ZN6Gq|7zE-A_lTFbNOJf2SH^G&EeKFsKRMe8mu>Yat~W3^Ov6~ zz2hNDH<+wZ)~W9VRpTBsru3HbNltYxAg2(TWpc%Cno?MvjrAZPEk9IYuij7-i;|Ir?psnJcqOTWR6K7JYJ#c{e=R zTX5z)l(pc7btu=M+=_xewgY7^%CjhNX4wZQU!weolY@OZ94_7!r5{QO${3VMC^JzO zqXbZzP}ZPagt7tUW|S6``%oT1c?#uKly^}+LjkAoBe*RON)8J4CW=r-p^QhVKv{@V zgVKny3grTn%TYF>+<~$cC!v(1%tJX7 zWf@8{%DE_)pu3qIw7 zkGSA{F4*FNH@n~l7rY4DIn&yXSmU-fp>>Jf8UX0G!NmY;Y;dNFJIMvdxL}D3_H)6m zE|}qhJPGn2UGPg6{J;fYbHQg_aIXvQaKXD>@KzVR#s$~8;93`qxnP3}p6-HkTyUxj zj&s4GAXKeIC_VsSoeg#e7_h-Cfb}-0C5N$tXkBWxvaj9Nk6rK$7kt45_qpIM7re&> zH@V>TF1X$W&vU_*F1XwUm$=}37o6^bC%fPX7aZh*yt} zND=tTIccrb1fROhlk4%VXr*Xbx)|ln@fw?G2y{ih(iOSsIo{#BDAr2xGH`WnMs5Zy zh=yBlR?A%IML7Oa3-u_&)#%OS8Jn%r(o> zU@*_lP1{zMX#tst-t`W8M32%+{RVxr4kfB@(eG7Hm|bK(#9Ec%;CI5D{u#D?3w7i7 zP#(XR^7&RO<=bd9-%jQHe)dw{xLo#{sW&7KjrKB0e%JloL|qs;5YJr@=g3J zzM21v-^0J*+xT~U7yq8`=0EU#{71f@|IAnV5N1D&nUC;~5fpwZIR9D*{;kmX zL80@*!W5cFL!BHCUIY!0AK(P`d*NQ@Mo)|3FiDI!{l!|M_5OKX2CTX%S zY!PeZ-Qpa1pI9q*h;!v5;yk%mTp*tk7s_YFMeRTU#jptSuJzXf@(qEg-gOjbgjjB<|N%i3hYbqE)*YAxb%ZL8R)-6x*V9uoVtN5oUw6XI#@De_G8v5-Bg1WOFXoKsTaESFCaEN=tLr>GUuwL-hIc4fCtT$#B)J?yM<*`0+ zck}dXSzqixqkR2J^&=M2^&1dS^#`6|g_Q?r`4K80Q-$6v-(Uj}VH#{8LxhBwJCFT> zKcD*l`!6Ho2qE)$>;@QOAnc_8J2^tbD6Jg~q6r2YlzEWS5DXtdoObLvFb9$etgxNH zpa@ihV@t;dP-{{GsNb|3K*?;%lsVKzc0O(ZHTc&CP{qzZjt-!H^J@YqIh4}mFv@@n z-bs$2u5#3I11Pnj|Ca!&#M$3zPJoxSo6b=I)Y_^-71#aN4Q;LLyPSy~%}UutR)TBs z7k#DRJhh9gWEf|#A#5;aZX4io^&2r@;(pjZ^rm6V_T6-@TKBw4ta^SGstd{_>I=&> z)W0j!QC~F5DBe#V70BI4y6@sDBs?y$Vmt(U^OOOGbyfDZdSR~VTW3AB{gjSIO}*q8 zH&M@A)A0p-mnug9M)@*00`?fpjG_AeRIK&D%GK@snvDf}zOAxon|0KC=wA9gjsPP~ zRY;PPJRJl&1N?I)Wy>YhP5LQM*3bZ1M?>XO8X-e8MmEwE8Kx>3p#`#uYUK(#Uq)%2 zjM1GkPWQ@{v{jyE5xE}eu{7GuhBNh1Bolw-Hquf9*>oct1xP|Ju3@7AX_QBovl9U6 zG=MHaKKN6pfhp9$%2*#Ln5`IpfQ>~BVka+cI~#`)b{6Uc(vQ$kTg;Sjo~T4L@gMif z4^au!N3Z-NDmi84^DOZ@37IPVpwDI`(SlVGL9%%&H}oL3M0@GPN>=40l&;`}o4it8 z@P-z8zD(|>tE<(W!GCY1YgKifs^HP?q#Me#T8K6_K|ARWDVVn& z5A*ipb^P(@4z&xFgY<2-5^S9l8~sxf@zGrQsKuBYa`i{?U80LZ@wKRa&UezCWfBO! zyTDaU_%k73JVr?U4$PH%;Y#ATt#r?g)HPRofbK2Rb9D^fsv5Vc zCANVVIv{hBk1tp1Sq@68XAQVSJ!5o>^;}Q4Slq;{MUh+xmE1#l#c`Xf;*HN*&o`%UAXtdl&hmjoe0?>L$ z0KS<>T9@o?R=xO!B0;^xESFntlhL+2+16sUO+njUNZbK76&ZE)mGA)8l^CDNrs3<0 z4vFo_rn4E?3#1|;d2Akxd>SM#gH5$^lAJoQ$*cm-fM5fVWroOL=Ff3s^9fco-_D@3 zEaWH+e9(c}n^lV}V^+&Bz&sNqwbxfhzrPCoAWv~TD>14(h z;zXE`1Yq#K7V_V|XQPWSCa?e#r|I>Yz)-20L8+IK(A@%_d3#4g%DqtN#x_IeYUzNgTt za@jh~!A(45J*yQLxH_N4qjd^oa4L1AY19>R*&E+V2GR_u(+ZkIl^DAaqi53sJgWgM zhC8{9UramrrSu5Doc8l8=via{6>XceYqIG?8j z5?G^%oq=A7mHObDp0Wdvfb*7EV-@#wB<&ZP16RsF(O@hLL&U_b;R Vg$c85QfGX*e;$kX!1D$2{2yOAAhl6v*5>_C3e(YtT@KoiK z{36YcblPUs?b}X!n!6=k$(jB;6^Pk`S-3zTm3}V}Y7Fg8OCY%jIhR(q22I(sScE_# zm0pxaP&Lu2E1RF+$evfEiX8#H+8y?r)+?JkacNdap0yfS#tNGafmOewvq1(PW0NEw zAD0Pd;0c}@xW%sZ`BL|}9;?r=U4cj`dE|kP3_Qbg14aI5X=Z3N#Ce2(*0$Wb>fl7=AW{WZ5nSLR)&!lG54+LZE6}dqjb@78{@nh0>!-vXF(aiOGgSt=igZ zYdzGnz$4_9)5z>8NKokI31ra`A9;bPKTLqzr*`J@9BBp@YB6- zzXPxXpJ-^saSd(^RdE^i;)FOHS23dDBu=R~t>FyLdT~z0^BOc5Vi^^e5fxDtrr3_D z822Kk!qQMLMBFgEm{2jPp+TG*5LYoJzP5Nw2&JSKFKB2K#!}*&*5DwcVH)RE%xIX! z1r;xf7wVkBMHMdz-Iu+1MS(kNIEDgWc*2-A+T(^j-ag=@EPH&P0?&TSwww+Hs{>1) zn}?{}orszWYQmOn_GP9<&D5YV8t08KoQN3lAtPmpv%GXWF)OX0Hf(3wXA)EG-HE9w z({^a6K2Uxq(7lnofP#vEI9HBltawzxmgT%D2cY*UGo6V$3e<7a88YG-`n4+1yfhL@ zqze*N=ZbrmIiuj=1>~N1+#EOJ2br%!GZ8cCSP7fHR}VNwWU|*t$|!}k?pV&z+CWe9 zf_5J>MQJseNLxZvfzm_e1>x>Q#-^(bl(+6kB00+>(%B`cs&o=3dcz9VE{UTnGd5$*#C9Da zv?*A**fRz2;t+9l9-(2T(PltK$;#X^#gVGY5gX>G4TzD)eS;2J|5uQL5cH?oL z*5sx6i?)*|>g7<$CSvZAm^~7+I*<7kD!xWVIczs!4?@SG64>ejY&A{pB%Di(6$#tU zFUJTf1#A({le;{UgVFN>5=O-y?B$J!^QiQxW*+X5IaG#&Ep#e)n@^>|9)*f9s@ZGB zU~6RJR8FvcIj`zO8%p4!SB$d=m-p)QGF~a@C}h7wdgUq0yvBPVfl4Gx9Pt#=E#onUQs7Ap%+ zHKk9!+AQiml}f&nmzJO`W<`!8U8tqa40&*#%$mhoTtE{pVjEsUGcM8l%S_c3bm1z? zeT|8|j$z!uX%4ZoxQPfSlSuUr#@fULKZQ=lexR@$F6ZL`VJ-Yh0a&zR!Ls90Wr^=n|ujY+az&IT=agJQ_xPYv-_z%q3J7 z>xYBB4Y^Mi4;5j3>?`1B7UUQHhxjYjI;itI6ds-K!J7E7IW~sVmYp(URrA zH6+Q#+Q2Qe&7+YmXGonHmfyHP*>G6R{OhL6Z3gQ$;I3g3?$Z#3Ts^u#ad#toyG*etnH1X(Bod$z67OuR-b~shMFu*Ib@G$_ZX{$ zH|8qjkS9cXtf4?2fmbo|>QH8!-lSUnV2apZvlcC#hh{r-*}_bU%xD$sC{Yxnz@TpV E8-~n(K>z>% literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/DocType.class b/build/classes/nu/xom/DocType.class new file mode 100644 index 0000000000000000000000000000000000000000..46007dc9631661fd352b3787bbea5067ddcef9bc GIT binary patch literal 6411 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 JO|ih6{t2|#BX$4) literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/Document.class b/build/classes/nu/xom/Document.class new file mode 100644 index 0000000000000000000000000000000000000000..7612fafe5bbea8601855a3a7a5932b9a7bc70b7e GIT binary patch literal 5291 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 literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/DocumentFragment.class b/build/classes/nu/xom/DocumentFragment.class new file mode 100644 index 0000000000000000000000000000000000000000..5f64191fea2eef6e36c1c1ec9b129eae569c38c5 GIT binary patch literal 1001 zcmb7CT~E|N6g|`JZr2I|s|(^6E20!bY)ptRYw(+dq<|6Gpl`F?Ng31aH2qNgPrmp> zqKUryql|aDbzPSw(I(S7ckaDs&N=OmpI^QK*ubid8g5!xuH%*s11lD8Te!nednhBB zZ8B7xwGM;%O!WlAj3*{~}drR#0Ri5}_OKQWK<6ZYO*(q44Gpzn0eh{U39IGS~y}ei@ zT#J2i?2CAy+B)3ZX?62J4>lPV&TyJsEh{g1Le6W_XrW49T|ZW_>R>oWJC`D(SiO4! z_L(*YYOUhqn22!gKRC1}AOP2KqvWtoQL8Y_Ph(Cz9+@@#v6g`iGhQ>e(6l$SptrgRtq%(DNQOAW5XPBYw0;TKM5ZhQtGU3Jc5A=Vy zbM1__GcNoA{wT-$pfD_O@jlME@7{Ai-k-m}egk-cW(He2GI+EM726sf>vZnuc!Ij5 z>l&UiC`YXh18Xy6oiG>)9*7nNL8HsBZg8ZNbIX{ zXt&-v@7m5u>$ihGa&on$Yuz?CgWl?V?`7-QGjUR(i z5=CJwynZWi!@eJ!&1xW->9h9n9NYL$Qzxw+@Zij*Nl>H%S#Kz%s{%=X*RBnZ3#8Yada>c|6_+d4 zp5^${^v&3g?av7ew8<4_sra?a3{0omd(nB>Dg)>Z6*|v zK>vd8d-hh{w^n`2Gkw<+&{rMD@)j#*t!6Rg@-edEjuh4=cg@;*&_z67cp-42a^XVEjP+m)wf-T6(l`t&)sJy#Ac%k>$SOW7AvN+Q+&Gh z+A90hc0u53hcHd-NX6laX@wALP~li(qK&*2Akba1cO28Nd!)SINfqwYeY;X@iZ;{a zY4tG2_Oy~)_sZ5ITa7`FC~m0Qbqr!i#{jbGYeYvsG7=AUEMiH=1zgmzj7I{wpwWP9 z-7VG2yAjBtJRIS=o2${2Gn=DSkShYCEs>r(wR*McdcL*&w5sMzVGkh``6W^vJ#d|4 z=AmpMhO$$@R)I}17K#X2^sJ>6~rhWvA)_gZZ|J zm3X+F9nKs%-nl-oG{pF$+CVz3^yfuRa);xf_as-dK?KlejD1DSI6&Npe?{UmT``>E zTcd@XLbtqy(_HIJWH`>?JgrdEi=b8+seD=E3le-{-_i9RGVuY4_($%?1H7b)k$2m$ zvJqB}Fn3vwPg`7j_KB5IcHT*ehu5{$Mo;t)9ub2C9#iqBPy@U6<3WSR<3 z{Y1~^0dz$cDiwCN($-ljgiJ56ozW=2NUQR@*v^Sh$C9VolDnd0a9V{Zd5KhU<|K`= y1Blj;Lh3L(7e++gsCgUgQ&A={hHC-B^?>&Vih&)+Iob?8z5EY9OPl8!>C`_{=r&OR literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/Element.class b/build/classes/nu/xom/Element.class new file mode 100644 index 0000000000000000000000000000000000000000..7e957d4672d05445c099a26285a3154caed5f592 GIT binary patch literal 21916 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 U7I!{x{3h-IO0Od{`W?yt12Cjhg#Z8m literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/Elements.class b/build/classes/nu/xom/Elements.class new file mode 100644 index 0000000000000000000000000000000000000000..b8bd31178c1e526387b9e228279ceacaf8d9df68 GIT binary patch literal 1238 zcmZvcZBNrs6vzL!D=RByFp&8$6~(7*D07N}ZjR{Ggk-@NAO_!cTb-A5ZPIlH{Y<{l zM1#}B_}UL;Jg2L)R9@^poO{mip7TGq`~BzpPXMdf&|#pYp{zr}at2elIfYwT;i018 zHqTWZcW{?is~YZUxUXSNVA8U!zUBA>f!Wr+`Od5ieQ8%(a^TklGTXA}nEueS1eOxV z^_DZN9Ju|;voWF8pn!B;I?`_lq>80o0k!FNDRibK9cydYZ(H7u*|y0t*K#|iy=!`s z_n}es-%83OmjDlls}AG`3M1+!jXld3m@jUY5}xEZA)T&uARyS!>~`5N>BX68VclGI zq7D*8`j%(X^%b6e9xWZqjdvW%xHLE;4ID=DBz($_IRlV7E#ksH}D7z z16OdBdUf1|-$igkEPHBSWcrsr^@tim%4Va4I2VBhZd4QyD!}&tt)APL|CU`Pq^iqh3-fX;E!6QbMz6JxN9{m*7gyvcLY82!!K{KgHmQ%E9LZuau zD<49Xe?Zw&50Ux`^>g69Kw}mu8mr{ELXax*v|~60;WWE$m_N0<=! zNIL~gN(U$P7(qWG6mUKyJRxs|yt7OwjF49F3)-gyWh5L>_A*8^j)(>>V~I$Dp9=Z1Xlub9m}cTPWJb^hk_qm`%A5{y R;~rci%y@MjH-fT?e*v-&`*i>S literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/GenericWriter.class b/build/classes/nu/xom/GenericWriter.class new file mode 100644 index 0000000000000000000000000000000000000000..cbbc2047dd1935f0a9ca2d1683a08ba72647aa3d GIT binary patch literal 1488 zcmZ`(Pjk~o6#uQQSh6kRL`_nP3xpCNHcA}K-_{V?8YgjZ5?a|w+Y7T%79#3MMv`f6 zz4S|j6Eh6MC6{)xuO~Z9bZ)o^j!xw3cqmsc*+>*6$Nv|6+jJ+S4L5r%Opwe+v#?eF=j#(kMe_Z38C{P5(9Thdgqqc90 zPUv`Upkj#>T_JXYT43`|I~(=Xw(ysmGHB_}H3o%H5qSREhZxQb;RE2vWd z9jmxYu+>`Cxz&3*8d!^^`%+rR21D9fsV_C}uhuOc_wWrtW)aVjty>#&<%M!NGW-Y= zD!$cWp{b*VO&yb%Qn97uI}{iuh5^Pf-rsJD{V)cRVKjng&^LnfeY_ZE-s-g6pxfzq zekgXLAs|c!fW5WshEBT_b(7kv*4CFBb!%nNha;ynKiSZ0bvnYYayftJ`$Ke8r!cI2 zs^jj6{f!3ub>}bGCz(N&oH7K=EtWOBZdxg z`5wj&q?U3>HgO#%r~*)?8kUg5GQqe)ncSuN8d1(uvHwBN3n=0uMVFbTplCTwC&?r! Hfw}(xA#z!m literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/ISOArabicWriter.class b/build/classes/nu/xom/ISOArabicWriter.class new file mode 100644 index 0000000000000000000000000000000000000000..24f34eac110bac0addfc2482500d101dd26855f6 GIT binary patch literal 1387 zcmb`{{Yw*Z9Ki9e;-l+pSheL_|bH zL_|bHNJT_hR78b@L_hX7M1=oF5WR0x3W@K+8hQDOKw9kjLZiB2s(x02jo zENO-HR>N6q@q%^KIP>R;N3xWZ{C=rQ3g=Gr>M1+3d?^Xe1^)fxc%g@~Oz%>k7D~s4K&kU0s&NwfxES5YbaklbhNqTgFEX_>ozRNxrQnO`+ zGWkHV+*4&w7^>chWapuW?J88G1{<&mwb+X7s6zu9u^Y|UgI2Vm0|9iQ8_t})=)(X8 zaRkROgb*Sa#wf-Rg^4&U*f@n0&SC=Ra2^+M5tncoS8x^Aa2+>r6Sr_1cW@W?Fp2wk zh(~ygCz!%>yud5G#vAvP=MV4sv(9h#KfHg={db=(dnzKU-~#KHQanSM^LY<{0e1#> z4Ba}}Ue6s;Lq{L`8|b)^j+^MXnU1w|+(O5#blgVA?R4Bh$2vOJ)3Je$JL%X+$6a*X zO~)oWHq+5h$31FDTIkqH$GvoHqhmWAJLtHNjsZG$(y@z<`{~$C#{+cip<|Gay>vWC z$38mt({X@~hv+y+$HR0yLdT1fh1rrt=L`~9Sv7E8UCgqjgseUPO3D5uqDNvSV#Mtzlu^wU+6d0OV5E;TEK eQpB^gjBm7C0y*DQmHI!gQpJBADP~@mEdK$MX$7nR literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/ISOCyrillicWriter.class b/build/classes/nu/xom/ISOCyrillicWriter.class new file mode 100644 index 0000000000000000000000000000000000000000..b6745bf40dd36c973d4e4895d59d1e99135a53cd GIT binary patch literal 1672 zcmcK4e{76*9Ki9<_j?alRkTHw%2JHgub$Ci+EwQ$<&?Q9^ZV)bQ4iOZoJ7Lf)S*}7$Z1Q8LDMMOkIL_|bHL>3Vd5fMc8e(t(tLH~5^t9_pD^X>C|@9F!V zi?2_e5s?Dvohly5@=7nSWGm@dWJheLSjoV=z;d&}^x0A0oS5xcvBKnj*o@Tpf=?W+XEZ4VmFeGiJN{ZG(oh%&u3`ClHDI8l$zo zvfvE=s+b)P+o86rDDgz&j*|2@FRvuUiiD!oT-ZH%VsZapjFK)9%c`!QR39?y5|fNP z|KNE_R0w_!2OTrCV!Bz^wwBajG#(3CrMA1;thZK@?_MEI4C$aGyS?Qq$!I%NVKq9* zw;;Kqxuxz)gA)k%m8~*H-Er@bp~FsyI-Ho`-rX8$aV0 z?7?2NU>^?PR~!N-RIT_Ef8hv@;W$p>6i$Pr)LERv-#Cv8xQI)*j4QZ`Yq*XZxQSc1 zjXSuDd$^B(@Bk0-2#@guPw@=T@dB^#I(g5vdp`_xL<&-ohOS6QCVC(X+3175_yD=+ zj}I{jc^HCW$j1nbL;*%)EXJV-#VEl9Ohl=t_1#}aJLB)3{k}%qUq$jr&hH{LDTy8r z@_PXOKtH~@cQP=!GNYAk4|TdFEoFN+?dDUMkHSV!*vE{Iq_9yGR-jts6ABwmVPhz4 zEQNhaVdE&Qkiv>6>@x~0rm*o8RzhK)Q`iIw^HbPF3Y$b>r4%-q!lqDI8HG)yuxS() zps?u_R!(6vC~PK$eL-QfC@e@}73z-6rm#v1n?qr9DeOxMn@3^uDQp3SEu^qT6tvM6jRh1F2lG77UPY&nIkps+B7)lyi5!lD#bM`2%4Sd7BzDa@g; zIE6J(*h&g(q_9;Kwwl7erm!Xo`-Z~SP}o`uTSsB*DQp9UeM@2A=@R*#!ZuRaCS59< z`K14#r%JO9$d9^Qw&_ssLuSdErD5O$YF0#Q4Xl{gPtp(?jG8VQ7|vgVf% zN!ek)O-%CyLUv)SuELHKTNOd|%3B}w^;W~G|wWmy;**{l8Obyw#zagi=XVu4xlICU4FC*xaY+d$7 zEnjVcRX@IvG;cT-@!7cnXQ>%CE@Oc+{1hZ1naI59LW$|){l#`uG_iQhfl-pqW<>vn z;#4bNy-t71efK|b5j-C+NYOqmUU;vH<|0++a}s5^RacxX1-D^}260+baeKUa8qyQP zCf}`dO=QMqr?;L)vTJnqS?5pu8E|#THMO&3u`HEkk}WxsC;75U3ZzKZh*#E0scevq zvRSr@B|fpGN&-?VbrP0s(jYO}E<0qG?3Py9BYUM&_Q`%ZAP41;9F`+;RF27UIVsP{ z3-Y48B(KP;^18esZ_3+pUfz*+<)VBbAIisaMLw0!%8>im-8`^0gg zowzdD{=ZjFHpogfU<+KgR+CNgR+CNgR+CNgR+CNgR+CNgR+CNgR+CN zgR+CNgR+CNgR+CNgR+CNgR+CNgR+CNi_dw*$-}wF#6GdkiF>}W@9(-qz1~S@79dlo z7u-2!I3wxk(pwE)Mp*3!_5Ls5a=4j`hqwX{b0r?32amE43%MGPaV;KaF`i%vp5%Hw z#WFn2O~~RFEMhqpvjR)#$5K|}8CGK%Yw#?C$Yu!3S&tk>kjp6Y*oYNuLOyq5C7ZE| zE%2}n1#CwlJ5aiG?}@hT$x9u2&PD1SnXzo3!7Vmp6B6Mx4J{()Vl9L=TzyNw?$rV_2D8f~TqdrT1R zCWO7F9vvovP7_6!X~aI$gl@AF`%N=?ObZT}HuRcy95fy1GhH}jy3ucXaM<)>!1UpW z>BpcMz)>@ZAv1(yW*Ecf1df{#oHXY!VlLoJ{JS+X@g17@ZkaJm#|%C13_aN>LaEo8 W#s6=+j?T#k%g`4ecW7>d%)bDZxMJu4 literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/ISOHebrewWriter.class b/build/classes/nu/xom/ISOHebrewWriter.class new file mode 100644 index 0000000000000000000000000000000000000000..9b6ea4f5173354c8045e2fddb3b3257e3c0247e4 GIT binary patch literal 1253 zcmajdSx*yD7zW_?qs1a_fCw0FBnAZ+>VmsLBS-)XF0G)5$g~|{piJv92*wyAxbM5T zA?_QlF-nLr#(3qGcm4iC>X3jbDoiFpAZ^MJ102E?+G91WoA=8B{ z!MN3iY1l=A+&oXSwpVi-VRwyX*t)eU_8ij8Mz`0t46|`nezm|^91iM&DIUYrtJ+)r zy5-aSA&#VZ!T~K*tyzY89vO7nO-7p_%VV~?JHxH+a_`16-EZj~BYOxO;dWb)GJ11C zl5PgVL2j!`M_c>16~QD^*Mn{A+X7l7y2zPVoL?)D02OoEYiog)N-Z+dNwPQGZUyvG zL-m?Is*eIyea9V#M=IbhI!s6DIGv=^be7JOT6>W$(^cxB8+40$sgG2x2lR-ZklLi5UearNOYi7C z4LJJa|2gB1{~e9B4t|@&@AQ+k_R|46NQbDKj?ghWL8s^poudnMiLTH!x=uIgHr=7S zbe|s5V|q%@=>@%t-|_!Ov(@{vNk#jEa_AS$WRRKspBt;-RNu5|NK@GlQyR1d6tiui|L7kLhr<9>yDzHmDXpkysvI&0K zf`IrClxpa*4ZEcljZ%jusfQsAXcj+OB#4mgMyoWzlxBn_gov2fBN13)p-pVqvKJlF zgHE}DeR2!k(#tcb&bT_YJhOk!IPX%C#uGi2b0sJmKZGtB;~K*LF=lf($6D#Em4Io# E0aeoang9R* literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/ISOThaiWriter.class b/build/classes/nu/xom/ISOThaiWriter.class new file mode 100644 index 0000000000000000000000000000000000000000..f241ebaf857576e2c8fb0a3bb9957c6df59da01f GIT binary patch literal 1535 zcmciB|8Emz0LSsq^R&=0N7?4A69{PtLYd1-$U=+|Kn9aR8DCll4#eHsW4+N`XLrT@ zpn#4qA|e7J5+wu;G3pE zQ?J!4qC`xu#?)#wdyVIN!CIr&Q<-7skdyYx>D?7Ca4V~)_e+jHl+Fef&mUU7yw51w zRW7(j^D>_A_Etv*-Ac|GEODecQ_ef3KBwY^`!fflLD3sCN@V*vb_CJk>~TiD|ZF`DbUZeeWGSl$_}4T`3^miHT(Ji0iY4V?VJ zZO-V-I_k6KY9;S>d$pB5v5JoH?hT4+mQl;ImTA;9b2#UY2h&SeQkKUUZWdfDo+Y1p zI2s<0wY|}PN9Ja2K#bq@wSmhRlu{l0$?0L5!usp-ehWShb;%G!OT9801 z=3^liA&JFUf~82I4ejW_3amsYR^d&s7PAIxu^t<+5#88~Ey$o3JFp8m^kEPBu@Cz( zfI$>+0L94U|Nm2a)Y9KP=XKaCX#&S_5~pw)XK)thZ~+%_377FbuHY)J;U`?j&-ewu z;dk6X*vu{5#vS~Dzh3-XM<=;HLRB)p!nLPqju&3*C}TZkyiFM!D5HxqHd4kW%IKzy zcPL{sW%N+S7RuO485zphMj5>(sqK`pgEDqf#xBapQbvw4-ldE_%GgaAdnn^Q%IK$z zy_B(!GTx_*{gm+mWeiY;Lm7jVk*ACTWw?}afHHNrGk+}-C>9XHNg-!Jls0aq0|gh(}%M5AGsoN;Scaf z8E*?AMsd+QId|^;&YAS{>-_`3If`*akoU{BhJuD2hO{GWlgpm49n(_X<`o0GWk@zG z<+xnAJuW>F?HUhy^I-VO5W6sy>DCx>hpk8c%qx;BqtbS5QyJBx9z&#I>4G8EGL^XV z#(iOTcwbT{)3OF+#L15Iycuyvro&KdDX%iI#+9a&!r-#5>(`TkcycLIl|dT|$Kgf* z`D4mZS`y_)xX#G7UVP6WUv$7dhe)}l6AbaT<=F#qWBQr%OY)rf304tD!Y^5d!vEPB zcITJ(Ldv0KkA<%2EsMutA7T0(0AUbCj82VI2rKka7$m5!Ql~~bL}#p2o`SsvGfArP zpnFQYXILY>Ih#TXX;L(gb!3R)v$AtmlRU$8r#v|vCP$8pKesQs|oodPkH*7P~5if#DlPWp7x4ek3Wvkfg1Z}JC`_dJDr6oe)Xc<<87H1tt-fK=* zCKzoQMZ!(xe%KRAzl8b{^QsjFkBNJlH0OvoVx{7*41#$X#GxZ=UXp&}&t&^amNA$p zBxRPc`Ja6VE5o#*ZLy9K{waX4j2!ZO8;mj-<-_Sv;5x>gW5yZ2^QGM$=sD$#b2ZcM z0nZ*{g7JKxg-J{?Vm_uZ!y1X!>_Dr|o+JEbly>)e$b1(&;i1!1Y^_iG6f*{5Jc>CK L*^1aOiM#Lt9RHC` literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/IllegalDataException.class b/build/classes/nu/xom/IllegalDataException.class new file mode 100644 index 0000000000000000000000000000000000000000..b329491ae6899aac54e91a9e5c48c6d4665dbdce GIT binary patch literal 820 zcmaJUyPDqc%f)&8lwU0o5|3Q%bMw|*$?1nL4qOS zf%xo?GMw424Z&(QxjXmHx#ylUGrxXcT?2T5r&YLEwqa!gWjyh)>S0Y_CQ)%Hbyvkn z7@1G)mVkICFtr<*B$X!ZN}Vb9t+N!I{j6;Xl>0K30`<-}c`SV`P2lgRacF{Vfy(R9 zgz1hzWBu>#jV_yaqrMWD?u16Y&jyDoK9Gl+m3k-YQG}!v=0&lb9)*d(a>r!;X*BTL zTB|_n76t5`_SE;3f;rV2s3egAS(;x=YZS1KPZ@>zR(c>a-cy zB}2C5NYCfeA8;!W$&a`9YVPq+Wk{Ww()61Qr9J(Y-*e64($IRoYf7WBKVV3-oEyQA z(@iP5fqgC9KEJl8Q_!6u8F8{BJ!>ZXk?Aq)=rYj8j;(bpOBmehbJr05`FJSqe2Udz zP;B9O+=w`Td>LvB(tOL;AGyv$Xd6XL4td}askCr{LG3w#I}{gY7`w7K(oq;90~P78 z6dAVveF1TP-gmhz=I=ular!9$Lm5e==u}9>utXo_L7HlYI!)3sI#ad!1neoA$x>CL z?g{OlVwv>XbP74-Nzpu3P#{LgD$ZCP@{H4+QmY?K5c>^l(Na5t?M}z$Fl7d#Rji^! KR*Ve8xa(gcZ;3(x literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/Info.class b/build/classes/nu/xom/Info.class new file mode 100644 index 0000000000000000000000000000000000000000..14de38c1faaceb62642a79a08a61ca06a559ea2c GIT binary patch literal 2644 zcmai0O>i4k68^?cJYyk}pg;nIpBKUsC$Z(9B!E#!kc|}!B-<;=Nxazwo}?#flF^Kq z8Ce$pT&UXPo;Y&kNWsD0+7#TlY}KAPa^%Q?!=AR5g8klTuq~3*x~yqU_j}!6f8DR2 z{`t#4z5#FnA181YuUc5LkW1hymQCbMyk_C`1jbNE;2?_nv0`G?!rD0gh?0qN0*Caj zik9mZu4#Eafg9Mc@Fxp5_18aJ_=|-%EWByqEep~@)k4ie-GZ{vu+X&7vS3@dW#P63 z$3okLYr-=ycFA__@Unr?i8I#>99Z<~%D~}*?W&b-yQ=((tUA=B3SLb**Q9Uj_gHx# zY}tW<2Mca@ddq81=iP>vp_8_>T?5A^ZWeCIO*!pIw>e!7ecNqjbda$i^p$KIcw}I= zJ?`vuLoT734o39$M|X`2-9|(Cs$NpEu6%7*Tf3Vh=Sy2P)d_9S)zPKjB&ehJ2yVdK zRDQs9I-$F;F_UsAYqyKC6D4ROW1_=)7)X@8u3u9*TXQk4^GxgQNu0x^fkU(Dx%36$ z`NEN*^65M#B#xF2w?Y$dC*gz0jzxi)^l)AV9{bI(kBvdj1`e)Qa#Jtrc%goDlh{O$ zS?(pV%YlI-5uWPoE+P|KN%ZlKf#Z7{1{GQ8+74MYkgTwng8w&Giz114@g5OP3MpLG z6E}(lv0A;QYN05yL!5LH@1tkpgCsu0M+VL;dY!&+H(Q~Yo0*xL;$vPcIgagxp%SHT z5cb8g^gX9ea#~^7d1-pO*XyO(HED9Cy^zN%Iy4bTQFCMv2*eqVIJ@O}Y$Kx7puzwadf_ zLAv$nC{EpOXl(SgnIp$6Zx z9VceI(REm%%S=mL&sUaL*DE5svLUW#OQq~eWg`<6#G_Shs(~lF-EnMPDe3#tWdnE( z0~d=+rNw3L$S&jy`O1cF(OkZ=vQ#dM+-gZ=#agyh$uF)KvL&&$URql%FQtWnOqp3} z%Kk4cg0&o!wXQ;GJGw3AH&~sZ<#nC9Xvs~*D%F&|NpNg6cJBQ-H*jf~bs-(kZ4TOx zowVDe8DTes>xGk|=i4kjTa|DPJaxZ%Op3f)BeugA=S4-4D<#$(S>u*OxvM>#KR-jp zk1cpX=(&@km=$7XZgzHdYWDoh3mn+%<*b1R_KjX1{JqmXIITmf!GY)G+*`G%&so9K zbCsvPslswU2vxhJf@t1lgTYMZ!J4>xUY^;V(EA1!C-B*L?eFILJsj{dIJ&nup6~jK zuy|tY`Vunm@I)ME|1~n4SA?02u>E%L1qNT^jk9;DjLA-iQ-r7H{;BK>5zj@YV};&t z%niovuxnl_9UaJV8T($YuR-7pFMs_v!Vj+u5Cy+YQyP>9&+fqZGBT(69E+6C(F`O9 z3owIOzKvrJ=c(al<0q~=K;6%&kwR+p?>JDrgM%A)Ft#z7GIwC@V0>k28;Qc_FpH^V z;VV3N2ZuIxa5!~j8xNi1Czax38%MYCFs-RaVtH&Jw{cu+AC0w-#q#kX>xo$VL@b{i zvOX1SpN^%7<;kH{&&1kOL)vF!?H^+KTr5w=a)R%w~O_8lM zPLM@G)=rVZXUXAd{uEonMHKNo*6{*v;ziUjkK1?&0W!QKFX3anjKAVC{)Q{~6tCbj zWbru`@C6pJgIDo2J$#EC{>7hh-(eZwBaa{O8h*s<_%{mpFIJ-3UEt5TaU5sFi!A&J zzCF)~qr@@FsPj=X{LHu+A|Azm82M%9bD3{PG4d-RkC?b(5`bYMJ4(|6O(R(RA6C!G AN&o-= literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/JDK15XML1_0Parser.class b/build/classes/nu/xom/JDK15XML1_0Parser.class new file mode 100644 index 0000000000000000000000000000000000000000..aefcbe796081159bfa3334f34d4c4f948944a242 GIT binary patch literal 886 zcma)5+iuf95Ivh19Na)5fzYO0%1wkKx>&SA5QKypkN_7drK*Z}KpV3pTiI)Cy+-b5 z@j@jk@d11kVvJJ+0;D|Ho*D0)Gd^eh?faLn0M>BB#exThMGqdjE|xqvSaflrfr}na z;F62WE|wXNKT=xR#|-t}tsMquBY7_w<^!eWt2`b_vn@suRa!w3ifBg|Rp!&8V<#$O z=mt9HMH2HDgO~pL?q=Y>ySpV!CQYBgjfECtX&4>^A)U!`%@Z@`A{F68@O6z^#f%G9bvXD+T#K1_0Ie5T6TI)Cx5?un0rp+5s*`g72X z54HyziT@2k6 z6)vZ}s9^t)xnFMGTqf;S^?CaYH+w;8ACH6{^Px3LkNdw>UXS)`mXwbzt zE({L{h!KHkzim0LZn@KXwqS&ApZViX=5y;WfgKA4E9KIZ2Ch+b1@h zwE3`4Abi5KO!t()uJ+`TzM?0xx|K;xx_Q&e^mR-NDE)TYAa2sMjIly)-pEhs^I7gR zB<&Of(-|Jul}*K6G#!E6Nvn`3+PTDtbjwgIBjwt8`imIF<-Fm@WF*8hE@?4XOc~2E z1xH0zpx!aEygkM+hP$SmS!Z^ zcUrlqSYEo(jRd4y;svi8MM-5Pt(ghkU7WhHY*b^VO+cEo`cmr8>W4$ zsaMjV7-Xi9Wngt?C02nwTVnKO5uJ4P)U{E4*(0BIs3NDrQem^82y!Bot%w_Jmr_P1 z?Lt0f44G2C#?8_kl1i`&{JM?lD`rMArlAcZa#+^z5?%rE zX{6(>9nTnUJZ+~6Im2?}Qn|Rj5Ko&(E6 zrP9>C=b$RE*Xa$8;ciKY8MpiFx14&nG}%fuk9*so z-z%Ie{6EYeJFx6WJm#LtlLwt$YY2~am!R_N)l)*9z&(7!$u@yrC8qp_$UTh2l*fql zhI(S5Ut;9N$0~ClJL+GI5HmpC-F+l8dwG!vKabNN$HPhjUoM2oXl%VvH}$INy%* z_zaW8yhzj;e2rOrPs<;18TW}U*mM`)e7e?Qm>CJLehzw-0ch)JnGLSt`JbW4x)B~zVg7^B3%t$$0FwU! D|7#_W literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/JaxenNavigator$ChildIterator.class b/build/classes/nu/xom/JaxenNavigator$ChildIterator.class new file mode 100644 index 0000000000000000000000000000000000000000..e7727c6bc788082adc19982aa84809ab4d45194a GIT binary patch literal 1987 zcmZuy-%k{06g_u$e!wz_fM8d!S`>i=+$u^D(NwA-n9}pjED+QOX1?!!_q*rZbM7pE|M&YJ08YS8 zpb2Mc(TKC^@o@s5;G9~WSF20{7cgpKEP)z)YT{x731sVV37;jHlT*i+O^hedfHNj0 zOnh$Qs=z4OWycEy%{i}R^gY~Ocy+_{N$$zFE6ar=S0pz;pyxJBN3-$g2`Sk?N0T@F-x0&~&!$DL(% z6YuVsC1Wq#)Ng`RMG%*~CIdVF4R(ls$D*wfvUyKg_R(*@gGNME^k-^m9`GF46nMYsZH&;qF%wfXG0a>l zK43TOf&C>WiA7;*eyHfGiZ@Be!Y$mka0I;;?x^IhZF#PFex+0@mIG)0N=cdP z7QNBsyi-yO6L$rNs$e@Q?{t<@ZI_G`r$XD5w&!M(dSvvj?G!~=(D>G{)2Rg~2YZJwGpK>3-WjHEFf3f1#M4dRU@KE$?g` z2l?4Hfr=x}H=W|IKup=jcat)UZ=HWh>|<6NS93g%@x3?o0+NcaA(rZ0Lp;^H4s8wk zFFcB&ozGTvQI12CG-_o4yW|8~XBO&6!sMZ!@`xat54iP^_)WhhkVy{%O2U5QxHqyqY{xiDx zinp!9xZJl6^D!Fw{zA<v5L%p15;~DIi3+R$j z^vFf@GJZ@hZBc8b@<-4`SRH-nCLtxpK$UI}*GvzU-M%*jnSatlSdjX>_;Yq^VWB#-aJ!T0h7 z9?1fJkVX6`E`E{%o{ATWdzB_k;5bF?;)EQ4kA55_b|;pQ#)r`O%;Oj{bUb2wfd2%Q z^ea{ApVE>j*39$BB4x)QYBn$g!^BC-o-#2gCO!gQqa*a^NUZi>=ye+`7H5u$A%=NR za0?`p<#zKz) literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/JaxenNavigator$NamedChildIterator.class b/build/classes/nu/xom/JaxenNavigator$NamedChildIterator.class new file mode 100644 index 0000000000000000000000000000000000000000..6d91fa0f1e849c38626401eef0f4fe4c7aee2662 GIT binary patch literal 1954 zcma)6TXR!Y6#mvpnvA%Y!q?-fQo@*4k@*-?z`7|NQbR zfB`5AT^Nj^6VJ+U#=>(L5^Gq7=c5>r`K-mm3vzbO!YIz0c+rB5w1qKb#2Oc`3nngF zXv3h1OBUK?zHDOB#1%rM;1-o%CbXyhN@^v)l)9idGWmIRiqPQs^J;}aql74*4(BW4 z9QM@;Ppz9VBRRFiSvVk;%V*tO#$8f`c>0>V?51+AzmS?J7d?N09YT|rI9KGf=X>Qr zLOQXTqwe0FasOv0$?b%Pxo$#B+VfSWvNWfPm)*IXu0`s);ubww2b7e zxLKvUZki~&**C`Ag1%p~FlFK;hW0AsoY9zu64HmMs)#bifh8B|OoXP*S^|>HMYmK7 zo*{H3l682E&s|elp(avPrAm%ztLLTubSb~gNLds4N-?X>dIDSf_I8g7p*9Yn-^L#7 zwXqxBCZ=pmW5&iqvhy%{2&}wE&|~9qaeNuCn0VF3YdAq@4pbH>#>VTIWe9Azn6r_^ ztc`uxPw3Iosg%83DwCh6WEX>gk(I0}l)b!9IJ~8w z5Er`n>tqP`JjaD`y=Kbvn#tEQlh1Pl{}13%jy}lN?;IyP1I}%bW30n)de;$hdN&YW zM}yP1j!1X-9&9IWt|PjJnA6P%QFf2r1|Ll+$oVwa|KR+nY$B5G>{5U?vA^Hpsea_u}fs^zl&e1By z=qqICYh0vn_{09KM!@0n97g{*^J5^zT@UDP_v08pvnMnbQvp8Gx5@YYjWL4{p2Ufu z%OReL51Y8o)<0l|*ANa}uhkZjL;7CdXICBTGBRL|XC!_fp5jr2kmXVY`aP2G9vbhU zZTdFicV(FtwtH{XlE(#K`T;HUBRc3O?56AbqONL&;Gzx|=hHaJ`K5^vsD;f_HJbX_ pSsIAmK|9~nahGjFtEpQ1jk?;SK}rLswbwH&1Hj_=PD~&H>t7MKqVoU% literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/JaxenNavigator.class b/build/classes/nu/xom/JaxenNavigator.class new file mode 100644 index 0000000000000000000000000000000000000000..a813576f37c33a531505a759cc87f31a3388eb07 GIT binary patch literal 10157 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 literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/Latin10Writer.class b/build/classes/nu/xom/Latin10Writer.class new file mode 100644 index 0000000000000000000000000000000000000000..2bedd4ccec3b4d76d87d6c01f790b7f0a309f684 GIT binary patch literal 3069 zcmdtke|*e!9KiASXLsLiu9z^)W@b~_i;buCj#Y|V70yU1@Y zU6Lh?r_s2DfjbM&C0l)&HRdB)-PC%MsCCsRMDW{IePT;aY9*2EC!429J&qW+2)Rz8 zxQMY;U!tqeZXhw{Wo>Ef^FZ3PgjgwEDw-vbu!>W)Bz~TGcgOZF$Z%o5A(4A3$X}`QHmv4hUHiZ7Geot4c1~kHee$*qYPV6j;+{+ z?bv~xsK74l#vbg&KJ3Q<9K<0UMkS8m7>?rvPT@4p;4IGJJTBlOF5wEQjMoWaV8a@Y zNZ1gCXv826jbKMSnj#TNNJcX>M+>B&6vVszLj zJGk1quTt01&^3i_*L8(!j|Z=3jtQ%IsYQl>`iAj3N;+c`#-5D4rqY`+fw3=RB4dA| zd;{YE#w5l;!7G}tOhRmOpwuX@@n$F9T$WVwMwE6$ygjMCnbf9{+FM9%2TO_EN@_cj z+S^ENCsNy))OI1Yx0Bkgr1lQ(-;LDXNou>3+H_LegVgpUwY@B*(wo%wA+>!;Z9h`m zpVT@??Eq3ckkk$$wS!6R5K=po)Mk*{Oj3Insm&s_!$|FLQhPV49YJdEA+;k(?Y*RS z6sf(B)Q%>#PEwmqYF(r@ht#@B?HEg?j3u?>NUev|jwiJfNNp~uok(hRQtKtPd8GD! zQtKnN`J~oQY70nhA)l}ZNNo|ReUQ`^liG(!?IcqBFsYqPY9ArBC8YLIQagpzK1OP% zlGqjoYc-BwNH@RnWXkfQag*(K1FJ0liH_A?Hp4345^( zklHPz_FYn2PHNvHwOdK;`=oXosr`V|ZYQ-LlG+`l_9Ifelhl4pYAZj@+9Ra)D5*V0YJVWL$4Tvvr1k`97YN!>f#@b`b xUj60DZhlz`e!0@7Nvy>2RjMJM){}Cy-errf{-?Lgl3w$dMe+ype~hQ1@jo=SfYty2 literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/Latin1Writer.class b/build/classes/nu/xom/Latin1Writer.class new file mode 100644 index 0000000000000000000000000000000000000000..eb5d11e43be21cebb0736b91f3e830e91fccc76e GIT binary patch literal 530 zcmZutO-sW-6r4>`Q`^RV*{XQyMQHtK5PGYKP!xpdrKN(mZMxLeHYsT${Xv2kFP;Ps z{s4cJ`Zftt#D!gU=FOWoyw9)q4*(tP$fH0e>StjiRu@r9Z5_gDkhNRJ_Y+Lx n8HWBsgidmhscP)R#`Q$_Yiyy63e~Hm$Uw`V(SEQ=Ktt^Z&WUN& literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/Latin2Writer.class b/build/classes/nu/xom/Latin2Writer.class new file mode 100644 index 0000000000000000000000000000000000000000..26ec1a9b2abfd2190df32164b5c02995b5ec758c GIT binary patch literal 3219 zcmdtkdsI_(0KoCzkL@Rjh=7Xt$U`18Gy@|uQz8>JH8c}KGcyNFox+AN6yI-rzn_^I zJ~ARABtjx0B0?f0A|f(1GBYzXHB(cgzL&AX^jPQgZ{vKndw=)0d+#pWcPp#UD-c5T z7VTYyPIxXct4Dlvyy>U=)onlImqHU--F(Anj z8xU%+n#_TX2arN%NwiAgar=>_a50*rEYTcO+i$~pIi80UEzL$_bi&|-C_`MsB)wlq z_i<9lD1@*%%xZ|5Ho_3scp9!@mc;ld;}DZ}qMnW?;jiu7P3VP^qV*k4lIe}3;l?DZ z?ZkzzaN-(jHz)91!G&aL7^B*_bC3Ia9uo3k!vtsUT^lBLW756Cg>_%sG|`+b+Ra(C z5N_;5TWTrX8)ml>9=6@O9$}pWsoIX7B4wXYy7Z1|OjW(P+arXWFN7``E3gV{unrrr z30trYJFpYGup24Zi~Tr&RIp=RIx>)nEM(h`r5*21bUDn)MIQ3O`%G7eA{3(pr6@x= zDo}|kRHFv9IErI9fjXSTDV)X`oW(hu#|2!%Wn95ET*pn+YyT5)&Ganjo52OHaDzKM z;Dt8uh7UTR6FS2eUC)fLNOAfFdE^A zz*vmKcuc@VL?RlKFa=W)12f_f4=ZLM39~U5^RW<%u@uQzfmK+8b=ZJS*n(}?ft}ce z-AKV+?8gD5A`R)tKqj(~jU4165BVrSA&O9p5|pA0<)}aWmt}tSdFz{z57z{N2qr{>fN7u z2T|`wsdq5-evEn#px%#D=z$bEghCIZ(1R)T5DNVSg&s4G zDfCMedKZO$nL_WT(63PF6bikELhq%}`zZ8&3jHdDKA>MHUZc>d6#8`vokpSGpwQ_Q z`b`R*L80HG(3uqaAcfAN&~H=dYzqAjh0dYShbVL|g?^Vp=TYeQD0DvWruQjy0fqj6 zLKjl#4=HpJh5m>_7gOkuDRhaxP#mVvr4;%T3SCB_Kc&#+6#58-uAtDLQRqqv{W*oM zqR?MZ=xPf6C55h`&|gvLS_=I&g+5B5zoF2_DD-g(eS$)NOQGv1^mi2cB!&K-LZ710 zKTzn?6#7RBeTG8+M4``8=$|R{ISTy?g+5QAf2GhDDD*`NeThQLZ<0U#V05q+SI{rGlliN|i1uO*U8Q z(p6>17AjM^sVv!2WlMLJBU`Cl>7nwZr^=UJszA0@g|dw*l5JJ7^j0OZohp?+s!X<5 z<+6jSkR4T}?4+vXU8-7kRyFeOhTnPKw%={`-+8K5cnU9m)wSkRepOs-enXcm|DU%T Q!t@U>jF-=sE*GZl-wq$1m;e9( literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/Latin3Writer.class b/build/classes/nu/xom/Latin3Writer.class new file mode 100644 index 0000000000000000000000000000000000000000..b34d3d4082e2fadf047ec9e1c936916ea8872202 GIT binary patch literal 2954 zcmdtkYg80v9DwovFgt>T3aF%F9*)Nd&A^n*)XYRpZ6y;zGc%WEl~vrOWic`%A~G`~ zGb1A+ykw?iYNkeJL}W&0MrLSaWMpJ!W=5t)J;UrC7LOk~eGJZT=bf2%F6`xDtDDPr ziAbz;4-{3x{N-|g>841#;dY1JJwlNyqs((G^DQxUXUr6r-EDIXGuG2Aj@dB@ZkOFL zdsyEjMNDzd6q_QQ%yx$@K675O&6Q|LPGd``*=e<;C0Sf{eciLs3oTsWV&Z-qAkm9 z+_*%DAJ3uw;|Govo=8UH*{W~%?{{_o?V@aMnc&Z3V9UfvCS4@~tVbBrq&<7|AAh+_ zf;fpj>L49kW_OZcYPUh)vjnt=NVgD929l{!%MYi7M>I9#ms5 z_F+G2Z~zBUi^HhH5!Bsi5sSeXf}t3W5g3V47>%(QhY2tv9+NN` ziAcg!Ov7}{z)U0~1+$QfIY@&8>2Se~`N+ZoEJQYPkc&Jl!eZlj)Za4~Gc93%J_@iD z%di}USb-uGqXeZ`g*8}$H*NLYJ04ApK*3$?cW@0oTc?#-g{`f^gVmq zJ#E*#^Yo>@$Gh$w`!3ra`QO)kuW_lfc<<#>`;0pb-@uD~1$e#vcppi3mWNn|8^8+@8IfnMH9FU|CFBE5{Km$%W& zN%Zn|dO4Y1CeX`7dU*%EOrn=l=;c&;c_+P`rq;_{^m4k|Aa~Qs8ET{4Lnmj_Nei7! zrju4WnL;ORbaED*oJ}WF>7<=b&f#M)mrkb9$$50rK_{JbGM!G|ODA1)GJ{UK>0~CI zoKGk3qmx;5@_stGfKEO@Cl}Jm2kB%soqUK+=FrK9>0~aQe1uNs(aA^Y2|AfiC!eH~1$6Q$I=PfiK20Z=(aC4%I$1;~ zU!ar4bn-EwD-j%=WlWpwg2 zI=PWfzD^G}(ZkL3a0@+rgC1_Bhi}ruZS-(EJ={SL-=c@*^zdzZxRV~fLl1Y+!*}Un z1wDL^9#+!B_vv93J^X+k?xu$y(!)LU@FRLyZK{xu>ET{__z6ASM-M-xhx_T_XY{a! z9)3;_575Ie=;1+n_$588rH6;;;bD6C6+Nt@hhNjfBlPeadRR{nzomyq>EUT!(Zv)X?pk@Jv>7Xf2W5{^zaXQ zc$OaiNe|D_!}D5ixxoLFzqCkc)}rKZt)C)Ve?`%v6;&Ijn6wFsrkRz1mTx{``lnHg m_kQzX6e6K~&*{u3uuf{*pI5V$|KCq{h3}=p{}lfEuKxhj|7&Cb literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/Latin4Writer.class b/build/classes/nu/xom/Latin4Writer.class new file mode 100644 index 0000000000000000000000000000000000000000..6d9344cfca05dd848a0ca8b209c5ad3172ec0850 GIT binary patch literal 3206 zcmdtke^gUd0KoBk*)AX=0xJH@Lmo3U50DX=5|OB>p_vexnK{P9DQ-B1;_u)1+sw?& z%utDlh>%E$h=>S@$jHo0&B)Bm%+QQXjru;u4oG%Re>cu&d+)yce(*NFtg4}?LV)0mW*aVA@gAuf>--zclmkQi&QnYHz%LA`yNIa!KMQI?dTRO`&3 zD1+T>328ck6gq2)T?+5kXO_a!;+1Lq{d15fXKcR zrI1EMvN_srFwPilNNTzak7#R(&1f2K)-Kei?^pDERAGT8+Yq>NB{jo?rogl# zLA0fI!mDw1d*SUktVb#9oJiGl_YvuPgwo}7&t|F$V>dF8i7aqnT@G@QhkO)Z9|}=~V(?kgm7)ygs6ZvEP>qAA!66*R5gf%a zoWM!c;uKEf49?;l&f@~=a0!=Dk1M!}>uAvKJGd`;7WA%ghX*|21#kGGBmB?>-OvL) z;g4SEjXnrKKlDc+f-wLg2*p4QLKwmkfgu=*;TVBYh{70*#W=(u785WLlQ0=m5Qlh7 z#WYMuA}mOP4R*|Syx-b;-kLs@?enkzi?9UCumWjVjkQ>hjo5_E*n+LthV9saok+)S zWFQk+$VLuwk%xQ~U>^!mgkqGS6lEw!1u9X6Y8*rj4&gA4;3$sa1WuwBr*Il)a2Drq z9v4uDOSp`BT)|abM+5gqzy*4^!W|y)gcrQwi;nO^7j#1p^n^cpp*Q*<0R7M(fe6L` zgy?g$A6?V4;`<(Q|X4@ zK(`OzI3E-vOh|wkGcXfY%tA6!Fb8un9}BSHrP_~B z?Qv8)nrg>T?MJD0EY%*bs}&Qd_G46gBGrDJYEPotPf+d2RQpM)J%wr;sCFFHHd5_) zs%@g$Q>k_Wf0WaxwwY>Ar`j{9b|TfDNwqCh+e)>QsP-(XZKK-BRNGFqQ>gZAsy#>V zFH))YT&g{fYR{+I3#j%&s=bJ6FQ(c{sPoPLVfMzx=(+S{r23sidt z)qas`@1)u6^^l{#lr=hvun4t0K=I_Fa7 zH>h(Sb>2&z^QrTj)VY8TTGzek-*sq_2P zxr{o0K%L8}^M};Af;u12mx@a2{1J7oqRt;v=W6Qw33Wb5oj;|{HPrbt>U@Yge@>kb zQ|B+J^AYO&C3QYZoxh^a$Efph>U@Gae@&fFQs-}|b1ilLmO7uJ&fihz)71HU>U@Se z|Dc38OPznD&gZD}Pt^H5b^e(;U!cyvQ0F@8e33d|qRzik=gZXjH|ktZoqwm!SE%zJ z)cGoPzDAv|Q|CXaa|3n$O9e~nETsyOE-F;&)Ih0MgQQYn(p7~^Hx(hHOroS1# LJ^yRDFP;7dr)ioa literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/Latin5Writer.class b/build/classes/nu/xom/Latin5Writer.class new file mode 100644 index 0000000000000000000000000000000000000000..407d5d14caa70039d0d47bdbae5a306872010870 GIT binary patch literal 2136 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 NJo(S^*Ne9y@E@01a!vpM literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/Latin7Writer.class b/build/classes/nu/xom/Latin7Writer.class new file mode 100644 index 0000000000000000000000000000000000000000..4aac191627c5035ffbc371811f097e5fab0a7c66 GIT binary patch literal 3081 zcmdtki&s;10KoCzkL|}}BJxmt9uLQ3ijSBgAt4cglG-#ALNhbRm^#Ic#vn2yA|o?1 zGcz+Yd_-nsYNkeJWM)QYW@ct)WTvKOW@e^FeeW(iz>fU`ZRfMy$M62`W#{h8>Kbac z2_X_hN0Ts!=pfNPNOX`QFva0?cv7XfF4i*DT5L5tT;?%uhsW+7q_5{&os-R39=F3e zc~Gw$DU2DeJi8QaEDooAWYP3oyF1&Oo6nX=i_2!s&#}54+Ir(gqi2evP>OaIXOX$Y zHQj8ndK}JyjVF-8;41P+5$=CxDT3`zn=6lVYV&%ptNC?E(b{RZ=M@eqv{?(hgN(5m zy~ar)ZOCAEmd9$FHo{uacp0WFSCQLh&va-P8qxeB;?9K|Mt<>oNK;5yBqr5hPlP zP$toqT8l96=r$r;pVpMYIww-)og+l$N}&wZo!2l_rSq~}2w5tGp#rlp2XiqG^RWO6 zu?UN?1WU0Dl~{pQsKRP6v7s7ku?`!s5u30DHP{O7B||N?V+VF(7wWJZd$1S#upb9- z5QlIWM^KNWIELdmiBmX(vp9$IxPXhegv+>!2JLf409Y^vA{ZuwA`Ib(L|a6o13IA# zx*`VM&>cMxi{9vqIP^n25|D@_BqIf>NJBaXBNIb092ShkD2zrnaxfO-Fdh>y5xL02 zBuv3n!|7)!7e%TS3GScNLA#u`*( zE!JTJHewUDpaxs94Yk;g9oUIosKajT!CvgcejLC-9KvB7K|PM*7>?s4PT>sB;vCN7 z0xsebF5@a1xc>zLU_>B-VL~Xv5ROQ+MKn5~6S|-)V$coU(F3vQjlPIOKg1&eiAX{+ zQjm%?W3|Ei)ZevN-pcU)t9sxT5i+U^yhyaub)>x zs_6=R>rF@dW^Osgf4~2_?~44lH=R}6qtEF-v;VQak_|F$JDnXxXYZi1qv>oGoz14RchcD$L%kS7XUEdnyXfpVI(s*r z9ZzTPp|ca{?7eh$BAvC;*<3nnqqBK*)=p<9@%5TaXQ$9v2c4ZtXQ$EGd^$Uw&N}I= zi_R9%+570Mo6Z)}Sr45pqO-+x_I^5BLT4YKvoq-IgLHN#oqdSTmeSdW>1-LDeT2@I z)7eMq>?}I_7@e&!risVt>})#w1f88jXP>0AbLs3;baoz{eVWeBr?bz{*#&g=SvtFr z&OS$H7tz`0>Fi=U`vRR^LT6v3vrFmhOLTS_oqd_kR?^w!ban-uT}fwG(b-q%Y?ZN0 zyh>+R)7jVP>>4`zI-RYivv1JZwRHAPI=ha}uBWpb=@6y?=+)eM%*==<8eL7o9XFs5`+v)6wban@w{fN%)G}elb>Fh2#`w5+`qqCpV z+1+&ZGdjD6&VEj3_tM!f=FhZ= z`x~7-PiKFpvlr;>A9VI2o&A%}UZS&q(b>y%_6nW7N@xG3vki3i9~Cd@tduHI2B;)y zP|4D$QlwI;GEk+-AeAnI)nFN-GNnlkm95lp8LBKYOnGD*RV>3*iHuM)WTcuYqg1JE ytIA}1?=P)r{g;#PmzF9Q5h9Y`RZ)CFR}2;MfBopG|8@My{2S!MX!kFv1A6)Z literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/Latin8Writer.class b/build/classes/nu/xom/Latin8Writer.class new file mode 100644 index 0000000000000000000000000000000000000000..69960ab96fb6b512a4d67099d4b56f6a90165117 GIT binary patch literal 3538 zcmc)NeSD2|AHebN@4EK=jhUI}hcWZmJY*h*c^Y{Njf9XSXWPMH&NOFQl9nV%k|aqI zk|Z}$BuSDaNs=T zl``Q{HeAXXu`L6^Kq%Qrjrf$IzEQq}KxRUptU$=0)gpgA!xtQ!&@+@32o7#htGAJ` zcA4pZBgIkzL4UXG;i>+tUcS@}wv76%kUYE|pNYS9*pFXm~$TZ)Gyg^~{?P~Qi zV$#r#-91CTv|(L+BQ8uMqGx7yR+_(Kz?*3COD9p!J9(spi5e++g-J5}!q#5?oKXJ6 zB~Bdfq4#jWvBHgHsWV%>?cufS)LkWJdES6>6n3;n1$JxgSnW81z3nh zSd1lDie>rt)>*;wO1y^EScA1#hxOQijo5_E*n+LthV9sao!Eul*n_>;hy6H!gE)l4 zID(@%hT}MalQ@ObID>OI&-?@q!rUccc~9^4;&Q=K{=SQQ3f8?>_ivtm9r3@`u6(Ay z9&zayZ_lMODOmQ#6fFPUt2Z|9Z6IRi08H`--iEcY$;0q$cZ zREGEd;W&qV4#znh=hOk`brR43jnEiP(F`rn3axP++MxrkM`zrCuDB65p(k!eAKZdl zaT{)j4{7jYFaj8c;mE`t7>R7$iMwz&?!mpd5BJ0SH$8}l@Gu_1qj(ID;|V;8r|>kM z!LxV{^YJ`hz>9bZFXKO0j#uz1R^fHLfj99M-o`t47w_SHe1H$}5kAHz_!OVvb9{j> z@fE(tH~1Fc;d}gmAMq1@#xM94zu|YB#UJ>S->HEMMK}th2#TT@ilZb-qb$lJ29;15 zRZ#8nzrD-XulD!&Psd&DcyIgvbB-&G{ioZmHV5zcf@ciBc^=#suQ{=N1_}2nmisT2 z@~^{^OQ$3(*>HCyfr{0qH4SJ@Lt4{_)+ExJ#i zYm#YAYg*HW)?7zx+R~bKw5C0+=|F2bI&I{7TGNTvbfz_3Xw40@CWY2?r8V7X&5g9C zJFU5i*7Tq?J!wrZT5~h4>Fo@ZKD4GUt+|EP^rJPm(whFX<~CY0fY#hjYX;I9AFWBH zHEFaao!0ni%^)XN2Gg1$v?f4nhSHj0v?hbr45u|gT9Zj@M$no&XiXNa8A)qGv?iO@ zjB@76owO#0*4#yFM$?+RY0Vf~a}TWGr4r;J~}gw&fHID#?zSx=*$E<^B|p> zNM|0RGn44d!*pgcoq2@LOrbN6(wV7r<}o@mjm|tyXQtDcC+N%!XTLm2XJ*owr|8Tq zI`cH0nN4S&p)+&n%(HZ6E}eOf&dj4T^Xbe2I`cf8S;*V!1v<0H4U-r7zOmRXBrmxU zvc!#)m)$5?>PE{lH&K?mO=N|eB(J#5Wu@CvUUidYmG+m_IzV36fwD$Z^qD8u?Dw%3)n6-|Kogq8sD~-6%(O zll-Wg<(O`fpLDAn*KP8%ZkH3fLw?bna#DB6uew`K=^pt__sVJAC%@}{Iim;UtR9qe zdPx4z!*X7a$e()Dh#oUWj~ho%7*|i4Fg;~dPaCUeOt_vih4j2BY{f)a#}u()Cem6{ z)D|*PHo_FMktW(knc_Cul&~dCNn6U4vSmzZTh5fR6--%M(Uh~Xro4?a6>L=#W2>8r zwx+3M<4vrsZ7SQkCeGF~Rcw7z)iyNMY@(@do0u9l$<(yX%{8{AiMPq7mThBd+qR~T zZExz@j^ X)~WQb&f>zwV+{Ylky3o>I#T+-i4?f} literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/Latin9Writer.class b/build/classes/nu/xom/Latin9Writer.class new file mode 100644 index 0000000000000000000000000000000000000000..4eadab395fd034e657ae06044de3927c1266f21b GIT binary patch literal 2054 zcmciDc~n$Y90&04@BT(M0Ra_J%*fPm35>(>n0kl7RPs7h?)SaNsWW?P0HmX71QhY1 zXcCG9K}a{Z$L-G$v`)4UaSV5)y1l9WeQv+Y*EQHKc6bJ*=J|bY&!Dbt3k0@oZ;?yT z!0z_A`j!nF=S=XiY*KY);9wnVNagod(pf;tX(WxJF*KIO(Ri9b6*P&a&@`GsvuF;@ zqXo2xme5jKMpeZ5Wi_p(^|XOD5r3m>qaC!1_Ru~$KnJOoj?ghWL66bn^dvn^&(ibs zBE3wn((Cjly-n}Z`}84wOrO%{^d)^w-_rN=BmGRj(rNmG&T?&pl`K5IY!Y5VK=;<5i{JCXN`@b^kC zjpjYWe87Cbe87Cbe87Cbe87Cbe87Cbe87Cbe87Cbe87Cbe87Cbe87Cbe87Cbe87Cb ze87Cbe87Cbe87Cbd;<3?^AYAF%tx4yFdtz)!h8aCTD6|Tyd*Zbs@7^=y8lz(rK8Vh z!&;P29!wil@c24Dmzjt|mP|&rOhtE@jvg`-IWim9%Utx7`RFAJ(OVYd26+^AsYD-H zj=r)2H_9sXlQpb~%hY z;c1xn>B_~jdv$#)noKj2>Z2_xhel*@0pPkzTpIfMK8av5a|MjIQ( zm=HW*!Z6lE;6W3KaV8oMnHY>Wad_A?!UPkKi6#LRrWqbF%`wTez+{tzDW)Z+npT*` zHJomeF~hXOOw$3gObTY3j+kT8FxRAGp6P`7rZX0pE?8)~VUfweVv~s_CJRf|hDr^= zG7ZCWjX;$~VueOyrN&^D#$mNK!WxaoT1~(@ZHD#Q9M#$a8#D?r*)MyqCYIhvc9Mo!09M;}A zqIMkBzBs1+a9ne7Li2G_3;29k@9_BGJNeu@TniD)dxrl-*f$ju!^2KXx%_wAY1IDf M$yrq+R%OumPhHWW=l}o! literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/MalformedURIException.class b/build/classes/nu/xom/MalformedURIException.class new file mode 100644 index 0000000000000000000000000000000000000000..9d59f549a626bfb63a5e4ee66d1f28a6530cc912 GIT binary patch literal 615 zcmaKpOHaZ;6ot?5s^Oss;sc1$oq~}Kt}rYFO)%AkK#lH(GM1Xsnm&}3|H*|RapBU1 zKgxJp8e){V=$)K9_nz}j`uX+t4xo;55;1K0WlKU)LWv>k2wUfdB5X&u%zmfMz|I*m zEz5LVZn_FLJQ2UW`KZ1=E7uH(W8Ku+w)Nzv283~ydk#4j7EUlEyOw9G;!O9ciwlVkeTFoWNcknt zQ2vjfp%h+s3`1z#Xmgj(Ux!^p>5l*uhBy**N~9uKqK{~hqMD{olXQg6M5Q_fdkJPT zRFgrsPP<1~CcQqJ!V0paXdbJ`k%#Y<54}2+8Kpa^QazX=^4si$mRfM-)-;poth1Z;?od3Xv#+n)|pJHY1Z+*`3uK{3%ZX4}O5; zo5bEt2;%E7JycJ1RWQo6Q=*|>NeXy69Gey;o zyZbbIfN8>uLl$N*ONjWG!#rt3TDg%{gFKV;W)(}jeI&k%RcYur7TXxo#$wh;j7G75 MJXtX^jN&eR0_!7*zW@LL literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/Namespace.class b/build/classes/nu/xom/Namespace.class new file mode 100644 index 0000000000000000000000000000000000000000..3b2c3351e42808ab9c382184bfadec7700c7afff GIT binary patch literal 2622 zcma)8U2_v<6n-{ovzsL??GjqCMV11ZCZ&-N1zJEtY_*zx*pxy*#Lebyvh8L!B%6LH z-Z+C7Ug(WC^v)}8oKa__VPup`NB)8S1OJ42-rY?aL!F@?@4k8Woaa2}Ip@6Xv%i1- z6~I}1CgTJ$5@sdL$(YB2hz}$z%IHQ`!jgz<5|%|=mvBSIK4iH~PR32#lJKE~+kE>` z7-8Ju%f}o|6Ooso%NXG1VH6}V5i2r=kl{;FLP>(bx2qD?xQQvD%%7GBTR^B%Wh=&} z041}kt!qhBvx>>AQ#GvOq<~<(Y6$4LF`KzFmztf*E~L^^^f_%?HAl0YWzDQJ!nkNy zhI3iKx!Cg{dz@JH~cy*jlc7e=Cm@aRWzM(%GVZWcv@POum$urZJN=De%8_SwOOs=qLJz%DkAO* z2p5^N;L8z>jXuv@fJ7#K)_j{T8D@ck#AZgDapouyq9Il6n&B9>B|soPJx$}ZUFWod zu~9nh((THY03K_POhN@lL`$wQN!0DwR0n$Mw#i{7$M)((vb6zt;pMDduj*{t;2QR} z^cm+hjv#?40U)T&vT4;Q{RpZcL5F-6Gp5Okn#tX!Hg#6<@<=R|nCg0{Uel;$ZA-Om zN6j-ePk9wo$5u6!3NX%N@HEMs2|o;aqeGE{MHDW%qluGA^UyEWmYv-3|1u;dWq?4b7@Xgz#;cjDqpkBy2D7#qdx9?iAQWCg+9|w> z^gM)7dJ2fsJ^-1%sV^`_>q#2T1@h>kH{0egHyBGtWXVamNI}k$eHlm(i zzYEmwoOdI1xjx`a;sN*>+upK#y@+63H0 zLhj+`Zt#d_;0G5lxu+7bR+7h%A&YUM7I z=Uap!;0jVc7Vj0FMIiWH2gdhpECQ_*XgYn*N&1h2ds2nNIj#ZyMui3rb*A%68&H1> z=xt2-Kxc_22MWc>+ieL?N!g|UJB4}Hh7f8Y(2e0E+$T#e$Q4dQ3AmjpDS?ORR$k6L z!BDP&VI{bO!_5%VeJQ#RXMD`vj@!ZUPL*SA O&fP7I-XjVDy#FtgkT6#O literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/NamespaceConflictException.class b/build/classes/nu/xom/NamespaceConflictException.class new file mode 100644 index 0000000000000000000000000000000000000000..1b5d506f853d5c7e80ce5e985ec150dfa7348570 GIT binary patch literal 633 zcmah`$xZ@66s*TKhQ$TMJudO0VB~@)4HwWvL%4u=ZpH@N3_WHRv~o7(ogRXj{wS8Ng{@Ype!0#GO$dTcBP|))s~K{Y<+rMBhU#U zQ?<403GKCo<;(a>>h|sRS$_}`2TCiiLdb8`FU7SeSwiIxEb>Y}Ifpr)&ZVKOJrTni9Lb7T5jwz2+5Pj|MSi3=xG?GXK zB}dr!cN4Z1IEFq2r-v*-yCirkPq`0QJQ(+w8J5ea! zgYH63hHEnHmU(s$lZ@wvEKFgV5%V#FENcW>xslc}dq(-qC=_?@5&14w<)KiwjwqR;?92pTH=v^ literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/Namespaces.class b/build/classes/nu/xom/Namespaces.class new file mode 100644 index 0000000000000000000000000000000000000000..f22df9da3f85cc6b74c45b6870d53de0cd1fc027 GIT binary patch literal 1919 zcmZ`(YflqV5IwiAZR;usB8sB;LQ0k58wC->7g_X8Zfk_n!c6VmXZ-OlY{DMg)^7bl`!8sWjrQF)iH> zr8y&yM@c-Eo+t8ns^OW2*)($|r2Sk*sN;paT6T*0xeN@={^GfA9e%{JGqZAsJGqE-_(MEN4<6I+R%wh|N^jSYSuqs+e<)tyR;0ZLF5*>B?7&MtRAw zEopnsn6qK2hAWNSc6BQ^=c_>EXszK0Tpns~qunE^6K-RYO6eDrf@p6RXBlIgTh$LN zrzZqff*dY5M)5rr_bN=3Oh;u7h9!g%9b(3UFLCcssF-Xo8~*YinPbIjZAX@!$%kph zzUqxK<%-p_V7Dp16qz~eRr0Sc!V(aSO&6*SyJ$YK&Re1$15xdKpKlW3Ro2AJUA@!w6&60{?74>ou?eK1`Id3&e}% zYK2-JYt32d?gt&1hv&xtekN8Qz5$ z{(;C!Y!}gQi18km_ry17Ulh~;&m)NPyv(EwIx&oMv~(8$=W&4+IJ8S1c!|+6zJK^D zV%aNAB$s*9vQ4B~m-O;Jco$)T5u}krH%4(9*A&5k3*r;>V~{1Nsxib{gjpFD<91Y3 z8UTISUr2Nv$}8?B^cG`EF-EBv;4lPM?Nzmq9_uo(Afa2?!)SSBO`1%q6U&!MJlCWy&>N z_exs#N*eruj+I?>=CiW4CXVhQ!r4koYocopaZWe8&*JK_#2s1T7QN*t-vtUE^ca)e zo*NvtER$cB$0@m`C+UKBf8sqTff1-Z=}T~%s=IRw%BY=c;#i1`>w#*5GLGS{V!x+2 G$NvK3secLp literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/NoSuchAttributeException.class b/build/classes/nu/xom/NoSuchAttributeException.class new file mode 100644 index 0000000000000000000000000000000000000000..fe9789df4546b109475760cebada25624b0ac2a0 GIT binary patch literal 684 zcmaixO)mpc6o%i^Po_gl)pyYyCAwf&X+leoVWCyFx8tTAX=j)Z{1kt~1|hMs@}tC= zX+skgi@7)F-1k23d**(8zPth0#DandrWJ%S6US^EbA*&5Z9}x{(sm5XJUyro=#Vf{ zwoJzrrdtNL&#~SSGcn}g=4Gg>8)*-c@58%yLY<0ayeL$ z5bsFG5zT<~%bAcLkf$$Utz}!czHtyTsteCyQf8onpj0i-*5#hz2haW$YTb{aLP0{s zJhFtvf2JU$dy4ZT?Rz%Du>ViX-AHTwbM&Pim=rmF%Lu i{SZfkvmGXu*}W1_m%93Is@kJ60~5$FkJ#DIJ@o~uz@9|_ literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/NoSuchChildException.class b/build/classes/nu/xom/NoSuchChildException.class new file mode 100644 index 0000000000000000000000000000000000000000..538178a9322471968a86d6d4c3af2a1d817fef74 GIT binary patch literal 672 zcmaKpOD_Xa6vzLkPo_ibQIC3r9o6W99kDPaA;Ur`me;sxuS{oT9{3EtjSY!~4`AUV zSos(t&P*GcXe{R5oO93o{=b>~`t|$*U>)-sQkc?^#B>&=EM^D=UwTT|4e9yHbx!tb z1llJI8Ls08!U-C}4rRLZ`Cfm!KmQxS@Jqy zsSQH5Eqz}!W6GaYLb;DikKJ+0b8n;2STJpg&}UMy?*u`syP;>vT@^`B_Y+!;6m)3F z>6pbFVdfuu!dUn8?7;l_CcpN#4|&@YH@ds-(ANr*Aty7Yh0<%*P8Q|F!^g_3lIkKzn`H9azz*kBaFpk*;FzlL>B)5n))Rk literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/Node$EmptyNamespaceContext.class b/build/classes/nu/xom/Node$EmptyNamespaceContext.class new file mode 100644 index 0000000000000000000000000000000000000000..716295e0eda7ec427753a75ffd5e24cfc70b44c4 GIT binary patch literal 719 zcmZuuT}vB56g_wIvARZ$soHA&h$s?$=o%=3r1T{y2q6~yc%Q^EPT0(b*^R9KN}q&2 z^tDjvUlQ*mA>CqL?wxbzo-^m(`Sbeo7l5xQd&pqTLl$c;KD*d(u}R2(Q(DE}37N{l z4Ix_(ZzW-|p|orcdM#X>rS3C$m z5W3@E#zyH*?cjZhC+iPs*7`ewi!H8K{xejq*#$VsksUwnw92l$%Wz=Y@=V!X%-R9I zSkVIVa9wN@cG8Tgvk{8_d|b4;vdt@64^7AKibQIEGVWx5^J&d0VJ?MH=HJYMkmJwI zz_CUc%`oyg6X)FF3!G;@IGE*_w?+C5kI#is1Lm;6hzCZIqa`16b~roqWMhi?cgS2F z-Hw^IV+kct(dsXcHBQDF)&Na6Fp|klWO7)+>X7zv`0OXlb1t!b2J39s!w1GF&b$FK Cf0v2? literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/Node.class b/build/classes/nu/xom/Node.class new file mode 100644 index 0000000000000000000000000000000000000000..3af682f1dd92530d38d1c9353e814359e4fcb7c1 GIT binary patch literal 4527 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 Y@i_q_SR-Tv>vA|ua`hhFLKEow7opCqH~;_u literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/Nodes.class b/build/classes/nu/xom/Nodes.class new file mode 100644 index 0000000000000000000000000000000000000000..63ba058fe12ec3260ac7575a9fdce300689db12f GIT binary patch literal 2034 zcmah~ZF3rB6g|&E*kmCjMU!e;TdY=r*rN8u7t++GHj1UBl{8wvEo3Kdg@sv`PU;`% zZ_v>{;M?eoPA1k-$1l$KqttVs-M|Lo_<_6o+~?kN?#luG`upc!0bImg3prFwtXMEm znZYq!F|j(0s}`p4mN>7av4-nn-WGGi#JW7+5%aE?s+jjo+%!=$aZ6#!>ux)Jh56cj z`+;2^1a7-rbNfL>VW#2kcy=)G9fi|l*DEz|P~Po!%5|n!RvDdIaXmL!RY(+yTMEhR zyik~{xt>!WbefL8X*b*S?$Y*rF^H;eZZWQ>-8O<3C9k1SQXrq-Rn8tw!(C)>jgGLCnxa{Z{5K0 z?V#(kk3uolXf^V4fM<`q7;i@5QkdShy`Az#^S;vxDlv}+x14oXR%?kE z`sYPR89arjIscKUuKB+GP?VLylUPtVrLQz$eb8>-?z$e!zOmbKdV$;Z7@*PeG6Kg7 z@|{6H$Tyw5*Y(bMOb~{;m<^O>6kn-D5D_}n;Be?uuHhCkcmae zd`IPk{ZpwU*TDc379wHj!W)6_dOM*kxp=NFJ8D>Zr7Y}iF7i-TG~XguxFB*F(L^`0 z&+4SRxP@2vozH=Do-$z$p1~=e1n+c&_lR)@eI>zL{u8s0F?BDO+JpIPmwK`T;CZ03# zJRM+BXPF>Pe#oa3W$7lDqxM45u)R%^-o*^IVno)E$ifSHgf9{!Y+XFZNgWQgz&C0t z8o@8=ef^VoKjMV?#V(Q557$5yDzjZ&naa>?3X9cVxaFLm9+s%?@kL z;vCNF3d;Y9H+v-BqyNEsnX(E+)8WEI6iZ_ZX0<1|)ocQzrRF$U;4|9KDWxwcye~CR zK13MSy%a4|WFQ?FdaV>^mZYx^_0?jUtnvuz2|b0>Uy=H2Qh!V8?_w=3O;C%DBEk#& z8x4EC8toz-Np{YdSm!S>)btQF9W}%sO67~$LN Kc!O95-uxH7DQs^5 literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/NonVerifyingHandler.class b/build/classes/nu/xom/NonVerifyingHandler.class new file mode 100644 index 0000000000000000000000000000000000000000..ca8756eb0d7fdc920245a5dbefcb8a8cb7f67952 GIT binary patch literal 5823 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 U!N&a(QNVja;RF7w_(O#L13ON(@c;k- literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/ParentNode.class b/build/classes/nu/xom/ParentNode.class new file mode 100644 index 0000000000000000000000000000000000000000..4fcd8fe353ed172e017d5067e7991555e90eed87 GIT binary patch literal 4234 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 literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/Text.class b/build/classes/nu/xom/Text.class new file mode 100644 index 0000000000000000000000000000000000000000..177b99afb9bd98a48f6fee39756c57eac310ddf6 GIT binary patch literal 4793 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 literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/TextWriter.class b/build/classes/nu/xom/TextWriter.class new file mode 100644 index 0000000000000000000000000000000000000000..3990d1a040a5a2fc08d795f6e2b15a3ff5018fb2 GIT binary patch literal 10249 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 b>&VD$WMs#eF!D3|uw|7+rhZPg4DR|Dgsb#` literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/TextWriterFactory.class b/build/classes/nu/xom/TextWriterFactory.class new file mode 100644 index 0000000000000000000000000000000000000000..553c8c5513b04c15d78c7419c23f8145a80e3b1a GIT binary patch literal 2782 zcma)8S#T6p6g@B7>ogOAVUa`>1OgekJgQ=yfTuYgQ?Wt7GaQerC=~Ds1y3q? zisNY&#e#T-<5?A@0-jZHPQh~=&#R~u#0wlRs;Ck0C61R>)C>3u$9WZv0=~*|K}C~* zuZaL&=XgU!ix^*2Fs0y41#c<1q~NlGw>jQXu?JC(cRAkUc%LC>yBRg@CWg$dMZFAJ zT5Ql@Sn4;UMpt}vz_5b)K!g~#KNi*_y}D(Jagxlkhs|+@0)I4KJ`o!&4;mA8pJm#H z)uxB-m^InRkT+!5DW0KjtA9j4u9us!@`SW8F^=fbq4I!jnbD!fqI8GkDZ`jx*nHbM z6djL`jm0e67;KG(V}oSaIuSO;Y%>-mb!IGXGc3C;PcFuw7)iAVN^WKD(hSR;`Ow&y zVQKm}MV}Y2_3+V7eM|<<@d0I!K@G&>R@i7WMS@nRf=mWqCY-y_gS{Ldy6_P`CPNoK z!4=wFDA?x0r}&J4hq`=P_wH7PmB}=Qq9(;-Bz7zud`>&?1-eTsytUP}r6DaK9Edw_ zZ>gyFR(W0c0$)u^;CGPXo-2rt8q-1r1I-sps8I%Loa~`eSy;2N>b+#lE8WK z)SBlF`{ob3{B%;afaA{DP`eQ|tW*tba$3oMHG@>Gr5#Z^ zGN}&GJuqT~MbAi4iGJizaodcP%RWfwows(i`+b3q+g)VNEVZ8{c5F`n*qm&7b2UYa zH6L}{)-8Ly&$NfBsFPN6`%}>YhQj~TLaCYO7{}szWPE|6M3{}j5w*fWxfa%=i+(A^ zl|ld1vcRy9P&qvrJ+q6igI$%=0lMc%ay^2CT?qj~=%rCE`mmoEGS2iPi~hj=F1dz` z8Dw^P+*vcoo`TEc&LMCSd9&dC#Uw7okXp1sC-Ek5OtSBt(DZ>MBU?1>m;?FsCyl1gQPYRwaK9h zCAFETEksR$HosK@im=EL6-!acbcx&JP^EJ7Wn@?G*m)hh3P)5a?W#yr?NBw6swG!- z4plE*HIQnXLp4g(c2ezds3xh}Nvd5lXqGu`kt&U--K3gAgU8(}f!E`1lVFR--7djB z9(RWX6rWE5itj!G(h+euN*tP&NNb_52rA@-6ITIS8<&2(o=GjE3P#yAMA;}fzcR0p=fW>}7oZZAE zMR=0^fm8BG6eEkUihdlRsObRUeu{-ILI-h(j)%hj#Djz~aG2W za;*N`(3*Hd>)LL`>I|9}1`NeTTZXi79j`}*iWr6ZAA%w03eg*%jU7IUKzh}#-7zqS zu1(*qfIE+ud=zKMw7k%F#JN-{^YaAzN;!)Z3<|LrkD(Ng+F}ySB4ZmmEm55WT_S?A zQguNqrKQ5C@9w>Uy+(i~dNUEQM$kIdAb|*mWn`%%0&>Wch^h)GM(7gESoW5BWn@;& z#50oGv|?&6Na|BdHWAs;CoP>e(3A4M+Z8EfuhVg;+Dxkh*$iL{DcraFQ3Km2`Z A>;M1& literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/URIUtil$ParsedURI.class b/build/classes/nu/xom/URIUtil$ParsedURI.class new file mode 100644 index 0000000000000000000000000000000000000000..bd08cb8a0040dd6c269096fba4de3436919e528a GIT binary patch literal 2218 zcmaJ?U2Icj7=GTir++(ZyOnP1Xo0bbT?-TtHryQG#*oSe$QTpxhqLZpum|#fEZq-OJlIV>g(HrlKG0_W+(V)?o_=gejeNVgjQ^G~x@B5zb{e9l| zJN@|9d+!6-jq^eDV!s~;0*K?mAjUBv;-H90!4AppA=w@FA?L@#K?HF`#FT*3B98iT zOw^8xctnsBKFkEM4kty-iZ~@=&WF=JJVx+Z`J%q06FR5PY0qfcl2%^K&e*0=UL0kx zaXVAh^G3nQAJpP>$fzmkArD%C+or zWhpzYl?oMeNne;fk(*r3>s3+Y^h0ZDjq8gBo2qNTWO;#8u#1Mp*>hc%W6T=4a#=Sg zN}6TqeAT-ef<4dLMrmM8mEC(R=7sslS4xtXVih&BN@$<4wfy;Mt?H!i5PgQ=PD#;z zyV4++ur4*&N(}R6>Q=49oc6Tbj@JqW-5lkP5`r_8nwi%R8N!&_%x=51P{n2p6QH6G z2_Md=cpOitcv8euIIChk60&2(Boc(g-ALsA%$BP{kMv>_#}W7aD&|FO6i*w(i7T5` zk;j4r(^YH|tn2>+)yQ8Q6jUstD8f*&7x(*cjtg!k(n2*M@{g-y^XK$D)AKKAl~JEj zUeK416}U60+#pX$8@JKUt%3ooHg7qT&AFv=xjWJyW6Kuexz|2Rw-0Kz#>`tbKLcyd zkSV|8aPzZL#6M>siE!J!TD7Xn#BIIPV`z!jY&2ngYN9opwcF5t31imd{0#xT{NpCG zgtXXMxl7KADPeCNffoXDk!xuR|H~ z#5}Io(VmIyXK>#NA{lqgQ-`;4PPi_jJ)LnU7`Y5zl2yAMgN}42;W=A}|1ttAm`k!= zCYiW|?yCr%?rggPbwr6PnL66z$_I{a(s9Ac>6quPicWZT%W*CuoX*5N%|IcEGO!vx z*+j#fb2`JPrd>Gw4g#H#Iyzp(v@)c~F_Mibb#xASV_vSHCoLEQoP4yw$qF_)U`&a5 zojjwBBOh4Fy@EF9%-gpditl0hGDLpH1b@mO?$6PIFS%)7VGv(q2;X25-{LsF10%x>^N8w(iSXu~hR%s|4Yjt-6 z2a(}hg}o1BEAM#JD2Hv>!S=G-$>T)mpuO0|w=N7(wCOKSJ8&Ok9{#Tb_8@$l-77xq z@v#!y;5L7;R?l1sLOsb)TJs*c5l$cMZ{utaWtFIS-(FPKMwb!U6{b zUO|6G=B7(-qSv6xP1G^qjk|df7Ekgxy65CR+=N38%rB||ha9sydfwzz+)f$eh;SL3 z5TwmaYCn2u0DZ&_q7-&h2BWkUM`#;PGP!4HC+2Av&eI;4G=vK@jOS^@DZQ&vUbFNd op2G-6xp-+sJG_6P&xZ#(yZl|EIL4q0`;g@|$VzcMitX_K4f0IRdjJ3c literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/URIUtil.class b/build/classes/nu/xom/URIUtil.class new file mode 100644 index 0000000000000000000000000000000000000000..baf800f6860359411d20535e918c2553dff91310 GIT binary patch literal 6747 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 GnDsB;d}F=< literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/UnsynchronizedBufferedWriter.class b/build/classes/nu/xom/UnsynchronizedBufferedWriter.class new file mode 100644 index 0000000000000000000000000000000000000000..1d29b5c467b6d2546d87fc276c194f329f942693 GIT binary patch literal 1844 zcma)6T~ixX7=BK^yDTY9<-?X}XsLu?30g`&XsM94k`_p(v?$YFxFiR%8g?_AjZ}Yz zciuRoH(omL@misO5tu2_Y_j4#Yc;!@{>&kRaWRP zxlYTs9Dl=Vv~?87K_Ri#-rm-pLgZPA5ly#c`?l*SMBTQpFtGBe^|@8F-Qq`{?Q3tI z5w~o|_7@b+=1*=;ZYab`ZcQtkUa=j$*52LH-VaCeJOoz@plweGo&{iUug1>AJ4iyhw+77EGyvr@S%a0z#NyQRrA(a_G0Uso{h zy{PJ@h-f8oUtut$*|40Q;=1qI&d$6u%et>if2sGWK7Br^R~{=P;vi@qZX( zVh}?nhLJHbf;SZ=g5m}0h|_L1UC-CGN>h85_|r8?q4;rSHMiB?xtWuKZ8hb?xap3hCoL#ZEe0C|7zo4IHuWlr3KfcGax zW@nILYoFy?1aI(1TY@>A37#~E&lJy`!+DOQe0$Dw5k5x>FQE!&a`-vEk7yyfk62-9 zAMqc8$SlVp#5rCdQWpJqi#adS^Ks5(FUWzndAZ~>B84CVE6s>Vfb%#Ss#w61nPhAL8|4tLchD!DutkSK*kBHUgV%fpicSvOC zVpF4|gRxJz8kL;`QrP5^OY~EAZu4YqLG~E@Es}KI{QmEKp%)FDD3BO%g$ibo##K5z zM?%-Qa-H8B{M);UhnUA2XX5-U>7+TIq95XJr7NTo3X!!#-zuSR@A70H&;N-b^#`s* zPN=;cs@)|iaclfI;X`@nzj_+T3)CG+=nz>XttB==iQsp8oF4BI9A^cjVTDW-Dt(Lu z?@~x&fk&kJrg%l@_yPKl%9&;;uuLswDtZtAnVs_?&`}{Ha!tdm&lw?Evm)(lwm%b# zuCQ+D;0B!3w1yOYW$18Vi(Ab Gnf(WRr!qeP literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/ValidityException.class b/build/classes/nu/xom/ValidityException.class new file mode 100644 index 0000000000000000000000000000000000000000..c3046a3dfce9c0301ab3421ce812cbb5a1ba7857 GIT binary patch literal 2729 zcma)8*>W3I6zr=tmM4yFSyncag-weSWg{#yhG0A3ASVGk&cZsDCd#B_4K*5pLwMpJ z;FT(P<$+X@s7xq|SNI9|3wWaFb7wS?W0&Gm`D*TRy8HGyXRJT}`{g$P=WsiP3A~fU zVhRyl8bTWH%1T9sB^fTua7BizvisgJ-p8^GD>7V5Vl{;kTuuGfNa90< z(U#@fW@FWITej1@wm7e#E-CDtbDAyBYD|cMy3v;bu zmGi3#u{ozkJ^Lzl)4JSVU$fj5*;5#=I91-?qzjw}i!twx&C4C^($fXIW_$N~LKTwh zR;y*!HH7w{Uxz}xYPMV4%NMKxO<%Uc^p24`(g<9B zhvR*R1LJ(^Xxp_FW_KCUhqBe|%nkq|Ww>s6-ApQ^bNQ~n-9@H%xYd7VBDd{`07Y3M zEEKa=TX0>+RXCn|N?}P<#M)3e@|1%tajMj?b52`|Fq+FV38P#M%fKhi;Ym^H8l`s(B9at@^%sDVPJ2DYz( zIRRM!yK{fgx!aVp^n)$weKu{#p{-_~Mtb*=t8kf#rltK=b!F|YRrSjGTePrce@SUk zS}2FMn|be*V!%&JXMP6uuVc_`H{E z%mByZoZVn#l;7ip;&cb9@EDPs9Yh}@_CUuy%W*`XC?kOr7{d&nG_@ z1%wXb`~VptJP_+3F@W+mh6pf$x~4qm2ZofHkTMfeW&%nQ=-b9L`Kj&X(^DCeCkK$< z56HiID*0&|Ecf+gNYZ>6KKYq|{65o^;9iyqCHaAP2YUw4e}iG(eG=bl`dL3Tyn8mh zdlp^#tad@@v)Tp8YgW4u*Djn5ge);7B6qA%>|khsgzsti3I2QkK}(qQgF*>ofds`L z6)y$Ql4U8ic;RQHe&Ry}Ql|;M_&~!L{(%tYJYMFCRQxPYi>^eW_zfgc5Pj=7abg^rwzSYvXn`J*RKg%}qI?{ZB1JMFkt+AKyCmDL*U{RJ(tiaK z2#EtffFFezJ3&Q+9_*QYJMZnA-TC?J`wsxm@Th?bc3s%G>tN5pJqPz08Z)jAMLf+e z8FqrijQx46{FKlAfu82sFv(3Mju~n%q?Xw$hQ0Q+Rh zxP9FY&iRb{itDjI%#76I<5i?{ty_lrG!K=G2K~a_iWcpsmuV*AV&7d{#2Z<((){D= zaY3SHPjsY`lnlHT*(5o0u}~46Um(--qWS${2S4et|`&9~NyJ zI_Dn^hV5t~qKkpfgwb5}Py53>OaxFG?&3B{i_7wY&FIwsZLF2d?MLQ9JLJtaoI&KR5yofCmAzn^& zdRm+bDo2B^z9>wV<^eWI>LyvP18fQEjXSUHAG*2J;I*Lkk0RTB$@;dd-y1jP!r0}j z^Y}+xg^zN8ZNch)h7jC$lUry;VBK5|)SE z6jo4U?yv`pN_fcFW3A5b(Zt>!QD;wPt?4OiT(+~eM8|kSy0i9%w`=d_x+5OVRUZ*1 d?tDDO>QMHKLWMO>4*FDd_Z;)&J!THf-2huWg53ZB literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/Verifier$URICache.class b/build/classes/nu/xom/Verifier$URICache.class new file mode 100644 index 0000000000000000000000000000000000000000..d7487d6f9c6adfd096772bdc16c8225d5c4ff2ca GIT binary patch literal 1040 zcmZ`&+fEZv6kTV|TuN=Bl#5tVD@80tDHvZ2YGSG;BvlftO_W3(#t{yd88V$3;tX-+@l ze2R2NwlK%ZKR9FX*X_tvO2dpP)dx5q2Wazpb^avj6G7vx6LqsSbC$ORHNG-9utzMbR}I7Ki2i=QyW;141daT;fOJX%RL zP}1$W?3qU8ArJE@htqb>euLEBs9idF>H`@eE!(6LV?uTkDx(R3(jI}*Uj!t?L=F`7 yY_Ibbo|>;kQN_NDHr-lAEngSU+QYLjfl^X7$*6&I%m4*4IK+93P=X?DAoU0GM7*H@ literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/Verifier.class b/build/classes/nu/xom/Verifier.class new file mode 100644 index 0000000000000000000000000000000000000000..472fd95077dfa7e9a77856aa4730040275fc51e4 GIT binary patch literal 19248 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 E07X4H-~a#s literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/WellformednessException.class b/build/classes/nu/xom/WellformednessException.class new file mode 100644 index 0000000000000000000000000000000000000000..dbcf205a14b8f5a7b7ae68065204661a46538ff0 GIT binary patch literal 613 zcmaJ;OD_Xa7(HKKBcrrbJxYWfB^uHlDTzlyhD{l^x9!!A%-oTA;LrFGR!uA{#L5q{ z6yJ=A2xT$f%{g~N1jM}(1G zSNVZZfhp`zW?!Do`*T@;BjmO$Wd#jFdC9mEH=)vYxH&r;pZWs{KEbvu$!Ff1ic zLZ>CYQ_-@S88_TE2MLa2oRFEIXZeH$Lxr_F*QuRL+rD%?M|PC-{r$VPybie6CP8zU z5Z&15S1_SEpw72~r#;WTjdJ6T*|rFMb}A245cH-SdTn`NMZM?#=(HBK$V10)G)jc| z|L;I3C4!4%4P3(%?M9DX4iRl z1EY*5KUFB8$cXhA!#I0HUZuqAkTcW#Pphu1_L2H&w#`dBvDw0>?8{6`%y<7|mTkvCsnj=XNa+tJ~p8Yf1?N{22Aurt^vRQ|9d9RA;| zcR5@QML4bbzjbM-*2dWrR?;8t?fL)*$n(c#i+J-i95*>TgSBMb#vABvf!qSt3-dW_ ZF*+Q9CbrSy9B@}!u3fGOJB&H(egQePXAl4Y literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/XMLException.class b/build/classes/nu/xom/XMLException.class new file mode 100644 index 0000000000000000000000000000000000000000..31a6b5679f1073078e1345b3178a1974d6e0f12c GIT binary patch literal 1130 zcmZ`%O-~a+7=C8ErDfS7Em9Pb78HSPp_+IDNlZ}#77rFdG#-XBmeuVp*_{RAXX4en ziGRSeCL)O@60iO!<2&08Z6Y3a_I+pO`I?#Ef4=_&a2?kSn3(ECAEpf&W>jURP)eh0 zAc?6IX49B6kXB_Sg{uq$f$(katP4M|J@>)#5(8Ud$W%QykldB)+zCbU*N68@ukyhp zL$blcKrjr|p7A|iak$&8tnK*TKHqZ0JVQEKuM0^$j~IFuY}b~H4AyiPpe}veZO(Vi zbSP%l8T6{RO&Vuvwkz(1tu5iNDSC!n&1;ZZPL`>UCv~}F2g;EfRt~&Y<>6}W)xo;lSjL$68~?<#i_b>;aFG*g$n@qCr< z#wm=t7y1ox+g3TV-5KVTYZG}COq|7#i3waXk;5QEp%ZA?aYU0l#45!}e2Q1)?vyNg zd&1xMZ7GWJ{Z0IXyy-W?mT+Ykc(g8@r?U#d)nOK0PZoED-?F6?+YE!J95};BhxvZ! zO1ssugK>J0COy>zMvy^2=#Ht=FhCy#ifW2Tf1TFy7^d+Yb&qL1LI12(Is&s2htRC@ zHzYQ`M3D0|<_H2T&@+Dm12>3rkwk!E8;A=SrH&|Y5o1KFDkZ5!LWNl94)q$X^>u_g z68#a?N-S|VqR+RtpV0Tk^o)eZW6}!!m0;5P3jH$`8kFOnXmTT>G20trnyk8Fno-f; zk*IQ1Cb0GqnIA}Q=6a5hIxbnCRGZ$+^&Y`Ewn~T4EnOXr(nv%aV@OfGPvZgQeL+Y1 jl8n8^B;LTn+pY-ZlL+PLTo^7BQiqmR*EC7BhAV#oaDwf9 literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/XOMHandler.class b/build/classes/nu/xom/XOMHandler.class new file mode 100644 index 0000000000000000000000000000000000000000..44ce38522d929c1cfb5fb159f16bd0b55a0efd53 GIT binary patch literal 16578 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 IG=l&K0M=kH0ssI2 literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/XPathContext$JaxenNamespaceContext.class b/build/classes/nu/xom/XPathContext$JaxenNamespaceContext.class new file mode 100644 index 0000000000000000000000000000000000000000..5521371a13be7e1742fe048675c35cf644e88855 GIT binary patch literal 926 zcmZ`&+iuf95IyUpc4G{6L(`T*A(T)ON5WL%Ex=0<5-2K_dJ(UiuuUDDb!2ale*}ro z012qX10TRgA!c1GRO<3FyE8s>=FH6c_n)7?06azDVhML^WbV1J(R6ViI}RQ=XgPSu zV5_qzYdv9T4CFk_lWF+wfUC29B9+Khj{wgi8L1Zxn{6YabHY&VC+`Kr${>9jkI3|tGXl=e8Y1g_xV(0Gd>b!MF#J+lp^iNJj(>Zx~8fvV=k%P zRwFQui~+m;laf<56h_O+am{U4;`#AbPUTg z4~fD5*U;PHM2r-}R+5gxiQY%JnA>6@4HGwYQEBV0ZW_MQ_~In_FrT4G56=dJ1Lpf$ zH$BPl`h}d!1fL6n^$So*KyWm$PO_*pHcP6PgsD)r9dysZ%tD$Mhy{<0Tg0V>+StNv ZVz6+hP_T_vI_u;w(O>7&xj{bs<-bAQ(LewI literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/XPathContext.class b/build/classes/nu/xom/XPathContext.class new file mode 100644 index 0000000000000000000000000000000000000000..db93560528be3ca77dfda9750793d7239bf0149f GIT binary patch literal 2455 zcmZuzZBr9h6n<_%Hf$CHtEi~OwWUfDB*m9j2(?s-iVYD(!Pr_|lAB~USUrBhzpv+qqcVTWH_P{eOIykfM)VK2Q-#tdC8$DnmogjMu%Ig*H> zZ^_k5>m)=F=XhOT=h1#7A*B@ro-=u!TW&5D8ul@fxbMKwA9{pG7^G5IlVp{Oa3ICWUw@{-+hO8k}~WF6=vIxbXFFpA3xt{_Fb zyIpZdZ`Mc<1tWN$RBP8w$|cA?qQx*$bKS;NHoLL0F}`tSTu^_!kk3v`PF~MiVZ$kS z4xQor{^OK&#WYIpT)V^@u3=jYC-)cbX3H?k+@VM-j%`)72vmFYU|y@(jz-ipzwlF< zf>o3hl)*(>6$UU2??M(@rnzVvG!afm%!x&>ZFo~_HF;SxEX@;1<86X%Hi=hLE9sVH zyV@$(EW%*8e9)zAYr=&vG`GgJGOy?@)7>R3p{k&UiUdQ!8rBtegP(o_nxNC=ej#H!WXGtD=LWYPmu*rbyGIxxMA^IUvs`1i#VOBIF zO|XX*-e*M7;dT2ZZR{D7Fo%xFfr(nY(IQQ*cV-mq9rW&`9swK;CbQO@CxZPj!fjlMbrroOlD=S5kdea}*)-oBmoQFmbP!p5Kw#o& zf}Ua?{N(_?K;B~S6v20;chFtj#*yF9lU60QcTX&pq5yNWrjf)goWyM}p5mVm;xTb4 z1=#gpJC2@9 za-TYwKrgOQJimYiQanbx>JCm*zAT?fZ{Z9{oOw;wn8(ep<2QuxE&B1D7xRRF3Lg1K1X-fck(TV*LmNiLr>FPeU2Y>UOtoXMtxmKKtq) z@XbUMkwg=D_fPmojPcHP(=J*cIx~0n-gC}9clzV^*Y5!4a7{xBg)Ss9rO`62PQ^Gb zC2(0o6ooiu5}4JHXeQ0YaV3tc3?~}GF}S%doQ7dr50`E+uw{l+$+jAjTXLP7u83|d zf5`rNGyju86Z>^XG*mJ}X5|^*k83-Qy~npqLEjVJ z_p*@0^q3)b-LMR~K=~7a0v~O1ogq@Pw+XIy#jwPxTiX)OnnF_Sc7>F4(oeB}8Ie0i zgJEdJa*O+Rt+;WY%bnZ%6;YQYndsv+QqQYYuXi8}NN8(TDiT#8OU=fHCMG+kF?6X` z{H#I{J%II}(o|XU%JX}??)Q;s2-N& z5mOXGntlZ8ovA->r;cflQ4SeE>uHMaQeKGmZasepriTv@`s5`K)7pbDtuyqOS%ZdI zoS|s2nU1pu3&UL@5wJ@{O+umzv? z;R#I5iGUf#aLy+x(#{Acs((i0BOO9ex>bw7st1^CCis9%O`L92>o18)wO*j&&^x5Q zA-b7~9U^{|*War!v6<;Qgm$Fo4-nBKWR0#k!XC$2#OZ9&YLm1VB+VhIlF(h~*bR79 zXf;#tT41uy`6dAF~~s@DP3SPM1g4O$s7fl0r&pfTNl O8}qiKF*;F)kpBa&bPH?% literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/XPathTypeException.class b/build/classes/nu/xom/XPathTypeException.class new file mode 100644 index 0000000000000000000000000000000000000000..bf7075f3f16e12d1222563b6e0298c8072f7948e GIT binary patch literal 1251 zcmZ`&>rN9v6#k~SE$wov$X%`ArL>fKK}F>vR1ir46(ss|XveZxw#{~H>Z|w=1`{ob zCjK??0elqyY5aCuprtX{ojJGp&Nt_r`T6(jcL38^4I&;w4-y^d#$<>~GK49lLb!pO z8g7N4;dT(y&VDC|yUu{-2yF~CpHD=4U3^PwGf3?I6t(QA-`fO%qeYR|w zeY-A8E=diugx)GvtgKowoSO`^vz&5DB>)|RnA6dZ0Uh%g)3Bi95f(Kp=}2RYXHRQd zPrFnx3OQvt2icxc7don{C5so~^`rHcs9aP=bu43r5K)8eQ!*DVzM<7*y_02SPw9B# zWaw>!5YU@p1cv@wZT7XEu{yRyFlBS?I#arh&n9g>^z8hjZCp$2~q zf%U`>jDCam`Ut^DM-8D#{*f@38uY_sMd#irvI!T6#s3L?8ap)G$oZeY>bKFQ873Qaw;0cS_51aG34b9tLYN|ZXohw3LaOn z6nYTjVm>4=o(SSe@j~>c@N^K*1aVk6dql;vI4V}2Q*cbd^Ah9^(=y#o3BG949tr+# zd(elG5)En>h&$aVsAix3jUNVWoz%x-*)K zb(!hi#<;tVSXq74=*?#`4_ao*L$8fCl~458seIP3#6HR7vxQHdUggIeGfT5od+!j# z8hYl~wku&l`Q*loQ99@wv{Mpl$_o{cRk@^uMez~+F+G;it#quyiU-hB=crw&~PgJ=_+c7>ern#S*e}lEF&AT zovhA4>Wj!ONs9FtDKo2Qdc7q<1%YcTEbq!&F6rQkoHPhPVx+AS0{W0Ed2gvk|rEHcM z7TS|cV@X@4SEx^sqPfWxtEV-*h?f++tl<@`(eNtHXgDjdZt@Pz4qd}*I8DDboWpqy zuj7J-H*jIbr4q>+-o#rP?!mnp-o`s+!0S?6%1?E|$lA`KNIqu_wnv0V?`qhIi)1UI z;F5;-ghMMTc11i^BWc}9lGRAc&SZ>~V6Z(RVPV41EtXKm$mJrI*B}D*GR`QtqTzjf zAWVG7^!{&4ux4gB+mRe0IIDL?RZ2E1z|)y_Y|D=6EHcy*@J`*y(UFRkaI~Uiik0pa zYi+dL8s(nxIz75YOtL;UW>|x4;%Il%tn<^IQsHvnp`2@E*_CN_UC_0QmPie|dM1jJ zeF^i52D*ot5H@4Za1;8tnayW=9o?g~$4r|p`$bI~?sV+o>%eB}f1Yc*dZw5y@6KWy zs6&onlvM$*GbJpai;OZ4yLD5{)@UV25pQ|8smnKowAM#DW zzikqMenQFdNhtlH-~?_+EQoYAhSc}5ptbRA0rU1A^GP31{Snfrt^Tj}DsJ?3Hr7bE zg6`1##)EDCus;+WYzu@Iw8^2GHYKct12xLjGe;=O;Xo)vNp20x9&&3~nX&Lib87*$ z1uSg&gf9BLu=b;l@+y8{W2hwyjpShkLs`lBP6pP&(7HKqp%h0K9z+iYun|f0VhEcs zg3ZWt^)R;L1h(<^>cV+!$Gf~Z1XV*sju5#W>u9lUS{k7B4(j|wT8aB81-Q2fos?vv zF30_p6e4XZomGju9vg^R%WI>HUP<)!DE-;U6!lWS4Du9>`4wzZuvr010DmF$H>xCW z@^k9tFM+M}v&u`?@3gA%W!1U&|a-F=yIJFSB+V=)O!;(oX<+0mB%L-WDvM5-< z3JRgyITR2rplP;g)&^LT^?Uu&QW5RB|!pBe={vv#Jj}#D!qgmO0(8=xVgd~8VsZ{3%^z9Ac$)JO{+v3=G@ZjJE+K zzs4B8$D{a#^KWpbOJEHx#gSlYJNSs?1qAI(?+(sE{KRs3fC;U^ceK^V^eiLZZkC&m z_TMR;*o8CLL!C@tjx&ONJb8`?{nV*m34~B}4P6TMD;V(qElOcC9-;-AhzA&(#Mnef GAN&_Sxs6}| literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/benchmarks/DOMConverterBench.class b/build/classes/nu/xom/benchmarks/DOMConverterBench.class new file mode 100644 index 0000000000000000000000000000000000000000..01c58aed1c132cb1eb508d713cc96278e61e776a GIT binary patch literal 4243 zcma)9TXz%J75>JSJ+eF)Fc{)Amy?oI4hiUPSwg7T7=yt!#>RH-&?F;iB#ksTk46`} zm-Iq!X(3JTmz1>WCF#APYZ+Eqt3RMmT`Mnr>RXVEHr+>(Ed@{vMrY>i+27vZ z-e;dVd;a#nzx)+I7&kpQhASR?63_GD1wOpk2N@|5X%BjF#f=QVDtyTDA?HCJs)&LI zdvK)>&!Xst#wWT6!;Lc@48jy)c`$@^KG(cCTv^pEO~B(brk(RuLup@zl1&+&CUJd%Wk)p)PSCPx ztz4E_q5$?RI&!)=D_ix|5|I$`RVq%ZKdMFno-?(yVsT(e#McD46KSnk3!X)@Y^Rke zm9PAvdzSJzuOkV51jwRd`d_*PhI9A-}t5&PWm72_>Kb|&q2|vP*TbSn3 zaDrrMZ`0EDN^tRO5`Kak5?<%SPw_KaZ0^Wmw7&H!XfS(2K-cVqgr6fI;NkHsl`_l8 zc1q4EENyCn$%L<==}FYoR5hQ>>2krh zna^f7)P%1gBB#amBwJWoS&Fl0AQ)z67yN5+#)5vLK#x3$tgdEEX63DN>ZCX1rHHZ{ zTH&NO67+kMa|NAU(o4mvnef#W?*@e10Ary*(3`xRGmL`FrYcn}{Q#g)$WMNzRh7-B z%rcvn*{Y$IbLx5rY^3GTAB{!FyvgOwl(n8OXV{d^=ACM#18}qj92<>|MZL+XGMmaO zY^hc$W_Je#BC%2OcA?59)C!x(v$Qp|FV9RCD zABg!W!%KF>W)~f{lu30e0)Mmx^9LibF{-5lZcHF|F22tH4gDGaaLAOK30Z znVnTTGH$EcU?3b05`IDM7K}+p;V`hk|P^wYEYhSX7oX@6z~3$;>^jqQnKWAm+*L6T9-K#iy*^)3*_nTZ)k^b;fO9(w zupP{aUCiI*zSRrwPnT_*+TzR9V$Z6Yrk23yyXF{uY|vigT^ZeV(~p`#k_73_M^}Na zJx6arc$;pz5TdcSE<8?OR)Ew{Km?;S>q8V{q($JacSvs^*{*gyJGgHP`{SfKxH@>?77oUH?_g+kxc3$g z#l_*CEgX)!yGQ$m`u3OoZ{Wai?=V?=XbZzv`-C^&|5uxrhsn#1`2<2XgN~WMA&%3SL=VmiKAgc6RoH^?BtwO~CCt^XwqD#bO5lbSL8zXIl r|KKoII7y=S339W#vritYZvxsFgU|dKdwJ@GwvFPa*&4~Z@ZA3aIhdF3 literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/benchmarks/DocumentModifier.class b/build/classes/nu/xom/benchmarks/DocumentModifier.class new file mode 100644 index 0000000000000000000000000000000000000000..4cbd8f1fd6e92aaba6bf344005b60bd4c82c27de GIT binary patch literal 4170 zcmai1dwdjE75-*lvzZJb1hTLsp#i~e-a>1^P*6gt1oOZofH8`l>`sz_eRX%1B(+6U z#M0K%T5X}0KCo?SrE03(AgKr{RqOjvwbpknzN*51)}I!?JG(Qx0a|~*# z+}USeJ$?efl{l+lHFlTa1K8uo2Niq>AC|FKffu`Fd_;ka-D26N;G?)%Ec<2LqCgeL zw<;*ZZDRSDSZ){O17f*D$lodB<1+425WpuCRN|8gmE0|sdj#S^8HW^93;1f>>&K_W z^FFa07Si|o@qmm6g+UL=I3h@&mhrHHNAReE2|Om2&xqH@Wqj5TQ$Z7siuWfJd=8%% z%NGP~QpS^he9@0DDRlQKLCOlYDX|=r@ns3VtBtr}u9e^p)oqpFS)YjL5@vT8alI=O z8`M*M+F+EFijG8Bi*D6YhIr2tJ?4m!maw!Vo@p6N#99XRcz7hHrFN!Uu1SP5F+FZ} zCL+d=p{G_8JEj?N34zc+#|~|m))Lj?!!5mL%7_oI7P{S$L|DQc8_Y%!lID1lHk20~ z^^w98cOqj-xGV=~BwE_WO?_P|rHyxI%w)zSx~|2@r=)dS9yM;zQxfLdIK2*0D2ZsM zCc!n(M%a`#DxuPWZI2g&`I9MqFk?g`5~NKM{IuKhRYFNJkv8*3GPy>z=uR>SYHsV& z$7bMmZx{>fNz+KgX+hAoYqOS0Gb+Vr9xXM@P|v+Y?5Myn^^|5N=$kJ|S>dD;tTth( zm4I2jrWW4WsU@xS$rxa@v(RN6mod$PW33TjR7=G&NeQhX+Z-Fget*3*545$5TuKK2 z+a<_WFoZoN@l0&9mQL&R!ezu|Jk6{)9+Fs^)Kf!=RIE=+GiIwC?XRcJw#ca!p<5YB zM5Bq(E~}Eu9USp))tNVviN(f?BD5i@i&A9sbRdEuhBrN&=ttX)@&y-K3im&18%({wi;IxWo@J$IdR!hEC2eB>l5QUZz zUVKZ%xACls?_iCJ?~3Jn_`Zr^j7X?*5HmwVdP>^iL(sx8#aOhvU<8_?d(Sv2?AOsI{uD)*R7m?cP-J9Dc6i7kFMp64$fYa-1gRL6mkBzPaiX7yrAM&cwWY@Rs2RM{w;o|;`dln z$Vk5Qgs-Pm`~fe@_@j!Ka7M+S@Mjf&5uoLH&~+w<3*=A1h z!nAebS43v_Bn4BHS3qUx(l>zD%A15`mpZpw%{E+2^X`hsC#^XJ<;m{-d#m~nlGq2e7)amY%T=Ri1iSjR)rOqxZ{yd=_OD>W0&Rm5o# zsuQ=0H#E>Dm=_fE6cxQSn_R=<3JhmbV(Ro6F}>4>M#UY=>zP+g=rts5M;x0t%Cuxs zkMoME4sEKtDBSt3VGNrt8S6sbml8{bN>9@mgUVb>n=Rgm^d{fWa+pMiM z!aj`PR=(YZC=Mcyr*J)v^8z`EG)^N!{kv(wIL_b(4)fhOiyLu{lK;XUD}hH)PMuF< z1FofYjmXljjqp(KL;R%JL>oJBH*M@-I>Y?Y(Meln%ImauGyK%O4ez9NCA6R$y+kRc z9h>M$4~yeCLiDGH9-c-Gz3QR1GnC(kcTw9pYuC>m%Y=Ro@EO31^s$Xav&h@Q)$MakzT+9JdlM-MRrRt~(Dx3l4T71c8MZ5x*U zO|}Gud=y>EWGUjSBk$K*J_Vq!z9Eat3q7h>)H~?)otAll9G7JZ*@-1mC0V#0K9};J zUmfIPw508n^N{O6!zqLgp_SNmS=49Ia4!_TG)`dIG@AM=ny1jxNy3}Dni`w z-D|3yv+DerOPm$5(Svz3?;ghdAa{pQh5N7!hf$CF`RnuncFu#GVn^uA!;I=9tj9;$ zOB1Z%$Ji$mboFue#!=Su6U^sP+>S{ni_5S8CQn^Nn~Pl-Wu68w-^#3;NFj__r=xkA s2>*fn02I4%cA8{maX$Q2i}7EP$oEUHAmI8BBxW-(BkDI&+=ZL|2dC{c;s5{u literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/benchmarks/ElementCopyBench.class b/build/classes/nu/xom/benchmarks/ElementCopyBench.class new file mode 100644 index 0000000000000000000000000000000000000000..7bb9755e0608353a64441d2fcfe891bfef41e936 GIT binary patch literal 3344 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 literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/benchmarks/ManyNamespaces.class b/build/classes/nu/xom/benchmarks/ManyNamespaces.class new file mode 100644 index 0000000000000000000000000000000000000000..7f3d89377fb37893ccb32bb5d722febe57786545 GIT binary patch literal 1664 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 literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/benchmarks/NamespaceLister.class b/build/classes/nu/xom/benchmarks/NamespaceLister.class new file mode 100644 index 0000000000000000000000000000000000000000..feff99d18fcf12979d813814498dac72cddc4be6 GIT binary patch literal 3135 zcma)8TXz#x6#gceX3{Y95@?W8N}z)2otqTOMWBEd(t?z$AUI8icFJTXOeS2t0*a#I z1@DL#RO*Wl_z+mK7RzUUhYvpZ3tVdb&Sa95wo84;nUiz&-sd~t{`Q{v147mtg!a5KOclkRbQjXE2epiz%W5e-Y2;kpS6VFLO5-w@)_N7CDfM!Z}XQJCXnHY(oQ0G^sKJYDxja0&`=C&*xmiRx1UYv zS=X>Fn$ItV*rPc)9yNJS)|?Rrn==C-hBrOnoMh>#I}++jp|<7?Gwt1J_K>TkP7Y{U zj}QgD%rVoe;1UzZoJpwiIFZnkD0%-2NhOo=bz6DQutEMb*Cqlb>*!}R^CTCucFrwM zWdW48C?Bxh)CGMWsbSkOMhr_cC&4f6GfHZdH*s{PrA)QIn_I8ovV^%Ky1PwJnVO@g zMM_0RNm+B;!O#Q|5MBa>D%~9Q1KStRE(M>c z_!OTpuPScfa}_u7g^Dl5>??e&;ugN44y;n)_V!xLK%!N}xA=}}5canzqp8VLF)q;7 zRqnlecuY@GlC{2RNzRp(lE@^erEntK4@DU|$Hu`53fg(bs?_%$b_|z&L)S9YO_`)g z%U+?-$fT|t64Mnbx=q!vC}IgqXWB1GumoJ_Q_E&`iw;eRZLQNnRysoGjPWYm6dPW} zKJ41XB?>z+Jupvx=mteL_?@#XU;Uo91+Pm<2(zIZw$8UAiYHQ>7U__i*qYS94g=-|Iya-j()h&hjB_8e=&8o!s|}$mb^)`aH3ifbP{;7&hz-)Dvw-M0W|gLQ z2)r34KQijQ=XuOQ(7VcB%q7GXy#Gqf#^b2LTEwu9R_n>bCSGbYS=fSA*ouwl!;^dt zup^orZ9Ii6`f-Ny^Vo$8Na7-Pd$b%S6>Bh$zRUSJv4s zeEvXi)54Ad8gC)m*0HdF1w?Z>C~_{8S4>wk2?9SDt8k20IgVO>M9znXC4453F2kz# zsTV}H`q~t;BvZwjLn8*6s3MUn0)L@i!5)dkag8q)ALvydXba&r$?1(vH_+JLRKP-^ zt9ELvnTop{m>8eO~ZbV62`yd&y>yS6SfcBfy=OGRPJm zq{wkK0)Ijwu14bGyUxdToBzZiUG33D1;hu)+G07qY2ght3*wgijFw)R5a-JUJSrdt z3Gvqo#6~aeHUVUrs#ApMP)J$UuIs^E&FD>pxu40FSrUspn1ai$GR!U?rb5qyI5e5d R8U=@mxtVC6p-%uu{sFw>;rajo literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/benchmarks/Reproducer.class b/build/classes/nu/xom/benchmarks/Reproducer.class new file mode 100644 index 0000000000000000000000000000000000000000..d570ff50a9233eb173e417d9ef5cbfc5f70a3102 GIT binary patch literal 4796 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 L#6Q0L8Nq)8Fk6Aq literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/benchmarks/ToXMLBench.class b/build/classes/nu/xom/benchmarks/ToXMLBench.class new file mode 100644 index 0000000000000000000000000000000000000000..f5454cdfc01b74533646d3fd2d21c60b8efd23d1 GIT binary patch literal 3035 zcma)8+j1J&6|L}~*^AJ)3cx>|65+I=DFE4z_w!gf*fF10HuqR+2t?-~g`#dR6h&4|%*6a0A8Q+Mm`&8vf}`2tW6qpZUw@A^d`e%tPUDQPovLHQf!_ zl{rKU^PP-%SS+BM>*Zng&2^g|wa#&n?eLe)= zE2q)tDe1{hgX~>>j~WMF@ie`rIvhSc^|wBjkO#g-DQ;L*4p&ZJ(#i9a_x(GTY^y`K4|ZL-+TN1wQQACW)M08OwUmSKs9#lWSF_9x z4>A?XSM`&M3cHr)RMn#9*YEPAAS-?w!uSb(%Hh&U06{_jF_NplMdc763 zFB`V5u2d}}j2g()Ddr?#-%_0%oLD+d+ZQ*&XhI94n2N-f-9p6erbs06B)h&ln%{515lRyUeWf;FA zTY*34hw^Yx5b~B&NM_Q#q@GFSMLG!icD>cohJ|!S%H_qAgODE#JA-ziv|Mhqn8+&K zE<0=uI*RJCgI?EYv$aaIS7nL&d2y^M3odHZwfZ22|n~4^R9xK*Ww%_lz>)#EEr*diX_Poc|wJuw4 zvQooiJGR`L0i6momrTXu(ZaSlG+4RCUdmmzC)=i`&Vc47q2^*zHc3#qchq7V?W4Zj z(UtFpC6noRB3gLTQP=~^)eW5$Wto+2#d5vItcYXriQhyzC#6!+!a-N<)3wtbCu*@B zQ)%n<;SBf5sf;t(OhzL3Zp~wlYK}7N`lO_ZODPu5#i+vjPS;^CT()0RW?h0z zsa%%s-L})Px<_owF=oVu9D9uiqkb3 z_Ka|;h*RIc@Jk@e-F~<%BxGK%BL*mOE1w6@D7pO8Hq)f&WxVw5ua5pdq8w2+qP=f z9yW3{Lbzt~K^SQyinQ4j{7k>6v!(^db5t$;Y%4>B-S^;gh%E z{z98ML}?9rZ8j}cy7m`?|<2d|Xa7ldC{f1H%X(nC9 z3Z+R&(kVvI5PpL=Bq~aT28{$w&U0kHjTF-4{bR`FA&CI_dW|f}7Rcrba#$rRHR`}U z+J%rOOCMqG8#2oCxX+`&<7Yh9#w&H?Us%KgQVNixbpkv*YuW8lKd48MkN%2)pENhs Nv%7TKAlV!q{RhBxD1HC{ literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/benchmarks/TreeWalker.class b/build/classes/nu/xom/benchmarks/TreeWalker.class new file mode 100644 index 0000000000000000000000000000000000000000..afee5e28cfee25823c2981b0c2244fa406a628fc GIT binary patch literal 4876 zcmai2Yjjjs75+{pGj}qXkT4`eCIpyPiXjhDD6fV>ODMFVVL%ckGBMU-jKut^K9Twfx!T62EFu4)isogD$KTzxiJ;ZC5)sgJI(Plwbzv` z^>clh7};E$pn?vlOR)I4 z&4Hs(BYVrf16<~hX^xjo&Jm$$v$Hv~vw!olRb`R^k-T)W!Z@hV-~j5`w&`HhNSm=# zl7@$DI=1VXES*ulR;g!(>DxIoxQtfEOe3S4DN^c5bFXmg_^4axv;1DuXX??>t$Nyu z7Y%!vR?H|3&ue&ram)}_nB|yYVOMBvv0*uZ;NYJ^sDzHs!JO4_UZJ``)po`fJyGSH zP0ZpY2}_0Lj)s{dcA9?CaR|5i@eBFQ?`%(GAK&h6W0WQ?JnQsmR?&Z=GB%a-NfSEYGP9Pk$n zulw;={LPOy@ORduk~g*u?l+=zP;C*aos*@o;f&lXWX@K&Gnvh$)2WPU3~fqAQ$ywD zQK7MT(k|XCLosvQsX|lq_KjyvBT=5#R$X%LCWqCsX$4*`^jDl0W<$%&nS0et^L9$w z#>w3`Y?zK^ftK=`RJ=v2Ge#nHz}Prq-FVHHB)olOG0Q_mwM?ODb=W|ovwPzX0G}}JK3IT-qw!D^aW}u3{EbCCXj-yWYioLsa0_rpe6fMv@Q=+EJJ&9R+LhcFFc6mY`%GHHDdCU&E^9V>Y zr%MZI?QLEjbPo0B&|v=rS9?PmXWP$VZkO*If~%_uV-k%#HWaK5dW}4q0`u}{&SO5Y zi9}6Xh>{CrSX(As8?3Q}7dpa%E)br;6A^<^9Dg8^&fJDiOo5iCeA zl5u01bYn1RNnbTZx~U+&SfrcFq??0Hmh?Lv=@SS>C_s*uoItfbJWanXP@^h|7ONje zEBHqP{gp=t+tqac8r0D3v*^=Q#!X!9p=YPR(J4%k;r?MNEfS3*hs7{w5#ih zcQY!nLA4U`&9tdkEhO$OjKiq%5_vEGS~`q>5qkt)VtoVKxYtL-uhEVOs)_jpyo;k6 zqCP`QBd8_ryJ=xRW)uBU+8IHB_{Xt}qk3AfoBtvR(uP}TeFTlPVhK5jpowQy`}LKr!G5?LUe- z{!v>`ZiL}*VY9^&KhZ#5`-!odN`%)6Bsl9~FOlVwH%d`)rl>evP~2fDcIUCQz3%2b zI%mMp#B)GTRO4P^xR1WQAM@}47UID&EMW&r*ul~;BNjfaW+tJ4<#HrEL;w#Hz#}B# z(K5vij^ei?fv>%y;`Qt+grL^8JgzR$?XzYdV}KsBGz1G$Rsc|(L`X<0i`+`~LMI4^ESmDQ_=sDbxOh^yD-^87)(v%%?NeyN zDJ;TiLOR0&^$e5$EW`686E1HVx`VKqc$kJg9a>;Dy^ifLdAcW$8WI$*zw;&5K11tMNYEZB-@%?>ze76)yENNZR0Jo zhJkRTf+qc=WKtpfCdujZn1e}#@S+oV6_{&56);W3J61+_;7;m`a@GP + + + + + + +

+nu.xom.benchmarks +includes some basic benchmarks to test the speed and +memory usage of XOM programs. +

+ +@since 1.0 + + + diff --git a/build/classes/nu/xom/canonical/CanonicalizationException.class b/build/classes/nu/xom/canonical/CanonicalizationException.class new file mode 100644 index 0000000000000000000000000000000000000000..1cb86caa25b7100fc2ebf0d0ae88ab559a079cc5 GIT binary patch literal 1029 zcma)5T~8B16g{*3{=`-(iU<|0z_!q6e8Uh;@dHzRu!zA2b=nSPWINmJ&I0k#f8if6 zK58N%(L~<;H~s#o9e_2RiF^Q!# zqNpTbCQ(fz;fJ*ZmKn~u!jatC5{@fv^?YNUfjwr(HEiW-uJjhSJP~_y_tD;`7r*ih zu@3iK!7$l;%lCQR;;L8Q+;!{&-nPUlLozsT2~9jN8RB=Ol6sBsi(>^X?MT&IUD{%Z zHta6ZO*EwvPrZIyIGa8bL$PUhNEat{l!u#9y(?XYT2p!TL%Uz^aAhk=xO(GnB;RpO zPVOIe#J~?oQ=%_im-hnFk?K$cN$v^V7`mETTpE)(?knZ{2vN0hFC)-drMZ^ooxLY~ z5SmE0Y|rV42hz7Wd#3OeUuqVUC}nXT7qTdz$S^lDuwhxE$1P&l;?yopniv;!+1?k< zfg`mjhhJ>|pUQhq&+7}N$BJiK!rEE(D}pJOm95JI;q;}}qRU|Z|J)g-M-88OO3VI8 zC9cxV%+Pg>U>aHEz>ufRzyv+yGl>-1)H7Bjyh@|gkyK3zQ6TnlKjLqJGV zK$#p26}_jXzM>ix8y}GSj_7tVc8vH*)%@tEiS1(Y7^xGpdW47>CB>%SBNFtOrGF84 pMeA#l#7S literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/canonical/Canonicalizer$1.class b/build/classes/nu/xom/canonical/Canonicalizer$1.class new file mode 100644 index 0000000000000000000000000000000000000000..ba2aac0bbc367e21bdcef59e45abec7f810c40e0 GIT binary patch literal 221 zcmaJ*K?=e!5S-O&Y85O#Kzi_O5d1+=@E{(1L5!ic!~~jF#HV@i0X|Ax4<5X@!_Kg< zGtBdKe*jowC?Es|gk+h!tu425l@jF8}}l literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/canonical/Canonicalizer$AttributeComparator.class b/build/classes/nu/xom/canonical/Canonicalizer$AttributeComparator.class new file mode 100644 index 0000000000000000000000000000000000000000..0bec46472e567825d1237ba7e36806663ecbf497 GIT binary patch literal 1436 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 literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/canonical/URIJoiner$ParsedURI.class b/build/classes/nu/xom/canonical/URIJoiner$ParsedURI.class new file mode 100644 index 0000000000000000000000000000000000000000..702bff0968d3633bfed466ef9e9f9dc9040210cc GIT binary patch literal 2264 zcmaJ?ZERCj7=BK_Zrk0pT}QVST0R7^YmuVDKwy9yLy8+91191JciruJp}Vd3wq_*$ z@P~hli3xve5fua)eBZ?8@N1}1AwT+_bDr~_ z_kGXvyyy19mDkPzSdAk=%*SRwwgeEz9YOS=Uz)Ab3`lI7oZTsB+ZE*e*bzh!cS$oS z!69jO`msy8?Uv?liR@7@9K<~Am1dtb`=uFCa6rMm0zNxm)F*U-=-^@PVJ%zI%<=4S z)iTWS4Fnsz!xcSm6pZ|C&8iA`AFAorQ2~F!(#9urlTbjbRf}cIs1o*6v}%!oHyWl< z-6Rl8&5hi>pML%2G3E^q8m7LpHZiJOd$rM$Q%O0mmG*0vA=gdN&!l$MD4PNzC*aa2 z1r`pPwd`bhB0Hp&3T10TAKSMlH!zvkE7FnWhi1#_)5i@SRcC<#bBrZai-yhWc`w^J zOplyt>Q;YAvu&MQ%V$#LHM49QtSieLJIbutS~h!(sXR70A~*SRN!IAKi<(stXdSL< z`6EMG#kqz<`auDAN)q6&mK#J1%u998C5aqbx?L-g->x~keYHYCw>D5+f#7htX65y5 zhGZ`^Ti;44sEQk~RsbrNAfe!(iu-WCiU*{55QkJOKtj&wm_$M#aXm_@);_Z$$&$(} zW+oYasftl)7Rg8pWe`_3t0IpvnM_x)SYjRjZLh`+WPpN-aTKL7RIJ183Jwb_YZh`{ z;sqlAgxxiISkIHJe?c?+Duy|xPwpyEr&O++{nJ8$tGO94VAn=%r{`H`Dwn&a7%`rl zBLkIJY`wZwt7_E2uJVD{BZln%YqmWfoL9zY1+ow}flsScblHn5uJugj1A`TtKI)rj*;1Niu(__UDHJ0sO|MoQ_7RL&XMI~?8oQEpHi!1XjdP{7xiJ_C`C zx=zELiL9@~)0>Gbt;5^niFsVlp*0iPOmNc_A{lqgQ-`myPPmSvHJx!Mh@6I!q-&>h zpe>zAcn;O!KaIc?Mw0Z)Boimlc^1I~(Uvn%*L&mMOdYLp?;DP9(g{K5bj)*IM<+eQ zGS6{@)0vp3nJ6T)ESpK6Y@%V-oX&97JQq%%LLeHcqwN_Cd3(Ha8Og@Hbwqo7F&}Tx zm6jL*7T@0BWD1KMFy@WJWJc|!;0w3Xde1ezpDIRcyg;Vmp4va#mGf zMa$U=Hz7r9BfzTC@G{rTY#LjU;jWkQ)?x)`Y&DU?%~-|ra&`;bNuW)v!>!zPp+~eg zzRK`k1#1*K@eHo=OE$9>T!*@np}5-CrCz3kn8pH^!h`(gHM$hN z3{N+*Jlmy`XP3ip$Z6?|O`b10_fs40&1!}G9yLOVD7{@g`{Y^7I}llT2C+e^t(-@D zMvirKj5rUVb3``5d4we#kT5&5;Tg=x_UVug?dPFNhj!;2C+_A(SO%1Dq;o{>hnsN7 zf%&i+aL74UN7qSKl1Tf_bi>Df!vg-ytE4ALu( Jd$1DzzX48W?IQpH literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/canonical/URIJoiner.class b/build/classes/nu/xom/canonical/URIJoiner.class new file mode 100644 index 0000000000000000000000000000000000000000..e904068ef61f040a142bf6f0fdf3eb12bf47d52f GIT binary patch literal 2806 zcma)8+jA3D9R5z4Hk)*5)8(2HE=q-TOB)r6NGVFo#X?#tEd{Cwn{?X-(oNWGpj@0`t# z|9o=;z;e79Km;)b?E!RPiw~Uvc=5cS%T@(lQrYdpwgCKy$)!gw+vT!D8pfpIPPz2T z<%Iy2;6(+yq~A+&vsrJaheQC?GGGV#6qxeauV6qbX;zIv1xW>arGB4W zQc^kOgXKe7z~5=uS+j53);0mP&9Y3pHDzS8W>%nVb<#>YYXnLnwLJpf)^wjKP#H^F zW=C!)Zra^OJVlKbODBv}k6|a}zNqv%gGt5+$E;j*I6V|i7*^U!(k{wWo6?M7Hwgrc zcs8BNImyF}voaFfYaB45DZ?6wb~$#^8fcoVshzA6@WqX+>Gq&a_1c@wmrb517b z2+WyOX-lrZpTi>0IIpgB&Q6#cl5*}9cP7~&=cb|#O9kLVM#X;E0yPhw{au`;TUNye zG?Hl1h6WW5asspOr$S3End&ob6$fxoF2gDg;joG$XjE}jE-%Za73+vqRK+pmR5W9a zidUrmRUB7w0w)D#O^z%ZRB;NY-4f`00%rs~Q5N$W&N5yDcMa5^lTps$by>h00@V+0 zP#}0W&zAUJGeK}Cs0#e%Av5>qj1;FgbK>+04o$Va_vwqLLLNuOwED8!laeO)xQ?69 zwamz5XeCfTG4{B@6oYmVCh^0O*2%;v4uG!}Cp}*NjL2j|T9joIgXR!9;x}^6VA@VP zhj?c8=S-W=K-MvAr&znvZyPRw1**UvfNOsSHl%r4NafdnmAw*K&k+KX@ra9sm$+ zN>M&}7Er2RgZPSq2Mt`yTxAVk18TtlkK+mM6g-J#)X>>87bPeK#A?l>=TUMIKK*-? z)?FEccPHbOwlBSkGR>EVasl(k;NN)-fu+6FZ4_nFypHA_Qn-eSRo?om zsJw~#kSmO$(iMWE2)aVmD5_kc>^e01kKju>1aG3||Ll%w9$GSe$pzGfys|v{Q&n}d zm_)`NLA(2|Ag`4k+VnhTT!40O+IcjSZ?9t}!-Tvcue*^oO(>LE3Z+x19B*dUC}xGc zdCa~-OiNtMZGm) zlQ|nX;W@N?in%?E`3Li_WSyP(omH$PiJE9S7wR-|3k^Q3{u?b^`L|?Ad38up6cUA( zT7f9|i4Kx?3EiDjlzbsl?id!ia?qvE>>@`b+U0fpCqJ%8OG%bF&0)_cl3h|!687%N zBSglyq^h4_&ol)*=ktaK`Vqna7GRL9-N%wrScf6@VY$8Qg|gf}X^aGMmAyA(G0|Pi z-a9d!_%<@8?AQm-vRer9YLORsp*wMkK}z`Y+c8v=zjI?e4B<=oTG!^eteIb|(xtr4 zw&xdG+dOjn7hPN6!r<4YbNhoXPYcO8k6Qw;aJ=!$Q0l^YJTh|oGhKVM=;5KqN4mDi zO`__h3{1^wT{_h?hcBX>B02V$i(F+L%UZl$qZek>=_L#ok_w%I^lO*-?y@mX4gxtg zdXS79=I#&?JVNdrB_1cquT%Vub{acy2629UO!Cp_DK1_IaL!^)Bv&Q+{h%gF^d_ + + + + + + + + +@since 1.0 + + + diff --git a/build/classes/nu/xom/characters.dat b/build/classes/nu/xom/characters.dat new file mode 100644 index 0000000000000000000000000000000000000000..ccfac0f96b6b33871eefe814374c0a90b19c2a7a GIT binary patch literal 65536 zcmeI)$#&Z?3_wxobg6~^|8I+d07#8?VyABW)YhUn0OS>|)M-wqsU7n?P4atv%>A(} z^ODCOZ@C>F+J2i4n{R#ON6SkLI0%1|)<#^lAB5FvSyUZ$(brb{Rq1P^R#C&^u3*wx z*H9B*1#6cX^TEP?v?tVV=dn9e*`f~}xt+gNI8J%rd~S;@=HSh8*If zZc@>J1SFgaBWy*$}8oK^i7T&_vPbTkIn<~ z<%N7{)hCJ_HenypY6KulDR8kaz3cu~kyr1lG2?dYS)U8--PGlmR934UB5&>pnZ`xl zi{===yBNEfHfcOLptz9lc>3beg2SAv~PF2fV+B^|hTw=o&3p zM$AIjgCYV5Abj>ZRH5sg?N2Odc>Mtk{qw}It5CWWU&yNA@UmMr zMFbE)009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5J2GF1P=eLlLiD3KmY**5I_I{ z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0;G+cI?SGAAJ_sOy00Iag zfB*srAn-p5eALG%7K;D^2q1s}0tg_000IagfB*srAbt4g?TD009IL_&9-A{@sGu2q1s}0tg_000IagfB*sr eAbl#<0L@)It^9UusjAOQj- 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 wJ84&@`sS03PY>bqj + + + + + + +

+nu.xom.converters contains +classes that convert XOM documents to other +models such as SAX and DOM. +

+ +@since 1.0 + + + \ No newline at end of file diff --git a/build/classes/nu/xom/doc-files/xom.png b/build/classes/nu/xom/doc-files/xom.png new file mode 100644 index 0000000000000000000000000000000000000000..f4478ebdce3f6ea71323244fb3e3d8da81084579 GIT binary patch literal 34156 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 literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/integrationtests/OASISXSLTTest$1.class b/build/classes/nu/xom/integrationtests/OASISXSLTTest$1.class new file mode 100644 index 0000000000000000000000000000000000000000..9103cae532a52f3ca3565c430f32e6b750cb184b GIT binary patch literal 235 zcma)$K?=e^3`Kvm+FAvRCvc&-a}d0Mh=QQF7#D8GI@Fdr1D&aOHWwbiLy2+e&U})e zz~_j+mqC<$Kt*cb8^HLKA`-R-dqud@2+WBdrZi>)fnY6SnH`z}6i#>;k z2@_L`yS<9sc%4z0GMU()3Y~hw_%Bv61fjvt Xf2+v?UjbU|0optx?wB*gjwJd3)Y{yz^t*=^c#ayk^k|;H*X8oG!+HxNnRe#O)1mb1i zwVO@bsV$gQzv12&Fp7?2xrMsvc@{k`l$=&>qp_Z|9p9?Crf)a6@O&?KX}(-6UoV#` z6>g_`1LXx`>*ignVr}>W$C9Nx<~=i4H=SBex;nLdYA5m%iF@=HgiG}MF_0Iqv~DqJ z*Ya9*MjY8WEdh9Vr0kp3yNhNsNKnHink4?b&$Fv7-_mfIMXKCyvLrVL(CG&rUO z2KHghKn`cwA_G@YrsBe?209QJ4!5$g(!C;wtJG?5@ui&{_xCERld^$jT-UH-;08W5 z@EL9z_#9to_)_3}7;2Y2b zL2_X7@oqn5^@5&V9^l$1i~In~K+ZwPq3W9H&hyr($zrM?F#0Ux0)OGtVY%$1?Jcj_ zd>lFoR+EL)4JKkXo0h|?dzkw^)KH$h<9?&_E(q*P_WSXMpLn6#;V56&Q9iridm52y z10}*;jF cw|FpuJPva{L?#K8IP2u9BT4`Bv?Ct-4-FFBivR!s literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/integrationtests/OASISXSLTTest.class b/build/classes/nu/xom/integrationtests/OASISXSLTTest.class new file mode 100644 index 0000000000000000000000000000000000000000..4ea06471d5c8cc3fe736e986541b10f263d9395e GIT binary patch literal 19138 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 C!1C7s literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/integrationtests/W3CXIncludeTest.class b/build/classes/nu/xom/integrationtests/W3CXIncludeTest.class new file mode 100644 index 0000000000000000000000000000000000000000..2a78b41f27cbb4df726360c6d299582925d56e60 GIT binary patch literal 5471 zcmb7I349z?9sXXjo0)7TO?H#C9cZB~(55NrmaD*qt4&f{njR)SQCcRulVsb??lL>u zBv4LE6-6jej+RnR4Jx1oxhY`(|)vY6t-QtVK0yAMU!S&AjNqZD`IF4?+U!95D@^+Cl>9~6Aa$C)oH z=vQ%{4;pqzp|7a;s_cAC#pSYpzlwFT|A309f(LyVhp#L6h7Y>b*Re~*Ln8@szY$Q{)6rPOv;-F^&!RwWRw8D^JA<5##ycCPuMqGwCL*RlMN2epE>3s1o7OU;J;sqh zOCoH}Z0Gl${E$JgITjf_ONVj+o0hKm?w+(e0RX zA}Mq);7ytK@}xj5y6ERpohhQJYd{v^>$9Fp@S`cfuYh(#}w3s#jg+Lj+= zl)mQPu$h!Rke(XJMIjAs>8MQ(iX(|I*FKdFhs~6f%R61LB(G0n3gzs+BsKEc!C04Z zgp=h)*iIWU4rE0H(NAqzm*z4DJ1lbDld#qY=cI^P!kK3lC!2ln~p9~a;>@zCOVCjI!Tr#(`ma#%iC-t zynd09bkdYnhc&|4YHulcTfuJ?{4TpwxjDHX6}-b0YfGf9uql^Yp!%@9ohg%5!z!#M zXTvwB9BtR|Nwg~Xy@o$vrG^z)$;8NQ)q));Fflhex6d}`SeDUOm|W7>yBhw8_XM<@ zWEPSqV%UVxhGpcvH=5m3QvOdGmSdTQKjVFY@r6oG{0`gI%iE0UYm8fA+?6S-bMU2(NIJp>;-BrphbDpex zRVYZWf?A=8@e+imgGsCjT^vCiuBoMrf;GOtz&NJ}OzP<}4 zVxqvY|4SXw#8KjCO-vG#1*W;~KJFl3rn-{O77ngjvPkA#qf>4w+$q*Hr=Sn*Ik!k~ z4P232)nI8nobVwnw^$CBan4HCU{fBK`p}V5xM`e|q=bi(bEb|T5-Qlzj?6JbQ*yU9 zjDEH%ou<9mIsK|@r;ik2wjE|ho`T*U%Q-BnYjX!pjuywW*G?>NWreNGS!|aFp(@J%DU0Jq~jwbh_9L$=0z=LcR{e+ zTcg4l!IDaC7r1K1BQBZ}Bbj7Rpng;_j*r?`7tNytv4FPhkv8+Sl;{(lDLW z4vQ@}J_5!Nc+GHbInA!wQwzyAvBsXGH}j z^Ae+DYi$nU1p@oA2X&FbTPtYMFquof54 zx(5c@T`w8OE07$7vU(6IeUuKs_bAHfQCl4-AHbO1sBk)UkDww@IRO8#fk454D_)fo zuX4o;2C5zLsv-1K_>l#F6ieWemAa zy=;32F>bZ)9l-cd$sqLAddUEe2r0U9dBD(FU> z&jh`DXrqb0Bz-!edu>XOV^*G!kGe&+W&|cMG`BfXHc8D<863Tc?@8NHK8VSy15*ZY z%p&scVWh`qaNOeh45o%WWNcanwIOfu?2?)i-J8MmyTyyTx27b6I!R1`Mvf1ux*DLu z%xx&?7Y}eWD3wZ!XZvb=da0vyi(XpelS*ZJSzwk_(%3jbHp=z#eW=sR11Dy1(rwa2 zgq@Rfot5mIlIxsHrP&#rn!#zIG4y(R2BGcH2hqTcKSNhDIJ1(@&&uHJkSZfPURMWj zPN+h*^$Kdwp-X3k?P^*IRqB<`;A9!QUXcwx!3ongjDP_{268 z_hX+N-CJPX?-=*%l|&^?`yFF`*H~bf=_1nw+32_xS%0U;}*UK@EB_34N%=McnHbvsPV!W?adg{~0Fk zCT@qTn8KTp!e?;_uEFKF7FXjs+>9-_1J~m|+<;xU5gBa7Q@9DwbLKVNiZ`$g?_j$q z#ciSnJH#~HE@ok;I0bi#M%*Rl<3S?5S1jUtDf&e#?h|X+HgLZP^WA}6!oox1N<1vK z;1RJCyTv^i5PR?(BY8|bg+1a~JT6|q6XGT86|dt-u^&$t3G6E>#WO|6;<}IR5p zvty+ScZjRV*hZ8Q`$n>J0j=&5DO}1{IX`0?a2Z=;_)Sqk-uh5M20kF08(AW*p`9zp zvk&XU6Xbd$3qnc^knsyxYOWS{k@w9kP%FhNWd2GP4weMB{P>Ed;d4&;sb#EJ({d4- z#k=@C@fBmCcpG1!Jr6%lU*n2xX4GE<#E}Rb!08IERdC%Q7A8f(mTXVK^$KoKa3jn1 z0n|F}tp`vhM-F0kwy$8TrtQZJz7MgS`A5;t3En7jt*w-%J9fdbo0=5dTs~L9EuKT9 zz54$&;TR&)>!qz*4 + + + + + + +

+nu.xom is the core package of XOM +that contains all the basic classes representing the different kinds +of nodes: elements, attributes, comments, text nodes, and so forth. +It also contains the basic classes for parsing and serializing +XML documents, as well as the various exception classes +representing the different things that can go wrong while working +with XOM. +

+ + + +@since 1.0 + + + + diff --git a/build/classes/nu/xom/samples/Account.class b/build/classes/nu/xom/samples/Account.class new file mode 100644 index 0000000000000000000000000000000000000000..2ceae7a9054b6a29111a3caede7f42981cdbd0d1 GIT binary patch literal 2961 zcmcguTUQfT6#hCMvyId(qze z-2Q>S_J%CVT3vnXTKyOOA+7z+nS=yH`_fgy+Gn3L`|QiNzjJ2(`1_Y%0UXEI5wv1L z!{rEkn5aPmCZ)I{#gr7&Qe2hd4Joc^cr$`1CZxC?#tk`|^2tmTNz7`nBAm?0Nh*qY z*itNLNQaS$U>6p{aKbnlMoiAKQskt`%_#CHNa1QIMsN_fG~ABhu*~j`Kp>f&wFDXx z3+64eD`PsTu5mYSJE9=OwPC2GaKLSTKJ?>37#jZQq#jb+6n9Ep& zuJMYup3`!(?G#+oNm}$>SG9S>%<*Z@#+6=gT3yy=|9#11w&=LZcc|BPZ1=Q4wrz`z zk0JD5*|c9K)*gm+3EQzoi;FW>{*pP9QJu*q&CF#pZ_9aUG2qVItiSn5#jt59%N;5q zC2;@$t>?*U`O_9Tq?sP*0^7!2Gnr=ZRACyHNOS?eIXf$`uWhrRYA#67{DnDEc<}aLBi!%#W(%rD+U3H{JWoNmuB#xQO zSq_QVxy9*ef9&v=U?U;IZno^Wk({e!uzs^{tHe`T`pHp&*6KK3wmsF`OH1T^u1ND! zTbNe`dzypVN_@!^rku<=uF0!aVCNQY+ow0JmnxCWjCwE)%#$?)cm2?g@(s}=4Vuar zC~}tZB~IGBSs1nMFg!WV2mwb;HeyyP0|)r}YvsE&%oi%TOwA{Wtwz#XjbyeONp3Zg z-)iJ3fWY%gKZ=N?^7{NZg7ie%a^)l~R^W%m@n~!XBDMzK6jzoJh{cx?jCCv{6zg1u z7Bd3N2*-@zGHPN*Xc>_ol>1YZt*D{wM*uyj!&x-q91NUC3u$^7gFJ;pOy&Y!qSc@m z4BfPqe@9hOLTR#0pVDN3ex=D014@%c29+ku3^8UeUiOOM_Aq}ydJR$Lug4!^TSF~H zM_K7CD@I^l3sQTCy6(`As5e5Z*zUt`Xz11q?ROklL&H=o-qC3UjNl4(bZg4U&{omt z$E2Zs&v1Uo@^>>C;B_K8&cZKY7!$aPNq&q>tCGjO)JolBlB%DwTYLy%nzzI1OjGI|}g^<~8(b#9r{;Ia_M!G+jy@jo8l!#8uAOem1~o?alJhTW1gqmGirxurpl|wiz6oKL{jJ88g@-J zG_Sx&C~Y^j5vA>+HmbC})Vc#5OoFIC)5$T!(Fjn{sKnW*mN=W#5|<5Xk;$%NpFnre z2(Dtkz&F?NWm0wpC`?1*0(^Q z(kvi@1Qs!dEUqC(i<>C$gYg~8yKof_lBNYxY>3s%?%pdQ62WbDV3+_j(nC8nA7doh z(P4tojw|fwFmt&^;$L8tAV#=br@gl_%9S%**$dwzE```S4Hv8aUjt8mm~0yJK*k}} e5q5hGhe_>ce?q-%AFo5o5J#v}FwW73OaA~t{H%xo literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/samples/Agency.class b/build/classes/nu/xom/samples/Agency.class new file mode 100644 index 0000000000000000000000000000000000000000..e38453a1b6397d0993e9e34de32f0065a095c033 GIT binary patch literal 2607 zcmb_eYf~Fl7=BK&$%bSJl+xPN7U@+;NTasZ8YmUo(hHE5fxY{C|{BpXaNZPD6V z@8A5af55NafI(&)zd577!oT5+*5{mENTAg5gER1+_uSs|ywCfbv-$1spMC~#3|~ZX z5IG%}q6i?@gjT#M&1Gq>NHZqQxQ@4?XhKe!i3lcTdsWU%#c&O8>v$*1&UM+j5yMT~ zlE&1LkDw664opX2MQ}8N+p;$!&8%Ev$8ZO8(iC-+qUeO9qa4M4iD_OyE0m`#fwqx5 z=3O&WG@Y5um|L-(nO@F0W{Go#Td~Y)t+H@hE!7q*vm&79YaFgs1zNqmHP033B9-(lTN@R<2gcTa|G$UsQ~i z3ubY`tk|;e4{GkL&D5TI22O$4jOAYNIS?@aKd8?S%8?g;fO4)ftN8Xuw9i|33Z@SF&Cs- z4J45=@FETyI3!J%G~GDNbT+GEux1yht%`vvTxQ&9poY5!jv%Aso`L%ylRGx4+e0A% zykg)zyszVdfe-M|z$rWwh;K}oT+}x_qP%kI3bu{=SFlckfe-OWn#Tq{!p9`Uz$f_B zz-RdUX%t=|44lRpfo*jf&YRU)7Msl1bG?+mV-=WR!;tqRuzK~K7hJ1ix@DFjY|hVH z4)u}VLazSpZ@8pBi7b1>Ew2S`r!49S)?c9J`gsavkTMcD+<1``V^)KNBg=m`Bs;S!#&nuHU5`72PWrMC^4+ zcdpT)w7Nz$Dbr`oYRP(;U^=NS~h(_VJ5xkRPEizdV#Sy@1rYYN>M7Qs}DX zC4fMW^3#Y)O|SP~LWnylV2&K+iZ2o52yiI543S(#U}_n`MQF*?B0|ZYMTC>-Md-(g6QOSzEl6a*ZZ9%X5A&HAK^mjTB8PLhtkC!S^B&2>plFxq zFUn^{p}#2K7RF1E(5yn=#gB0>^9`YoD`OCU;hc_>I{KN?DU#%|euLX(`ibO^&{8Yd z9>CX#`lTyac_R@^;h9YCQxJTEg>bwqdpjjgC$y)w6^>DU!b@mC$G<;1%rU z*PP6+phMs*uBP{bU_Cms;Z%bA_i^PnPyR^Nxz8d_K-VeF8&u9ss^%7%G|6I~wJ$K2 zY1YAFRc_-7X8AU)WLX%=mmMg zRYo16#w1T22s|N#FiWH3e8b-b;7?w+2DeNKOim(B$SZCY(dcq J!j-pj^dG1YP38aq literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/samples/AttributesToElements.class b/build/classes/nu/xom/samples/AttributesToElements.class new file mode 100644 index 0000000000000000000000000000000000000000..1be963d21868a00820942fcb5e317b0cc9a766a1 GIT binary patch literal 2680 zcma)8TUQfT6#fPhCWHYrqBKQnT5ntv1Z!(RD?(H>ggXM()*(5Y=?b|Wu} z3X~-Ubwh?}*yk10c681w@Qqj*p`bZwm?GummPKJ!U(QkzN?K_>JFgcEIroGq_-xnx)?FxN!?YyIEGXEwt>f`RJB>58p1wP-=UrA= zF9ejCQgEo_#_o6}g>`woP!uGt0(a3dvKdhzTy4fmE7-qnXVgkNG67tv5$g)V+Y6I~ zAft)(w8-0rWm4f_*_vU+#wM!eKE1HQd>q)r8@Ki>1;H81DWt_ELl#T8LSt0{L?w+H zPT-`5V>qs$$xD;Rgj_0Wu;FO9kGO_4tZUdnoKZfXPz3?zE?%a+bJe!<@mOqaZ7sSs z5VZ;`vHrfkQ?Z5d1RExz@vM`7nz$*s`ymHXKoe}B5=Ni7imny!} z@HM_s(CY1bK3!aSzAimm8otGM3R)sYF=ASFWKCqVy|=AGPGq7QzLyOAfQL+{yMJf0 z6`UG%M8vitx*4e|f}V)IDk9~oi=-_ln{lBFLeE6l9I9x%q^E7Gu)$*TgcYvb!=T{g z3!Q3Na^r5q8QwjE$Pb^;L6?v4`FdLq1G#vRjX5ZnvgR>#Z6@G(Vn^3xH ze@D$@&T7!fcN4E)@G|zHm$z{YU7TrUZFHlDGl|seAx&^s4Q0(0q^`Cda3KN#r#!fR z!VkERhsxVLNCLQy@7A|Ahb@>XNxxi@zPsH3wiLo{QsEI4$Fi3f>e)1ZQO2A4d#tuEqm+uo`Fa76lK9_giJWO~Kpa z=)^k|yi>uu6g;fp-3s2L;Jpgor{MhxKA_-(3O*#H9#QaN86Q!wSHYukG~lDcz{jdl zjgQOtgqVCXj+^k9j87@}bR27NPCOnLkI#sm&&v2*94VYuFe%yNN(mun~2b{4H-5S9T}tp6=%z?$<4jgBnI$FDRGIXCy2S%hz~J&@fb5q)6TTC{GlENN) z+p);RV++aiQc-vgEyg*ysM~3mb{m_5JOxDiPmvEo>XKI0JzA1+rZ(o5kIT4ldL$hA zzm>npyoie;5v2AGEeov-8@32a zb$3tKF3r|Q^VS59cpmqfStBFb2;+R)wk?}gj0vEXL4ue0p(q8Ei5RvtvuFrgTJBCq zv)G74gubuh2l%0iAK@7`!iB6<@ng)X_z8Z>Cg&?HcPI(5gTs4TwhBEzQ}J{BLd7q! zL&*P1Lj6Kz-HwsVie@LJRs0%PRQv|d3JbrL@jDdtJ6+PI)SOs#u zWlF2~3$6+af0eM>kHmku-{!?~aO9+(;dPR5^}3xQXqjYnc~Nv^UKfG&Fv2jXayhd6 z{O%X-B%*|Cuj?gZRWbbQM-=}$ufXTQVd2UrwBmq%ia_`Ya71)0cd{aocP)7yQF`qn z_7`s|s+P@`(&z$TNK&^|R`BCM!YyI7zA+!~eEZc}p`e@DT_;E_VjtIXjy`x?sP7HS z`R_VsxEf?=2y zJMT#eEY}NCVp$2|06UipFI>$NMfW?Y+|&>7Dz>DaslJLHniLQ zdWsb^tH>Y)RA6XBRoCI8b!irST(IX=Ws+fIIVi@z8oHkKWH?&PeEZ%M4C zrXii@Ac8JFE8WFS=;m1U1h5OcIja)Ij0$l$QlzpfK&qKVB%P?3MzntxvGn?BR1TzO zAWLYECZjV@Bs_{Mu22<)+GAodF7XDbZm&$n5^A#Y5|(gK6AYFHgJr>Bc`!%>gW6!Q zA{eX;26e%pJ{a5}21ysfjRJz4lxNY9PP{C!Y8o$}!Yh(;vNEx128|KIx@iilm&-Go zm6ghJ`Ps*@G8tQ`T*50|C1(|B^4i}fuQknLO}aU8^E7UuN3oVEtWCzI&?KT5b)z_r zRUEe?jt*~5qq@Oc*_dJKDEU>_{>;y&!>&!0Q+F#7Na`tca0 zp1>e3vYTJVow$Nm<2elB?|lCQ2k}qbB}H&pQjnIG;HaCmFS&CBoe)6Ff|Dvs7=DfkRb_h{gQ;GU8^`&{J zk$axrBh>3@JIZxW{xyuoYdPyd^aV7?c%6*b%Q$uoPoqkPCS#r*1V&FhExeYXdDF)0!_~orj`j z{VY=HX*4I+Pobrm|5IoUfRcJC&fCgR>!=bm7N@Tb^tFS=b`oeeS5uy`66`J}+=Jca z!;WK%o92p;17a`iKov~n>FiD>FG-EUfqkjD=sBntaBJeWDQq}Di`&zwiVf4)*q54% zokN^E{gYtrZ%U`4T;JSJ54Q}otiOb3me5X~ z87rU$MedH1u%}3;39Q9wY`_^}zn^E$^3(%F_aMD{$n|?WPpx6f1X&|IwVFEKKo2%B zO5WNQM$ICrMXQpj-FB0?4-T!M6bn!4D5V5NS0T3adDP3e=Xu0Q%xj|g$%MqmgiHnZ JQYeD^{tYiWP*(r| literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/samples/Budget.class b/build/classes/nu/xom/samples/Budget.class new file mode 100644 index 0000000000000000000000000000000000000000..4990d2a703a6dced1f61d1a987f2ac6ba7d28eec GIT binary patch literal 3979 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 G_QpRO{Wewr literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/samples/CanonicalizerDriver.class b/build/classes/nu/xom/samples/CanonicalizerDriver.class new file mode 100644 index 0000000000000000000000000000000000000000..149ee61cc150aace1288f1d6bee50d9827a1480c GIT binary patch literal 1251 zcmah}+fEZv6kUh543vRFxd>jcAZRJjN(BYV{iY@bj0FN-OUC8@DV=bU|8`?B`T&)+|;08HX-5bcOHV-#ae7!M+jgofuq1Q63O z5rl@M5?-j=q=qTw`BJ&P3Zezm8fG-i3N+4{mgz1C_@X0w0{&I|Pzr?7rX{z^g#+pA z7zcT3!f88ecR!jXfuXcjPMp|2H3O&YEK$OD9F_ zf?-+$J<$*8BjeaeeDx~6TZIt(YrsypkHA)pivyg+idWaQ**M42L1InD{jD-(>@ zfr#wBPit7vv4|ysw#vvW#R>FR6#p2Q%Dt>(1*0!JYE+poC07=>Uu5@&6Vpz-BkwNRmqvk;y{_i2nq(A}Q=UuH9jzg= zW@q{;dPZ)0+fzYQ`xNk~6d0}-TP3$(>tG!97aN=tUnj zD)<+C@bg@VU44UohK8{39RAZw1okh`n7)LzPvr%gHb-N=USP{EpD(8T7Gp zwDR9}O0*t+1t$0r@IhryGAAepcx%NJ+AvLzSvHZv2sbs3Wm*?#UBnEQFppKQ(n&@P zFycNQu+0%l5AleYNh0^-F?FmJ5&D35Q4HbTsCUyiuS+^(!F6L}UuGPzXviiW+%>}I=}mGy3W$g1v> zivDxZ_@}Wdpl`YLYC$g9KA&eeN~;kJyoJl0&Ai7EO^t{gU ze~I7XJAnbyu9=SOnLo)=Y3c{JT9(DMf$y@$=8}CU9f1E=|uhEywKQGi7=Q z(rg}}S#ay6qF=g7J#o-0|ED&dcL(VD-k~f|co$k3N_N!RzVxz6c3HRX&`af0X_2;7 z>2zhHEdgJtXNqq9T=Qg^X>Og#9X)JNdL9KRtul2I_T1e(X&y*Sys6JFHoCF|w2G=x zqO1oKZ)W-5%bpUk!1#IoHs&rp_gIyRbeJ%8ZbFBu=9km?L?WKsG*P!?CNFDBT^Qqa zGt8?A2tp{m^7%G*8LonBIQazPlrI5{@Y&-VleofnqZxpAFv?x85-lw%`7xt|Tru66~+2N}|gZU&wbY?J`Ud3mdK4Fj#QXSfId4#K!a-#m8%m@F@4CBd2yGuL z^#KWf0nbR4s6-!}s!05R{-M5AJ+tfh64Khz%3Emzig#|q? zYROUxcl5`y9==FpMH}8t;Y$Pebil7t_}aiX2EG;OJL`B(bY7r4dvsZ#XVxz&fx&{~ zshhRRstT6ms!K_x;M>w&mVu+c8^WHb?1Tab3tlb1=2!BetW;eU=4S(^68hfl!0{p# z%+PH`I-bDp?7hMR`AFtn>6P+}5%JQD4r|zng}_L`4@&v9ic4o}`IY&?f|AA9M_*M2 zp$Y_cY=pa9bKC^$F8a2>&JEy-Z`Ufyix{~}tqJVi0B+L-V_sdeRW)*aj|#&{MvkAK zyVWii-1NSVWEL;2v@7(;pv2AZ+G0xr+ZQ8gKb)7XRe`BHp)9F0mWC{^mLtu%2FN=d5 zZJOM7ykhLE!T*L+Ca*)-t_;JfjLJEI(f@4>9_1Eew^ko0JL(WEJ_#dLVQcy|0*m2tB6^5?M&sei9({M4%s!?eZpNiR+H8zI zS*$;3D(m8*#r%>HPzJg*Pzn zPOKv}KRMn#T}S^-;!|~`sqqrO6ejAi#T#tDyN-caWiYd&j=?&1QiaOY$Rw+VQf3!n zN{8wg&g@RsBU~pI1oqUi_a!C@G~O`TPqoB(qV*Cc;nB={I#G0EfDBCY9n|wP?2G4D zI6y?xu#91j$Iy%8d^o3xevBb}f>E3zHK%EFgVGtCz*$PqVL?BixPYH=5l@Ld!(}|j zEPmzs6|UnqzSeaVVp^~B`5wZ4jAMdyjv|9eOtCs+NFztj-SoP`*vE-Jr`|C}67-Ow k{3KVT9Un0tFfwS}J760)^*8PrIL*iVOo}=)M7nVHUuQ2UUjP6A literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/samples/DTDGenerator$1.class b/build/classes/nu/xom/samples/DTDGenerator$1.class new file mode 100644 index 0000000000000000000000000000000000000000..fb22837d2186b1f994443daa63fb9b256d83e021 GIT binary patch literal 214 zcmaKmK?=e^3`Kvm+FAwcS_Bua9RzP+Ra}S*y}($9+A^JyPAi_xg$M9ZV!Cr>KFKGL z|H%9Ed;wUZ@5A@eBZNm=mxSI`Y&Pj;gWz{( z{LHuZS14m)v@`wyf0N_*oMgF_Xw_kI&U@bTUZ3~bU4H-b*WUo##CKs_LMn`Yq%_Qi z;YX^Sn)DfDFxQSd_*_Faj1W>Ge4+L)L&&LpK8!pTl-Z(kxT``ewc`c~VJ<}_Evx08 zGQJt+~>1=jA3NAGI(!+}cB{X5=uL=f`LP z1ubE0AY7Ft${ht)mNw^Q)oZ4sEH{m$O8{5vuAxEFtGH98<)i4!aQ>K~Nym{pil#c=#bpg^It)D3u&(17Oo5(; z=?QkG>ZY}3I7~HtC6mj{XY$3Ejty)QgIF@ZGNZ#nS%a;kf~t<^c&cLngF3EaLWhGX z9W|wMl~h;D2DTdElEq>!TPVf^1Ves{GPSY6?EGRbn@Kb7hxkatw!qc@#YU3Rvp+#` z4P<$jr+;(8e1cE+EYSb9l-K#V9wVy26bO|nwku7WPfyoL0xqm>7$r9`^5D%hJ2lr} zyZ%a@zFkc+nw9t>-v=(=3Wh9`X>GDvHS9G4a5AFz(imxBus10>hTKZiO5nn9t066# zjCeDnfL2xcE&EgthZ#vrzg%NIXEOQa`OH$Xm|e)TOyhZ7T5LSLoKI(FbIGMln&g;G zCyPmeOT)9R6&#nUtsAa-I0X9M0y)7uJv+qs_U`Amz{fvQ8G;&hy22m%M>#-={O8U4 zJk9VMA-G3I58xYp4gbmk0^I`(naGd<_S{n4>kEMsi^0G&TFiXSR_ zH-cRA=)eNTu!u=4A%Q}pl2|jFR|)eX&JI;KDO{uE!}UfSF-avKn`W6}fFav>vD+Ki zLv$bKU*fD1FHn4e&>p&O2fT_|U4*4Z2KUje3TMr?gR11-px55q;YoFHqSYQ+d*0D^ zC^yZBrUo7(Ew(#m$OJG&Zqmwkc^5E7T-STTM(NYHEsT1k(h2@&o##Iyt+bl7x{N?sM<+{+vDc{`ddCZvos#O~*6}qbTB=6c%;d#XSx8 zHGHchi3d6o_%2+&*YJaeB^_5#NMSjY4^w!gL&r*Zc&y<`3QskxYA9(~6EHR%M|mY% z`o8i7CM&XKIn9dOP#dxyxZZ1lwTio1UKdP&CV7U&B*U$WSq~|l; zpe-)FCY!rDq^Q7HHIVf`wq!e+Bm(w{fL?VwUR`Zi;lz`JQxw9+4P3_!4eJIr@JwKS z&zDWLWQHcw=@f`Sp^rdeFxK4LEo=DEz$Tsxr2AO)Clpw?1ib7=Uo7n5r*J77*g}Q# zMUcLajKIu?K%bWzFS#ANVLEPLdP+7-1KZdUNSTd}G6R?A7uYpW#V)h-Tl)}QRy8d@ z%JG-7?SCCy)z!i!tiHN}hcZQ`u%e>~+J1yk| zY%?j_ZRHTLg`oi#K=p3P#cj}=D%etfNIZe5+}y|0@KXkEFASe!iZ4s}i-DH~H@MF7 zRHPJ?iTnw~ab*6KYcf(6aFctZ=YY>}i>FbTOcn);aXv*c>R#pF{te?F#4;mY#E(yr z*gr*bUz|WYL8?5TAGzDb=+AHPyiBK4T-iT?ks0eE9a++uac<|kn4sulk~!Ys)_`l@ z>*~4rm}S%W5_5d;LRmuwBM2K`;u(B46S#*lJm6`GjW5%uMDIt)V+HGYj0)uo>S}n3 zJ*=WlZ8&Tmar&ol8((n9JibB}cevXlT3KcZv3f;qj&hEF1M&1P8uEu4=4aW}*VM(Z TfEmgRch@K}ks_c|_yqbpdY^Kp literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/samples/DatabaseBuilder.class b/build/classes/nu/xom/samples/DatabaseBuilder.class new file mode 100644 index 0000000000000000000000000000000000000000..9c3a8b200bfcdffee72bddb628600fd23a1a237f GIT binary patch literal 8207 zcma)B31C#^wf;^rGxuiZLc)*$!y-{!LV$#=SO-K+0EvdMYKWkAGPwzZlbJXRn{}z$ z#kSU^8`d3Kv6a@iG?AkCmbUb@K3n&``dYR1+1l3H*5y?c)9?JVWCFDB0r&pf`TukN zbH20O;m03-?*M@L((gwWulw36yhZh@!4YO zz_z8r7QZC{KTZ%w=|?H$Xzf)7q)NtUFBp)qQf*0%AN7=3#>sf4n!uriESae7BtIJQ zt|gN#nPSUve$11py0=y-kGJIn-j_r&O-}U7Npi9!r}(i*>hv|;FZD7*do%ULqv~ar zB@MPT`q3;?bxgvj%+}tVa$x9iK<3Ikea*M=u#Kl|`G_qGY&q4IkJ@sYEn!<0+Oo)& z(`{L7%Ne$uY0Fu*G}+QzAxmVbEzA6Jwk)@$#V_Z`x%z6gWCa=NMcVvuWu?AWDeKkR zTcfYFmaMbnJU`-Uzqp)lOS`s{ELm?$hb0&Ik(LdXY!vvT$wb19W@5>Npr&qG#ds^97#fWS1+bY>g${wrp>wn_3s? zjB_T~nv6!`=SNa89p@)KnN6{@ptd!UZQPdZZA?dc`{Hi8aY-Z->5Qb^rfe+U<)+Bk z+T7I{J#S5`9%Mu-WlZ6hZi~8oDtp?J3kBXz-6=TfSYj5qElEbRy>23-_e#X#XJs;a zdR`QcQnAi##^tKh9dqMdOXIFm3#tk<`597pGTZxjNof(-+HH&Ua%gGS9P3HsCbTgV z-Yt=M)~IFpHY+TwiGm#v{E&r9FKwch97RUaOIAes zj1h@KOD?kHVoN?|NrZ8Lu5JM35Kvp(RAyar{R-NxU<$=lk=4SMmgxm;wh=ihdS^V@ zsR$|}u!$2~pf>ahf(b_2=Q186GQ1J_KN=B~c3xF$yurP>Gw|ZA%kT z;}|FA0jVM-`m!0ijvMJ+SX9C~GU7?)$t?MeD@u~{u=Z3eW90EBqZwBPiVgef5hpTe zSwcPd-E=h4=jyqFv2`1oOEjKmCNbWMm`aht7k3jqnN7ug&6^@riKni4Is^@T}nH)fT*=DqfI=v!|1HMD@pOk6xK?d|AdkQTM|@6A`^>f zEGdOdDN%Dgl1{fJGs}|ML{}+MK5UxUR66g8qAV#ppGL zg^Awl8KtT6waIKM>Mo0^2Ue?hmc+*fRhNTp(rrnPBby}VU<(;FeTjw<-Q$3AyXV0F~NT1JHbym!%H0r8=TOHXZWU&L!>F_c~J}#Hjc6uH|Fs+

9>x2PCieJ9SOf&e4$r6a=m=P!4GlJksIVjM&>*hbtOk`lACGeLii|& zIfC(d!c~z}n#r<=4hAU=Gqv5RWN&SA>&m9u?g)+CrQ&|kkuS-Y9sCqO6M$jXk*~;C z>DDR+%~xIER2m+1<4#9zk)4)&&5>KFX}LwsdV}0X;qqe8JS8GbSei;DQ?*++k#jAh z)ht)T6~%P$6Fi|?Z>O(1a);dM$X&{s2cGntIL5&ElN zM`=?aPlu!f*~5Z!ZFRX724~LCnJS^%aS!qm*wZvo02AC z@$hVv1V*GxOcSr8`;^Z*Gc+rm+cT~ucRP4ob_wR3myYzf;ab&T?Gf%&%Q~ahxMS@I zXWZ?`*TD;pC&(LmHfI`odmDPUGwwAkF+OX_HypV~_Be8{d{b~-Ue*7gs9`M16AXtb zwcWQIxsL|KHMmx-xYvli7GzZ6cYe?R0B% zZmE&ZRfihhd+g{&=#1Rot--H=_K^|Kk^5ETs^K$pr46xerZiQoU0n*m64D%&S&0pk z77Clv$zln?ixnx0lZ6t7=qxU3if70(<6?3zTwrAOERYp*8sko#CYfAh(CM7XS40wo z5&c24*l?vRn10N{b6A$rqB<8a^w821Dx<6nGH$V|)6|zwj7EP2rLbCX+-+`@rE0!D zto!G@i%R0o0L32G)p4Z^ie!a#l8-jGjAwfj&9pv!j#^>K>q(gEf&*r%oFEuc|8)5~j$}L%b|1(+oc2DXj{S?u$g-C2lkxNttSbP%CaKd^za8 zN;1#IPAY-LAPd~02qR%h3s_2QfE+9mDID@O@-W(SMB@tCaE{f(|GEpYjv+^Ra=@0mm%7CY(DB{T;uB8 zx7^)U5Ou^Q^D_ODOHN`XeH6%xDsU)s(eI9M_~LxtZJYRgob#~K;JH1`FT>IgpH=)c zFMMT`1iO-H+OAKD;>^~S!}m35fNR~!RL-3WA8@5MDZPu?pu#2Gt+^Bx)mn9gDU03| z%A1n7T$#<3dzooQfX0L*MmcbnF;HZUd8QW3ygOK_ekD%y!ofUzoTG8M4Y(XvaKx>+5?AriPq6P{do>>>cT$l-*$?5x81f%g|1iq;!6R5P zbH+Y+%g{D!AAEwaC*;}3;tvbF`(bwkD+b{Y`?wYm+=4+f=jfc}Szy=LHQwFWX(mVM zWJSnRWADT0GA!`d_#Z)KIItg89l2 z9?j)hdddW@R)#7MVzMC#1%ttfdPWtAsz|g4V?q`6B(3oeV$uL6hpbQqn`>>^Gt?9E zg)9#C3@w|v4^spoU+}nKZ~#-;sSO^_&VHQG5%LXUT0d1)ZWO!&$FYAJ{0Jk6g_wv% zn1;n@z?nD|XQ7FIThWYmEI}uhB8FwO-g2tE1)s$^_$toD9aw>T(S~nhB_73U{5#gL zf?vn4oAdD^_rHSm_#Ha%2VB6PE;r&3E|PLwY>c^=#(5MLK8a89hs$=F?bG-SG15td zTt^Oma!lcKG;n}?qxd}O9L}%C^_&|;{!8!$&W)x7^Kb)hL?xxX4L6a-ChvQ2GtaXW z00qFX_eBF3Kc)B*zD%G5@jkx7v&wn@x3H6T_fQMJ$Jcm@m%4ZfxAGJp&jr!!8{(8@ z;dTpmSh&-|T^8;Z3%h{B7{^wIcz)F}2y`v?aN};Xo$s#`!$Z84ee4~e!YT~9eH8U- zUb9TU-}3mrnF5ZYSVL3p;~V%-KE9!$p%2(DWP3Q@eyA-!a-L!P7~5yrp3U|-wj1kb zOx}kR%P@$O`n7v9f#|7Mzc@vTlXaZ*hP?YQolfH$uX+Ih}6tJ@gF{eLVu^;m~g7ZUkxQ{%pTo;f5_i%iw zkwV?UAMzgrJ*KfE_)*;z@(GWH0Kl&Lca>!nTYF=$K z@th!D`-qrJaUs&^Mg~b*aSQQuB~f-0!^Cc4v7h1OLCzeYiVre?JV{KxKxDl}M7}}m zzfC-9_}NK>t){j15OE&T&%rnGEgI;0BIZ7Wx(B(sm)=l8UhDB4O64WTmE`aMF|>u! zJcx&g$%A|z#&=2g0_v!^8;SNeh{ZvoCqRAy;`t$-MdKE*%bwztU#sjX#x-^h8BZxU z=)49Mivra{jJtk=rn`hG29H?SXW@J7?N{hKQY^sr}2Rn;!|L!PSH z16UmLOnC%n4B*U}A zUO)~ZMEUv4MPB|iM+(Ggld5X(hIi85Nmc&4a_y*mTVdK@%ibfzy`S3aXXJkX6DUY6 z@vh?LQv9@{kkc5}o+Pu|xaNb$witcO^k*Bs6<7{u8RKygGy6Hr@NCW%EGBj;!yc2Y zTQymGnHWu0Uts1|m`oq?6%)3H$yQUfuYRVNW-uAn8}gZ?dNAZ2&YBH0D8Qm$nJA!s+HSnNjAyRO z`*bz&tl7`Q^UuK3-0L-3n253=PWcR~o?!|isF+ijf)wMHgdbZ8yH9i~oyv%1K>2&Qvw)9u)XY9Yp_GylJmPuJZVXG9e#!QEwjTyt`-5%$!Ik~NRp!0AKgfS;`h#oxgX_%uJTpTA&U5no{$P85aD9KU zqd#~-e{e&8aASTWkIQ+|W_k*U?k5PlpAvXa^Zgvb_H!EVMUH-jXIRuci&r`OTRhJ~ z;ROcnpX1N?FP0C#z`LY+5C4r1DCQ7;C1rR?ym(n`ydnYoT1MkHG6t_oHPxWdaB^lb zCWkTa6olT#6J`J44>QK|WSI-9k5OpV-tPJX-d%7W#D&3&25|8M_2qM0>pgQ<1V7eR zf53M)ta|UlXppd2s5eE1;kYo^}3uT+$!;)o%-DGTX6C+po|;`MU&*(_gb!#?!8&BV5Hh-A0PZ@Kt_@YU7`D Z(K~inc=bXH|HJ1sANT#1G-deR-vMCvEKUFb literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/samples/DatabaseConverter.class b/build/classes/nu/xom/samples/DatabaseConverter.class new file mode 100644 index 0000000000000000000000000000000000000000..b0bf6b0f389fad4d532f5b24804733e839393b9a GIT binary patch literal 5899 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 S2SVcRD~WrO?kT!sxc>)~#rZ%0 literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/samples/EZSerializer.class b/build/classes/nu/xom/samples/EZSerializer.class new file mode 100644 index 0000000000000000000000000000000000000000..94a1bf12f294d861b7863069b08bfcca23d81dcd GIT binary patch literal 1107 zcma)4U2hUm5Iq-mq0rS9XsfiM)uL_zyMCY#Qt?4+LPF&M(?k;=mgN>MfnCDxD)D#t zM|^=q6Muj|$~brVY^cV4n7emo&YUx6_t)?5KLHf+ERHy`QQVCqgnKgFmqC}|P==fg z`6voeJYa|%3tM<)hOmBk#-O}%nw(*yDs2AN?=|?~o!RJ8CRueXvwLO^guG9aiq{q{ zL#Ar`#?a{*uG#B%xoez!sB>!UiZ6UnVo*O1ghw-S`mFJ_WBEO9d!=f}{A3!!F>1co z_r1C|;AXERm4us)#gJG*P{r{*hMmiy3~_hP&5)YmF9?dC3@zUGgk!r=95IytSJ;HJ zYuYV?qzb!Ls#K)dHEvr@lkU=}V5Q|Aa#`3-T9<)U7-BthSmk!hYm?Zz;}0zUMo5nn z%f21SF&fsPQDN%uVhLF=G$ip*!y{<&`dGsg6g50Wkzwy2UxZZ!eG)B zidX1n(8=gjb7Qb?^b*2Vijg3xV}sshD>Yyf2|8nBT8g?P_#+Jq)0z5#(EI!t;nPBH z1f?*7x)_y&LM_Jxd_kkj1b-Qf-Jd|Qg>9+h*4hSy#9FFWkgAA^hHwecdTSk$0R|^CaWYZt{h|M% zt9|SX&-5YMx|XZY{ULqsv#YCgpUH#(vN{hl=X~e8?EPKN{Q2+iuK)AZ^nZBE=CYRPQjA2U6z_F5%l6y z`T8t^JGdLgJ=~YC2MRuyT3;x5s6Z2FnlLQGofHTrhgJkaQ+7cYXwMjyo~;yDb!SOi zH7SW@?7U{KXpSNGzA)r&7-fOMj8#c**~N5OE0#>XoSruIqHehvqwMO=IK37%!xD%m z?`1Z%CtBLnto3xxbqs5LTt*ER?7To{-OV*SUy=6G(y?tRe44%_zBxhHgynfyA3UVOHDVbLlZ(&eDUPS>c*~oHPTh}ip#H_g#C+bQaWl!_7_t8nm`Rq^Z()=c6mtGI+o6|U5*;E95#Dz@;9NmKD9zEbfu zzG-0h>8bb@-w6!-Uwg9B4a{?^n|hvAiFu-mYnbU7wy@^fvLhOLxo7O`A{*DPP`lA- z^Ir8_N7st%mpwB3#H_tnVw3gkP98lDVXp}VP91e#@2h=7((d!CiBK>O%=quLmRX4kGwuWa<8H%YKn31C()%LY8evzB6S>^rEm zP|-|d&Cm;3tw>cV@}|f~DF^ip=w)KBb8hH08+(e0{N66XqDkaBoYG?&-^Ijd`g$;xtUm!!&%r-EoB4cA4FPg0l)S2nAys3O-VpEZ#V^ zWZ_48mB||Ge}OJ?8?NFQnex+JMSOPTH}nYngdQ%A3;coJ>}Y@Jp-kHDU$QVY&V9rS z+@z-&^1Vf@w}~Ues9BFsf_g(le8FQOf6yOuC7&@|WG-rmqX_(ixPpn-gdLHWlMhlS zO-VE!oapcS1)ap%k*K2Y2O+k1|8BsDc~lk|D90i!q8qZsWMZU!7n3YQ$^B)n;H9ZW Q1`UYUi1FwOg#lds51EDO$p8QV literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/samples/EncodingDemo.class b/build/classes/nu/xom/samples/EncodingDemo.class new file mode 100644 index 0000000000000000000000000000000000000000..65354ac719dd6917b59b8633dd7595f3b6ce573e GIT binary patch literal 1924 zcmaJ?ZBrXn6n>WMvSGU|5Z;=WT3bcJOGu@*rO;O(#n?csAevIG%VvS4n{3?dQmikZ z`UmuT)tP>%esQMbOwutUKfpiajN`OEHzbsSjG4)~d(S=RIWN!IyMMj>V+X){_)f!H zNQQAs#atLu8bWwqMM^^}J_sYO;j*Cd2uL(3B6 z88=65d)h8o3qgL>@|KKMhnh&*H4SIk@NBWKD`kJfE=f3*E(D2fH=igO`J!W$63K$; z=Ip}ytd)1C2%0zSf`s1KN_x}SG7^qaSWhha9xbM7L34(0Na$$5BpoYn6?}S1HvcIm z=LS9jcHM0^kqP`_;1j|!@-)**7V~oKN0uj{s{ymPr=&@XwJo7%4}Ijo`Dc8Ptn7y679`gL$J^>zax+l26jf z1T$ZB1JAT>*`mjt`};N~Lg{!L1GGvlX5teQ7cR%gB^;|ayWS;XvQEb8+D2TV-jL9D zFyZOIc5;@d!^Wlri1mRE2YDR@fzwgPx#^Ri)xc+3ZRUpI8K!S}%w30zq5%I=hli5z zV)4goVMvT9WIs6)6hZENwrOlT@36)?{%djsSfFDI4|QzgD;*4WP0M7K`&7Hr$BBp?rc{h%SFAx~V;F_D)hHINLQX<|Y-Z?? zW4*PUr+72e|FIAIZX==Y*kNEI9@&jNvqAPGs9S~;SeZ4Ef2yhdV3Q0EB~W|$bR(DB zM>ZRBZ~|&(9eer(-Vim_NIk~E_py1Lwvy$i3OT;loCRjhb}A1&TMStD9(H4TYxv5V z6O=f^-Hj1AFj%JJmgwDHz(hR#wM3N3-1NK1Nn=SQf+q49JTp=?C1K$)+iH9J~? zy3mQAFUmb~r}XM6=4$6*o=+-KrHZy}1zL~%Lm5XRdKpK{I95ixIO)hnI(y_7h*Z#3 zM)zn`DWm7HEIoTwsiHSqca8MXX@vjC@m=oCb+>PNh&T;oHE@2#Za2_lCav5B6 zxQI=j6)}nLaRpCs6;E*uzv4Q6!wurj5_uXgFoQoaSL^6KcJUH@}9L-`lFit8`Y Yr{ac+o6-rUkb%v}wWYe~sbO5dZ)H literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/samples/ExampleExtractor.class b/build/classes/nu/xom/samples/ExampleExtractor.class new file mode 100644 index 0000000000000000000000000000000000000000..f9bc4e6b454faaeded3c478f8357e255c67e4ae5 GIT binary patch literal 3643 zcmai1i+>zd75*msn8{?4G}|PwZ7SOqN|GkoQk2$BtbMCp5 zH~;g>WdQHQzf|nPX%$h}DoSt~P{tY2oRu*r#OD<3R`Fqcq#n27UIjxc`};)mQ3dx4 zjq@r#h6fakiphftJ}&wPWIQC}VHNE-t>6*SUl7eFMDwVMHhfYvkBR2-dOU$osrWRW zl<|~`&G?Lriwd4ru@%qA_^gU;_?(Jo@p;jFL1=tY!E<8A^DLmIOJS)r*c{OOOU7ME95l)7dK_)Y&yDA-vzp7!sO?%z`me z&QBZmgg%|4BsOHF_1vUxn_}(@!%o&LN!UD8C@1Hve6pnHi#el|9GG(l19Oh8rya}g z=c$G_k+8nAsy>i2@ZBux{<5*y`y?XdHL+^r>OJ=$s7}Mit~ua#VA3UF>}UU4AZcPCDN+lWxS$cH{Pw`5I&&ccI+bsW=6v@ek|iB8ZKi=!>f3WH=~rc z4THRE!3ayZZf%bETr&~r0DgUiVc&TTf5Bg6{7u8(@egw3 z^S?Hqm=INXfozg+%Ym|yaIA!0NCf0P2`6hLip)vFPNc1JE|Vx&PJ*{5lc-YS-i5XK zr{SN%wHy9VS>kH-O)^PTV(&ne4xD%aS)%#Wz5^VG8l%<{`s)2WYJGWeoi@W5kUK(M^bL;mpmWqNq zX|3u9T%)!Cb7xu4nKP!5@k-um&05im8mFm85N(~od+u`7-^GJ8n!4R#jhrq!yrAj}@N~}GA zD>bsCbI|A4O+r<03jzk$9wnO-F@BJkqm> zcch}1a6>#=L1G!3r{d8?Y)Q$>*g6%LV>d41rj)XbZBub&5jUrlxDpRn&>1IhU2(aB z?s+t(>fKSio($FT%-$GVWrK!dGCU zo&(97yX}+={vMNwmf{ojfe_ZykGqLRgzzvCglR9^efc?Pq&&90f}Vx$(9WS{^fFR? z!#$i-kQ`wQcZih3-eKx7Clm9yRSc^ky_F&5?l?nA8#SHgsyes4Yk}v*(1jk{;`Y0k zQeu9b|5SJ}8Nqtu$&rOT8eyRcr>Ws!1I}Oz&QbRQci+o@P445K3*_g1vU{G)J;*3s zprwb%IuMwn literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/samples/ExampleLister.class b/build/classes/nu/xom/samples/ExampleLister.class new file mode 100644 index 0000000000000000000000000000000000000000..265b7d89b8902ea122ba028c77a4abd065afccef GIT binary patch literal 2831 zcmaJ@YgZFj6x|a76UIR_2r8hC3N;{*`a%VZt$-~ypn}l)c1T7ToMd7$QPfsz-|u&= zwzZE>eAccNZM&AMpZx*-1^pE+SM9zt6Ow?fYdQDabI-kJpR><7;o(2OKLD^DzbJ@c zyc(M@p+LneA+#xY6-R~Un8MF-p+2GDBu*{CX`B%sUB;w1i;J^F2z_E>gs@la&&rrm zP>1mlrp0QC&rAsCLa@YnQtVUJu#r}99+?obG8_f#FfKkh1s8Bp#;iEJB;#@jS7f{< z<8=vgd|FRCMn-}(B%#Kb)stz<7|A6kg|2FknKGR|2^EpH2?@b|J7GwujhQLmnM(Mc2tqczH+hiGm?_6U z5eFU`dU8xh#3IL;5YtYHsAlvFdeqWWQ_(Sp*QNy8q;B%8A#!~2sQ@fb<_K6MZyxeK zPs7WhgdNX`*d#131T^5QcpNZhC9EzS9MUsc`W&2%8!jLk)O$WnJ34f*R3FqcQ;f3i zDVWGzE@R1v$+*T6Ap3-|CvN$~Dr0so6F2soE{%R#JZic`^eQ^gsiF<-D&D|#KBD4H zyrp8Z&}_qY6>sAm8Skoi5AQSkDn7u6Dn7!;5|;YR`-H0K751Otx{Bw7^eH}*uySFN z{d5tg4@Xn+Ild5|FIlEt%u^3lUx{bF7LdMCaRcAVxT)e6zLU@~p4F#}9!)%~rE*;? zzAnG`y2>Kd#z$jCl)hgT-{S`vw^jUzp9s&7VIdpB*UwD3i@ux?30nqohUVCsp3+K) z>d>5NLrarZBcsLboR!d0wxeYXJ)ucx{=YV1H5JozaB{|ovxLH~D(9G1bcn5_JGQ90 zqMcW>Ecq$JaT|0*PqM&QMA{aYE_>ZLpVKW?QGKN3*z?ufc8o9cnSRlYKAocA_*6N^ zSypN70u`Aw=kYZZyqDqTEqPkcju^9yi^GQQf+=@Xj@F0cfjLaP=2_E7_;C^6elwFT z11X`UG#tN;7ow*7Dm|SxQk*oMPqkl|)nZRKM}`*Xvv?dLstMi7G4UbCE>Kd+(k0Hp zB%`)-jJ??5hs2zsy`d)wZ@$h-)XbSX%q-5MGSUiBiupMyhL42Sr$*%yRWgDK+!a)8 z4+b#GN$a!DvRgzt@s;^66wL(I^H(gwU&R0vF+(URVsdb;0vi>b{C05{XRykCr=V9}v*7lM=9t|Lak?@iAl> zyB}kVjGjlRctj&bOiw@Jn|{RI3&Yt|7S7Uet(ypi*q3=B)XY8ks~AEfX#j@Ngb@;T zkW`E^pd*AeF%Po|E9YT$Gn@j<6$m~?$VJ&&ijtGvM|s0XIdB(Mf`_X1@bWx=RsTTU z(R)}CuFty^<*_on$`z}GxSHbTJQ}&=(KLJ)%_L?`u_ips#}UE_)Z!HJo+c_C{fN7m zHMbT9m|lUjme}`Fl4uhm2`>;+HHql=FiK6d4$|&u`-9t1=`6ffcx$iNN|@^iQycE! zCXe+aoq23eo*n!2#2#gvm;p z!BSYXu+hlB(l)@M?KR%I$R3$xO0V(eW!A?P9L81tIeFa;wvPlh@|^(;($5CStgjdO zekY}uFht!hXp|06Vvu+lF|J|S3^NsyTW_lnT>1oS5RfqTYD2#6kVs8WRhWkkP;+bv4TxY+W>NrglkKSt+^C~l?YlVlOY+LOH3w+@BR}X ze3pk)?f3Bm_(?v9>r5t9LO%0l=FHh=?Y-Aod!O_2?>}DvxCm3j30z9yGOnojG>NMk zVkjnYN~5VHC)ZS5Phw8P4cyd_!YygMEnlk=v!>#XhBVew*uY)+^_hlEY^nHMMOh$m z&GzizhCnR8up^+X`7KM}Xxa9x>cDMUz57Pfp(RuHO~ctSdbXTLMkVOjeSy zk1r*1N5@xqAdw9nU*jQ*8o|eVkf=v0zR~e5c6EG*dn!n)DMQai*#D_0^Z#4MTJwoz zQm*5nX+nRhSgWoJWb@MtpA7s_8_++$@Fqs!kkRd0UTdvmJFVC9j{Vf9hBL5gk7bh0 zsUha4B!4rN>_gpXwGMhKWId^K*skgPxGAbUGodL)Pl1&v(}W78h}NW;-FOgb-0hOa zf7Gr2)MtNFMt?w!vm@HJU)bi^2`GVOxblDm{(}mS^lVsz*OvviZOW}QdKntD478E3D8$8EMnNMXO zu|`JOL%gb7R#INZk!~#S;aE|5j!aG&Vs?n*l57l;>?-d!V1;A~q;nn_Tp-y` zFwd*>94_G&Z^XN}f(owk92K#H5*ozp;yP^JpaBtbqmQ_`dD>4=tvGEt%;6(Su}%AF zoPkOWx9~AB3ef}PvA|Km4_M@?U~U&@`87iZCYBf_#`rpxBW_^CnO6)FS8-0o`S5>% P>i@;NS&*T~ixn6n6X>8JY2-m;Fgzh|jzEfYOdL`+44To^#IgocDe9?_YldxCAGN^H>TX zjb#-d`*AA>4>Bq~@#D7q%=$4IYgM;2ypT;%jWE*c9;O(pdqK66j0WR>RROXHBa#4G*i?7w}y(Ez`L{zv$$e zfH!5A41s~HX&Jd@wP@5=wPJ;qaMsqf%9>U;<$v4gb+*lhzAIO-Of6+n^Te%crX?^Iy_en5cC|!Bv&xBrQ#Y;hyv!Te>VyakcHq(#qiR@= z40nubjp41}s^)Ab7R_?Ta*VQ3=ejU?XV`?@bh>V)oya$xTGL@3L#xs)*f3as#r)o= z3k-MC6uL&b^cZ^rqpdsy$mG*|x>0jX+mdBy^)mY%en}I|U9lg~W!o%o^HsNPR^lny zy1-B;(~_+>+a2{498JH!qSag~Rm`dQhVww3DIqH@*v-0bESr-1ku!YGND4z3#{`2y z&=3>oZ%feTj7hsen=K(5GNLGO;s3>A?N9f|$3xIj450)=er<(N2HWa+%8?mp19ZBP zxa9wwD=ayaMV}^~4RqDrG>Ur3)MuPMvNO~MIuSrXN!2dSUXhSHA>2nLgeoRTc?bNI zAS$d7Y}8bI8$ulvX7FUT^2HrPXO0oqbY@~GOg@*U&e4}oW3N`L8CEH^ZB|MGe#dT2 zD$$~oFGetLw4-p2+s7Sfk5*#nOgq7u{PS&;vu5_$Md}Q{+#Spq8h|Dty=A1|us*og3q1`tNXmp6s5k2B*vdYE3#-xVfKWv&BCX zVgf#%cz{3zuiKtoVl;$SBEIa;Uh(MoSv=h5M)a@84>6GR9$|1jJamX~QaQr#dPE729AY%- zi})hm1B^v{2YBTX`jW~ML?q;^k6GUae!@P^@i>?L+{GB5v#j?L{Fp-+S4hiMcJU$N zNYXNoYq*XD+~C>2fLmP8;U-qGNTem0SjH}`KOjTg6(ZlpLuBzA@-9a;^b+ShCNTwt z@c~RDMixh1{2AsoDMSJnxw=b6-^T}BeaKp7S&SeWVKgUd=sJO1;hI=hg%ztq8m{`x5ReS`Y;%0!riwx~S>OW^hq>KOn literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/samples/FibonacciFile.class b/build/classes/nu/xom/samples/FibonacciFile.class new file mode 100644 index 0000000000000000000000000000000000000000..4e157b06d5f8389043ec7bfde69662422ce97f73 GIT binary patch literal 1804 zcmaJ?ZBr9x6g>;O*}EE*m3BJQ zvD0y;{TrQ*pY;QoDKq{6KlS(YL+N>zBp}*`WOtwY+xR3cuUoXl z3r<$Xw$YGzZy>4NQNww!vVsn;8p(abEE>yE9L%jSY<88ggU*G*f(@znjo zrv9Ctv2?qdDS57GS7!ujWTO)xp}&(XXBly~SLt8Pg+*>RooH#Q8{s2p?`={88I zjj}H{kC`tO(^FHIr_&R{s~SZOEagBa-2wSkY)0`I1UtLJo=Y}sj&D`$VJ}(J8x6xw zZa8kTpnImBo;ahT9)*pEWcGiJ$zBVPf^ICXZyIIRY%nk#>M@qGxRhgOq~6`XVb9s? zX2z#R-u98u7qG0%*G!9&_oq5NvBYr;Mxa-(aAUj;a0q{jT?^;}LL0OWo$U}0!_B%3xEks*ZNhqV+P`43ICzNeO#UIn2YqYl7NS{s2eGyIkrs|-l^yi2C6pG9iN`n};{z=WWSgR zao49xX^eCCDLG__s|eM|Xp(ms&lr;;^DurUgA_{?#}+xB=NXnUMUGeLHAUZxkar28 hsF+rf4ZeJT;%`ju((4VgiffQmT#qpE4Tkn$_FwR1stN!A literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/samples/FibonacciLatin1.class b/build/classes/nu/xom/samples/FibonacciLatin1.class new file mode 100644 index 0000000000000000000000000000000000000000..40722e6c6bc3496cba0a711dac9375afa687c3e9 GIT binary patch literal 1630 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 literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/samples/FibonacciServlet.class b/build/classes/nu/xom/samples/FibonacciServlet.class new file mode 100644 index 0000000000000000000000000000000000000000..998cb48ca5c374ae9caf62ee261534269d39bd68 GIT binary patch literal 2968 zcmai0S$Eq+6#gc*Gq$XxsoOvWw4^Ley1HzIIy5Y$ErB!%O(+e!Vozd$ZMl-1rtF2X z@B6+lJ%=Zja~w(zJi!Az!2jWa@{KGzX$bTr9!YoZo%`MIF4FIR|M)Y2%W=ZM2Hcs% zHEG<1yA9lfdnN27Cez5Hpcf@EmBh4x6p9j#0TcI0+@C}#iL!xCRJ6!Vp^C#vTrS}y zvBQ9mz(BX&Ord6Qb4K4C(aWrUJSy=(5)T?!hllj?uwEW9@F*TLa50WaJTCEsK>O8B z#R&!kVtpGA3dDE21tqXz$f>B|T6t1=Jik;6pW6GN;si0pVQE>OEK;S^%(0%rdJ)89#TiId|RI@j7iF6OB z!!_jx{byA3t8T?t{TlVEN(gmiYM-ho&kh{!2#7&}HZ?2I9iEjf*_G++p4q&rY6W6S zO*L@}bTwnxQ&MGB2_ZX`f;P`Ix2_8U&zY5W~L$v1k%%s1J5jqcZ`rL(*zWAYotHI>$usGT2QS8 z+*Wp3$K$y!_hSV&FR-#1@eOXi7O`RY!BOV6sB{+BH5EoZH?Mr3E(R-p;MMXGwXu?W zgohTL>7szaTswBjc|dv0RnwIDj)ZjIF&5bQ`}f<`u(HD9JTCF1#8Ye+iKp2lY!3E? z#4|jwl|^6TSr&1@9d&);GREARmsfioU9#Ov%W#{nJQKa>lQ?eTIXo}%f{7RL5@G4R zB_$$ocA#d1>}CW%9?DOcn9yAcn|NJ_y8Z8_(%u&5k5BY2|i_Dk3(j86lk|uA~47CSGS_LU!POD(l_Uy6>biHqAv~i=fqHf%{T!XG$fD?@oPJ^m0 zIzFTC9Fb2#du z+q(>)f5)XY(+&r<7wj%Nr2N*`#h=`s9wnZ^6Nqv6=ZmC#cb7Zijgc@(N z(=cc!i6fp9FlDP-{+n|l3?ngBs`;FsiG0cR71r~;y@6j9Kn$cGeOx#4Da)(iHL+<9 z;#=OdVH4N(P`DkNd2copumxNB#JFhGrj6TX5yuM3{@k2tJBe5ua&asE1L6ZGkjy2z zjDang_LGop*m4p{fm~u9sfmm+hjgxe9_EDAK8KE+oJZ$GW<{oJ4l8p>OIpcT9hvX2 zDwooW5#N#SPOtqO`$Oy1+S;5)_eAEb?(`2hdk$-I9hPZz)M4?Gs^gqG&b8VtvySsD zBiCseTDUf6Se?1l@w7N`>ic=Do3Ij@^XJf$OIvA6*0J77*KvWBsN=$K8Ki+OtU`ip zX*y=Gmg`PR`zgPcJG=P(bPd+vdh}p7+1!K6uonZ^hnsN|#xR6K*w4?lVN78JhcSwy zxCO^>0MBsuHH_n39K?sXjdSi0zNX$cxE(*?4*Y^U@f$7w;P;-UpIp#q4BK%D*KYnU zrLP3f+{tgmE0~^ROvVmei4LCEfvYI*#J%*GqeWV$CrX{Z>&Mk$I{#z@15D!@%ta>~ z_>~b|%ar!v1g>Lb3Htef(OtoE7~sq521Y3v-*HB|4@u%af*a{AMYJ9ULwciTx78l1xbA5)!wZqMrpZC8NM8o@%yKwM^oG#F)gm#H|ts`OjMYi^tr? z!{ZF(5PuWRdL)Dx{{XpZ^Oia;S}13Ok%F0MM=|8Ir=iy3MYed65ig4!+)mpz-0=_o Cmi06M literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/samples/FibonacciXML.class b/build/classes/nu/xom/samples/FibonacciXML.class new file mode 100644 index 0000000000000000000000000000000000000000..6dbe0f6d7613d0c577a85a15ca13378bc579b101 GIT binary patch literal 1259 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 literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/samples/FibonacciXOMSOAPServlet.class b/build/classes/nu/xom/samples/FibonacciXOMSOAPServlet.class new file mode 100644 index 0000000000000000000000000000000000000000..c36805dc6919b492c1061a6987a108def7a15776 GIT binary patch literal 5592 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 literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/samples/FibonacciXOMXMLRPCServlet.class b/build/classes/nu/xom/samples/FibonacciXOMXMLRPCServlet.class new file mode 100644 index 0000000000000000000000000000000000000000..131a1e121909f9106be11d38894baf5394d5780a GIT binary patch literal 5552 zcmai234B|19sj;G$$L#+Th?7<0~W?M+OAs^wgK5dx-RVkZCcvys8{#azBGXxC3)F8 zR8$UyAtInS1>_oxLy&Q#+X{?R6g&|x6ueMG5l=i&nBV{Zy`*U=pMAFG{r|uJ@Bcf# zzw0H>ynWyi04tPQAJ$>Fid%hfVRtp=;x-j~RNSuN4j;U@Q^Q?;d=+<#k9++18txUG zdwmGuJ`KY@EW!Qa;{hKYMA3&a?DJ8|e(`ZY#kh)xe3*lW#r_c$2Yr}_hgCf4!%>3h z5u3*}Jnq8+JRv@w6q=s$;p=!>81M}h-_&raQ1Oh0ofz0tnt#>jx=5GQV) ztZi9GLSLjk(la2eY3UOu9-eH7MK*UwTKi~UcTd;aj!36CIi$clV&w8>PQei!nL=nZ zn+~llBvV6XZZ*%=Br{3tGzC=+jT;rXTeCx^g1U}m#*7uxTg_abu{A|XeMffCNNqH7 zNwIf`ZYz<@D`+Z-%^T^Fl$j5;CAVfX#^7Lbb64l)&W@h$)?PEWEoE9%=ob1ExXe)n zvpdc5632G8j`|+3swfQhFMsgwYV) z6Y@g|%Nhx-=ZB-P$2_-S=B?F-Wy_B+cIIk^o`0N`%%Xhoc$G9F}2G zbGG3MW|Y8PmmMrP7HGUL?G+F@Atlabh^RPNVNI-%N_A(G6rQVOId&3@h;>^wM^h?! zyxD>^QlOAWkMDJK_CqZzv39I^wc_?3!ZtN4wImt{{n$PUD=q2jj+W~GgD%pM2c666XR zZR0{yBh^@frNm0@wRZlLgedUHgyD0W=nG8mp=^OgEOfRR%!rZ{1xp&D72$F6 zX*zAyadFWuTfbb}%v2|L%-6h?D-04ZCbo#O&8uaMv}rG^rCs(~Wt!M4-R3Sy^Gl}E zPNz&}cxkIiRkWUEz&0^hNEwz{?g9z|4bev1ov8q_Ay{U5gbg&wu%@!xNk}Az6Dod3 zgL|`u+#m&s4s=Xe-qfzLTyzl~8?lMtPwsF!`mll7J58UVL_wX%Ri|Z?)1M5j*+Ob4 zn8{kf6x*L=294kuEt|W8c0&~^Um=$-Y=)s^f3?yN-Y04Hf^?@h|cGw~qhdO&!ijmh9H_cUC;;7EiRVoc3 z$HNI6Vw}Kpr`Jk0BGQn)d zT&^pAq;y4BYTl87Q>T{`i;5JbbDX{j_PL=>PHGOQ#gmhGs)%xDUUE3TQX8Hn@tD$7 zOC>b9n^sc0Dj>B*OeuA~Cpb-1&U+hcdobJCNVl@v#oL9P!W=DZM4VX}t8d2$BRqSW z+hSbT4VzYF|0g63cC)F#&3-+oWD|{=H!Eg?f<^Ckv4V^DJX^)<6U)!BXfi0Pr|Z%} zRZli+m3w|YRXLeG@n$iLTy4o*-fB%yL78{abe_NSw#M7?&688ial{7JftoKfQ&U}Z z-&``yik>)eUP4&WvdeM%F4*&p*GH9|%x+_t0a`i%$0VSqnPUu_XL~Mb@z$({k|(7K zm*MxvQ-yqDZ6O{PFRmWB_sA} za>8}Rg}bG3Rhg=6FSZT^%PQ?~;B^LE#f^!#?4za=IB{In6^pQi8AD8}<&Nkn)x)fk zA(~3YvHU8v4hkYr5kMQ-xn9rreix$LpHZLE zO)1+;9h|oO+{|5wPj+~Cn#Q2)m8$z4Cg4Q8pKIM_zz6U_?i758mtFF*1vgi~{HAfZ z`o~c9Al!TS{bL;7{(5!k7_@_XvOnadkZT zFb3qk`Hs>pyrIGsQ(^eAl^ZvYg(HI)vTbo)Lz>Q$r<&`l_rvEx^M3dh#Fp*{M_Ab1 z^Z?uecM&yVPry?|ZCDMc^>sxY5!M1)5i^RY4_61O_hIHL-)!GL%nJLP0{$XqhrN5y z7FL_w0kw!DiwFq9N)o(+FyA38B0&>`qaDIb5@z6#ARQw~<5o)ws>B`^IiNlyswPvc{qm7_dr80 z5$q#ZKNb?xMk06u1Gk1@YbCB{5ZNB2DA`~bw-Ue<;sh}P6BgIoF^nDDUCi|*I2${0 z4mFKnH_pXf$YC$?)VqV43pjvLJc*CU$h|~M8~M+qG?f~IloO{lYe-KZiE3)Eqr7(b zXoE(nDaLsrQ!I@Pz35?fWV!OvDxG$YP|9GLmXNe_Nwdnd9g>zK?cy?RCo@Xqj!JuH z&{B)51Jr2Km>f<%iB$pHWR5*WpGRfh73tS`lr|=JW7QU-dm_|B1kR)4 zV=B&9@o^QOQ1MA9DlX_&aUuUM;-B#rJ*8CxyIBpL#JHTaTSxRMy|qcxnyxRggtV}s%i*(o0&At%S1jm2izi`n*$z^Qq2MH(xS+y z<>LtTH;>^x_fm&T7T$6K4qQk$F2XE=wtyBj(V~^Ols}y8#Ob(<;rR^P%H<5g6*8>r zZ5zw5ZIOZF?7&9eic|6|wj~kjD#~l2lsI8)!Yay&GpsAIloI2zR`~*J`|c90ppLe@ zB0Unm+aAAnT8~eV9!u5}#Zr~a6=JE$r9`J%E>{YwZ-Uo9!Jy0KD#1`QJ#wN}381L@ zW>zS>f_PMmhrfOWF%9?wy4Y#;Cy8Bkz$bQkphi4=^Y`IoL9S_b;9o}g!?l6hi4bHk zpKgDF@O=@pnO#AAi2!|>a9vCAt|OG!6Z)^PE^lC6+{nDV8Rz2`25UEAycJjDHgfO5 zO}L%^?%;2DccRF;e3(D9J&C*VEHn3cQeR-5zKr{22zS{~mqYjpLo4PC;m7A#nJ!xM zq6}sgZF&w@$`A(ev=hRmcnDX?5U!%stGQBX>uvZvR~jw8mj1+9-I7hv%6#VlJ$o=e`-ojpLO5afCTiR@bj7 z;?#LXoL2unE{ZsP3@r!m!`cr1b$U)adfqLV-5ho|GwoYD1MZHCs+2v4URx?Kx93e0 zEdslM2jf_bgX~d{vZ^1$3OtUJ@B~gJa;J0O#@{K$xLZNalW5!Z>`-pb$GMD0J7v)} xcCvOp8=2}i@RY5a@w|~M7q*ao1gaLljcOG)@xSsWJ6~y3okHH5$>G8+{|5(ypqT&w literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/samples/FilterDriver.class b/build/classes/nu/xom/samples/FilterDriver.class new file mode 100644 index 0000000000000000000000000000000000000000..a02e7d8787d6ff9df75b99aea1bd727107589881 GIT binary patch literal 1743 zcmah~?N`%w6n;WmQbGU$k!|AE;oM5GEg~xNrcl7KmEtV0F`ba6p+?iBCruUi=j@B0 z@k8;9b3FUJKWb;sFHLF2VOw+Zd%5rTx%Z}j{`LDu05f=@;4{qh;12G_aZkZ4?#q}{ zFpmYXEXr6?5W@o*593%?pkPJZbKmFzXVo z>OB&o%AJyl^U) z8m3h?TnU%jmR4F6D)%@Iuh#HrPr`7UT{IYnW&Y}$kC`?FBn-B>tB&5N8n#F6F5^hT zc*t$e((3hsF@CC6;=qq}f{%TXJ`G638Vk|)^ zIt^DhhysBd41z4bfF@&C#gF(&#U6fUBv=XAwXtLeup_=-WG#R*I@LT@a1WARggaVHFbN6q-1vrO>DfY%SJwL3r|1+i- z;zcl_RZo8Up;7feUkEr-{ zfgOV}uGe_Wa+kQrQYVm(efH;EyM5v~zTjDH2_QiuKo7=oofIld69Lpe!j=sKmYmt0)P$pS;vjA1jZ5= z*Ac;lfUP5jBS{>ElSB@#T=D{i1RhR8hnGYVj)0#;JxT({3AB?2t2D}l{EF7j@6jZ@jPD8aRXiycu8`;EZJTWcvavv4e_nE zYx~9=;AM`!T%ylVWTr;OED+$MR$J4yE-EnG+9W$L0FJoi2<&0`rRB@rlv2vCxjmEfn z$GP_eyV2{qme=i=#iB*0uL?QRXBO5dE{Y`_hTiL2Ibx%fleZznJE$Uxdsa5@(Qx>R zYN#MOYWk-1XkMU%IA0;H`l(VQ~LT*+#XQP&kC3EO$l9q`HkEb#&&k-Pje9ljSqd zkIU2}Jy5Y@8=@KRc-WGU>GlM7$=1X4I+G(pnP}AHUvd9hGgcc3;K`$AT3TX0hLzq(#10hgiU_ z`m?0MuPg--k~^WE0OjD~3Wo}~JjD4r?X~zsBzPDK9+ae6C|K@j_k^vUm8zrtd>57P z?d6!}x9p{xH1>$cs0d?KQKkHqve=kYDo)g-XYhDYJw9`ty^n8RE#f@E?y z5mNICA?U#!>P?CjOs`09ZkmfBRE%9=%+=dWlr4`{QIp3qx~)UFmR}V&@ZbJw`X+NPnM;~|1Ju5m@&gm#{dfB|c@c1m247WF4 zHI1dxqjghQmWobcxePd}0^Wm0p1b&2w-rs;hFh?mr#sPuyD*Aw6tDx2VHaM&ZoH0O z&cQwS5PLbSGWZJpIEw-NhJ84XdlYCV2wQ=e-%L$9B~w_>QD>+{fKY-yO+Ef{wVhkRtoe9^a~7Jq`P$j`vmT;(gf~N=Rw{Bf1&px z5{QPGsAoTaQF?eC4)7Gg5Ut;fgQT!vv=r~6r0;IK5IqIsSEL$OOkw4@S*#jvTd;l_ zS7+KT#7;ryP2U;R&tmm(Ta@%``r6NWx`v6wpAvEJKt3S)|#;_UVlsQT%o46h!ZaI~^ZIoI=L?zY{N?k`C zhZw z%@gh#(w_w*e&6sI@(p{ae{?L}@CLjiygK!@xM4Hoitig|`~%lb zZuj(-?;3Wm9^i7fN1ZYkq^a0-9G_ur4)ekkmau(l8}2$ChSWUq-02Kw8MWmM-Hv!^ zNb>9d5UNQI8rG0vSebF3HKlHS-ya<4`e-z&(NHyeC~w|lNH!NU$D5z^V#2&-u|COR47xKJrpNS65y;EG)Y&jxT%wBL)#*=(75O(*d zZtpa7)j9lv@yl6k*5*=4O}PK0+P6a&0va~lnEjK3#C2TPyhe` literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/samples/HelloWorld.class b/build/classes/nu/xom/samples/HelloWorld.class new file mode 100644 index 0000000000000000000000000000000000000000..67a57ba85671e1bc22e34ce41a6e9bb576c13f3f GIT binary patch literal 872 zcmZ`%+iuf95Ivil6W26p(iYNkX@CajYA)rbB3?=n4{kvUDpWjel2z);_C}74;FUju zheVBg-gPNcv}KRGCp>Ue zJWHm+GiSuGZTpe+MS9kVdqY=@tT)1SYMJj5dndj=lkiH4^J zo+)hkzbes!3u)eV&qa@>-!PZQS9-1R;ZXSfgEPnNlWgW(fw?palMdy_Bb#B}tSvXW z#Mb#b3WY~<%1Cie(T=p<(fx#UOM-hOb}1h3a+*OKDv;}=0)t+k`Vt`ZkSAN9$Rf|k zr|T22?{RsJY$i@tQKWxk3P1_#6y>miP0Fal-;hL#eDwl}(_(Uhl-($%$H-hjJ8evm zJ!+1TJN^N^HO9)K`xf;F>QK+TfF{al;U?PHr5jU>^=VgJ#Wjj_Tqnd9ah7R?ZHf|v P&f*5yBxP?ACV`#5ic7%7 literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/samples/HelloXMLBase.class b/build/classes/nu/xom/samples/HelloXMLBase.class new file mode 100644 index 0000000000000000000000000000000000000000..374ccaa97cd911c457ef979a4c9118b621be8395 GIT binary patch literal 1960 zcmZuyT~ixn6n>U`>}E+RBv6eNp-PpOk`?F&3AC0^3kE_PKxpg7CfSmuNj7da1bgGP zz4JHp!b=A)k{KCw#w+#08y%-Jj{X$&c{d56g-qtW@1FO3Jm);;-Tn37?|uMq6;%~) z;e7>n6f7xNmhpjtmo-pJWvtALm@sA;$tB`k@2aDLFh8lGBOhU(`M0hZb|SAU)q%5owKrr zgtG~=XslKWX~SOE(|Kw_2`i)LH+9<-&rPM*$(dyd{fS~l+qVi@Sud3GMp?UKa%MiuPP!{y8XNjFHdiH_NjZAv@rqt@`zB+YEX&A}cGAK6 zQ&z>!7>lN8bkFhTMFp}ThHz0rZ_aT_aZRgMt5FwuG-DM`s>d5@RhUdfb50>Ip{vPH z6SN2@NN#FdEfMNRLF~W?A_ueWHfI$?tr7w`!!`z&26g7`ZhFr+Br;ub7*N?SZOuV%+57Nr2EKWmDkLj{n#n7Pi8x*5t)K|HhZl7g!wWj zZi?d-eO`79lIO81EVX@~)_4~kmTTw*UW}0@CN?x#aY~ie$>}IUWy4vs4c_5BMSG7$s8NEl)TEpfd^F7K}$~jJAsga+TWZmXq-~!&{3sU?#z+u8g%D4HXah16G zF4rL)xX15M`d#G+hPe+m3~&jT`4qqiu24hgjse<8_)jjDHC;o^p;;$hX@sHMUCgMLt*LLV^9o^KqRz1Q>rj^{%goH++ zMiG9>A;R0C-ca9Y9p`Fz#dUgBm;`7N5GH|^NyOW-iEMw1*Fxv(cs;I0RBsLa5p|@7 zHzH~c0}<8pqT*|)!mNf12dwrPe#f7D%X=WYvxk1lclk8N4va(P2dEF%Fo5f9?F}|{ zlDinD5yvv+25#aZ-s6klHt*jIzQ8=5VgX-ak&%~ifCTDT!S`6jPe|fttl>A@WzJ1z zP2o8s{EaP_jVI*efMkv$%Dm6{jnarj6)X_VIM)IEhzTO%P~tD5x=zadw7kKchn9c1 z=)6Sy3o*vs2;X3eJ3sS$>7tg2wMx`+el(wA9k;j(umX_%j{gWsm2q3fjEvdVe@;gG P4@t(n7@`HbbYSs+S|rMF literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/samples/HierarchicalXMLBudget.class b/build/classes/nu/xom/samples/HierarchicalXMLBudget.class new file mode 100644 index 0000000000000000000000000000000000000000..e2e642386fd76351eb4e0aa2fcea3578ce93a2c1 GIT binary patch literal 3012 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 literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/samples/NonRecursiveSerializer.class b/build/classes/nu/xom/samples/NonRecursiveSerializer.class new file mode 100644 index 0000000000000000000000000000000000000000..2554c0d9e71e2acdcf5e4ffc2e2bdf04413629c0 GIT binary patch literal 2830 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 T=rUISAb&%|KZcjdm*)Qfbw;BF literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/samples/PoliteSpider.class b/build/classes/nu/xom/samples/PoliteSpider.class new file mode 100644 index 0000000000000000000000000000000000000000..c87cad02040c9ea64349ea2c95b08e4fbb034428 GIT binary patch literal 4085 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 literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/samples/PrettyPrinter.class b/build/classes/nu/xom/samples/PrettyPrinter.class new file mode 100644 index 0000000000000000000000000000000000000000..984312e0095d907cd885f86a5fe09d13e9326c6a GIT binary patch literal 1690 zcmaJ>>sAw26#fngnUD?$31BT0r&`;PkVGje7e&N~#0El%(4xIfCdXuSGLz0lW1pcu zLVx?4{utIOYxw|usIKlaNl4JFCTs0;&c2ehR?7xf}6M%#qAiD zu_9qLhG8rTu_ob86nA5YVqJ)PLTm_eUx-a19!SVbco@Tkh?&4v6pzHZ9m6;Z5*|nK zt%Rb49frsq-PGN6hEQ_;kRklQs&a-4Mcw4PUZcY8Cu+qYO}uDns&S~=x;S@~VYjY3 z40A=(%eAaV&QTjpgFCrBo4f9tJzFTNAfN znyT$^n_;}0Y17kN^?xC^L+8pzZ)9 z;|UH3o{qNHJk?Vumom%CH&-$@#MOr~zQ+$Tj)ZuMXEJ`oPcnYSEW^c4_=f0uii!*k6#_7bKMya%V&3D5YbmO!^ufNMxOJ|y zsi0_Wo^2pHp>H$ZIiCk?E8_ufj zQ!_|SL1cpnJe3^G-{Fd&E5o%x_AdMSn49kS5iTXqZF+8}g2CBG9PSp(Dv_QhDOnH$ zBa>=JZ56p$bL%vmbR^EuJiOwYs>2WV3&QoOa2n;uOWb&gz0`qgjb0|ebZ8o_{ zJ!A&<&;z$Uw!Qg`x>j2yu9*M010rILisg==JHEgrdfvou2uutF$a(s*e;USDJCd59)u?1lRgqYrYI|! z(mtkX_YRl)9OIvoOWV8ccuXCRA&*D&=8Cn6iwHq9wTlGFEIsBqjN&?3moQE5{Tyx~ zjoYMIrriqGu!;?md2CUZyV%A$4b?p~=-kA8{Dw{ZL0SRMIzcr}Ilsg;0%Z6`cPU&91AMCabLkw z97P2s1HE*5RaS`I2ZmbYQ-lo?4CUDGIU8J;cY&0x&m zw`&5~q7&qg+)BP?RH|jGmS6Q;)2h{MXRqMYd@nG4+jWX|&9}U1_9*jBAenB#mf3S! zcJDCv?eeZb_nC}4faM*L}l0EE(01hJqDlDu(R{^rs&d4~!p;eA#gJ^6S1w z3gOC~Rl}=U9=o=dz7w!j$S1n%ngW+viFwxyDwgAuX~a4bxZFytatW@taAaClxgi$x zHM-euzOd3Bj~U(`h3PqmO?Wm-0_wUOc&4>zOLg`CP%I}mq~R@G=1w)N;tLIHSQog` zRQ+76f(;Fu*wXMNwiP_k@D(0vxC&ihYO`kSSyQ@PSa*UPk1p3dy4<;oHI7c-Tq_DB znq8avY4{r72n@8iyrvN|ub;=iz;E%LK(B7sbjS7epR96u{0G;oSi3n5-(y>Yfo*CS z;?F89Ffkuky6@_Sqqixa)%|@-ZydF5xn zFa1iB?pgkv^y{1(ILz__(W3EIR2sZ6+VW0l@iU9A|IF)9A zI(&D#MAduKqi-6!O`JBAfKruflzAWe(r==|i?$=(FEDamzQwuiB6J$ns^t(celFlt zs>YQ&y<2hLZG>oeDpTTDf!4z?zgh$Q!U0hTX>C5=<}A-qaO}uDfjHqy1cQ8bhQTph z;k(u-z&jY?tV=qY5ea_8>L|y2bY?J9N8~qL&kWWPEoJ{i>{lcoqa#~KXC2Bhl6XxL^5iiGnE`T>&okPIjAK5ih*SPnPC_Bv~mWIT*_*)D=C-eZmAoW}@b zz%~9-h`mHayu`4A>n{*}feunlW3)k4^dJ^eJ;sXmr8svCtTB%2_?5&jR5<1%`i z+n(YaxJ8Py%-!Z}fyc8*eebevjfes_b)T6f{;d&N!U~@U*a#(=B(k69lEE0aH^i-F qG0tw+h|GmYxU@bN#8TCPL@ literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/samples/PropertyPrinter.class b/build/classes/nu/xom/samples/PropertyPrinter.class new file mode 100644 index 0000000000000000000000000000000000000000..b1c3eae87b0663ee83b6c36f7353f90dcd4e7334 GIT binary patch literal 2711 zcma)8S#uOs6#i~9N%u@93xTj1umO=U5CY=H5D*Apq9F+c62cP!SI2J)Uj?4LkhLd5ugi{e{7>tl(>KM{6EE*#bSQw3945u~N5!B&~ zEN5kLWJzd9M$jPa24Ob{yFu8g2$rK?Lpp*+^y@e$dq$S54p(+h5aT+|>zL3nsbfmV z%Q`OTcts=>zlw{pyr$ujhRX`&DbH1?>pWwQn=N*#r9Wf4R%W|GIGGx;+EQMUV%4Eg z*=i?kcc;R_rc&_wK7~*l;R>~#cGBwd5<^zzusP(AQ{R~yHl02*W6L?m47sQ6tb);* z^jaoTiI%LHNIO=xr6-e0TN!t!JGbNK85Gwo~oUSFZtO>$@Wyg1wCUtHIe zwt^gB&-Go*xv@LXBOoO4D^%xIeY0e{lWAYYPncsS z$69)6C^<%zigDBNC=4ad1h=pwk$3rYxtFn-!*s?PwI@hqtgPqIv!y{Ui|p+2Mn_oy z4XgEd!(?3)<2(ypCKW;MLYb8pJa3|sqcN$;YPlx-5fqHNEdQ9OaI3M&hac6pA| zlkzkBz$9G=N^T^)tZNKeMlzLbP7=9EtXz#^Gqxx!&-X{S=cYZEAzNl5P}FcuKyL~B zZCRdDSS;Ck*dqHQm+8H!VNYrk#XERc!+TL&#|_3D#rwFSP?Lk@>J`OJ+=$`>Sw58I zBLUTwR*m--f(@z?WK&_u!vfnGu`(>0?`jmc@Ubkn*|7!QstobaWTBKtpb%i`vkiprR{# zC$p}VP-u8C{QisvjlkYtR(L8Dw}@{>AG6#x$INo#7dEXg>Uf_?<=R4MIh;n7U~(4C z(+J|AirDlM$uLPC^i0PdwXKmrDN2@KnBZ8Ov*uTVh2T@Em=jctQ)#BtR&qq4`QP$U zz?Hx;lHHvUh`{xg8h=|ADPk2+rVG*tFJ+enMSbU#8DsK|CZ|eY`@ph4D(+7R9x%u#wn? zX{;r-A;2yUuuB5$(g3?GuFYZjz$_jS&>R{`uBcx*gGXcEp=<_5{VLKkSUrO^{RO~d zWNzfIer=I7bPuZwkjE)m7AnP5%)_h`OhqY1n}=xr;(8e}yJ?e!(rv$VU|3%gn9Hh#zIffHBj#FGmaS{$ct+G5#VGx&K;u=4wZc=iG-&DWh zG=7JTKX4X*!cj|+R4e!rZN)jY8yPhSSB=0^mocHPV^ZD5l==b})Ytqbk_Jqpig!2C z(>>66QzQM|ONxz`Z+}wzu%C^(T3w}&?QGC>wAVqZ95HnfFOUkMSzTak?Wn+JHOUCu zIb>VaIOA;RSK&>1)`f0bmLb}Na81>HxyT=*9t{Ue{(+{ya8|>iHEY(aLN9e>vJUfi JgmfAD{sB86ea8R* literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/samples/PseudoAttributes.class b/build/classes/nu/xom/samples/PseudoAttributes.class new file mode 100644 index 0000000000000000000000000000000000000000..8b0b718175da316799009e6c85b515c102a57c1b GIT binary patch literal 1862 zcmah~YgZaq6x|oW8KQ}O87~x!3z`$0Nq8yYm4v*4 zn80o0J25(!@xADuDVV|!5`L8MnqhF=Fb#KuVIVPg#t`1JiaNt+)-d%$uTs$M950k< zC7QJ~UOwZtA?EGHuzP7Z4EM99m#$frw8JaavhJjh9NjBgo33jc1<%!;HHP5}-R&+g zJWK?Kj%-WQ9mg;)_DsjMJX^J*33Is z-?VN1u18-L7Xlc0k=sqJNVrsC;7V}5VHj!WzAd+kGAa^G6&9-9-L|j{a*2Dj6H;Bp z&!BXTcb)D;&2XnpdPGSj$$EFX_&`oS%_^K$WR%X?xZ%W2%Z(dmyy}-?iU+kAFIjel zyNkkmk74G2Dwk@bYw&sDO4o>gI`FPviQeTn{lzZ?Mny79?I$!#Q_rZ6BpYDA``e#VJLoT_@EMKO761>B zpxF@SkR%d1%vKO4B=Bn!p?q|pfpB&{Sx2Of!I$$<$?s?`x8_5lYeLD-*YQDAtz)=> zk%J~i^9|fOOfA$gwi=Fw>xiyKV&Nvn^U>Q4Ow=(Mi~L5oseBVL!rmpy59^qYend?j z_ZAwM`ICH1g3~*22LlihEMu73FX-FyC4EAca2H=u(948+gf*;U3me$SV;o}>uaTjX z+t9G%NBk5LVIE)}3l!-VW{|=n@$S=9{&s3G}o&D literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/samples/PureValidator.class b/build/classes/nu/xom/samples/PureValidator.class new file mode 100644 index 0000000000000000000000000000000000000000..ad3c48cc2762818b4e5a87e9ae7c4b49f2d4f3e4 GIT binary patch literal 1847 zcmZuyTTdHD6#mA5*K7@>4g?ZXvb0TWZgxvZX&@mNE(xi*Gz3zTUb0?KVaa;evUgMX z1AVPjDS7L2p6P>;iWI4@{R4fi6e;-$eXIJ-+Q!CU$#Z7T<(o6#IWzm$e}B9NFafFK z5+>X6C9cIVrDGb`HQdlJqa%t*9c{R&hFcnL>(G@($DJ5vm7LQsAH#xUPzzipQU1~P7t-u3e7l9?|!vXov8igLqr?40R)#c87E zP1_YXmwb}hF`t`h$8@*SYktvox2Dyu(Slhl$)doS8q%G>c5+h1M&z!*g_=G@wf(*M zT}u{x+jHr0zGhq{3LzV9J(Ue@FTJ?ZsBblkTg?3IDG3R5tof$(Y{@Ky$0W(*k*o>m zYhF;apEmKj>uIC>GJXHbhhUps)JH&{?L{q>9^I7J=>> z>`&5h#(wmQd6`SG8e!(gYb7u~7f8eR4AV6lYB*~6+tR4?4fhS4T$rb|Ev;vUz{MuY zj?v9Nt8=uH-I11mOtrRG@@1Y^!V74qx6mpZ)@R#FGtag=%CZ5#pnk~8U8`TJnD13otVMF@ zWS(r3E$J`Gl4@XH2rlX`LikoJlH=s!$9T@0EH8=~@%U=Ra*>(nm-p>7Jt54lQH z3d*+O1Bm_5{1L}!s2sw_oX0B;xQtJ@YR9KYQo~^JhL2-}vSs-7Z;1bnNTQ{T*8M}Y zZMGgFx_JQY0I|%-aLafZ?T_ei2%XyZLPb2$QAWIsPR6{#pP3WpGe>5cM#^}fj&-L* zf@W&ZmeHN)sm#xDUIQ$g1ng@9Jbx2Ea=VfQb(XRXIgvzkRfz6SpzPr(ekz zeC?Hdi;J`lVeps#v?l8a|hcL`C&0`5880G9`I2+^a9r~$D kamLaylh2sf=lpl0tuxRt{;&E%T?xmDetKP{ErKup2eb^$SO5S3 literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/samples/RDDLFilter.class b/build/classes/nu/xom/samples/RDDLFilter.class new file mode 100644 index 0000000000000000000000000000000000000000..3f7a1b8e781fe3e345881c8efe094227c394c445 GIT binary patch literal 2565 zcma)8TUQfT6#h;Um=FTdAX=o>sjV7r83DBwtW^jIZ4!{1AX+_1j$vdn6K5s{da<`( z(T6_wRr?UuDr@-z`lGtK&twu}q+LGDoOAZs=iA@DoIUgRKYzUea23Bs@G%w@d=hk<^rU~ z$!Vy>b(7(AV*40SN?d1|+m5gqn$%}1x>4{l5G+`EhLa7S$cc)~0(l$|dkh1Ojb(z6 zN!rVcva4GrDfHK^>6W&*)G7~gyGWVuJBE!{uR23y)vDNeL3Kd!iXD|iQdVUY7jcP9 z|7YPaoZ4~S@|31ktJS1kC>Tl0E@}#lC`zD6^!i8r*hxT(!X!1LDC0Rp)YtGrE%zxp zjZ@7;hIZVN!H`&9pGhyysu#vI?f&GfrlrgK{)CaunJN6)L{$t#*Rm%YF+(ZZ`b z>9JZ{(e@<+M;B=5b|<)4I?G; z2DiB^@HTiKM)tuI&2Ay9XIhGGyqCq}IkbA{U1{pKR)zZD(fwMc2JI39!Ao@Snh|di zQ-dl?C4+dMd5fPPNsnenPTqTli^tGycD$Ar5c4zA5HWPP)@CM6Wsw+q*BeR=FPDW$ z7RHXX(4NbqHWYTPdW5Mr6os1+j=To+=?u^VP&dE@d_v==v6wk5&aV<;wNi}zdl4C84he6 z;M4(C?#OU(qK4BM+8?dq3~Bse_Y1*A$Lk3h)H|;>d9u=lujt(Hgi*rLlk>EYd22bI4MeERo&=I&qIew?dLtY|?rg z>-1~2fhxUag;a}8Gb%X!_ literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/samples/RDDLStripper.class b/build/classes/nu/xom/samples/RDDLStripper.class new file mode 100644 index 0000000000000000000000000000000000000000..cd2e09b90c3f281b3e881e0276fe11462ccfa6b9 GIT binary patch literal 2564 zcmaJ@X;%|h7=9)sOb7!A2v%_!TWNtnQqi_nl!Abj8Uhko1h*j>VKkYE$waWbR_(s8 zboB-5Rd*AK8@AED*zy0&`JplXhbpWl1 zRAB^BKSl!>!wDJV0r(IRODuqsmtK7$VKgn7EWsBd^l!j40FCmLd}3>W^K*1C$(fwmr!}gFb%s`LPcBq zqy+DPl_0e)Vwid~mzvWvC$+gGDGd=Tt|ce6j3LfNqSsz9vJ!Sg%v^ZMN`kywjV~3{ygL+w6Zm5|>t{wM1)2)C8CH0Bw4CnMGV}DhaRYCNPt4ZhmqxU-6QYyK zvl4t+5e;?P-1~--y5ooN-xpt?yVP}=QG!SZL|pWhYA%Cbl2By((^l3HzDkgW%Yt`J zOOW-#Ei$q)XeSV}a+$b()DRh|FGo2fQmvpJ9WrtXu3%9@a3ZVC>jzcAQq5e58449M z6e`zMC&nW(mK0pY^DLucE{iEp@B&_x@sfg<@rr_8Jfq-MT$8Y+D7PMXa@LpKs;TRE zO~Pi?$f~AgtBZOv*>%Cnr1V5c!3|tfa8p=rboAGzO~T&6oUYoIs+nq8Iy+T+L05@0 z>qm`Sxn#mIozb;~s^FF|*YZEbNpBWxJAE)5UR+!ZWfF;G$jZ!zWxTH74ZNw~Exb+e z6}*FY6}*S{6?`C;5Ajh6sbanqe2hqdOHg3B7Pt{8z!A|ms;J#JZc5fKK|*6u2ns@i8;(<2c=Pi9$c z?u4q;I5tfB(Y2u;>orQF!f#uJF~>%sBO`iN)IWzA4^b_pXVY3-pBNXdw%!PPRll6m zk{kbioRS_2K<~y&IuSm{eJc`HoY~#s;W>j#`VZ=2^N|;ZHPhZC!NVgr4 zPOg;>F^Deim4X056b7pBIQEi4WoZw*GzTno-1`Q~5AZZpP#Fn!RCMPd zk96kY{}EN>a~Gh*3XVm1x(kJJHI7tmLtP%Vd2Aw^tY9AXj^M5`!G=bP6r>A+w;50^ zz3S%cTRdmcK!1e)y{O~b%};=RsNr`)EeJ-)gWp%sO8MP+JRAxBghwPSV>35< zBv53e(-FEa@?F3yibn27>G=u9InGmzv7Kb$#E8WtW1eDk(?skPj$#H8qBY9+&+vC1 z=lG$d@hvom>)hSs=a8ryfnOhX@ZZl{>M4H&1H_?`Uu%QJ*+UI4xktGpB6ya&0)E?2 g^*0)29FrP-zV<)h@%#l@#_*rahJ@!R<-zfP0RrH9@c;k- literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/samples/RDDLToTable.class b/build/classes/nu/xom/samples/RDDLToTable.class new file mode 100644 index 0000000000000000000000000000000000000000..4e045a9f066039857bf8fc522bc2e9b61756dae1 GIT binary patch literal 4380 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 cmC3%z9rjehS+UtDk>$riN8kTiL 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 bf4nVzgq|_Rn*M2<=#^aR63nf#mVN)<^6E1s literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/samples/SchemaValidator.class b/build/classes/nu/xom/samples/SchemaValidator.class new file mode 100644 index 0000000000000000000000000000000000000000..d4ed152fee0eb9038739df08f2d4ceae62e20930 GIT binary patch literal 2140 zcmaJ@%Xbr19R6;TcG8d*2$Z)nT9EWX22l|y6roTkrlmA(Dd2 zLjMET9+z;A1>_udZd|+2zr>x#@6IGmTBj)mF8)MUb}fiNlcD!&D`v! zr)5xONPD)4Szw?XN&C^PtjAa|6U+ed708zT2JbTMXxR2{fug8ypToNtHtYS-rrq?JE$zlu1;udvtX`y7`ehw0=0>gx=DC?4F7Zd!72}WU&x7Qa{fdM;* z3K+_%wJ55ev#gdv8|DW}MvZ_;dz_?;a9LrTKLZbPsA5fEu$A8y;5vRMhlhB?d~HqV zhK!4vz;4qC&BUA;J1FMh!528t_OH|2ohuf}l;3bGrsqed>&uF{5|;)Q>soPtE0(Q! z6d2OJwAL1XUt?IH-;^}Rsy1^75a!?_B6ZxfeYa8bNNqspI=;^4N??YrG?W?nru59N z3K?%jMicFLiJSe%6nOjp8-g|5f$iq%LuE6TE>ihP7%8Hc@*8Ay%5$Fm60vKPtg)mw zV(lUe79lESvt^%f2H5lysf`vC7~9U^rl#8vW_Uy0 zZZ;U~k5EO^Nzv(n@Mes6^*}gW_z%M|w|7;yzQFNq)JNK#e#N&NHRVNmcF1~Nc~m&L ztwJXm$w=x9mQ3U)B(`#4g!NCNr2%g~^}(>w(bIgX=MVS=!j{<}CRa2-qY!7b(9$3zsm*w|N;;xX*8!?{fvy_!igj9j@bh%;0BAKI8ri z`7cnyOKSarTlf=;aUv#}mc6t;hH)m~1ao*2Mdou3dHOj;T7}XU-skBhj`DPxC#DA< h@c#wiBmVN}9sI_?nI!`s|BXchXOB|m6LNZR?q3BBE35zj literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/samples/SimpleSVG.class b/build/classes/nu/xom/samples/SimpleSVG.class new file mode 100644 index 0000000000000000000000000000000000000000..026cec79b51303449e5507245f23ea5eb24f90cc GIT binary patch literal 1933 zcma)7ZBr9h6n-{uyM%xeL=3fd3vCTTvO$zWc(IrSMZ=3EMN@5Y*<8qCvYYO1Bz$#7 zztz9tXZ?`Ol$rhj|B&B0JvZ4%5ISRKc5m)^dCob{x##Z3fB*gmz%AH8e2-s5EK9g6 zAtfOlga;WBD-!MnA>zJ>)gXd+z+VqVWF@RgSP!C)t2QKT1~Gs~T)QRVHh0O1$V>P& zh+#bH!8RUq<&z)^_)Wx9E_uc`zl-=o#EyW#f^O)}k^paXd|QAoWfqx$3t8P@>$S=* zv-0Y0nIz$?sj1~{)zbOi75bc#ZVMR68a1VERuo&URLjg(aynmg+xO-Pv7+jRfXmUR z*#q^Zs+3h@U&%R^ZtTzVh(Y`1etIL7FKlK6TKE?~Cg zIMt-093CFV52xd%wXfv2l$(jfl(Lmc#mF|sH;K8#E$*xk+*Bf7bc#IS^OJxs`PA~W zfCP}>j@(71BXp8ut1Vo-8-j+SfUBV6&NDS5UQY-4!UUF$0US5xm^{OfGe*4%rMd6 zJei=4-2eRleMEFxolXo$=$+Ufr;n<)lh_nVLHA*=C*Ykoi?^ zF?3D@E*Ev9$m$z=RMKg$@l}FVt5s$cUDz)T$h(V%+>!0D3Jri+BQl2CmFuRmN%xCG zw+vG&G@pLQB(?=yijJS zf1sU0t3a#&MiavOmik9}2U@~O{6zmlZ3A3Kly*HB#|@H@v#+clm1x5ADe98 zQp=J+hFT!Wz+@!Qz~y5M7s6MXh$O{GAR?0F14d#EeAB?ycj(Q2z_p16zI)%w_Z42_ zTX?~%S)tDla1;HQ#R!sA{5(~$fEg@d5qEGO%gEs_o+Cx`l%`XjL7k+putq2k@djD^ zg^d;)&xnD3IGDgB#TlW|iy=;=j$wz$Oi;ubvYw)7K0-)hn!ccZLLS9!vI`LQ09pJ@ j-@rHY%wn#U?OU>(ryLjP>4E1HUW-^1Nwq`*5AOU2x_{r` literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/samples/SourceCodeGenerator.class b/build/classes/nu/xom/samples/SourceCodeGenerator.class new file mode 100644 index 0000000000000000000000000000000000000000..d3ba763e26216f56d581584bf7307fe4eab64102 GIT binary patch literal 5786 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 literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/samples/SourceCodeSerializer.class b/build/classes/nu/xom/samples/SourceCodeSerializer.class new file mode 100644 index 0000000000000000000000000000000000000000..d2ebb8a4b276a20072d9e0e33c10115d57f03d33 GIT binary patch literal 7230 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 literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/samples/StreamingCommentReader.class b/build/classes/nu/xom/samples/StreamingCommentReader.class new file mode 100644 index 0000000000000000000000000000000000000000..fd096158843249cda5888a022beacd75d9b374a1 GIT binary patch literal 2785 zcmbVO?^n}i6n;`#Qer_?MRd-YIh~~-OowiYb5jsy7R2&PrcOiq3X%M%N$TK_*=N7b zoej8x|+Y0(BPIGL_wa4b%y6`6ybl)@`Gq|Oot2DH%AUjFBf}<7F5pzv@O?ZpO znng>o;?@jn+3-wx9~-m5hUqJ~*aGzpyI~1`Y$5Q3VVlnSq-)#4397=V3y;~3*v6)a zjS9|{DzA(;#+YR|>oSG`6GJVm7Qt-Y2zY>EyQ_k{gj*C_0U66})pdirmXIkGT?!0>`uW0Mq<1idol?SQ=3nt@PJW9Z^`gYJbH#}VG7 zW%#~eeV%;~lXh;cQY1OfOZfH@buCqA*y1;a)nxK)5ONs&m*}_&Q*JHdI{iP%C53A> z>ZrG0{kgD$`9^ciGHWwa?3BOl2f~)yQ?hLHo?8>X&vMQIc(~7j25nCkJvd_~|HhZfk8j>4zF%V&z z?%2%3v^~q@?DDY=ITiFS1V(Lh)@Xzy6OQY11=@n!^lD<-l%sXJ^SF%4*(l&NKB1t} zQ(M3(e5|5gfWS)?>jiAUn}u|8$wD+MDpwhSCIc4p7uCP0S;UR$92=!9XHT_7M6ACrR&+EK3YHn zuM6<-niUS?57uA7<*BC716Ma3J=K>HJ=hR>)NZ}zHm!OH&F4~G=RBwCIc?Me*V|UX z9uCCzgA+%&q?m_7=JV2RQgHj4nCIgwh&oBFBveyo1!_Y&Sybpi2cb)WUhH7+Eakyc zOR91J<3mhHjNF>kG+WmJD;XWme^Wju$+g>PG=#%|OC1AJM-Ly-`xBE7+~}#z-j#;h zX;SB$%301iCCDNkE-pUf%j_gyf(qY!)HHWJ+*9$qp5fgsba4mtmv^C*_mElLMb{t5 zzN0OJPkEK^OkfmQe2KH%6(WD=#Fn_{@Og|iMNjEl_zk(?9dws}NA_3Vr*X#ls=XTG zoQQm59Ep1#7l@?biv;mH?<8XGAXGQaz>O44E`iAy+8RanAofKLoLCYj)nFMWwMU6PFaLo~| z<5BO0fa8%v0+>qxbGRJRV>qsGCu!yI6+eBlguRMuagtfC*?&t?36qo~hg6cU;~M1f zO_Jn#m_%U@CSvGGu3_3vt$LIyNV-)w6KL7{47K?!6&V&p$&-ley;FYwODL&g4>k2} z58CP;j!>qBUG!Fl%UxG?(6{my_bZg$9{TyuN1?G;Jhp@44vsS{Qse-4Z*ex|SRABF z!0u8(kn$C7h!4ZXZBv#u-LJ?6hpDXwSq*`W7x_TmMmVDP(ggHbh9 zQHPCJNm0)rkMAHwBSm!^cetx^5^qD6j^||bU9Qvo6_D-YSLeMA757h*(F|=F{D3j8 M8oe)JjT3q5A8kl_sQ>@~ literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/samples/StreamingElementLister.class b/build/classes/nu/xom/samples/StreamingElementLister.class new file mode 100644 index 0000000000000000000000000000000000000000..c98f7ef425c761783f1daa19e21adc0d6997142c GIT binary patch literal 3211 zcmbVOYf}?v6n@^2up}%OQ9x=fDb*T+g0*O?sI7un+JMMK3)agL7FbEL>F!3M_lx#^ z`P_c(2fu1ZVW!MD)35z6{R5p&?eo6736Z3&(_v=c`}3T0o^ww4?XO=R0_ey0F+79W zC}Oyz;jI{I{Ke%cuHb4NuEk(rPD5J5e2kZ7V~E0(p9Kwz8kS;clABGi>Tq3-%P}+} zqaiC_=Hwe&Lq3L`cw2rP4X$MICNCHQy5LDuH7b%0_%CP2)EH#SC!Oa_K&bKd6S9fU(Lp-tdaS5&HfM)ud zl?7{)s6l#g%A;U&1<%w#wZc=YdGhHaxt!??W(?OgDa7R`a;4I_qIo5bJraJP!pfTI z37OS5j7)*#VNco6{tvGMA&2a=Z|m^?G_JymCdanDAUb18g1UTRE@P!fhN!l?%B;`I zX@?BkxMQbHS0?mG&h?x^TBfu>{}UNgL0!xISu~kR^?6p0Hz`$T+=V3&o3sl~+8nlI z-R`Phi@h>Ebv%bXIyw=je&u~Z$Fu0tu&U!7yen{c+BFu}?o7O?2}1fL4*h+A$v zXM6FRW+roB!FICde6NmMxUJ(e+-9Mu_m4LeY4_(kE@025@qx_Js4sMUiLclt1EZK& z1=%9x+1EP0!M7~%;7pd)u#xs`XH~;@Y+%9u;|b1UD|18T4Lh=3puU?OZ(IqjS}SY5 zWOvy%Tb;^=4D+hBN?yqp+g@q`Wu~d-8&PeN>QsAKEUluiw4m0==gr)_z=5g{${bP+ zezWD)dA8s4DNmxivI#JSncAZ)7N*@6PtMI0lqBL)nj#VOm<$)0YSVd~a%HbG;kj2ICUarpahgooJO|C;E zYaSq!+(6CDI>HYSxu>2z&u=5I0lf(0Apa3~0sFYp{f9`Po2w|2=q2b~k^pEn+2AX)%tTwl`w5(y<8n*KpkM^%& z2WNLsFE4V{c8_lDrUMCDCdcz=C8M*5#`BkP)Q^T5B}OE9ACbG9gp{GRJO)M;ahoEE1fk?r9K!(51&x0qn2G;@If0Xb zHCCWaXL9B~Lg8yt@pZH_u00I#26oQ$>|aO6rkyQ(05+T$E;5*JGSD-MEUqN(EZN-2 zAWNIUe0!C%5a*{DY(LMId0Pgd%cq?uE%~1M6}x_bE;FuY9Z%maM#z`aXTD74E7*an z48Bq3W><;1i*gQ;M@qtM-p%P5D(Vwoe2`^IeB!btKCsL6&IFm&X zL5iw{2|I>!0m&#w8KtTu%ZfzimVE42i=2%I;fgR1Ww_+tgd!1`#8d!1$WhT2V^f|z z`7$NSp{GmGGLK{r;15OsxTMxVN6qnF#0>p}Z4|#bzJ}emgt+tQcUkr20(calfEIYn t_nRdFLTF<`#0g5Mz$+<06rn#65`QpHLd+JK?K60dqwG7w1UQS1zX1e}*f0P9 literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/samples/StreamingExampleExtractor.class b/build/classes/nu/xom/samples/StreamingExampleExtractor.class new file mode 100644 index 0000000000000000000000000000000000000000..33de88ecc502c1c1c097b488baa708ae3774e21f GIT binary patch literal 4997 zcmbVQX?PRY6+I)#9!U(sh*b^=A`oh1Y!^y^*o2hLt-%HZ25Lf5#`4$}XhRx-=|-9^ zBx%XgW}_)-T9T5|HsAm<#ep;_P1z4V`R7XU26KZ00>Ljf$t-SW6c#l1oJ?3Mdu?<0O>g7_%z_v1!+e;|kl@lcSm z534vFz{l{2ijN0Tg+qQEk?~PKJ|RMn`SD44|CBtA2Jtwa5Y?X+EyqOQ$sj(1k1~cCep_GLaNKi zb?9B9B;1;b>&dNpE+OyEqR;G0E5yY#(!LsC!o$}yg4npaX+!szSPOhaxe zde%WVLAEw!GUl43VOv{#32S4Oy7rxO1no=fDFz`RLq4m=B_MfI&zWudZgI0@U{VjG zneQ958m%dLRMP00D7ok{`V~yEu_Q7LEgRPK$BnF+$fRjqHA?!cMk^wD>9{z$!esQi z3Z|jrMFQ^FpCw4GC7m{MtCD&?Z!l4}T#^4{BT};J+L$Bi$0o|VWfQqqu~$zPXxwL7 z7^Yq3v_pl}nYfL$`TqnrCYBR&!#Q7X<;|ZhbR`qF5tuQiE6IsogIo-ekwSU52q%x>K#SCyYLu$f-;+ohHr!A)m+8&v zsbnH=5*Q;Hx2n?HonU!~T$VKRgpgBC%8)70a`lMq4Fxk>FztGzsMivyaz(b~5^Nbw zD!!uPtL#>6ODbMc@ihfOmw_aXFQq3Km8ms1k0poYMpq$`?3U)^GIXWALZguq0owJP z1lm*3oSZU&l8zbYLR`py8W%SwjkRb&}Ohrh9BZ(6+hDOWBf$G|5U>c?9}ix z{9MH^H2hLv_2U%{zrwHCv-9zsVGyKN0%?aO=#VMDutBs?)o<`ynY}8Sf2Ux2xqLY_ zMCkYAPHDe<&*lirdP_%h-BOYK1DQ~gTv_PpF>*$?EhW2uB!e{kNzDIQ((o6s)|PI` z>%B%}RE|Z{g?e5=_0DZkf7xV4w`^)9-<{;SA+B*2UiY>UG=J6bH~gI$a}JH>pn?Uf z3r5t;MD=vEtnJQ^ntetzOU4?xXgpI$c1P10Gs=6dJIdx?MyO`%X5-yD#aSHLB}oY@ zR%JKbuutyA5h}&A6m6S-)}fBRTxOqr3s)L@3VMkw_d zg)5q^0dGYM-xRFHI!F5r96emCh@D2|NlrWvMU~cpChPE2TfjL?QM96s`-)sZd*U}lZBvxQNofJ^E-mt4T?C9OVJ-R%es@t*AF>o7~yxgb> z`)i8`9ELyUV-&(c`)z!z^E5oZof4he`GXiIuGnU$5-?~mKtmyVK8>Jf(E))FwRO4> zYOu+2jdti#8vzR#A3UKjaqx|BBeZsqDuuJ>}M z3)2xt4BcdOoN4Z(E4y5GrgLW-T@<3UyNxk%g(eN+3ZhJRg%Z&nV(*=Kq@clHBmjBBJZ-UPLe!p5;t^ z19e4Q+vpp@?9LKN=8zUw*_;@}b&VB~iX!H26NyL#OYIGfmDCuiETU!z^ExAygNQY% zN~3=WwVe?)Op!Wysqc*V2hq^zkN6|LBHkDwH5WwGA{IvcFJPv;zUdgIH3r-@N^&g{ zcy@r!I`SlrTccgvJ&17agLLsACgKj#@J>?j!v&t~}1!6SQy?CpbUN`7``+M4H78i|*%nc01n~ zQmcLQyB?>=H3|H5O7G#Wmp1RA{x$}GE%}#+$u#XC&kCGXQp0-e#XhK%S;w#9{Up9H zZ7C}LjoB*Rq2iss*9b1aiDJK7&v&Wdzjv#654e2bU#Q~qpR{}%PfO8+i1EF+-DVS8 zsND)WV;7D=ZSNum-cK8K4AnM#fU~9S*`*%R$;2O|l`v1-Vby>HaXNgV*KjpF iD(+;^Bv5y)WmAdr^oO|bAwAyCF~}g@!qnc1$bSLdEyFng literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/samples/StreamingNodeLister.class b/build/classes/nu/xom/samples/StreamingNodeLister.class new file mode 100644 index 0000000000000000000000000000000000000000..0024bee9ea1e22c65d773f56a884e5c3ba2cf3d2 GIT binary patch literal 2876 zcma)8TUQfT7~LlXCJDnW2uiJOO4Wv-K`FH@UfK#`k#Lb98m-nL8DJopi8B+0UhK_Z z(Z{~{TDvT)Ro3zc^uP2EbamD4GcyShqV;9w%sJn;_jlRfN&fuzk7ocz@F<1^vI-Vr z2q7Cq4Avja;JJvDLJqx5FVqAKu5+f^_fa(Nw*iYr6PCY87r?97d6|E-@#_sT{Rqm zqg4V&E0v47Gn930T`L*pie$_fj;q_G4VN^-6zEOf%B*SkwV|SBu1Hhbot4}neM6wH ziZ!R%4o@y^hbib%i}x7HBPL>-v~gD(R*x(4TA&bQQ2#*lHx>;F03P~6Ov8+|G^ZJA##eb-QpwqI) zDvsf}fLcFbD&9fAig$5Tfvv&;@rSQD+KN7!kUj~slIAp~gJDTGz)oD7&nT#% z-vB+3EwvbIykgvBU>hnn@s+^Az7mFvCExV)_ z(ki~iLlxiQA$8{Yzc`OF=_IM&FsI&~bKikx*u(4v*gx>*o7+0dabA@6$Jy|5y# z<=>d>PI6%XbWykz^fX6=dfSu9?9!T^cfEK!U&zEy!AqalNpfspkrI}_E8x#cNXpq; zQPF%y!8CE4&-eIs8BJUPJ*jPo)DA+qZ8ZIkaEg!SC!Sl9&vu#tDTHx?KWCRQz%SKz z^SFo{E_oTr{03;5#t`x6p>TDIEAS`zG=}*;LWK8; zD)0fn4k6tB9DN8WIHQn9DEMeq!N&^EM{(A#2BwK1F}hgdgR&Thm`my@!oPAePp5KE(Hy=@ zIRxR z@gRyRTFcsX`%x4ah1BTaHs0K&l7F6n+oZZvmtJ>`qMPlVAfTXdKM#O&^pOF!$%Y)+ zS2Z6pj|d7Lq*MltYmjZ245Dts6)$ReI>nV7rwn`UkJl={WflAP)b6bpYu7*w1{v3= zxAnTJ_1F0Osb9Zp{hlnO!gyoXSbfFyHJv+5p*8=4E~eR;*uvY7gn0b?FFDx$xcF@< pvv&6oN12j#Yc&j^7jwKbWOMogYAO3DLjNEn{^raHs!ED@{y#=qeLesH literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/samples/StreamingProcessingInstructionLister.class b/build/classes/nu/xom/samples/StreamingProcessingInstructionLister.class new file mode 100644 index 0000000000000000000000000000000000000000..9cd5b50b2408cff129aa0c24e8715328dbf4d495 GIT binary patch literal 3015 zcmbVOX>-$76g|(eBS$z1%|hz3iLE#eao7ulQXnBE)CpOElv0pw5k$1Ok_<_=f5KW zI>v0xE*ajEUb48;S9Hs|^+5Mb`5bR1gBPYRaH$UJ>vq*L{ISKrGj!W@R_8poZ1_H< zX~z$|S~)OXr)c_t;nDX#TVFFKT-!FBK;U?;_)>qRk6F62DiLTjK3qqx(@j?NK$mP< zR0Yx!Zpm0DR<9HnTsOF584>#BHt4M&4S%s6NhTfL=J~ti;#c*ujD;WQUNEDtkz&I^ zw{&?cAn#HwFcercUD~KpJu~e%hBsm9zHcyEPxi#5m0LX&4Id{Zd~1k0mKUa(@K?H3 zqw{1Cav1uEb)1Arw;XYu`k&+)fvYE~*V~EyY#70GwN|ps^7JG#r}j?5rBBf<)C6I8r}(TPHHXwr)BC2Xc>FV=h~wv#NW(;R)=oBi*ixxR9jvs?(m7@D{v~N(lS@#p|N1-%!B0pDudTx zE{5vlccp@Fc2A7hO9mVGQD8tbea&$L?X_W9qt9K>HY$Y-egfft2D9HE+JBfxfpe2J zLknC@ceJJipV5LBh8ES0R(5MvC4}ZstS}W9K6a|M5s#)NhfZuI8*EB12d=lF;1^a{ zy#L`?5IDWJ=3#;FZ;DLVm%I$z8vAFbc;`5cgD8Qvx9nqj^Ma>K1H(-f&DD*>e{ zF)Vi4U>jz#(S~yMMru0axO|f;$&O<^w6m-mafa38tS7-vy;?OK8jQ9zXgY6r&Fu7Z zI&h<#hwa7@GQ+!zbI!PQDDC2-eJ3F|?^`#ZWuH=%A~--lJu6Y{&ypw43e3r%jb4Yyg`jNN9#x=2vF7q@Vmwq2OOWaL() z=~Nb)lYjpP8G7u?(BHdlbgyiqhZWqr#anPB-*J8upFDhvyG813qn~dE!p*_H1DhD! z#1PH+YGf0iQhJM{O^SV=Q6&sMq4BTHV#$>TD=a=~xm6dqA}%(qY3 zY|p5(M(q;MR3`TdX8Ami=REx?Z2p~~l??r1q;UsRJl!yDzGmqP>KvprO^N5kJ$%Ep h$o~wIJu3=k_{ltbkc{qgFM&B+;Hpyl9DSTe_FoYD%NYOw literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/samples/StreamingROT13.class b/build/classes/nu/xom/samples/StreamingROT13.class new file mode 100644 index 0000000000000000000000000000000000000000..7a5e8ff5ce3de70fa2a94cd519689e37274d331a GIT binary patch literal 3221 zcma)8X;&Ln7=A7U2Brf|p=D`_jn%dgwyt)eMJct4Qb3kglxl}$fN7JNIGMD7OI_-2 zUFyD`qkiaD>xbZxbNm7RB#+y3Z-z9Gc1f8Bj#|v?sKJ5;TSpz{HC)oM8J9IAb!Ce7I-Pa1~ZxS2d>dbZpLharE#&Is%rcCx*T zZmKt9rqW3((>vjNmYK4h`SH=o{RjI5syx@OnY0<_y6TFV%yJI-3Q+T!$Ocscta-$XC$_+iHs2f z{Vt}32%lMNEQ4CzsF!Gd!p(Yd>x?ZgMdSKH?~%1_pdB4_ePf;Hi&DdD2Cn0E$@PYT zH}RH%xABgFckv#pr#QU=^}#kMR|CD+Ck1cF;eFO}K+hLtIM@{uExA4rXbaBErdslR zsNo|658$Ri_f*E5w~j|8_^6ZZ;ic{gE_Kg(U!zmw!x}y|@Ck0*K^kOHAQBiY-w~wM zPYryA&jq#woOd!Psb3iQ5??XGDvaxsD9~IQTva^+2Ty0MsP9HiCt8&DuBg9YMe{Wm zjl0=oBI>w))Nq`P8;e%y7-NOZ)H{+0v_2K&X{rE^_Ey^!C@ir>aw!7 zH=<hrfS6$y_h!czeu5fDy9!lojmHb~qT z2|J3gyg@?mj40Xb72?)E*E@62ej=($9($ew= zs{cS0smIZmj}so@UTSDuMa}doYNtDYfxd!GD=;L`;*g(IAe!>*LC7hH6ErO|b@IQI z)&)xI%P1ZAAEo^`6(F_wl&m78<~-8P)16)2#M&BKLAZ?KGfMHHyhs6UGX@n}s=;Zl zr0dOin5#;)I>Ug3Xz)=C1!kvMaEg{UyG8lZrGnH@#_Z$DY&1}puS;p-v4U9{t_s%b z;~37HV6BE!ttMi>eGB7fG&XF_VcYFhG%+L1(<^9M;UB&;R(T+Y?Ppi9W12iGXdCJ7 z${}(KJ;U6uVkbGH4fp1-OL6YX;XbOl#dj~sti0X)_S~lJdLlTAd(?cMkLG+e_VZ~D zj4@zO@UcEl>N!L(jop}GZC;?v0;MkE7)(rZeI6E`#2lVwt;TUl`Q6W8MbLt?tYJ1K zgfU8u6_l4+$7$&pU-A=_7nI$^)f87ig0{|cmQ`yYRBvl|@)-@&n<)Df&noaV1~}{F KevmjHLhHW~Y70F8 literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/samples/StreamingTextExtractor.class b/build/classes/nu/xom/samples/StreamingTextExtractor.class new file mode 100644 index 0000000000000000000000000000000000000000..51cdb37c2cdcab743f1d6b448b7bf2bb48d4eb24 GIT binary patch literal 3559 zcmbVP`%~Q375?tBiDsI=dav_dwUPNu_*?$y2LeCM3+JXU=3?LWT; zZ~}iV;Rvp2FiXhcS{wGFtf5lGQV9iE@_t>zavN4qEujrJN<3LD!A4DkBgbwDb-by; zD`7W$xe1C06x<5~&5SeM7oHQd(lRtX2>@BrQ};uo^MQ^J1SDdLwJex=aj zhJnJanH%O!bFAu)UG}PhOs|YCYT&XE28IJH#luu zvGwsL)^{37{Ymr~VBRrnWUEaUziw8zZ}lucw4*d7x!4x(_5;%kX3bTGmm2R7cU}w_ zb2$tw4Zk7mh4nh2^l8Vjyh+>ieT%$b{a#+~^t(Z0YU6Q%-$`%brfG*Xn-3!Ed%r_3 zN!^rNiM>4Yf4Y^@*+_4fx1E!@h}U90EZfz}^c3^!uls>jlWo5cHt)F=%lAp-wBrX} zSdsiHocLbO>@eNRyewNx+Mes|7k?#A5km~)0@*XrAfs_MAvN5x}R@3M->ijx7S=~+w*SK;gm_eQ0R;2j!_H!z$jaW<2oY_ zB}WDHu8zm?gu=eYkUAFz^)QHCi4`^cR)pRY@!!c~kaI!D`?$-<8fuxg)$sw|)9`y8 zf50C(Mk9f=km4+$0kUPb7x^ z_nZ^^f7bCAe59~9(Res>yxAs|jxei<6&)Yr6V{zk^~qyk+_G$YRWx&)A4DNK7oy>VO%L&JNCgBe!2#@je;Rrufa{ObzMLTvc*LEmr zDL#kzb_Xq#0FDf8LJe&px44OxuaF=9H#)XZSnO`yg!ZqY;jd7Vr22RNLD)&a(ej%2%P5GG*4fK zj#o&~*~p?n866s_IvF|Fm)h=2Yzsvia<6K5Mx!GWj5Utu3aQESQs2Vv#o=R{=$;$d z4lCwm4ket63=PGyDZEa;BsA*ddG2zQ%;5!`qC*NVrdCezO~TEoZS0t`g){)kMI*Dh z6gQXPD!hamva3umf5euY%g%q%m08*VSjz?}4$mMiYBWLkyVq})95YyDi z)eIZiM9(B_6fhgHAx7pRBZ>^pC(x5zg^i}r%T!~k9F_az$5p!(7U*Wy=?!!P4v$U549ZMz`o1(@X8Ejws3$q2cyG7 z-H&eI&;|^u@rq6H71ZLwIjwuQTD zY?rCY6r7?_UN>A*{*RP#f6w$3d|Ctb469NW-q?!o3d1t(-BnkJWl?clU$_j`Wf}Wo z+OaHQ`wFgQ3onhIjj^&}?@AZiPY&0hYWc-WhHuF5O{NMG5^hx-5UY2GWq5lF#y(MI z%OZ@RXKQegsu^DuHRJSo+bG`|r?`4pVGHJ3gm>AF@9_@umY(hcPX)aPB~REcoLq5wWT6|$>dUxS*FD>8>NIo;P{G-RNG zw_->21Szy{OKvL|YUNquIq!k0yjJ9;X;do0ro(7UhlYZJTkULe628MSI4wNJ>~Qns zk?)+&1uavkwmtU;;=fS{0XhPQ@xZx1G>DvOit8BSK3@Ru z2Xp&>U_L{ZV6w7VRwlDajr@srS=h)4I?l2R%bH+WlMRrmdS)pMBNt>g!s0{71+MXT z6?iXz$%O`Wn0AZ?1||9{UIS!gJGh?U~U9Ynt-a{=RjN_C9CtefHjG zo%!3p-n{`}5H}0R7X?10;?ogqa5jA=jF<3P6`zY> z6P7i6UdERNUJ-aTf-m5U8os3AH4R_Z@D&ZOYq+H0s~Wzh;p-~C5uwdx72k~D9(+sS z+amdniZ?X88NqIRSH<_l&iBJuzz-sXAIj`U0zVe`iNH?&RcQk8et>X&(eS1zR2#ip!VAFWkG*XrPyirQ) z^Epa7#|s%fcS{!wS#vSHS~Nxq71J_G^c5+V zvZiJKrl-7~H5F{{dvg4OzN`=AbaPQQa@UYV;xkqh^wcd+>7_C?iIt2|w6X=0*0wqO zvW0=OiRJ8K93XBZPxP~SkNt9(%5!b)=A8zjTPGJEL(cXn$VZzusOqV z@Sul-b~}6OF5P`RM!?kbtX)`!a#7E4-XXq*E#`T?Vi_ua%~et*1<|Bw8l{n(UM?HV z-`P8I+8#zdiiVH-#Qabrx_UzVv(xPG;If{p(0RbJW9a%XyWNzJ7BWt(WB+GxZBcA0 z6$+M{u#}xFtyr1QWixW~wDM}%GV(I*k+e;f3K^p;eV8=MR;iMaPE;^>N5QnY4zbdU z25TOfDO5@sV=OE8tjFu_^h?u3@fb#;cn}XoaaiDCfg=J#0!Iao2^<%A1P`&Mt+zdj zM=`A8H&Of+zf;h)z7yT{r^cqBn3N=@*IT80Bo@W*@s_}qD6Zmd74JlG4K)Q@lcVjc zX~$+PFHN_@WnQWtm(#p-JL-JrrR;VX&`Y(W&WWDNI!k&f56zbKMdL_JauqWx{iM!* zm*x7sDixcZ9#?R$oAwXlLvpdZ%FVmf_j(k6z#pSXVua~)_3OEh`Hp4FF|%OBE*iPq z{-+D2ys^+9#k+W$yl0p7c2<-!tx4-FMe!&6If@%HJ1p?9z!8BVfujP)1dar{p1U@<*Bq>BXX9E-2XVag+A6 zQB0GjclISeNUd1~`rvhC#`@Y9(sLtOEdlXqu-~a1ul6>tfcQ9d_0@?==*&%u{cHv2 zg`XwlUF5so>c6326U5}#+dFggII@L>e zm!s3XbUPhg*h_b}ql+|j&E&?}4d8v>$?JZYw>{0vUgQh$8tZzQHT^3S_jgwHpGdSM z_I)?8VLXnLZes6ZfdaI3E`H-pM47?P4>OZj*3iow$Hv!i&)nYY*fp^)?th?$dr$AJ z;l3&cQ_Wo4iq6iDFxo~L_SbMf%~Y{L7V_7gE9}}{>}6Z*w#U=xV@ah^j`Is1EOn4k zbzp*xGKKBXYom#v(Gok-2=2d&i#IBRL)YI z@m!dsJ<#Igw0VN3?q!aqF~fM$l=J)>|4o@+#jJ``DoraYo;a^!PUYe=c+yD}JCvWb z+z@2Tb-cxi&yGGsG5Gy2?YrGs;AWXi8Rc2(6-InVX0rdoj2zdwtdDY(c94A8N|OA`e&5E%HHI-XZ6Th&)m+q|=5DUzN#E7TK!8R#|4YFJguNm$1rf z^(=1UIlPDG@ejPvXgg-Dk>y*BEZ@R;JIkWYawfI9g$r2XWs|mxBGTRE{%vIya>zTi zU<31mnkzi}>?PZ6+*#0Y8-(c=H;97kHb_1Yw+fy?$u%uxDuH;Kex;>vf-R=Y4b#%h WKK@%Y;bVrL<`|*L1^fyIdj12@^}R3v literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/samples/StreamingXHTMLPurifier.class b/build/classes/nu/xom/samples/StreamingXHTMLPurifier.class new file mode 100644 index 0000000000000000000000000000000000000000..1f2f5d4338af8e32ed8f6eb3e45de89244c07892 GIT binary patch literal 4490 zcma)A`F|8=9e!qalS!6I0vke_24ukiImj+&1p-k>LP*GJ-N5$tmGrL(fDIb2|-I?$EJ)ZCJKJSFL z|M%uq09)|SFt*~fijRg7!0Ad<<6|n$gi+?bd^}9aqoVVeERT!U6S6$1;wcrMsKC=< zRN)yBeNr?&B~Qxl+@KPAJ;bl>Or2^+F@Hu&WwGyAl z7b@^t7+=JfBrainIgB;H+Z;0un;V6G;cf+s6FJ-F&OpBWoEj#;tZk=XKS5YM zNy9U}V*wC1f#usVk&yJ39W1n1LG2hTnVl$!5N*!qlsBH2WO>Fm#>^=NiphVyOvuRN zTU=_)#uq|%Svg7HEk?JM7(SIDqP@42(M@v9m%hffEXT(zm1OfWxsjxq=zl+yB$vdNX{`-OtbJ_np$r< zZP;B&J)4!&b?lY`&Cfbt-a?Efao;fyQSjga_NDBkcVu_i&`Y zPg2?zFR+A$1jbeTT*EK$wt}q(v--HPBPvCVrgJe~Au+#fv4#5=J$Rr`#V>`z220o_y^C4FTe5d%YBmV@h;g= z#xPD0L32ydIo>~V+(^h-EjJ#>=}Dfbx?=Y6w_A7-g)+J9go3pTZ7<|7n6WZ*752H8 zQ%f!s26jIsx{X9qw{)Z6jr!G!}ANl8tX7 zq_wzklSv^vM6|s)GhekRv3HcX?uxqZUM8ePD@W3wqpv~2b?AiO5(Hi7aFLkJ%7{{u zY+HW=KEKf}%rckJND~XdEl__99QLyipy?D_7~b`9&nxvcvtnWO;l^q;A^oXVI|DFU*p*<%dw(RyyTa z#jo70{0%OoXll%VUUvE*>1hCd7$Zf#pXgUCf?u9jZh z!>Gg&VmQji-{Z!OdM*kzZ6qcc2)ZS*UChz1ZUcow81`ddqgff1I=zHz4>Q-LyMmo7 zmcNFkwiVN;+YxGOU|E*Q28ESpg4fRkuAd1i=db@u#)ti`pM%%RW&`)30{1g@9ZNAn z00~a;C|Y3fZ?7@LDc=gyg}sH*8>w}W5JMcdwK#;sZYrIHR63dJQR1z_M%>L^FO@|I z{uechvhv?_p|A?~`Y9cQ>h`8i(9PnuqmhQl?UBYAEccNH+@UO|I*@izw0Z`Gh;=yT z7q1QiJf>0GTh{9e0OlnkeLo^nKWl4onGs9Uv#fVC`>*R5Lj zEEKv`%}D$j@Z&wfRHa+1BJYv`c?nu%r8g;6oRs#4&8OBURbgIMVZu*&6`Ld}i{5dZV3A~oLI^}EIe$r{JXlIEWdh4?y-G-z32*f@-hF8n z)g+guzFDl1DOuaUzBRaM8tVpT(R`FzGiccnY6!JYqxC$t4Nx+R^|WYr)nbwOG-A`( zKv&$N)s}T zXR(g|*{~VUQU4q*pJ$FQaKv6@56*Ht&(X`v%=Z;MfLGmG?PKrOv3hA(^u8J8$Z${i zu?04=lpUvr4acpOjT}2IEO##^$#@E54zcg2{K0KRsQNnE5mfP@iVvyyFccLJB~?7E WQsohv2JjIA5#m&MZm#g$H2fd@tVPEF literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/samples/StreamingXHTMLQualifier.class b/build/classes/nu/xom/samples/StreamingXHTMLQualifier.class new file mode 100644 index 0000000000000000000000000000000000000000..4dd20a84dcfb898e93b96533279ce77f5b493eb2 GIT binary patch literal 1985 zcma)7Yf~Fl7=AVhY)C_(;U>MKeic828JX!1@JBg5XOqwr=!_q7&e`|+oacStlYjpG`z?T5Fe12w`7rKA@D&zR z+zaDtDO&8peJq8s9Ki}!RXm6wg!u>}Sd+`TifkurrI%!< z6{e>v=)7xKhP$93I6AhgAhcu`DD6obmcHSYbGoxFawaA5w4E2`u5b+b-4ur0l2KJK znzp>ufn82jMY&??Rhdl}WhV47J@SNU>>Ikn^l|2P+>Cfh<%+47b<0&SGkO;3S;<%{ zOVf|hLY62KTbJCb6_Mw;JL`)4%Z#YBAeMz;DHt4mdQL=MxU(Xhs_rQ0ZH2$@8D_x; z8!Xs)1s7Y2WjpUlz)T#pJ>u?bX|g(LF^yk*uYi7_V7R5)Vt9GigS=jG4cnsgKm&jN%4HRP06Z1-@hHcB-PN&uKEHW_d|=L9*Ee z$q&HOb{?~-pGNULeozo=qH3}(VLyxFIesMZP1QSgrIt6#fn^zLYEKhP&FQv3iCq9` z>(`8`X4$S*(@k@7-*(D+AsNL!icyqMWC48t_sgSTYT45@*VcrkwUuo`b4$9`=pij{ zduG88&Gs*7?Di(c4Gz_+$h)@lij8&}9-C1Ai8I35542ChX6}Wacjw1QbGy&0s;igV zyJ(A}?Ka3}cb%Yb6Dv?F(#Yf_yqAJr@>!6RKXfw1ZD@T>M@~+Jf*bANTAr;wZyS!` zrX3mF^xTTq!X~qH)EvW=ML5ZJ;waUfqEgW<=9)Y=*JUA5;shpr+Ix{?y#n;_;BShVW*Ki9H>u6B%G3Nq embHA%=ri0Ub5Vq1hKk!I6?ZOEJ4;yrbN>N366Y!a literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/samples/Subfunction.class b/build/classes/nu/xom/samples/Subfunction.class new file mode 100644 index 0000000000000000000000000000000000000000..b7cf7def2ff4d8325cd1b5f5b59d2e20318825e9 GIT binary patch literal 946 zcmZ`%T~8BH5IuLh?UrpVwL%p{u=v&87D}a(2#6AbCQT_17^08cvcQtrJ2U6Z-2M6M+YbOWteMDQaRN8+K$?frJTmY&i6>>mJBQ#s0iro?vX2yYn(afPQ`bElgdHZ_kxoRfq3YJJ}(naryB*KfT;7Z z=>=Z6B~UDm*NjK%)*(rD6v<@63*2VZX}SHkPD^&qHM(ubKXm$@Jdc8Uc)H;rWcIb5jic6fh;k&&CdlaodnY+H*af6VzslipUKz2Mq51bAK zm@OR1ZM~Xp{V-jg{X5~4t@`7_sLmn@(?k*Foga| zW#6XFtDF_+9DV|;WT>&%8t(AUW|Z}q!7RN*l20fk6=(`c?iizYF*jl+i|YF3JL2zi zi3=DFg_1;j3QZ6-6*7s|^|JjL+83mj=wtLVI)VNbY2wmH(rA!k7m=i0hlve}*~C0v xz`_=)c!}qDgjyv+ar literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/samples/TextExtractor.class b/build/classes/nu/xom/samples/TextExtractor.class new file mode 100644 index 0000000000000000000000000000000000000000..093af07deff92912a34dd5e070814c100ccc555b GIT binary patch literal 1578 zcmaJ>TT|Oc6#f<)WK>~-xum(2sLiDYj7=#?8z7g4OG;`2je+L&BIK=6B1;}wYMq(n ze>4w$?lXOGW|Eowfc~Mrb^5JrW0NrPjCZx)clMk;XTP)m{P*@ffCW6&@j2!O@eSrv z_*N%!Rl_wM{g_k3cRIeubq$LemUQR})Ulkx4K?3P;g*g;+*XSpQn;fQD{5HPu$IEQ zhPxVm6c|``JSV&{GDY9E+&wFB)Vd8O!vm)- zFj@4XLc^~X>Q=Sp%6ef(Ho~<=7+7}b2TSx?wH!}iBKxHH%=*nLxRzHbl)}LADoZNr zK+Ov3G7vb`@pLD0+_F@A6J_5P80iqJz8zJi7n13d4S_QqVw*1LZ>?d=TIl#54aQs1 z9KW!!)r%*spu%I1o^Tq0p;Bns&o`}FoMT+xH37ZkM}aNZowydq^2@7~44lDP4I2jT z;U|IIZr!TLMN?6x7v));e7iXLW1G$02Sp7<1Dm)nkZ#wpoxOoAY-{-0zyp*7PPf?) zGbQt3RgWC(U{_$wbn2$(hvx6nb!UI|gQ_g&4eTkWgQgXlE^pg3@CzOq*uf*BIn(yt zsOk}Ytk5UQb~Fxiyqp5_tC2KA-?Tilr_4DsJdkGVUg;}x%kc^918F}u10Uk6Z1`I zn&{64ZHHx@g*(Bm*O zLCcocmf<0@O(TmL9$U>XG0p?41lKXbah8v59z&QTyMPH?Ws=uux5^#Yk;5YHVhJ{u z;d1tI124$E#4WtSZTvwmsz3|$GJy=f!bKKllF#xbTxR4PTmK5aCiaT8zCypM(yuXv aY0fkJr;$uQ)sXvFJqEMq$mfanq3}Q3M1rdT literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/samples/TextMerger.class b/build/classes/nu/xom/samples/TextMerger.class new file mode 100644 index 0000000000000000000000000000000000000000..427449503d3a210976eec996c0781b98d49a4605 GIT binary patch literal 1262 zcmZ`(TTc@~6#k~&c41j;xe68(M6oR`<$f2H3W}Dhfk1d;DMMM@ZtJooB*q7gG4d1e z$@s#vFU3e=^wnSBUy-1ArWX*wG@YF}m+zeKoHO(N_vbGF`Vdo4ji3S#I>Zu^5th-Z zAc8IhE_92fM`(J*u1_re3M8TMU_i#8j3EV87?v?2W0b)?rklDw&fxS1<{6}DdWAE1 zV!FxW+0-((=CoylB-OEWLNn$yOBd&&P_ozb4F+G#%tp4-smO+w${2hjGRL><8Ez%H zMG;&nv16$Bm&_h(7B}rUMH&?`9UIFG4Y74?Q;QgynT*WZmTo2|va73kfO3=IqHDUb z!cbYl$a@obq9A8F1Dhr~nLIW_P08@DVB|h)Yl){bS|(3G#uyckL77cwtpuOa1yRpQ z@|_|j6*Z_;aTDVzZs9gVMUkwqBUb09P0%XtU_y9DRoum-iYeSvabMs}3&jIGWVlcS zn>2WeYQj)e6r7~3;u3rejsLANgZCtfN6YIxK{%x>MV*Luk{2o%O=m?EYBm+CtRy7_ z|8$^Kv0`b3c3v~G@ z6ei`L^xOtFllB_1u+xQsVNfleF|dp(>agp|F!2k_tq*J-*5J||H4bzQtk!;&J<_qTF_<)x|Uk=Wh&^Jgs zSlWkcF_eRQ4{|){la@%eI3neBt>H$AG&z(l1jVj=f%F37{n3wIIKw$8@1O?ByL@36 zHNnt6q(zce>_N?;au-z; zMK_Ii4-IKAo}eEZ2Jj4nurY+^7{)8|c}o_8cps5#LmW+LCe$$bT*ei8U%ar;LOTcj y-cV()<_okDQ@@KzO&qOPV@J_f4u_mK7LK%Fy_=TpTYue zrqGI8Em*|uB<`f}CB8E7bqYy*qmH`xLuiUSXtU6^f25jm*jQU{+Gq zu^&nQoIs-&1_C|X`xO}2A)<=DW4S9MjL~*ibc{*q=Da*96_w5;t|5?`SkK8~V0&)K zfG^Ot=G%ds4J<#HvuM=STa9Quc~X4mc@OKd4;;~T!o1Fu8IYY)xw?jy__tSnE9kz3j8pqJm}zC z?K(r?NHu}&4dPLZd%3xdqO4RP@8tp#u_&ryffk*Bx>$;95h+^RCIT&Kt9sS~wo}2f zS%aS5J8xc;DobGCDaAu1=A$zE#^{Hc&*_YfUYxuh=O-+Gg)QB+O~C9VHc{^WlvULIuLqLZrDna*&&eycnv*V- zc|DY2)t_k`!BKUbG*C(-K$ylutQlBO;{-MYhUQDwiacv7Wz!9ZIZMOQEDgVoXU@-L z4LnNYTdWDRM{^eyO}RLo#$$XZaQJ^mGR7fGv_&B!6o-+#r;L;c4;?UM6pW{ z7#t6!8F;4Unlb*M8LUdPdi8p`OmEo~*iYop$?JHQBnh>I0tUqHi4v?|h`{lx&v~~L z7K@%w)rn}M)H*?+H8NH87f7ziV8L=i-f(ws-xuzvGCjuw7b5(8o*vNF(Mz+S(^Jf){(k0H&wm7kP z_4=(3XEN|A8<|7F($qf}df`n99O|8XvrKjS4jPT=eyDt9I7Q@4al3I4aEu?l!~9@^ zPp&f@nBWt-~&7dF%$)RhFleFEY4<%fVo(V zq6RB4j1kHccyD)z`CTAR?GCZ93&bc+M~MumB(_WBriMsYgi#_1Zk98V;f@u!wS(^u z@F7hV#7Db9EbajDG0sL1=XQg*y#vH~T+k2~@k!Jxm-tjy+0s8yMq?R?t=)2$|E*au zxR%WHRQxW%OkBRo?gWy{1Rm z0a`rA@fcv{Eow%nhia%VSb}yo=_#J-`4k5^8EX8lBEwU1{Fc#%RZ1O>V1dKtay~rj zKBm%o4EV}f-$f=*n)XY}4j51;G7 s9VO0Hd_n0m4)axH`Nk@edyUWQ{A)*|o&PdV|AT1*H%!WB$Zf#vf1LP^ng9R* literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/samples/Transformer.class b/build/classes/nu/xom/samples/Transformer.class new file mode 100644 index 0000000000000000000000000000000000000000..46a3e31b62656ac8ac59db1248eda2f90859cf75 GIT binary patch literal 1892 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 D1o~g+ literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/samples/TreeViewer.class b/build/classes/nu/xom/samples/TreeViewer.class new file mode 100644 index 0000000000000000000000000000000000000000..a2c4a25f5cc4251e08968ce8bd7ae552dfcae57c GIT binary patch literal 3150 zcma)8`+F2u8Ga`_*;yt7xsVW&rfrBO4Y}+B(i+Qb>m%Zp>+tckds#BolRTmpGHDaea!plD(v9+Mr%KqnM z`Mil|@T^GB$@08`FPIpRTn6w(3Gsq~7dO!QB@VopdG;At*Zbte3L{8;ZA<(dUu&N(zjcre#wa?oNDgS~~IFkx!oti2IVIa!W zOo@K#2f<)-|4d z#@jd(*qL)vb}Zo$CI2EIYA zk!EBVgVLpImKkMLs)KfzBe+<`mU zaSK1g&k3Z3U*I)1b+y;9V`6_b%CO4r!z-PV0SXslzyqf5~XCV zdB3voYm`Z+oylDmmT)Z;cUpJ@Z(3N!Zw&m_!gai5;dj_!;rH0Vn;2cw8bE)*A1xfh z{cMi7uk^guItzco+XmjT@MpYA!u{7vPD-qpADKI=GUV~9t;#jk(7U=&Ve;Br&fZ5s zqRBwoRClU3Wu=VTKxsasf@#v*FWMRP;4#jD_QA?XsbevRoqDWf=jgO!a1~c88INs$ zm?+azIOxP3_3dI&6|x$72FGu7xI+KY)$XF9^uP3580G@T1_g(or14C9uHIssB(0@-i%Gk6xc*> z$2g~I*tgw~0sY>xjyvIUILXCLX9SpiAxMq8`J!8>&d0jqt58buD-4^t=m#pVp^3R1 z_1q#W;Jl4WsO$KRJz#RCAJ|^tpLBxxHAJX}BN4XxDu`ync+U0J5k|3Po)KL_86h$5 z76K+NQ&%Qu6s}TKA>cd>e*nY#ovPbqTkI@(HMBks6>`>t!@#8XIqB}a%lY3pC(~C$ zf8=8I4`Qv=)l0*&W6I;~j%KvurjGKbGhoHa&hxZ#|LeCo^NXUCD_|SHACmm^0d!~_ zNISUh34dK0w*3DDbQ4cD`+p(a9YeYbIi7?a<3;y@s|9{S9o6;gYT`zW2A0*BC7pW|%}1<=xEgcC<62ZMd5yj#1i&{kRts zTpz%FnB#LE2g6GCv94Cy-$h*wA4D(q&_b6Aa1ZwKc_;NMr~5HPPxlg!3G~y)eY873 zjV;jI|AjsSFPO@R1rS%$0ad(!LRo z(#Kv_(ux^+N)v%@?o45tar%kK5&G2W^)C9ooBD#v&4|6nAaMi749plfZs0)!Cx{Y| zXe2l%@lXXEyhS$xd2$&8B*<;)*xsI@>sQb*Jk+zRjKS&AYuJ9Nf$jeLBAU{#V|ciX zk*l-@`k25_RPF%1g6&PPkQS!D6$(9+``FBWwtN5%36Tp++|4w%GOCmqgGpH$-$2{$ s5DpF>ew3WThr=jI0`mxcOJS|h{|(c?quM{vdBDI&4XW5YXu#Zm0G+)VO#lD@ literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/samples/ValidFibonacci.class b/build/classes/nu/xom/samples/ValidFibonacci.class new file mode 100644 index 0000000000000000000000000000000000000000..10d2df31c03257373f2623a6424e8bcb1df0dd2a GIT binary patch literal 1683 zcmZuxU3VH)6x|aB1}4*%kWfXft=bPrlVD?;T4-a_G^tb|(L_udH4ZR=NrxGPA*L@r zxLmHKf5Qi#?E_e|mOsFs4w{b_o zau^?}2;goQ7gd^8#NwWU)i5rrxQ{gz5v&X219AIUU~&o`s)*tf1^F-vDq;c=MKOX6 zY>Kx}ReXjm1=|Wfmk_#b+NQfKA&^X!B*?2y)sS!|Z`ww&(=ZX?AFb;g!6g3lRkDD>9*czT1Gov(k-*PZk8QeuT;z|!!~r&mT)$?mp{~x^t7eh zwe*(TGVNMcq>UU5z!IW^Xls_yFl?9ZLDQ}p$AW~PXvL*(x#Jolwrez+jNOmi(A|3a zo>|M;u2C~u+?PyVIpXwIjbx|CP+;|z^Bo~|vsUM$FRi`3hYLN53Q|b786z;fB znv`Womb=@uFyE;ySFb!S=uJ-~1@j6@?4<;C%jvW##=0r=oPJrO1tE)uH}MwH8ooeE z!fC%)Uk6edO1j_gOEXxPOg4SU!jpHYDne5v6pd@Yds8omLu?4hiof-3v$!w0nq z6v+)T`$e&)VSg5?ZdJnpY6|KaOdM)>j7JJA4GlryoF8%gD3IX){og1b8Wrk1nvo&NmLKbd+JBVkf+HVwPFS~o3L`a<4;pK_#I9b@A_B*~2pab-;M zS5t`r*7Rz1IFugi(MEW49!(CWsmOEYnF92bu;6DJQ6cNo8Z~o%hLHxxM9`Qt?Y7Z! z{hFtS7%y8+AYRnrNt;K9LOUIHA>J>{bkdtVmM%py^oBTluCr6fGoja|m$xM-O#!xS zyn_V4T;ewYNz5_t=eSDqDe)Pc?Lqq8+kTt(kY}95dA@6X04^{H_(gjc@6keM>08JM z)Aq|#OyqeLP7v6Q$vp&%@{$~vqw23%_iQ2BGQmXfY8Ofu;S)r5V`^Of1Cu>ynNT8_ z2z7C4J`wEV^jt=nBkoKgq2zy*rQe=EJ3(}JzK5xde1aGg#HM?gVK_Mv40I8Hin9s1 zi`Tk%T~rW&sNf;LtiS>prpfUdSBotA16<)({C(WOT`b`NZlZu&*hB^;WO+^(v5z}E zHp@67;(M&%XRLZE96+V-A}-<*g4|2sGS?ycMvz1b3gXN;i>u5?MKFhXK4ttw4k_vt z#t&qYqWm!&lffnOG^yw{#*~?DhTN_*@(p?=F!7w>f(n)t-1Pn*vPu6S$3V9Tm_X(~ DT3Cmd literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/samples/Validator.class b/build/classes/nu/xom/samples/Validator.class new file mode 100644 index 0000000000000000000000000000000000000000..0ae9423c7d647f5d22088d3dff2e86a0727e7972 GIT binary patch literal 1943 zcmZuySx*~R7(Ev+V_XNQ!2z;P+N3d?-BOyQkPs5cLSh1hB_-)L*f(ZiJY(78l)U#R z^fgk|D9ST^0I8Aad(N z2;*T~$ApF(VFYkfkx30xVT3TI$gL1=tMiVAyCF=6aSZoDxUbHXlFx+jKryo#=0cbc zBZhm5P#IIghaoJ4aRQ4PmNYC21h1O5>5dEd;(cWOlTJnow5Lp4J}Bi@Wns}+wJ3?E zoU~yr8wFGG6`|iEx0{BbT>iF}ZA5X2x)x!Wbjw4AT}k9)FTrH=Y?u z%doS_1-D?@*)f%|MQ%1k@H|cT`K`^g%)6#zGeU1Q{G3rJ(ydV!D+e{57?{=8y$}j%w1ICDY1C)yc61*GO;77% z{Rs?Dm89-Ex?$@LdmYr>HK~_3#JnXdunxW6SQ8Yv()y5QSJ!3QJs>JW z@}TH>(Um#A8cvC}b*Rk|ye{pfks~Z>9W;mqw7fF1i2PW*8HL`f7Jsacz^Ve6z=fmC zsvw+l(xsfVU8V~f`Mk90Fm$9ty~lDxb@+x{r`lszysxJLbRp70_*v_yT|@Nd0AV8y?KV5)y+ zaCh8y$shBd{t5pM#{9#hfq~w@Hnc}HeuL1;4q6o-j=o2-jR@UeZ z`^D>h{}#tIfD~r=g;3{9eiYQX$1j2pYTPzXk`C~*Gf0~uwBciPW0;Z=($8pnm)@5# zk1N=~=Xi!u{D3jM;Q1qUFL4d8FpgjO6?=;b{Ei#=ljmR5?O_t{FeN&1NAzG=oWXr> zt>awsJfp_Z$ER1`Q$L^H5gl=64)Xs8>ltEITrm>F3eYV=tnCv5V3^!>Xl?uGKtRK% m8b(M3!L(1yV1qwS81)EWj9Q_{%8rBg2 literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/samples/WrappingSerializer.class b/build/classes/nu/xom/samples/WrappingSerializer.class new file mode 100644 index 0000000000000000000000000000000000000000..b279314bd986c2d10ed48731cd8100a4af9f2ba5 GIT binary patch literal 5369 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 VY}AeS?RVq4T8_S-{YCh|zX5c6w1WTu literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/samples/XHTMLPurifier.class b/build/classes/nu/xom/samples/XHTMLPurifier.class new file mode 100644 index 0000000000000000000000000000000000000000..aa9e253b3fc08645216876b821c1eea7f5509651 GIT binary patch literal 2805 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 literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/samples/XIncludeDriver.class b/build/classes/nu/xom/samples/XIncludeDriver.class new file mode 100644 index 0000000000000000000000000000000000000000..eb002460339f9e7f96b40514b35225864122e988 GIT binary patch literal 1199 zcmaJ=YflqF6g}GqOP7U03n(fgC|U|-m!DDG0 z(=d(+d7M-+CEe2+W-zOv1#>E%sF+vKv|u@wzo;Ob8ro41S$6k@f_Tny#73pKC%kQQ z&!#1ja|@=uV|tc6*NqYXy;WAwmvbsc)h!xjvski4*?7C=6zs~r$a>b1@W>J=nwF!W zJM}JiXdan{Z8`@=-uEo$U|!P3+=^eS_`dKIBrnkNf@HS!IY5Y74g(Z)TzIo?p;8o% zPxO$eD(GrJUa1yD$+uibQk&iZb4>n&S%JD&vFv@x+J1q%RI~7AYu+~tAJ)xMP_2r7 zicKLEXnD8d6~w9~g-Kp7c2w4Yj!+G&DFG2(4hSe(Tx*Tu9&0(w=$+ zRjW2hmm|eiwRxiK+DBr|*|N<7ThN*M3qcN5^rL6_bccn;fo zHC2Mqj0X|HC~SMALqFntdGZ$mTZ#nNBu@r+g?lu82IV+#cXDkCj05Q6w_XcCH*WA0 z!%g(iA}{~sD~)iUK1XQx4B^~4BD*JuCYsKmt`Dcf#% xbGySe%(xcZMIUX<4fluvVvb?}_sRKyrzAqZ2^mr`s1jaL@u-Ea6kQ<<{Q)I;7gqoP literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/samples/XLinkSpider.class b/build/classes/nu/xom/samples/XLinkSpider.class new file mode 100644 index 0000000000000000000000000000000000000000..8d7f7c9b8580ad20593688f07cb6ba0d6079aaee GIT binary patch literal 3194 zcma)8>wgqy6@K2`+014#Bq3Z%Fi=RPO*W9V1+_^4W|AG)-C1U4 zNvOS8t@l=2FU5MPQZLbZO%+?iUcon7@J+lb!)rReB|aFsi*M`rjzZEeJJYsjPb;)#E?AeX zbR}>K>8u?LDKuxD*^(7hJX_)3wWkN8WyucGrzSE(hiTnZwmjeV6xuVTN_yTcrjJ&f z0>OEfxKyz#L}_n8$vD0^*>%U(I>^a0nbGk_pB~RDbVSw#t2CR=2A)%zCB%kfZpja< zQgGTTh?k~=PRR)lE5v(xPb+B0+-dH%Wt@^dUMWu5-V@eTfjb=;H)j=2Tb?89>ZTUt z9bciVmV|E=%LUs{pW*e3*^n(s*0(J$msi->vz7^QnDnpsfn6Lu&IqR69F1yV$K70| zXqP1Kc*QFyw649aU`#uH&b=I!WJ5Nvau>&}au^#mqp+!`hBaKULn!eWv*+tfN=?SB z!i?(`?P(Tgcs^&B1IH~zP90VwmuF!b3RQzr(CrA7tmQp>MmVY#^GHCtrz(L>7;Vb( zZK{-d((zr&m*Uj%x{j+nD_TxTVQbI1yGtdZDh8lNX=iz} zaHL-C4K?T&v6>jdLDIV_+!4#qQ@ke9_z2luD?}6faeyvY&5wAVbwz||;-mPOiSOYJ z6W_-Vbo|i7kMO3BADj3IuIYHo#82@v6GxG$A%r5Vv#aChCVqhh6Tif-6wJ^`^?m4g z+r+Q&8@YZ=VS6>Kb)-e%<%6I+m`=~l&Gpai?{~e~^n(Kf1L^q!^=IO@_??O0OPE9W zgTl_bn{`Xh>X=xJtlu&5uFT$(;d->UWMWyu`Xg3!yl>(L{=_1h__O436MtEyuqqJ~ zf5n@uMCdjo;IXL-b}r!7ojO@#6}?r;X_foY!1k=bWy5F<+~IkyN><$4#GP3kevUZ565ONEvIl`6kL@ zRG_H0G&+tF9}-QsRdcWTW=gYmFyXpEl{O~Ml&V&%udQ{J4G|$-IsS0599#)o*CgM8 zXBXKF#Pet)B~{;SeX9tRJ+E2QBxv0@31>2@`a4#5xG|k-H@%zpdffG!WXd4YQ8s~7 z6MV{R+*NVHK1ic#OTO=pHo1-)^4P))Cp|ZCb1r+M&eez;(!yEP0tWuCkk>~~=G;=i z=ESbkPQYW*1go`>Ms!z!eCAoJ?5bVTsq1!YO{U>*W=+q$rRObw+@5DWv{LwIt)T1G zi`$-u-TX!F=l6xij|z4zC_Knj3=i=?`&Bq+bE@xu9G!R=16&V=XPvyMq!DsF#GR)Q z=M1<%wG5TIf!Nt)#IHl+G{I@pb?BTXIc>fUV==@y%&~(wpnr{?+vs^aeSd;GW`u@M z;s{qtT3FT7KN+Fi8S7g@%UJ3?=zEu7j=zHq+-ObhTSD71+83b7nX!Tng*Tur$|Amk zO)>V8&2pseUBQ+Zo=oxj*2U1>4(!54CL)3LqlM!^eo7reD|faqtQ`zu7X#XjVV*z2 z(-DrN$l%d1=mWG)kl5oSD#lxFv^zmO23t);W{>ce;@yqBIYPTn@jQw6d7UUG*KGDt zs_Jc`HSzRXU&lQiolDq0rp9kz^I0bPp=ET9FQ92qTipA1OfrX^gPNS}>(rKU?_fga zorxvvl3}+D-OIRdFd>VDF)fW%;ejviP-&!E<+t)N$k z+$OV>B8h#(>P!gLxBtD!uv>5stG5FW@N@Gr|3)-UVk;AP49$3)7FpVyrPV2P;S6mi zN#!}-_zWw5o}^hMdWt0HNW~1E#w`EkiB}}zMa=U9@+!*wiM+(0M?a)|g4A`9t}%>L z75hoEkeVRowD#S>xtuQRR@#w`U&L%+>HH65pQ zJgMW1&cKz9$=C-trQ_UDPH#bdfTWJ6Zeb^ZKFu_u$M7zlg|fbZZB+c;O#Dz+>gHSM z?vtvdx*lTjD7po-P`3N1*w&X~YT@?3M1}3d+Fhrwo9TgH*GVSdj%RrGJWroRH=aW; z_tJPVq#;Fr-OxiC;xMp>1 F{{g4n0A&CG literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/samples/XMLPrinter.class b/build/classes/nu/xom/samples/XMLPrinter.class new file mode 100644 index 0000000000000000000000000000000000000000..e213fd18832325cd6d063075efc0d0bff05adbc4 GIT binary patch literal 1378 zcmZ`(+fvg|6kUh5q{L9P2#R=#_m)Dd2y*p?ctKNdfTN=?p*@ypn$$E={1~5o#)mQ^ zGkky_<+zfzwkUQcIXP?X%h`Lco}a&d>;ag<6BUEV^ha%#(VRVS+Gr~QdsrLR~EvQy*1OTOHbf%JIqpG+2zPLR(4AQN7}@)TMDYu@fo#8 zZV8OEiDwMKc=xtSvgTW^LxZ7aHp?xnJnhEgrdQ#a2M;)nK>w<5mOd2CT2x@vMut*X z-M}l!`&QV9flhJxupSLZF{I#@hTFI!kX^5v6*;emlUZ)iomfJ`*)3G{_f|W0FUrk;HYlZb;tGf&(gLh-nm{?mh&16 ztY|2Pkq0CH{T&jRTn?n}ySnMX*7NtJI$xkmyV;5)fq~%_>Lm=crw-W*R0i~!weG#X6TgORD4I;N#AWk)K^9NMOhDpgR@BU4<7z{ zz@!-BdziZdSHU$gwga&p(Z~4ijle99^IK~gU<4<)>%%Bc5}~sgK^!q+AIJ8-Li>)M zbgY5+_AU~ychGC(#$uBVC`A(E4J1i@!$X6+U8poT6wz9`zX7d*6lqeq1_mO-GhK%1 zBQ$ARHx0L0MG6_@c${!u!(kpIJor5KzyzI^z%U4|(VGIlF;Hz@6Fi@jUOxc|Xp1{r10C0A}$phYwL6Mg?E$ z_$r4PT+uL_!w|}9_*%o&9CWqOaZSf{HQ&%Mr(-^c6Ijr3Q_Z&&{*8{?YI8@!T^&^o ziyH0;WEX7D4sQtz7AH0ZGF5*|3XIfjPp(96QwHl+(;-r*`7O)Yv;tf0`(P&Awz~r3 zH7}~{_->_ZxgAG#E00#|)os~&B7=FFbS>Kx7%To%du;t;RUFG}R~lhpd+mAUGTX6& zt_%cD?wPq8+0It1J0N!i&h6oA6r|R@otEr`w(rsSXkyd$E6b|~@r)I;d9u^T_!KzN z2(8wWy48upV7{0Vfn39nf|gveRk$N*2xWC@1LtsF!;*n-@twfLM%QY~Ia5)l7nK=& zxgUHv)igIA)HEy`xR37z^8FnStbmS$2ynu(mP*m+Bp-(>CH+Zl{44SluwShm7qd*mH{DQE^_alr?zT;YQray=N7{TrRy5jww5lbY;d zgo3G1;UsCIr+O$9PABU#T<;mYc)(y`jC|rWp?7)SJZ@lt&z_o}Vibc=5ij8s$0@#{ z)8xuzXE256rQ?k#<{)1|qu@dt#hR^URe?h#C pOSsI{Gp76!K9BSH9ABVFj}!d!$mCrOr8ij9FgZ?sis%5Q{|7v|a}NLj literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/samples/XPathDriver.class b/build/classes/nu/xom/samples/XPathDriver.class new file mode 100644 index 0000000000000000000000000000000000000000..4bd56291e2447dce2d01cc27bf9769b6ddcb3613 GIT binary patch literal 2040 zcmaJ?Yf}?f7=BJj*s!dET#M4W+F}7AvG$^((xP~6!X1iu>yjK`G1-lqU5agW{2P94 zXXLYfh|@abbUM>7o%TPpKch3fJ!i9lV40F(&pGe=yzhDM=jXq_e+A$JJknueOv42o zF^o0gBF2-rq~kKKD9TkG*Kl3Or^qxTiwX6a)G(#Pz_f-LB`~XFKjxIkyoLo0pXq4F zSQ3kBy`iHGOKNvh!>uH4>*&FfhC4~z)zPcApKGuL8qe8|?T-pH^bafu#4oyeDbSL! z9XSycm!&snEf+{>%eXnKuwZ$%I!DB~zh;*OdNWRt-gJxUvQ;bg}?da@i8e1Uy6Jrgv7f(hFx1*9LS(Z}6f zP?U~OUQBKZ9I7$G3+En`Q(;XfNHH0yC>g0bUFQ5u#@sA1 zIT}_Ctid+0j(fDnKmkP!jsX`X8fV}>JOgDg{LRs=Dk}y8YzXv3r2odk(_-{_GOW+=BldRDZ(kXg3pGKR*~hh?Oz?46;@K90{f`5fA2O* zgf%G8w->)wl{I5|PvU+cJtpYr|KU~6%LE&dv*B4FX>a@2)jv?RXP4g;(blZ;gxsX3 z94$*615SA8sNusgPz|QC#&;L98Rl^pJDL(y!D?X6R;529{lIe;tU|y>*%gaOS2$9v zQb{^_f#E%!t4m2ZhIV>Y=M7m|R^3D|eYa`=yYDdPwfc)es9xmvZJyFx1=qyj3y5bS z`yGB8L*g;?ac@)sIEHt5YC=B-NTIO!4RIt$`}HMaH(sD2Gx#d;4UFfAw1zqHHbE)7BC4iZF{y{RrOCxqw(V-AHPzS7H+TPsOQw6@Itfo`l$Y7F> zlUi@{9a8HSJZ2}|7H z#udt4*7xD>F-}3wv6WAj9N1}hhY#96*Y9B>2263Er9mX(*_#Y!eM~c~t zQClx}C%Ah=i4@-BZY$iK@C%QG9{tImy&S)6<14lU5 X&t^58yQ|?m2kdAc!5=33c<gN)6a|;egbQT0A{>ulX!LP*NBkfxS9m3BO9hTs zTF`D2%2C?OiMFM63WM~LRS|?TVCZc#mLkV3$bfjUf^RdNZZnpBJE}-e5vNP;F$}jE zn`DCg+}*QfRXM�)x#c9N${o=%mL*P@?zr9}^|{ivm~UaO43L_LdU%ZcbDixNv+) zY2(=D_^OeKdkpC%4At5&OJ@_9RB`+`N4HO^^6j9rNK^pdt$y0~M4EcqkD%4gJHpVVGKuq^W#UcxFe=CQMb9X0u34+mGBr z!*oEUXEKbuwJfQq4kF#i??_u6A~nmCdZA^dU0_rL4ZTP?uC=CQAe2uPn#k+QWVkeX zv_i((wb-^nHJJE5lEFU1g}2G;1nB-TIub$!nsQ#d9U2{L`a2X3sqnH0*X16)i8}ic zwV>Cy47BANE!ccENq0c8wI3>3AvHfD<%3q)q;6FNiWC)6NR_187H&l4eAr~Gt2^@8 zp-5p>wHcR&!QgQZr&*8`)vELe$;smcj(~eG5K_u-=PXUpNruy$*2GQ z{S?bNqP##a**HxW`uh55sbhfn#F(gKP)lN@L#*!%iD=oT*fV;fK|g>j&3V0t7@|1! ztCnc|0~2(cL<$+=PGbPq2#y&nV3vL#*Gc|1Y2H8%bJXkesM0;eA{1`oC$gXsSp|(Y z`p;a%2S|{GSzN-0|?k>C&Gz*PtqR4kK|oLf0b?~lK;Zs fn0ke?9Mc@vIA;FA#U+l}BFFV%lKY6{x^UxvDRRce literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/samples/inner/InnerElement$1.class b/build/classes/nu/xom/samples/inner/InnerElement$1.class new file mode 100644 index 0000000000000000000000000000000000000000..580c12f5a739ae9b34518bb6e17064fc309c61b6 GIT binary patch literal 775 zcmaJ<%St0b6g`!+>Dbd_)bTOC-;m5=TEUGdE_{IyorMx`S?Lr>Nu^r)A^KP2#(;t! z;75tK+EyD;7hU&x&b_Cr9-rw z48>}#!C>tMEx}N!OD&F*Zc~J(ys0QNTn~J%8a$LH&XQK#kr9J?sI>@p6^|l8;YwX6 z-c```BHq1FBJ!juduh|Y5?!I=<+V+MnaR?Ss>DpF)?R@-iBv5SQqKq_&-o?y6xVI< zU-Mk}F{S%YWLxt%2?>{PIPHXjx6)O&K9X${&N&H^&=&{NNE*s-^w&%w;rrTGK_qqi zNW`6>Wn;lbKOBaEOjNcT!@}1p54ax(;k7|7x){PR!_wE?Z~ug0^j&LC%D1yJG{boH zRq-+1VYb3%t$|g#?=rmy&U?j<;lZj$md$l)v zzt=wY#n;-d6;{hytg-9sG|+f$mLlbt$0q#o|lWMVNyp8E=G`)`&0y$+|6q0PV1P#tOi@h z23*oGry-?d6Vf^wk&$SQjx1h~o_OUVR|5}uxm?z8MJmi|xGMJxa(Pk1OFG(dQNzmu z#$YC6xdSOPm$PyLk;%NBnzCG8Zcbb>FPrg{nVE^7opmj9D&Zu}RG&cgs6CT0y}WA) zbgZq_pUC9n^G-T`w20Stn08hDcEY2&8Av3GW!4@Nat`a{uXpEk1Qo0Xc1 z2T4T`q`X1poA>Nge9*I8({o(56#IWbcGrND&vP+fpHkpc8(`%H_-%wtMW~VHlTlMV^W3{H$}Wv~$@~B~~N4e)(|ah+ao@ z*~w)pBX*ge#?pzjj5iu-vu1ACnlDmzL7t}Srl!b=@+ZkFO}C=1V~j7GHolYq<^;|N z_ZaU2=tP9qoLa6ulpLlkz`ZKgxd3rn>BPC}O4@-i?H=NNV^Irx@G$SFkG*``^0*|{ zejjTPLH+zkR zF$8+i=VRUQLyRpVG_iniB6z4JRzTHvXzYx&6rdFlDWH1%dO+N||3_y5`u9vI$hc(* z!dwrsfW2rSVk`GuI8@F^$5!<7UY0ZF=N7>ZmiRGxf1FYSfqT$391(Z1g|efhAs}fG z+xaO{MUB)VY8aQ%P+LG9-Pbj&<5J)o?FZBs&~OJEIz<5+hi;*McoB^g-34s!UO>~Y z%%XX`tAH&9Y#pYwr8m?Z`UwvR+(eVS+9q%Zt-aw`bGU%*x95Ur;FD9N_ethF#4Lt!0wb8jY0UBfo5QHe{3JPx(tdz;0czLcIQ4?mtHKk+ zku^(|nNDF)F@2Cc7cEY(O;6EBP*tc2A%0{f{zR>Yp?hf1FsxzZF1FDtSR=k+_~-G- zRkrV5+6nH}i)fpOEntVFE1>AAQV+&ekVa9d7{>Y|1Q_)J;@l-bnDyJa(zvC#+O8i_FWXa8_;01kn2Ko}ySeLp+Rhz_{0&MAX0-qS literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/samples/inner/InnerFactory.class b/build/classes/nu/xom/samples/inner/InnerFactory.class new file mode 100644 index 0000000000000000000000000000000000000000..c41002f8a213d98f8692fbe5dc08f5c02d1f20b6 GIT binary patch literal 603 zcma)3%SyvQ6g`tZqOn?A-vvQ&VXI;`;-=z8EEZxHRtqktaj2uoq$H{6U+Jo#;0O3o z;+-@;peSZ>A7{=zb5G{&{rMHZ9x4`cm^ZOt!N8)4B@@dG`2(qBe8`Zg);bK?qu@#~ z6dkEVYv6Z9c+R^XA!R3Ux!2*L)O`wO<7*i)Y&dFQ-v_=Oalh}0$d*cp&~ED3;BFj* z4|P(AVjjk){8p;ou_t_?Vut;ybHner?Qzw!+i^%|^>L*3c|kfyY6Zo85%sw%F3y^I zy)hXwqd5##I~atnXh`j&^mD6SJybv*Cd2yg9@1Miapg~9YL?bX#ai??zrfA^dF@~w zS$Zn{8K4(}NpXTYn=+%E+ZuvBCC8H#^9fjiMRj3Bz!X)WdKxnn_3#OaW++=PFfO-u ihR8fpXW(05HBnd_u~G%%qlno=)k29Xy;3=uocjQH*^Pn# literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/samples/inner/InnerTest.class b/build/classes/nu/xom/samples/inner/InnerTest.class new file mode 100644 index 0000000000000000000000000000000000000000..422c658be20c18fa637b1b5534f64ee140d36c7a GIT binary patch literal 1613 zcma)6+fvg|6kVq+q@7NISda8p!bRiGf+q9Y6=kEdrs*&w>!C|p<|>+>EanbN z+PW4C1C)vr5{k?vB`wt(h0YKm*L5Nen|*uVRa)Wr*+9 zNL9*Pi&wReruANN-OJpjZO8rEZC=sKx^omCy1P&44-kWlTLjH0N2IB46Ihb!qLej4RihLc3n(1;6q1)vn2_6#(IIy>5$U5< z0BItUA&_yJWrAQP8^|OBGVy>+=M`j90hx>Y=(@NMxrxTvKBkGr4AGb+$higzg8_wq L?;}m)gpm0SNsMhV literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/samples/package.html b/build/classes/nu/xom/samples/package.html new file mode 100644 index 0000000..907ec6e --- /dev/null +++ b/build/classes/nu/xom/samples/package.html @@ -0,0 +1,60 @@ + + + + + + + + + +

    + These classes are not officially part of the XOM API. + They have not been extensively tested, and I make no promises + that they will be part of any future XOM release; or, if they are, + that their APIs will remain compatible. + Should you find any of them useful and wish to use them in your + own code, I recommend you copy the source code, and place it in your + own package. It probably wouldn't hurt to add a lot of unit tests as well. +

    + + +@since 1.0 + + + diff --git a/build/classes/nu/xom/tests/AComment.class b/build/classes/nu/xom/tests/AComment.class new file mode 100644 index 0000000000000000000000000000000000000000..bad7ab66b452c714044b5572e6c468339fcbf361 GIT binary patch literal 338 zcmZus!Ab)$6r8NunjqE0neJk~|JP98B06$86 zse98Gc*#uW%_LvnAD;kjG03ooUW$_xeSzf8SVLoh)64u>y(n2KyOs;`#;!-hr9eD& zPg{C{wbK4xtx9GFxhqt;RNe&Kk}+*eEpVROMs8gtN!L`%$<$Sqwlrcp(pw6{ zf5HT^g=>7FXC~P9Cvy&-se+t;y$R|LV(jxn|FstAwtSm~3q<_UP=Xhaa!(kInU8o+ muCCu9-uM(D<(}=>2i85}fbrR{3Wt0XD!~ytJFjC#5xPH=M@nV@ literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/tests/ADocType.class b/build/classes/nu/xom/tests/ADocType.class new file mode 100644 index 0000000000000000000000000000000000000000..68170af65a21c6c9d3e597cb77c5940527579335 GIT binary patch literal 429 zcma)&zfQw25XL{Jp$Vb1w18L{q5R3x1r;GxluAfZmPXy3U=@QCSBZnjV=*By@Blm% z;tbtj;4Iz!?(_NXo1!$q~Vc=nxp?xU}X|5P9j-m(t#6!i4WjHsbESBT{ zD8~y1_gdcxhF&BK5!ZPt%7mwiltH93t`@wMHeZXbxtA5gK~&V?v(7^!DpQ4%sm_vS zCC1dJ)Y{xAk&D8_Jpb(V?g0+mt25E=7NMa(ea(_CoByBOg!%9*GrvqTx>+ bHtEKik1celi=}NMTkWGu)FQP*mPtmE zVshy$%DqUd6Gn}bmATEs>9dZD%8~ty)|Sw16wNF` z@TvCOST~wk7{lRzmO*^@F=vm_gn5Un mcXRs<+VRbS&pD{rIqMcUV?6v8Yq{{w*!6f*Ib9Os zoAN;uda1E`UKcCv?$s)1WssJc%9qNSz#B34kES9VrM8wYWg)$;d?ly1xh}NzV_s)f z@oV0QC$ws32+5+XU8ZLyg!HF#j-RQ_m+tj4cshu&ML7I7fY5F9CJ{qK`~jhe4_mpn xnccA(ba8fm@eX=p7(sAP_y#6Cbd7CB!@n`?@JlFxU37R!KzocL#$9F+dOy$9QfB}F literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/tests/ANodeFactory.class b/build/classes/nu/xom/tests/ANodeFactory.class new file mode 100644 index 0000000000000000000000000000000000000000..dbdef3ad5314a540be47ae964d3dfee1f2965195 GIT binary patch literal 2208 zcmb7FYje{^6g_J@mJx=;auRANEpMEsO9@Z&Xh;YcOb8^8+c$Mi-#sM_{G4o zi98+|STRwM=c)-4j}5GuDB_8Mrv@4YJS*V2iE%tJaS}}fFAS_R?$K5DQiR=5>Q%?33MUQ%FuIeW$tz1IP@=WlbxA z=W$;!Tu2c<%>K?6HbX&XZt(+3%L#QTtkuAYd$N{-HA)kP_{PH^5RY42N?cck7xvma zE!SzRl8Wds65Nw!MGD#o1BXWub-dw6B5XUt4Sa^Bk;Dv9=Y<{aa;mT#ktJT(9|@Q$ zY{%^?SCfpclcr@s7E;bMPA>QH3p8;0rr z&~6`xBqg29z7GPr`$34kyS3W>{{B2au)P*vbOKLe(z9Rb$Lx8EzZBEsH1O*ex}-ho z*<;ukX>civtu^%H)#a;cqS}2Nr3mGkN@02vwei@_T2nJCD#G{jeRD6FVhy{swECuoMR}kkl;sO@6#@}I( zYQTy$nzAn)98bZVQJAxFbfVItvBX`m@Do|GUJKXA$`ZrRxUP9#(wtM7MHb(#`JYP1FsZuAH=6M<&39VYImK4fs0a5AXVLK7b0|A3eH5taV|brSs> literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/tests/AProcessingInstruction.class b/build/classes/nu/xom/tests/AProcessingInstruction.class new file mode 100644 index 0000000000000000000000000000000000000000..2822482deb4d2019731d2833efa5350cd84e6536 GIT binary patch literal 418 zcma)2O-lnY5PezKuCCSEdi78c@e}vrts)emP*{pk={=i<8rde0%!>XjPl5-3fImu{ zU~l!{1SW6ZOy)iE_5JY);2H-xHn5+eo1rJrx-r(!t-#4?`6QoZQA@ihrsR!X4Az;m znLv8y=31ar8mlMGveN!RRy8xd(kWTbq&Kl%ktsczAaGpTrg(A7f^?uz4DY>DIs{f3 z+d#fiWSkwaACi8dDK1;H!_sp}QXrqY#w$HCQMEgqu%D4si_@% literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/tests/AText.class b/build/classes/nu/xom/tests/AText.class new file mode 100644 index 0000000000000000000000000000000000000000..f44de2609a62967f84931242897b72deac50f61d GIT binary patch literal 329 zcmZWkO-}+b6r8uLySktV0nZ-&NbH3hhJ=`K+7JnZds!Q-S=yxACHS*EnRxIA_@j)k z$c@+Jz0P#rO#1Wt^$lQvZiWWhDK1lV2+5;1+K&kRoBT~~r6{FYi@A5&tcSNtLi}W3 z6rr7Kqo(y{rQAZUN@lvbEo8ZrP6vD-WB;lvLN7P9*x8Nns`8Z>FVxNt*~{|`n@356 zY;J2;sEH2Vo$-`YC$jL?y*~tX3o%Z3_1KrtJdhy;*NFIkp@8ndz324yoYVH>^X(l#6Ac#`>^rDBs4?U&rIPx}L7l-KE}jHKzNeIkIsuPk z5i?vL`Hy_gy@0E!H_(w()7HNjm3YtL%xi1fGfuQPH1e(E5gB#Ddon8cQi*;t8;fYj z#{n^AKb-Jj#3O0&H)-n!NjVz+x1biWj=i>83Aad5@mae6S5$_~?M;s%JLf?{Ew)Y% zS^ZI4sVw6$xPvf>CZa1%pi;X}QM-Ji!)S3~#PhJR!BG1v3`24K;0%>@DmB9aEP4y` zF&d;x|4l-7$kG7Zlhd>Fy8R0FLdb$^IGO=XeF?UxYA!pG$2K9ezJmhkn5syX>2^_~ Z$~d_wW0#yP>=Ck1fs?XyKeV1h{R^|6f;j*H literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/tests/AttributeTest.class b/build/classes/nu/xom/tests/AttributeTest.class new file mode 100644 index 0000000000000000000000000000000000000000..5d85d8b6c41b6da0ecda64a379e79bcbec9bcba3 GIT binary patch literal 15786 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 Dnjdb< literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/tests/AttributeTypeTest.class b/build/classes/nu/xom/tests/AttributeTypeTest.class new file mode 100644 index 0000000000000000000000000000000000000000..857afbfe04eecfafeae223f116d3d66dea8fb345 GIT binary patch literal 2756 zcmaJ@-*Xdn5dVCyX>z?BP+}<{$}g*=Ag!Vz*a8h{3W4?l&9%_-tC#dBDe0vo7X;)t zj?Vbxqt5u`IQVRIN=Ig#(HR}bC;tNe0v~BEw>Y#W5Dg`H5@{Cp4_ZNsTO{Y#EEe zP?6J+z#tn>vGKHqapW19DkfN&RB?u{XH`sbv}yJ|#~$ZZTu^aQ#U&LpDl8QR6-5=Z zD(o08$1o=l9V|H|_prc*3#=P0Ikr(>DA-lg zDwN5R7_Aho@}yNQvF;lq?v+waVEd?3@4r!5=y&a!TkB7`6t7TsZS&@$ZIWq#;yTuX zjM8$dfX0tyuGOtF#cE434V_J9#Um!LChuCst7F!pe{P&_AG2LU2G#PFdbMbel$d9C z%jtc5q>k6IH-?gq8I*M_z|mnbDzGTBsIZu2VY9fv;v$PnEEeGitPj~|9oL?-s~q>5 zjw)(8T+{{Pekn}*hD&MBS}vV^9q;S7ftxYh((wUq>$t&`XF_HnIr+{HZsaY4s@ z2JwtBF_sxmnc1A7V~S}!;7AL2D4-fSQ{JJsnla3*IYl-JvYEUv2sI;fPfqhws@rVEu4M0u)BVX=d7M!%m!F%j zAUs9t3_B=j?r7O|=G-f?^|O?PN5HJs>3pr`T2;4pro{JGQ4VI7D66(J>$}9}f}vPd zHs86GI49Tn{CmR=+9rEw&jAq#9vqT=q#cGrA0WDW&l8Am2r1|%sY&ZDv?GZ(NbmN% z@Fw<=M*et=(~)z1|sy8puK^138D=|C5SZ;lR#}im7t@6 z4hggdGzsDj#3j%h&;vl0HS!3GOn{D{$R41`9-x38pnwGEEaF3e&hmir6luQfFmzqW2g2NVv(;AXAUG&RYksYxzPO>${!l8di#Tery(e~ezj$EmHe z)Yd1ctxr;0k5XHYQ4<=});YR+irV@#wRN6;E=_!d34DP`G;jvL;w+xgPuQQB78=fp z?Km&?pBs^u`I5#LEqA zUBb3yY)|Z$cjv}t_!3hf#n_Q>IrzOTx;knlCyIKIO!+GajX(suD-FD)_;hFhc$ zaiV>bZBr9h6n-{uyM!gmDk4&CT8kzD4R5}IMInMlLjneA{kSAoSY388xr=Zq7OPIp;j*v5ijLx%Ks(+2+!^fjs`Ht%O3;V z<=o?$Vo`01+3leAxtKj4nN5CYJ1$2~^eXqp-X{6Wne2J4+>}a={?;k2#E37hSN{j1%-B^5P#2)xQ7_l|s31%8ZVqM1_{Z;3n+g)BnkpBnF ChFTN= literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/tests/BaseURITest.class b/build/classes/nu/xom/tests/BaseURITest.class new file mode 100644 index 0000000000000000000000000000000000000000..afc7828da3fab68f2a607278ae6446be2cd57dcf GIT binary patch literal 26377 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 HJ^23s4#EA$ literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/tests/BuilderTest$1.class b/build/classes/nu/xom/tests/BuilderTest$1.class new file mode 100644 index 0000000000000000000000000000000000000000..4a49497babcc4a873bfe773472ab51ddd4b30881 GIT binary patch literal 207 zcmZ`zK?=e!5S-O&Y8C8JJbCbJ5PX56;6Xg-2Vxs)OH80i#kYCz0X|CH-n_WW&af~u z%=`0v0a#%uAOr@4MlK;R}FytjbW4>xP1DLnb~HL)~Y4W(3>}HzFDsnxbws)ShSPOSBr+VZ8)Y_`y2tfcX`dN>8o|qDiJU(#!lWOv8v&^j#;R?Tp%($OXv5D zmxgW`c3Iz|BfGr#zjj)N4W~RU>;+h;7<4w{O|#xK=S}kr@_M7l_g%AU*J2>uac=Pn zw_Q*5#F=jSk5HUIlE!ni+MxXq&9Zg`re)tDbU>>R`f2Pz z7*G!&sjtzkzQK9gyWnNhJVYzH*Ft54Mh&7ff%yVa`T+^;EkfDJ+>kUBX(AlOF|;gT z)Fbq%?_03T-i0=70+%pCpaglF3C`wTzwW2E&MR1j3iSn<}pZe|#c~03P ze^dzh2x;dCk!17`u`K-$(KXpbcQ~Nx6Gqg}EmbM+c3agbt`nP3HHry}LMM3XsrkG+ zq(!Jx{Q@*0O97g%P}FZN8qG^?)2MCpYTLZ(o9{GA&)O&4^o=SWVH5G7t?!7dKRRqF dZ9b*Vr}%uwEFomcNC=Y{q**5O3T|Nl@^73IPw4;v literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/tests/BuilderTest$CustomReader.class b/build/classes/nu/xom/tests/BuilderTest$CustomReader.class new file mode 100644 index 0000000000000000000000000000000000000000..c259cc42de89c4076ad1406b565009aedc33061e GIT binary patch literal 1397 zcmbVLT~8B16g^YgF0>0mRRmN(1SzO2C`z#?VueUjO%y_qx1pVsHM_IT?kwqpf65n0 zHSq`dql|ZUNm+y%W17zWKKIVqJ3oJY`vG7XFLTJFn1iX0Jm&J4$NgSBFz_&kEQ;D* z(1)duMFWovJZ8wQ+0s@o7s4qwl(tL8bh56@kPC#` z5?qBoH5@KhKk;L3Ib1fZ9p&4yQ9g)9l-!L5x)5f_9CIh6AUhzv$^C%zTg9s9H>{TH zSOITY+p-y|9WV52qO9RkwqzLXnnF`;wrZlOY)=LTmJK{%m?(C?(^8DuTBOc@%Oc=- ziY;67JDA{QLEp?oKL$)Z#fpKFiB$~Htfdu(spM^rgwqs$U~MR3=rAOTx80^=;u*>& z*6`fK3k(=oXPEh?ufTBUGWh+sRT@(XU!yaOBy)B)_B#wQjCF0Z#4ywls^Uo{qf&|!_h{drgXkQ2K!7+QzT3C zU(wS>nn5dtjQ)K`_TveRHhMD&ZySB-$ZR9Wa5lp*GKAx!4*@2iuz(ONIF4o-j(c=w zDq);*81bW-q0Ob+_v0>UdZty9dM3@zg-gcl#DJL&Uo&4O-LFZgb-hcGJzr6nPz8~Y-0RT zK9Ts~58y``?{rCnm?b{!+noB5AxB8OpnA#!j<`f8H(gsq{`u&qMw~B%j}Qlz;LS1zDRK@Q$k~3 zYnk>FVXb6BZ=mzIFrzq=HnZ`ETqR69Vz6;Xx=RSgBDH*Dr#mp|SzL^g*oq?F*W)}p zG{+gm!^`#K$)(5`gcA+)M~VwGQs&kqJ1 zJvR;rA){?Z#IfF^D|kOR^DyJ^r1}jHIB%2;M0muA@EEhqa_Tqyd9HR>_ad2Fk7AR>by+B)>fN&%6f{Ku6D56qSBwAIxy78(Q+3Uy`WIhB5 zK7$u3c;Ey0D8y_WrSU@$FP=F&XU@!?_3uAFe*t)cM<#TvnW*8e%GMj$z@~wF2JRDT z&wS~p&j}T~b4IB4BTo<(2fh@?S$HnuQ};aJljT7)bb~WD_7$ESR@3)>LRdJI-WwTw zdMjK{#02X|N)h)1H%SDS?SagkJPMsuBx&LtWPZT3QwH0m#(jc5a^r-jt=WSpzHss| za1u9nj^rpyPogXyihZ@E=1aorTnfQF%!gu>`jJcw>=@W3Z2V)5UZI-r@#Oi-_IN?F z*y5G1JZa8coCDz+qdi8%vDT$aXx})~U@AO5{fq|ZR>9!7$cUc;OUzQ;2aGh1X7@WP zKTsuHDVXER7v_B(tE?2vi3;~%88^89Ciho4-fBWUCTB|d=|oxYUV^SfXV)qO^{Jqa h7FNe?Z5GgQ2iG~a`NBH3u)qUX7^7L|%WYvB=3jm{*z^DZ literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/tests/BuilderTest$EntitySkipper.class b/build/classes/nu/xom/tests/BuilderTest$EntitySkipper.class new file mode 100644 index 0000000000000000000000000000000000000000..d79a79254c93b4c9b2d012838409eece3ea2648c GIT binary patch literal 745 zcma)3%We}f6g{3gaT!ut0;Qq6S}0Wr60>1fR0*OYjWn=es;W{~?!-zA9#3RDD0~*H zN-X#QJ_>PXQdI$|5?=Ve&pkf=_4~(90I#thqlJg7cvRcR0y_e`4DC1C>Tt-g-OnmF zPUfXayqqVeAF@wMj+8rial*id)kra{XWFWdv+`WIV|i{U)6J?vniJ`C9bYDSm}t+i zc4UJN-|{bdI#rILlUl3X&`9qUmHU~UCG)CGg7U#9?`GQ2x?>9V{u(?WlCGh`uyqYb zhPaqWCySsQL5u#`FijicBN-$^bT(vY`5;|jU@0LQWyfJcb4T++O@pqi7kEl>Ud>#g z-s}2x8%v~KeU@Kk2W<$F!^%>1@CgYLM_@)9j>$p`K&x^NS>D58F>V}mdlZlg=J8Y?Hu$%;WYy1>f6U=D5W8?eEm q?Fvlb4(<{Zjbih1mB1ExE9g;{Q|&&1Z<8P40oKUIl;e1U4&pzgW2Q_1 literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/tests/BuilderTest$ExceptionTester.class b/build/classes/nu/xom/tests/BuilderTest$ExceptionTester.class new file mode 100644 index 0000000000000000000000000000000000000000..4447664d1d50a9e7d7738136b3f2d8c63a202021 GIT binary patch literal 837 zcma)4U2D`p6g`uq8#me7c58K8>qqSh>$*5EzEuP*78X(kx0Jq4w?jKNnS^{u6x840 z&kz*B2U}nLQR1ELng+2TA<4bxew;Ih`T6VHcK{D@*Mp1eO*F9T;)aWxE^fJ4b8&~k zk|o1Z@JhTEJQVtnKP$&F$yKa37#fe1R{3LwPVb)`_jVba&G2&DR7yFy6J`VZDV*XfiBL%v3QSzNi+Eg>`A8CwsNh%DMY53!SS-Rz(+G zhTi{r!LU|mIg()_(~R$J2PB(p9!5#XVE2q0or{*@HeD2#E)F?n4n&bfuLqxy7DZc> zPmumsu>Soous%(CX6btwHsF_V{5NR&Zz%_^PUq*)rp#|9BD}D>cL_i`*i-r<{z76fzE^c?%*(s_16W`QC zqmRDWL=)q)KgxKgMY6OZo1NKv=iYnf&N;Jx{{Hv{;1-rln7FEksR2xzxP}=6vj*}e z`Y@;c^9B|S*aogM^gnPsM?GX1%NBfDwVQR<4tdkwEf%%}uL!w3yTg!J^(%s5sNi^F zvr*p@a+~kDW5 zRqc|Jj#pidc%JUz%z60(yx)u*p8LX1x9JX&hZk9!$9N!BK zEE>4MV3z!bEDJgWDl#1DazTTokiyLrQD-qM)|3jCY|X=h=0kH4bh>dwXjeqp<&x{- z^EK{OTpyj8n{R8o_L%qys}2gPdZ5qgWZntDE*?T>`1uY;r$dwFv^fW5u%qd zhRJBvnsAAhP>ZXhhmCsRGGwALN5W&6j`~ZFb~k;sCCYx)bKZ)|(IGQT^~RMNLEuYu zEG(YYQch}la49@ROmtIC0u!$r+$CjZqQdzvlNSu5C$-Noar_E(ogs+~6G+niBaPBc zjnSNfMdKh@>tx4hHgheoT)u_aw|2}Bjf02-)_WwZ4@g=cF-*R5ooJlKS+Xb(8S*iD zh$LFmHGRsXOkBvP9Tz;;F0gV!eMoA+VuqSO%Bqqn{j?snHlF$%^3$&d{8uuOzZG>%UMIIhg+d DEhjSe literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/tests/BuilderTest$NoLocator.class b/build/classes/nu/xom/tests/BuilderTest$NoLocator.class new file mode 100644 index 0000000000000000000000000000000000000000..c35bee846b234c191f792b76b2a4ec93810f164c GIT binary patch literal 616 zcmah`O;5r=5Pe&LN<~E!D0orB!H>jV^n~Fc#)PDDP(r*G*htN`OSY}yZ+SBD;1BRe z8Ka!vXG5tNNWHnBTkceC3R_%U9>kDItfvjt0ZY|06IQXa97{#f+;U{##NH zx$1c{!Cp<(4UOgi literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/tests/BuilderTest$NonEntityResolvingFilter.class b/build/classes/nu/xom/tests/BuilderTest$NonEntityResolvingFilter.class new file mode 100644 index 0000000000000000000000000000000000000000..6d15be13643e42a4a59b6e12060f9fc770af6063 GIT binary patch literal 1452 zcmb7^T~8B16o%hvDO52pA7wn#Hl!#e+9sVdvvbb-%sXfM^Viq!0Os)|1_Q|$T9J$*6~hE3qj(U- zl!oaj%osGxsHd~)=b?sq4GRp-E0$x)rwrj_YLB65&CLsj_Kf9-?Vxxlyj^~1(@H$! z=D5AbJxk4Ni%s&#@)^3eU1!~qmVCb>eAhm;oWh1>OW`pXn~o#AHJkgspq+CWCos!y z(UiiMzWFS$>^w=(WVBI@G(*f6azk(#cm$G2W{&wOH*M||%&hcCEPbHR!+~ch&cB{A zL{7OKP%b-QXw6EVd$+|)wKK%l%Q;b!mh1Q$9+6m~POS49vDATvMTWs-g?7ftM3x7vXi(`7y1IVW17|4C6;qmX=MkT(qZMuVDJTY`(4f;hS_cE6)|b zhQ|h$u*|S}cTX@3*Xtb#yCgi{+~3Mn`);#XvKhwe_f(m7 zM9wWZ)`u%lJq`6|=}!J^CwgVXNx*Hg>q>XLIy@FRnNCrQU0JWSsA`C=MnYtTK`*8H zG$Eue9i&V%lSXn4k%Ce%jzd*OXi9!7( zQhElazFpBDs%q5qNe$iTsW8o;7k$LiPxT@QC!#GFps<;i2B|Pp4NyZpp)o|yMjB&A z!sio{7tqfy=@L{GdKQMhi%$JDdi6c@>-!a|M77){)kuv>v5>{RtR98TYQqQtDc6>0 qR4B@FcD?1*;*rbZ5sYH2k~U6>A>2nNjd6+$%;64VxJPqzM1KRsq)s>h literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/tests/BuilderTest$NonValidatingFilter.class b/build/classes/nu/xom/tests/BuilderTest$NonValidatingFilter.class new file mode 100644 index 0000000000000000000000000000000000000000..e200bedd877f633231df0876591df929f9d50240 GIT binary patch literal 1258 zcmb7@TTc@~6vzM5+m_v}v_&Xlxha%hunUS8tT9m{2}!}kng)4W+OaHdcgyZ9Y5XR9 z2VQ)!i6;8)hccdND=7*lW|NsYXU{pmbD8<^^UF5?8`xB#BBw$_E{+uyc@z{B<0z?! zVpZzz$)&7dO~E=t?3v|Q;yFV&U#K%gw%jIX7_V9m-wWD@+E!*^cPDbUb6Bu2$Z3ylQ-*_2Vc9Wu$ zuylocg+cYX*yg4PJi<@qtHzKrpjycRLc*3yyKf_^I97P>*yTd)-*x#)VX1~+! z*bK{~HiLBD@P^xRtdE!SnYk8C*E9L&T%(K-5S0SW`jUQlSL9@- zuLEI$-i$`i2Z$gfyAOsNBpI|*!w@a@5c))i5XNbZNu`!SlFs_T04dxgiQt$bE9rcK zFnz#WI$ZjU$Qe@EGbFwu`Ys*oLHS-RWqXLrsNsIpG{K=|kF-vcq$=ME5snZ^!f2rT8M8oIBQV(P27xWvFqUA8A ztw7TXeX^{CoG!?zA$iEtMtRf=9g9SjrEk(MU1i_c)qNYpj$Xu$;tuZi-ImBPgk?<7 Qo~DyZV@T7wKr)i?Uubd=$N&HU literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/tests/BuilderTest$OrderingFilter.class b/build/classes/nu/xom/tests/BuilderTest$OrderingFilter.class new file mode 100644 index 0000000000000000000000000000000000000000..4a441f6f1c52dc798af8535d9fd82668eb3fc54f GIT binary patch literal 1352 zcmbVLT~8BH5Iwi;cDJ=aSry8M@*zrr(xr$3Euxi=grs5=B>3RNrM<$M?Y8dTrTsJh z2wx=8#2?^~GR|!Y)IbgKW$(=FnS18U%>DKI`%eH%co9b*GI1o4(J>!K7#Rau1D1gW z133eWD)!jGl7S}%o+{+Bj#V9bhUl8@+H##CoSr#gh-}mFS#eT98ndnWO$P+2}!SYsGVckf-Ur6$x|U>N-uiZ0%(1zuUu zct}pNqyJeYJ%N6h43nL5uCI{+v?z6tZi!KIT|eKlwT;cRx~+@ z64^41J%MMS4U4G0GZ>VUZ=2dFcLI^XFh+DdXPCa>o-j;YSM1@dQhNhcKWRXnn7!iR zMRys7yAtJSKX|2bF+GM4|GR=$*6Qk_sdY?ss|Dlqu9Nh{LC-g&hD6=JO`b(3jRM;2 z8A4yl2;mNWqbjH`;x5^VHUI+{B98=PgitE^3*n-4=6)dj{tS`4Hl&%F88vm&Fk{~k zCpTu^G85$XkUKk5(0fTV0F8*25z|+Y($_JqZ(>2;ZV|7yFFz+H8jRvztC$?_ zlND0b7$Ym(vc@ri2x3TM5)bI!DT+o2|B!_DW11{g^eUYyQz>Sbg>)(&Xag>YHM(-9 Zl{QO>A?GtIz%t zUwrmO5{>=e!e7d4Gw5seieTt1*q+!7 z-IDOPc*&tiqEIh$XPf)B^8W>OS+fI%$p_pQo~(($ek&^LUg!#+OHo;o(zi>Y6kBf^ zf^hM?=Lvt!;X$C>y9F<_nswKbB9MW#8rlxEu|>|rnG!A$DZkY^!_X<5sAwR$FV`L6 zQi}`^(}fp&ms<|^s#cNu^Quc{inMEXKrCp9#%ukyoG8xHBxU6j21a)UQogQCm9 z5L<7SMMK(kFEEiKzgQ3bvbblf>GrjWKd1DGBZe-9@pjfHS5wGqaa_j&Lw{P0GCNw~ zl4nPExf6;^95=8S#{dRR++@q&H;6Xk)yZ&P`TD5 zJF7w#gjba{nr1pfGc^Mt)%9f(?E)&i`F=K%j9dcN2h1@ zz&^IlyJ=Og7TOaS+Eet9Ke&%VFB0St9{o5^Ihc}@&Qz|iFksT2`vUE8O5a1|wh>v3 z4n$MNC+P2yNErjs1liGdQTE}_kLj80Y|7Y!smey6$_`UvXwT54aU``8#%Q0?1m?AB ztBw0qpa&@oA*qna9W<1q=rFaTVk2ZJQaM~8E7Gzq;u3VCeHE8U3fgc5Pj<;cAL0uC{WT+TA(c?rqr#(1r;HcP(-B&BvMsexACeN+3Uz&BcG9g z-@}E99{L0LQHa?%%7>57x(mX-@=xK zZ3gq1FMai#q10-hGL$=!Cm5Exz7%iL@Jz%f{4AiISj^sDv_wfeUbVBg`E)C`YZW>p)%lcLRHsV z-6%eHvoLTIp1H?zkg8sk#(i<1yES~tuv!?xP(8}}Vxas;CKeuAXfbTGid)&wS+hN= z{I4h?&bxT!Yi;UN*V+BZKXDs#nAc0gMjMWWM>cl!&ST75*ky43Uk!%s!r)Gts3;su zJ_{Su%JmN4kELK}7BcSBG|z?VL{bTkt!X}5{Mz#5jwHmMa8n^+{;TZB=cBULu>0M$R6 C5asXy literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/tests/BuilderTest$StartOnlyReader.class b/build/classes/nu/xom/tests/BuilderTest$StartOnlyReader.class new file mode 100644 index 0000000000000000000000000000000000000000..5db5c193f99d3f847d163e21cd9a5f0412fd3e52 GIT binary patch literal 957 zcma)4+iuf95IyU}ad2Il7AR>by+8|&LAaH8K}AS3fT$FKM5>C{ZL%sx&N}k0k@*KC z_!VBL;DHa|qY$%klqN_8;1<={3$ z?QtX{{e+?7w@(lWj8#?(=0t^Zu!1X;f0gp9WUn<~7D}E`el$_GgLAM8(b={NL3=7_ iqlvX~T8jiM+`whBEqbtxZ7fp3Rl;c0>2cfGf%^x{+T64N literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/tests/BuilderTest$WarningFilter.class b/build/classes/nu/xom/tests/BuilderTest$WarningFilter.class new file mode 100644 index 0000000000000000000000000000000000000000..ad38765c64b80b03c9922e98c952c48f82c9d7f7 GIT binary patch literal 1282 zcma)6+foxj5IwUbS=cNWqaq-PT*MGA3yK$vWff9b#)_yQ2#-TDhP5`kadx74@uPe( zrBwL=ew1ZTHU*o4Sma@+d$zmJ>C?UW^Y_4;?$D<)PMQcqpqRnHiDvh({4iOrxa7zT^3FShH|BO%}OBadz}#h}E!eJ)+? zw=Waw*bN!_KXB>0e&vPhDIpo`g6|8t>G3cW^g3Jg>rNx6I!c5pbe`8;uS|8xne9l- zQ)Z~RR9l{?3STk2%N9TJFWm9CUvYMoB#iu}ZUR|x8dc8;dBfRIlz3Dp@VxHO_IXu= zHC_^XI|bd>IRu72PdB?wfz;gRI``a#Ox$Of z{Er5!Em}rng<-tAQmdqnwHwrBNs!FMYbd7UvhKk~KL%{tg9i)~adNvG2XDCy#VIWY zvn@O^i$@*{uO?*Z=vk{k7OFK*SAA&Wk&VY#w=s+xCY~_NU2dcdQy0}gcwLNkMI(@w z@fczoBRbX`x-4g z!*sR-@0`?8rEX2&HhrfyeY`*q3_U+kSL8W#GCGsFCXDZ77#N~wN?+QqaD{9;3P1+e z$zysl6s42DQCyPFncNTboFH+SNuD5e*o4_c?@#m@$So``jV79~&Popu1K4jcYVTm$ z-fKauMP=F$R1hN=ZNbi9j4VTg;3ip=OV&6h=uhSr*$Il8B*y)iB1@B6r&Gu4j@TJ& kyrbbL;1n*2X)3DIW+~CY9bBU`Lspsux(bWrqu)^PKdMhU1ONa4 literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/tests/BuilderTest$WeirdAttributeTypes.class b/build/classes/nu/xom/tests/BuilderTest$WeirdAttributeTypes.class new file mode 100644 index 0000000000000000000000000000000000000000..eb7adcbc08bafb87726f6fdeeb07bbc9889a95a4 GIT binary patch literal 1191 zcmb7DTTc@~6#izr7bwfcBG7_25T#s~0wRK95HU7cNTdYtacIY~y4}{@sn&OYi7!6; zB8f(SfIr2bVB+P>VxR;_bRXuN^Uc|F`OcYdzdn5hFo%UCS}|fFn}mi@b&R!OT*(P_ zGikxGFwJ1z^#V^WGxTTOYFKs}72ky{QR8?R*f3aMkBgq|7Nds5^~n z#gQVCk@KML`IK~vjNvnWU!sD;xGaXgbLyoOk>sIV^F>7jlHp0#ea>HT$LB%WDNr>* zdFh{!WU21UR$o!G!7D^l`^!4_y=R^%9ojmVlA2GSXrU@au(Es98pFWJ%{^u&o>XgDNLK`9xlhx|j?&DmNJzDe487U>>B*^m zPoLKSuAzmVkNPwaqiCbkCTp2&jifm?egO6%zHg^fy*OwyFtk~8kllOeg-)c%B7bz_ zBE?{eJi1e{zQBM*GWQwU(~N$A#G;XyH+#&C@e%qvq%ua2nIheMYqIyhzmJViOlFJ& zSSoD-Ds4XnhPHq;a)kC%ISLyfO*NIn zCDIbHb{SWoQ`w<8 CX(03f literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/tests/BuilderTest.class b/build/classes/nu/xom/tests/BuilderTest.class new file mode 100644 index 0000000000000000000000000000000000000000..bace964c1c28b97a1055a232a475d39605bd5658 GIT binary patch literal 67324 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 literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/tests/CDATASectionTest.class b/build/classes/nu/xom/tests/CDATASectionTest.class new file mode 100644 index 0000000000000000000000000000000000000000..81cbddaa471bd9f42f940aaf6ffc92ab5c864e65 GIT binary patch literal 5988 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 A8vpJDzXXyCSubsHNt zHVF$4l~&m!g5fv!31%lANWyYgY58mP(`yx?!JWZO}kxB`R2YcPe zD#~O+@SbQblTIYkRPt4;t4F~&9tN3Avoz=k9c#rz>Mb*KYU2#rW-THNx$QtDGR)%S4Pog#fCtfHSjaAjW}$>iFB9RbJ#kWu zVPlK1>CY3o+s^k>8s8(V&95c6y?B&_GS^R7{bTT*T$cw2i>!5LJdA8?d+^b;(IWW& zyEQ`f_khp($Gi{jHoLrh55FF#F^6{d9NlEq1V`#Y`a)$#gq7*T%r%x0=C4~YJEiMJv$|o`OJ%9euc-rHz_~`RgMT3P~$TPA8@t7wsN;- ze1Q25mU(KNa>6J`T)u|+4Q2CtLDZNOPl*_Yi?~#fSFnOruD)DgsmzTWT%C4#z?O*$ miS&1gIhG5~m@Qnx^#XQ-^CjHGGTRz^7S^!D;;(Rws`C>ZUevh& literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/tests/CanonicalizerTest.class b/build/classes/nu/xom/tests/CanonicalizerTest.class new file mode 100644 index 0000000000000000000000000000000000000000..3ed9100d33cf4c6118c03f4cf203978540fa85d8 GIT binary patch literal 44795 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 u2{?G*8(AHt*B{MLYi8F6lTv~1G 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 literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/tests/DOMConverterTest$1.class b/build/classes/nu/xom/tests/DOMConverterTest$1.class new file mode 100644 index 0000000000000000000000000000000000000000..657d306b0651c8053c75d5f7f461fdab805d44a9 GIT binary patch literal 222 zcmaJ*F$%&!5S-O$GzzA%6fLYh1b+|}EJ6zXK#WT?#5=e<#kX1b03Ri8S_^mB9R_BG zeSe-W04oeagdzHbXm4(|;O(Vm!g!sfiP3kqp6!91UsU-$CyX|_sExbmGG%`@rw|cg zs&DFHuF8wgso&&$Nf_?5W}DQxbLN-%mnu6}TzJCdZ$~@>p(Xz|(-uHffR1p0 OuH2q%Bpwn*-1`EJ=Qt<; literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/tests/DOMConverterTest$StrippingFactory.class b/build/classes/nu/xom/tests/DOMConverterTest$StrippingFactory.class new file mode 100644 index 0000000000000000000000000000000000000000..cdb8c0f6136acad08357d3302a053d92b275ace0 GIT binary patch literal 950 zcmb7C+iuf95It)rad2E33@O*pv;;^}RJRgBLP5L)Dpg8ai9jEa`miyp;MQ3OdsCHP z;Va4um3ZI-_yQ#O7R0RMRw#ucUOX4iIWu!+zyAFE1;9hJY!pznVWC>YZ5xZI>0rsi z9Se1a!jnJ->M4WSXznoNI?)@!Fy9TN*qV$6B7VsSA!Xcd4RN_6`?sX!0AYw)Ql&Q}Ww5egD zc&vJSpJ<!uXf@*V|e=<8X~(>7S(xc0ZcLzSsz~vXxmiR`rYyinu^? z>xLHYIk=C8gG+ENG#OU^Pfk)Z18mWGPmi>CKFc~?c2+KWFyh^Whji1*@3+_<>C379bSk{~@O^2jo>`PMOvkK`CA(YK(J#y2?RJJSNpp-d6eYS34M zcZtj->$>L3C*(dL&yHFv4~~&PqO6%R*~AFrJ4(inl&v;JWNd`RWn4)+xTuh4U?Ih_ y5_K!$TGr|@St3g3WPhG>VOns?oX2(CNMSb#Xy6v+$-3kfu#Ahe!BvV;F8%_HxZ6zt literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/tests/DOMConverterTest.class b/build/classes/nu/xom/tests/DOMConverterTest.class new file mode 100644 index 0000000000000000000000000000000000000000..09fb19d6c4902bd70bb2ede9769e039a6e227109 GIT binary patch literal 19647 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 literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/tests/DocTypeTest.class b/build/classes/nu/xom/tests/DocTypeTest.class new file mode 100644 index 0000000000000000000000000000000000000000..c1d9d524b666551fb50fb10a23b27e711f2e2c78 GIT binary patch literal 18591 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 aHv-Phfb${1xdm`O{I0>7i?FTny#E8?iKe0e literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/tests/DocumentTest.class b/build/classes/nu/xom/tests/DocumentTest.class new file mode 100644 index 0000000000000000000000000000000000000000..fc4ec051945474fbc38a69e7c684e110811bf624 GIT binary patch literal 10862 zcmcIq33yx8mHyAOY`rMTrZ^#slPH9&c4A_(It#>30s-3zv6JAiD)LJ#kR?Z!5{E)5 zTU&;;6zEWfvK7)oDP?n9!T?<;1lrDYpwKB4y3Mo{N=pkJIxS@WbKfG_alUW9`MxQ| z@2=;bd(MCUd+rr4eEh^y02ax<2rk8IVZ3hRvIs1^HVmJ_&%$^kf(pE;m7m-Ag;stU zVdYn0{5patUelKUu<@VT_?s|(8^IX7rIp`l&)Zsh$Ht2`ey_vd)%qW_=Z{)?PfLFa z<%tDW?Fb(4D+%<31bvS(LCXwvBtWf0&I2Z9HU4#FpXO5Vd85EhB9iW#a)I9M#Ex zwDF`Zqr);Lf>XG(#8lOHZ5gX`!mLQ8md5FF<83)czZ1eT(UwWJOpc%xAJ|f5%M=|o z)s|_t#BG_b3mj|93|-?R?K&;;At>v}CIw^sjsZTq^N z4VfM%mrrFgf^bhEnRI%%Zk508hIHE5l}NAY=vddAbh>pM7cS3p;=&ROd9wVjI>Ct6 zd?I;4bE4a9NG*$BGbjWjb?sKiUtSO|u~$nWo!*p9W%5q0biGhVHc6RrU#sRqI-lxJ zJDUc)IOFVfOJ(^iEmofFOr<** z|AL7sXS36tP9z;2vMrVG+>q~a(mMq!2K9L$(IIP7J9j!pGZ#2pdI$nfOrr{?sk3QM z8RLt<32V~n>|Tch>})QzE0sy4ORU_O?efqT$~#?D)k@Zpm&h7|r6uZk-WZ^Y*EWPI z5$@mX6-204P+2mxOxNm;KW+$-^(7P4ZY8$yCwompOrC!9$({j9kg&~3r*~#^T~0@a zz%4<7j1ep>k<^mSl$yoG{t&AR<2$1B);a`mCq0#PtT7iDu*Z=M&%SCWz0w!R&+Yz9oeLkT|BXqoMfiQ9XG0@ za;mB|$x~nmri{vIx=xccN2NtJ>WZ64zHYEnc3q|;9;|!1Y>rB+4sDYyI&^DPw#gYq z4Fm8W)0xkA*VoqW-Me>z)0^lb5iTG|Yjx`-00ZlE)@-1*N|e!PMe$F%;hl zj`Qbrc|05MHbmPKXQ-9P@pgU`HWnZ%XURH7>roU=DGV3%cBP|owwiM0P9lICtL-GL zYn^mwBA2Z_V`Fn|MO&0Ms0@W_cgxM=7eN6CMH_lRfmp28t2=MXV+ zuAHZ4OGIUdazNa-f#>b`UJ`pezc=gKA}SrilXWY}fxZohe#U20fjEbY7 zQpQ>QOR}Xqyg5fT<0S9s@HSu+309oyIpHWo$b+~gn~x`oUYpUz%!0ciB6bkgd1X0j zobPlp@eadzPDfOB;fAPmN=i*~J~7yx%cirt{EbHC0-+zOytOywvYIxKh6rs#g(jV- z$R~2U9L9f0{YMILx>FRwj3?oqaxN~_2h$b1T@3yKJ&e8ShLCzG#Bg0)CDB8QmU0u4 zQOQV3-QcEbZH|r|l`wRvo;L{?g~a(kMnurDw=!AkH2_#q$qJ9=%AArkHPG!9DI+Uecx?C3?V}Bgb3i7WF&PK5J#Q03F*vR5OQhIb zdSARJbs=X5+RP|M^-<})NG2Y0E4ALh^w7-ph?|F)A8P8A-BHO&PgL?sWd+$2mc822 z%k0QgS4R)bO$8GM^f88&%^R7Kk(IqJW&#SC;5gtp>8r6}V`=Lg|72U|<>8vq+WBHO zJ7;dm+2-cBJbj`GhGH(iZg(M(<^eQ&h`jvBX)1cS>uN7k8xt(w!UTTB29wM0Or(ha z2+`e4;3sw&44LH^ZAz+x?g1NjobM#_<{qGvJkn<*P<787UKaGll?u8ha22vS!Q0d5IU9lr-q0z*qNc%L(NuJ~2=5bt+GB&kzJ@v^Z1{X5 z>1}k-@6P9mT0UokF`aHd$Z}L@P#MX}W(JGrWnz^>2Q-ve%ZEa#wfr_;cfYQ>4R<3t z*uOs}*k3f~rup885x%h+nIMr-c#YyFD%^;tICgXEUK%*IXnwzuZk!nyA!zhh4H}GP zT_?5W3Pe$8&US69JaVo!hmuSpxw^{)o~l*BSa&o2BBo~SH>)4eG1 zlehpTfy#7c-c1PkHTSF0e*9&ed@Wlv_RNxAotKmUUF`Xq2Ww$E-##wotBl2enC_Vp zQ#*bqLNqPp-_NqP+Kd1y=2ahnJZ#2Y#lQNf04zp1mf#;)8gJ?qxEj~6#__lo*HK0Y z*W(6H)<)KtyI{(^gRt6TWd{&?9ObN3w9k7C70%0h9R{wgwxqncp&~YqrxF;7d{jr(rCb zIA#+^wa|teF@xi3_+YdYTR3YQPQ@8)J&Uc|*?K1bo(%`*AdPd;iv&TkgCV-00}fh7z7$o|;~9i}Q)7`KM;MMxO8 zg%4u^%G`#ajT8SV@W;zsPW|!bAb-$8C3aNH9K@9N7%e|lJ>rgzO*??N>j;iF_UnUs zDdbssdOwc!tn0PV9f1puGGWk2q3GBQu=6`NJW#2ijg zf8LC{aW7vY`|%Q9p|xCpW@s|t0{SzJ7`pg2n5E5jqaHal@Hws-d9~AcsE1)UIzMo4D|A0k?Q$WbpbP+wu#zRHk&gJ#z_Tg?Ez zkE?y2v&%R#LhoP6(yPYXcd$f<1m6C!=k0o~QS|m?_4d7koO@^N>@e;M)5CZB9>)FL zx`u93Y_3|i5AzthT8hy#H8Ic2K_e|(6PrJ1)a-8KzZU}v%m4=RzFB5(ZImhSbvb=`Y?MQ!X$iyT0Lr3RX&~Mg_hdpSZ{p| zqcGW1Lx)1Y>?^}7a~T5#$QVVAry@->Yi2Iy z%w|(s!ct2Ot-Vyo^1Cp$%xr5!eP|+S%}4EQ)S`~4Z6yMe0czKCYaq@d_$G10(8QDM zdzxdv!_nWO$-hkmKZ~{a9=Yi`&OSu`d4V#1NGabZ3;lpt|JMMGCm3VgPO~_$iOV|- zDP#f+b`WZp(;{o>R7JT(sd4OQsmyTCJVj%j$30(B?zxlPGY28VJzoh!|F6w=4WpE= z`gSEZ>JbT_j_Zc9Y7uNPqt(mQ@W&X_eSPV02}Di7QDWz8~4c;^d+lQ)w*VbsGO?H2xb@<4w%M&oLjrz%u;Gs1$b> z3k+E688AjSJzxwd#HXj6*aVj%cI#j^Ycj06f~?zIU8C4H+OJgXn4VSasSnLpz$~q< z(NBGBwFjf}a%z4e8Cc_EC8O>%uI-Xu9pe@FEkX1awf`OC;vH1u_l(kaIr2T8;(y}! zzi`I;B)|`NoP8KTrP%|BhQn5Ge`6Ten&0p)h!u>vuhXg3{O2J9kR^o5H`o?3P^p4- zn1ag0BgHYrWsE(}l9FnluPXVW z-0;J-L#aC0sV(!Vm8-b_)eXcNjN>NMhssrR*LUS)2K8adiRbRlWJhjsay17P_dQm{ zyPFoqUk_~DDp zO~RyU<%dez^*~_PMI5eyku$h&)Tj;Ompoe0&b&FyP+-EVL7~)1n=GAQ&A+}u$JdvK zJS!SzU8_k68rYEX=w zW9Y&nuWvDdRYop7)r3zoQ%1$j<8yf zg|dNGITdT9$?Tk`1kVr6ZNEtg)G65cGH>T&8C^;wZbr*v`Y@Ms6IkxjAmj->nGXj0 z`w}IothHtoo7EX3r2TA*XX^o9XLT`nruf+rk}f z#WLB(AZagQiM7E#>Uj=-o%&TW@ln1|miXsD*WJ`qPrJEB*Cy4~<0w~CqmUx8#nY`w zTPCC7b8|$_MNH14LJ69C2LYHgifBed!GR*v@RU(Rm=yR&;3}gIFiID7DQ06oHWe2! z*EqhRj+9O#)J@Jtc|rxx_@b;XqKa0REX8ze`v_nEG2RO_WN;sUF50dbQ|7 za|!naU8l24y;$KYt$e5@Hck2#ru1X8)_C@@W_dN{VI_SB(b^tsi)}f8tsG|1Fx2GM z0h4`E6-SENW<;^$|0K#^CL9PB!(}z(vTd<5OcaKigSPf9PlBNiVu&AA03NK9GI_H_1h~TRw@$ zW z+zYImL}D{DNp6g3t&ieEgq~u(5WwS14<2pc?Pn#o|V@41Y>aX(HS$#M=n0=0m zyOPOl*-9kp@uIB<(C%V*K&;HdFzR(T#>hQ%#+R{J?j`fzhZfn7^W>{Y%l*j9*Ghu3 z;B7k1nR?&T{PerQwjT7j!7xE=8ZTUf*XPZRw4fKY*fg)8>XD@Ta4sA4<9B-HVrZL; zk1tHa1$B2)|QNtU`@p6D^Pd`?%e3CptVtW!B_-&G>OH^<6R3FFu)>r-6K=o%m zkL(U?`v2q+cf02u)f?ZXH=d<8zDI97M{hh&ZycgGUgY`veRPZ7$Nk5&-QJcuOT6(t ze@pTq=H?y#sd*3y^AfUyAc-Z5hh~h2CcVbajsq~A&QP5sI@7uHh)(Z; zg<>g&DUDuub&Xv|jPyE{6l0wOdmpAZ#$qNDhA}*Z8knDrVGe&TpPI(td_GEC!|zSJ zy4}yO%N=`|Wm5@t-Md3`H(#GSL~OMaKHQw56Iu~ zh#WS~*vxc%u1N`QqBiS;QZguA8de-2C?ZVR{R z&EEV;XlaXg(SOc&yv3B1_5%(L;dsVaDt3O++L|Qk)#HL~vGkx;I;QK$*3xrMpDw$$ zX|}4zSJVJr(R$Jw{iWC3B@6d3LyPsKPX6ZL@hD zSj_J#{(Bn#z1qBpbu+I#$aJB{gTswHRA7Y=u?Qr~#xd3~R9Pc1%^Hrl6~%08H0E1l zP-l(9VrwjxSe001O+ceH5vN#FaGEt4O;#0Jt!X&VieslW9T{r|dadJdu{9HyTeEPr zH5a#7^KiRWjk_&gj;;CFZ!N$>Rvr4R6L8R4il?l_c*a_S=PfsH-pOn;ZXlr9)(cEt?sfXyyXH4k@6U~PL&^#T0rq2USFHXmcoag55mx4kB| z?*K03<-QM}(8T+q`cSpGgIwH)Pxj%G`ttb`%Lg1h<^pT2hHagQm~|4SSZgrDYQS8p z5zDN#SZ%F0k)@A};G3KKn}N+o>3`va4}ZJkJH?N9EwGp|RPlLIZ)9^Z9xwBuGDLn4 a^Ks=$#=t9_UBOYW;wPN-@2pw)>Hh%u`3x}t literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/tests/EBCDICTest.class b/build/classes/nu/xom/tests/EBCDICTest.class new file mode 100644 index 0000000000000000000000000000000000000000..9f3b969db58faf6a762782c8b4b341438edcb27e GIT binary patch literal 2090 zcmaJ?NpllN7=0~iG_pL#Mqq;i1ZOdkZH!19ATfks36VI+Hh2qSZ%bo5Fq#pwfFY2r z!hgsyl?!L8f=$I$q$yCCo7pZN zT%d8@t(XGcMawa#!s?>w&B;ZZC#j-amiB`5EVXYO#)4(b7f3gw2uwflvxQv#>_mQ! z-zpkMR%x#JW-za^%G6Oj*9`=Ensx=-Yy{t>Tx>cxI(mZnTO1PtN#7*0Z9OzSfn80z z+18=T%`^SbrrXGs94E_RmYYdhZg#?{g#pW8$|_Y8o>`NgZ&s*ht_U1z>CHQSSgW~S zU{(rF*{!f_g_W{d3oO^6VVlG0_H%tPwCswCt(8-5^MpoGH)K=?TNt?qfjRDZ^1*Z% zv`5u78_4pVNm;8`x6XsBq!*c`&2yG&QukKrkEre&7{ai? zjs{5@WoAVgX!mQ(QM#UiL->G2`SXc^3k7l;)_wfjYlf+c)MzZZ-h6nnW7p zYiz4>5Euw?&%k|*8dy=&13YA?Ia%o^^ZQvd*2cg$DuDglpxXMWygf4T7~dNB4&MvB z-_-uUW|BoswTZ1(8u&q_wx<#L>wFg2`-imTgys))~01ChIyT;du zmyu+j^JLlNo7>L;MENMbi}%15au>k>2l-h4CL8M-g54gb#$oPGQy$?O9~xeRcvjaN z;jA8L;8Q-_V>rUEzD+G;ag;mi<3oC51@I9@8@?>=l-0mGBBfMx4YA_TIOJ%?3@}Qs=kWU$SQUnUd3*XUXH%8SoFBotEFQvf5qN( ztXCWD>HP(X)SegE`;#V~Zv4HDbSWKM!&_t8a9Uf%zOlISm5#r_{-@}i8u??2P7gj~ zFg>`700-*peg21{P?em)ZqDbCK#`JZrf`A%xyYW(unV(HZH_6GSb)ovmN1WNxQXjn z#tr&gqDRSVzQ|i%;Uzcmh!WM2B^K-xqMhKZ(dz=cHAc*1IE9lOF?!jF&)8v&79IGU zh;e#~5NVuxE7Ul{a;u-=<3@oKl;>G`zQL%XfOFLTg7RaCZxAM~<4YYAI=<3zUI+iJ h==%rShRU@ig3vLU(=ioeI*KR}CVYS{FLD>b%ztNV+uHyD literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/tests/ElementTest$TestElement.class b/build/classes/nu/xom/tests/ElementTest$TestElement.class new file mode 100644 index 0000000000000000000000000000000000000000..6c1b326a36f576d9a5f74b4bb21334961019f79a GIT binary patch literal 544 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 EA5uL5G5`Po literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/tests/EncodingTest.class b/build/classes/nu/xom/tests/EncodingTest.class new file mode 100644 index 0000000000000000000000000000000000000000..d3df92314474aeaaa3cf042c16cd2f905f505f29 GIT binary patch literal 7755 zcmb7}3w%`dmB)W4^SC#Yo7czyQv^XMB!LhTV1$5ZG6^vl2$BShC@`7aB%_m=I1dDM zcU^1M*4DP#)>^uiMXS56-CebY)U0mpZr9aax7v2AZSB^5w%ezD*C=@ zEFb31{lC8d^Z)wF=fDcjpgNA?9af=TX>h%>LJou+djNq#}UUdPl zpHeNa`S3M-UA=un!$0fzrYgVgLp9z|mETg2Z?pGX;N#TqGfX%Ko{{zg}5|vYvS?Y za`9@SQG`2>`p}6tHPLDp>;l~7gl@3&qd1x>M=(> z0_ri>C-Y=JH%Ef%@gDV9K(XXPu1prXP8?lEaTamqa*=9V?2{#OvG=lf#;kvuEY)P0 zz%`nT3hMh3*_K1e$(F8UG&^Y~GMfZ-14FxZ_4jlPcJJ@sx^gyv1BdzQD zQ+<()nPS~F0`KNnB9_@A2sHGm=9YLQG2SweNyQT5n;Lfu+~MS?DX8s>CCnY!$q_R( z7#WGP#^0BWM&i38shBF;mF~<$EG<}&7b;_>GwGJ@L^L@{;2=vB&z*=&T7a`A6nN5R zW@w6wYp^CCjbtKRPafADH|-@#jD6zCo4ATFKb+H@N+vUc%BfT`lgu2R;tUn(5yAYk z*43FE8#7Z%kcrs%1ovei6N%oiJu+o2f|^dvq(WrONUAG&Q-VyjjP9ZEww(fB_o1jc zm5C)2X-(d%$#RZY%>xs$vCRH$y#sc+cc8y*Ocr38gSTWMi!Xu7LTiGtx+AR z$cQRf?4Fcq-e3zbI1%B7T79lCM^YyDwKxq$v!omv+`7RY%h%RWzB)XR7o@v0+||qT zD2~0Pw6HP17oDve)~sDq98YnJL&z-_IH9$Dxy5tHJhl3vL^?Y)l}u&K(Y$U^OQI8z z6xFBWU?djL>8PVpQ|l|tLxR;g#Yn`GE%wNk5^gvLE1dQwUJMjCI$=g{=!nOuJW3EM z?7>JpOI_2=LsMpy0@0=-(M&eNF1l$ezCatv#^R&O>I%x-nX925x{_IvF3QP6aXJrY z%#Ktla=1U6naXBpjb>z${d{SvP$V9^S#d7N8^u7Ol3gnHcJdLUVg(Ag-b5)xPnlC{ zpD3u2f8Ax7jxRbv8Nm88xIyR%)`skX6!b$ZCbCvqyAfc!`Z7idI=eFiF(^08#QS+WD|HyUS0&v+jbgwT+LQlV)s?F`DSJGR`ciZC_9IzTHDss zJoKW)yK~mA4-GWi=IzOwxdLq1uzq8+GV?+~Sy!Q;%)C%gc2g+OOp66&M1>X_p*Gk% z&>U)`0TruhTg3ux(?hFnW~EnsEAy^Zgw{2ob)n{=@IW)=@Pwz&=Nv zsY*(eMC-}h_!$t2T+);Bl$1_y3u&_1kV}QmX_a+Q=V_@$EwU|4=@t)ep#?dYc~L2C z#VOj8QiRJ4xm-F7=|skmuzGY!x8TCFU?oi+G@*us%C4Pu_%w38PB{c_=wUS^#%EdyQn*eW4Aci?k5*MP2O8TNcGclfF zh@oeaxtK~Yhr4j*c5;=aTYNa3F(-MAkJDApxB8ZxS3HNs^Ao=Sz)Pz<*Te+jYy&y@RJ~V}0@LdJ`FQT)AT1a;PkxBaEhSb>@u>3DUjeiDb&m8KGB2 z9UUCwrnC&2ym4Duj7XY}KGSWDP&?Oasa+Mt-kC~Hv5Cf1na+-+tuUQ_O!YYX=uT6r zu4L8<+p8>D^CiassS8V(BSo40xtOuh)$+}!ZYArRS3kd(L6pblGtSZ$_N(Bxw`t{Z z$di1lz$Bl{cKBS!Y;ipje4Cr=g(Rj}Vt?F-6d7)$k;$9WAh5pKi(Yk6HzIJ!ez3{#nR87MOx@S>6?5|lkgWBV$n@0W0 zpnDqrJ6!VEsjvDk;IKLW?_skD4111a?hgOFkh?iR-uykTkSE}&7aT*MKN#>FxG&^w zX2W}$176kKJdFkInkAhc)PtH5P0xwuzmVNa#M6~{9*Xyr+B}!Fc?YqO?_0QumvQ4d zu$XCRBf@Aw7dE0BS7Ix!V%;8W!vU7Y(8q1vj@z*VceCzZ4B`O{;Rtq9!aevruEsMM z#2#dSoh(6UHsbvp>gCWo zaTn2dJK+{P;BMrgR9V~y3b?JdbhZ8^Gr0IcF4w=r#^#hKC*?uZaPx}`t+l1RjZ*Tb zjT)RT?gtC}lGQJD5cd{}dus{TdG+@=;g&hzZYS=2WpN*J!oAl4cNcMYm&JY93Afw< zcMowNDvP_<3Ae%l_YvYgS{C;aCtQOA?tbDvUKaOJCtRZg?g8RHRTlR#CtQ;Q?g(+8 zDT}+$3CH{XEUSBnxT9rp_dDTMIp7{9?vb*%k2~R-9dM5k_a|j>pK!vhcEEj}xIZt8 z`=k@D#R2yuaZi=SJ>Y~}2IDt{h@QyH#*S2NBUdmPd{3q7u{-m-mSJf@V`&D z`hyaF8Mpe!4*X4d{w4?hkI4V=`SXuC@HglAn;rN+BmeF5=RfSgzckOk)Pes?@_%*y z{6`A>GGWJWspuz4xK3e*j}hmi482qgw~yZ{0r54>V9Or&6Me2Hta#7M2=y`sKq^J? zGkk#Rnhr*j90n}D1BC{0xKrp)AfKG8B1uji`+a*B zmk%FD$9Be^v*;Z5hmWIc$1J*so9WTEsywCX3t%qLqjHifm?QM^vyGoC+dZ@B8xAtd z*xv4474%MHN4uuJ`h~+dWM@0`h-q9Es&s{X0bii<7_JZM0pB!seF+uqRY7Jy1MNnz z%3m{$!5IvNssq*ac>ht{AQ&_#%AQbdpr+1u zC{TOgDGVP7)dZ>oH8Z&84j#+McgxkdsU4pMv(!j!(ypNqxi8}EzJf}Eqm4?64@SKL{HT<=P zFKKu|!N6Id>%h%PtcT?$efy6gB`+E7mE@1nR=X*BrvK8<15 z(+K&moxyciHo2%t`}$VSV1IAJ92|Jy2sAfai^sZ%O99%+euWEom0SB76VI<<0ltnE uOcT}CFXpz?Vm`Mh#nN2V(~+qRegQZAMf^Rt?>5#mw^+f-m-t$Nm;Vn-A_#~8 literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/tests/FastTests.class b/build/classes/nu/xom/tests/FastTests.class new file mode 100644 index 0000000000000000000000000000000000000000..5a50af261302441922f4f548fabd437d153c4780 GIT binary patch literal 1979 zcmaKt|5h7C6vw{j=`+~rv1SmU_IvCi=+$9;|h#|DSRQRLX#;ma6F`Tw#;8oMst{2tBu1_?layAbS~DcU@n^ zu~IRdHzHK&Kuf|+!sxJTo;A$dBJzSniMm#EdD|1sf$f~(SxAyd?xso4@xp%?58FM# zlp&~j2c05OX^i4ySZDt;1V7ysg8e4)5r2Mu#aK&g$^44%0f!=x|Pl zSsl*nFsH)>9p2L+r^9>?exU-4Kow~pb2R2Lfh(B8RjTndq7|&sUuT(WTgOefxP>-u z;|JWqkGPAUu!^5a@eKFy9P9W41^kH(yo7~UDB^EyvMfq$24%K@2W$m4yN?R1pvu0% X7Tbf;Fl31b-othHl#&1zTd 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 literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/tests/InfoTest.class b/build/classes/nu/xom/tests/InfoTest.class new file mode 100644 index 0000000000000000000000000000000000000000..48ee5b0e9315409673480a67a7892a09af6b960b GIT binary patch literal 2290 zcmZ`*+fy4=9R3cuoRBUp4J~b|*tE79pe2=7+XAHogfHW00>@pBRq^_!uzyQ9za2&| z-U;DdvC3hb###tPv6ezmLMVr@9zrDqmE#`AeTLwKs;l-S!|{0fp1dI^G+AFyOwNntpWR&^!gRM!+UFRy7NiKLC9tS!r?D)ud*->#?@!-=%+BsPs|!d5KX zN~H9%k*BwD3a86e*LmMz3?X4HY#DrUadx-WuxzE8b!@`dgr^#b1;S<%LPf4pK%XJB zr}wne4O>Z?rraQ}n$x!Pt8U<Y`2CcAczOvxrlFbNJre)rO1`OfZ&7xAXRYSKpsvJ6p!BHbc&T!14GN+29_3Wl%MDQ=+ z49;>qkT8Oa3$Lv~A=mGj|^GvSDW28FK!hiS^$mq7LdNOwFq@Oda6kt;Oa@ z81zKhz2#oF_YlY?Evh>Q{qeglc9m$jPf5D&J)+hB-XmHf?-_ejB2d;Gt3pj=8+$!S z^C>>KI?WJ>uTG1TxT=?w&20I9W_F5Np5~fj+IiCy>=DNx#b6jB zi!Kq1{RKZlr2F%Gbfq7|SJ;Ms{xJfD3)=|pfJZ_*=+5|`AYAZ84&}!D7b8;CzlEN$ zKr}$>VNx9#3r2%J-xKuaq5&2SZsF((h1)`8m?AtzZ{flg`nGXwj7NFo_!jyff&0IP z-?xJky?rD*NwSuzw1X(Up6awB$us*T&+d~9?2|m#DG_A(A;?*Leki`uw(XN1-C}RqnNa8C@;}K@?EndYhn8mMT`3G*h#4k1} zZ4vLs@3?@AM7xCFXo@F@`T;z}CA>sG2jcjd&bv%FM)3pTc$v~SiEjwaRZ3Wy2IDnK z8Ye^`fBFEpo@3-W?&6pJfsGKy1jh}INsgNwQyfW-X-@Dk#m-H!Ue7<_6{X@Q%yYO! Mh#68O>B4LO0{K8ZeE{58=)Q@v zapX*rw=iqrfrWP^B_ zn2|H$%EJ!8KZF8NU+K3EfpFH451aGr(k~u3^;%Q80=B8FDcz}1&bF4-PaCq);ip`I zRDVa&Yq?q=vF){*RrT62gQxn(nKLuk7(q&4u+JJg(~xA;Hij^4V-5uy^H{L)2#+a# z)5|z+O|>(sT~!V3c&^RMA;`?RsFG>eYU!=LR~)0}uXbAnCIc!n>qUPACNBJYzqL_e zQ&IXHJ-Ht!4|LI`5SPpUOgH#WGh1Oj#BVG=djfI9D1=eI&6WXKcIR+~(fC;x#u%9=hO3w$Mg)_% z7GNzi6Z|)n|B29tbod03?}%Q;oaa0X9ZdVIJHX^p^^l&C%^=y~{5M385j%x-8D|lL zc-n_!_i%3XaCV7fbc`C9@G;`=(gUYRd_|;u6p*I~6YfYJr&wSa19(oWUecl$w5Zre jF&a?ZWS@o58w&9&&L;lQ*=uxqG_O2f`+yVb`5k4>@gJ2y}*-whR&fKM$gvN z7gp>{t;bvYjyq!>ogEDs^owxJ87j6H@Im5_c=X_m1Z8S==sIHPM4pPXq%L2)n4xP2 zi8T#~ybRsM=Yhf4u z45j-piCj(w7RHx$PF|firwjw@#Lu}NBtm2eRj-z@jVetmC&~o#Ut>5}9pnoa6CTBC zp!52TKM~l+8oh#A8kp)!`vygQiZwcmz2h0!ds;8it{x89p<8uq(pPh?LK!ASq{kL2 q#9+!^#=3%@s?6Rev?t;fX3+m4TZCw@Ak8JxM2#9MWStslX#4^ayn=}U literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/tests/MegaTest$1.class b/build/classes/nu/xom/tests/MegaTest$1.class new file mode 100644 index 0000000000000000000000000000000000000000..439b50210093aa4003387c47eb1c31eb3dcebbf5 GIT binary patch literal 198 zcmZurK?=e!5S-O&YE|q-yn66#5d48Ccqkt91F?qM5))`r@ogS_fR7Tl2XF4MGc3$3 z^Zq?%$u-> HSdqqGp(QT1 literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/tests/MegaTest$Generator.class b/build/classes/nu/xom/tests/MegaTest$Generator.class new file mode 100644 index 0000000000000000000000000000000000000000..91d4d058e3b2af1a57da332781c2954c69c3ed75 GIT binary patch literal 1438 zcmaJ>Usn@H6#q>~*sv}TB7zN7qoyT*u+Sp5C5Bc+O&df_1U>$t!!qK+W;bVd0{HHy z=wn}e)}z7Gp5ud${RI6E{RTZA_0EPsdW=1L_RgIQ#!uEw{c8&9Qo%h;yB-h8C=lu9lnp_2emk>A*bP@j$W8?wW*vUbC7D(6nN5FzAtH7ejukigHaBh0EG#YTTB0g#&!r9BCCRO=MP3clAeI>P zl2`LBam7}-b~nqOQ2WVGTTTod#}@`t$QZbSs|*9Vx!sDB-WGm9aV`!|O-v5Y<#i3$ z3>5G)gO>9>Pv&)ozH~ab&Lz)J<;-v-)Gr2#Sk!Rcz!GjS#B*i?Zr~;|3`g4(Lk$b9 z4tF=qlJsqN<5JCb)&(({l#hL}lWr+JZQvG4N|X)U#2@!!*|q+wx*?WiRpI@PI-9yidPi<38UG#Grmc%?Lw&=E>Tl?I0bQw19Y>xr z_UyyoO^|Wuq>k=qNEQD?@7+Bll6`d~$Lcs*NB?Uadmd(c4-e5pd8qtn&`q)g17*tg zHgU0xb992~iNqYu7jU2M$^+a3r(LV`{fZi_u--KJNMQ_L;cN1{O7#yT8%EgP=U{|< g_agQVDVF&cu{#<@6-i@cj!>A>G^)3Efc`Y_5jZ+hNB{r; literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/tests/MegaTest$MinimizingFactory.class b/build/classes/nu/xom/tests/MegaTest$MinimizingFactory.class new file mode 100644 index 0000000000000000000000000000000000000000..cff7edc38c2bb23f2cc7dfec6a9f3d1ed3bf7302 GIT binary patch literal 2405 zcmbVOT~ixX7=BI?vLRg{gwjf@7HPB)v>}Kem0GOTa7(YDVCEYsHn0>tu?uVytqcT&|mz>D~~yFnNm4 z)S5sfM?`_4f@#U+My(|6Rijj;C0VGKjp~|Vn`-VGBZ^jFxJc-l`Pj57OGeqP+mxv1 zElb+Ds^K`2E>pcZQ(Zf$^i0jzmAQJYCM{QBVzRJf92l9ZVO26Ku1%8p0IhZY$fn^M zN@SO-z{nQWIom~Jms=OBvI#pLM8P+8gKDznoM@?U`3elIxJG%mXzcl+7*L{by3|){ zxKhJ?#&Y%19wYal#{s%^_OSRl4nR{1uUlrsGHRp=-@BV7IDTNjiHlJ%c_?zj9%Sdru;8ncFr_%M<*Oi@q1}zzThW=l0iI5@ne~dWIXO5viJjTGf z$D~wGA7M~YhCRw8KXwX8$2TPS7L)j{jXoRDXK{sV{E&D%g&L;uhOc9t?>;W{JkIwX z-dxim!0W?R?>OpU8IHVYV1}cx0zwwERL1LrpY!Hni+n4sJxPA>NYYJ*Hj=jjlD7kr zcRUiI+SrHQAiWYW#()hKk_o;T$(R?iG~+dV60bo|-xDhQk)`ufTdc+c+A%*>eym2w zat-hKTsgkIBYNm0ZBPBr9^SYggJ#{RXv_!4P(=_*fW`T~54b=C1sGLI#Yr_bi)p_N vF&Ze0;s(oHRc90*;6v~DH$5#M;S%2$h?wMWp_jQbjnDBqTj4tGNW}gH)5{hA literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/tests/MegaTest.class b/build/classes/nu/xom/tests/MegaTest.class new file mode 100644 index 0000000000000000000000000000000000000000..27b2a9a1a469c0bf4ec562bbbca5780c236b058a GIT binary patch literal 2253 zcma)7Yf~Fl7=BJJY!a4R!c9F%aR|BL^? z4>}{nkr_Yu(ZAwokDL`$7$;$9$S%`Bz&Pb~&RqkZ?8 zg0mtpmh2MmITmwTo;?rP-Xpt!NLoKaTMBw*voXFWpEtP+hUt`SpNx)O-3H;Q`A zF^GzS=K^6{cOKUWc~T}5tVsuB?TYLeH3_F%76mT|pR^*w-7w0!f-((i#iDN8GYj+c z0<*~;iOsf6wrd$^P{G$;l-g50wOPo{%|(IfUN^I|yc%dM@cv%4ZWVPICt2Vld{@G% zB88-i+n7=D9_}*X7MCtpLm=X-srVEtD%LMCC0 zdx6PI+E2FglA0OKCXN%AL_Rt*gxf>KEf*4kq8ww@+e(wl;GHr(k+?|qQ%<7gJ!D(pT?0=KV5 z=|}RgTC7)eQ#QFO-$%zL5OcQ(1(B|1XlscQFmrpIZxpZl3<&; zXH%4u$nKh@#=YxU z#1eN@!hfV=JkCsj6&ij(N1q{%)h-7K zd_>2fG)l)ybg;}_i1U4pY_yiEpO}wymA^o=&_pbkxcuX4?Yp%wHa&VqKw>2H+(p>w=;7@y)w{Eq34cMty;P*LWW)pR|n zo@UFF1VvI|6`A1(9@4K@)--47AxxWrU$_WB7PQYjOm`UyGcgvD%9~8>s literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/tests/NamespaceNodeTest.class b/build/classes/nu/xom/tests/NamespaceNodeTest.class new file mode 100644 index 0000000000000000000000000000000000000000..58849f343af1ca6df157cb605c9f66c67056c143 GIT binary patch literal 5969 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 literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/tests/NodeFactoryTest$1.class b/build/classes/nu/xom/tests/NodeFactoryTest$1.class new file mode 100644 index 0000000000000000000000000000000000000000..2ae26ef03feac5336a631efa3e9019af1d2734ac GIT binary patch literal 1084 zcmbVL+iuf95IyTSH^(#~w55eoS_mcAx`7r2f-0f3kt&70G>VkRO|oiSe2Hua$gl7f z@Boph#0T(Eh}qN>C@4Z$viHpF%$c(@>#yHFe*v(ICrPA`)1c!)99tS9a($>Fio7%m zaTF6MX-J?PMebF@K=@+lGbz2soHXS;B%k@d`OA!p+K^%sT z9aME(!-9@9s??ltJTr|{Cp5#u%b-U(9%J|CR>QSz;gF`7&(8)!E@Sp+(DUY;qe|Id zDuq{oVQpR(!?L_J4ekUl27~q+msABb5*lJ8FR#PX|D6ow#`tI?oB>1T^4`fF)FtoI zUw8{0Y|w9@(;vYy8QDQX3cYD$z;K<=KH1Wv6-yt%ic=^bW-(d%#wZ3#9ybUtUqoS% zkbJZm8>5JuSP8k_QAU*ZYUhY_r%<1%tLizT-BP(SMeGye(oA%-$tkqZ`fc+)I)hf4VFjC})} Sw5qs=`&c5RQD%x(dcFhSL;oKD literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/tests/NodeFactoryTest$10.class b/build/classes/nu/xom/tests/NodeFactoryTest$10.class new file mode 100644 index 0000000000000000000000000000000000000000..863f1a32a952fe81a2a2e037dfe3600f4b846c5f GIT binary patch literal 897 zcmaJoZBnDnm)#1!MG6uu2x(s$0(}uS$)#D{?3UddssBnJ zv|zy>;Exi|-NYcRWMTKt+&yz<=FI&1{o^NqS8(#kqhi5AHH%#fhCZL>P_vN6Gwnam zVlRujKt@fxxOO10-3w;UWjJ+|j8*KMgkyQ+j#LolB2BYEWM z4%Tl-v#+zV)wjXO4`XIMmTD4?vuN08qGjU_Y#VE6vB8DJNi_n+h5JUa+Tl!*+zi~Q z)abU27uXlrSv3`EKt=x{T}(`1YgL%QrXF&4LZuS%`r)z!>TcBa1o!?U=WW$AGUHxc z$Y9KVmW!bq>@)_ba1Sl6q$n1(475^w*H8cjqjgAM-&oDoH;CpHQeP8Hk>3n4+*I77 zy)j3jgmrqvCv1X<2mCkZde7Y^9OY{mXIC(f+t)~+l`^d>WWOWV{=$DLXy$^(83uBO ti47y2WT?zzml?`paa<~!Gwe%rBvK+>$tOtal; zv8d$_EGigxtxvW)I&$Phx}m_IVM1}M&4Kq%? z$REmMTlKQ?pGbQjtSTff8?(YFwHkVRY`G)(-t)pliJ<3A z3lh+(YOsZbD1oq(kqqORo>&v5NwwBdGBh2MHBZ@&JEGiqqWYE-5wTOf4Y=)5xpLOU zg>=s;&vc$juY+12Tl~%!_{oc6QK`yPhHn9jAeyH`+R9_>)_*|kULo^6#@ywaBL>1KRjk&Y>C31ZAE95UR-}pvA28zC* yz*-4;Z4>KSIZm^6LoiKK!UH@ceV%F8u+B&g8!e<_CU@_yLk zjdF&#$^`>%5n!$*xz%g(!MJ~;2^!@8oYb637O z8PEutLr2;JN4(=cU6p!QSUnnwmQ&+hoS?9ZC$9@f*G7CCa)&Cf)a-YTg(YkG4-6X< zxF$@ijWxR123`CJy`%|{Oe*cd2uPSD7;cld51kOeQlWSTRu~~t_zCTON+03Y8D_qQ z1u6O(3Z_*M*Q&Tf{@wF3ERdz1rI4O}9kSA6a*N0{%B71b{X3#x5G#Dc%&FR-xH&@X zlyVv_*hGJ=WXnB)CuMwUh+i-oc((koIxWQWU1cXuU6Ek0 z-uk`>YaWjxL1uAinVI3B%(?%g4D?Ul9QfaU=o5=(^-i zx5;-CGUj^F4;@i=72WZ3I?V3y&m?4a*>}7kqMA)1JHfVrSqnDiEM$?ha0Mjj*jcTW zcizG%#w=nDZkYgG9K&-?GOxCFgd>;A9|&_YN~I4K!!+%=MfV^@KT;FO<&^5N2P9t} zv=(HoLZ=AedZoG#Rv92wIfS;A*9W+?kM#E#Fivk%g=z1Q(VCbb``QtVz6G)Kg)KYsz43ib#9 literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/tests/NodeFactoryTest$14.class b/build/classes/nu/xom/tests/NodeFactoryTest$14.class new file mode 100644 index 0000000000000000000000000000000000000000..29bd3f51b8e7c5a915f8465ff2af3db7959df7fa GIT binary patch literal 805 zcmaJMj!`yId@-pr$b=N??FyLd5&4G#`BUHC343G4Xt+>@=c0oSO{Y&jz+^X zk)H6gggoU?MLg;9G`4o;%v%u*#X~7Y+Dy3Cg3QXyG}U)|Bx>m+iPD1({~XJHD-lB> z4TC>bn7JDD374%)Q=ls*;L_x1f@ScI)hLZbJGP3K|7ykVC;pXFX)PHgN>kd7FayB?ow^jOHDKl-JQ?mnHsfD)AAmOqf9GjHtY}C8(=`Oi+tyrmK z>!e+dJ`WE;|22m_~w$FzTv#VufwEX!co7`)u{v7E$111VH>tQat1=~&gV#*he4+@Q3|FxwJ6>z(XcArHd9+L0aJw4G4;@At`3 zC**TixZx(leED){<$xjHpnQf*%N2a5*FEC?zJ278C)bjW?H$;@tL(@bS0Wh9Z6Ubd z@a!PqWX@lDOlfU{L`}aWQNc;ue(j3mEsuA(2pNiFg^{aX8F8t)0!q~R2%H^-Mp!D< z=RwaSqVy;>N;CFk&v$s!Rkd9IuSqMf>^C+Qy(Jt^1|+D>!xP!j@yJ9C>n5_eW+I1m zDm7~MfS1mR%!cd^D~PKpDC~xb$9Qsq7}ux^{??b0M9xIXFMR3nK%KiSg3#|dp(};r zc*-z$*>I_qG?;%BMT5Vfp7>8NtSW!Q7Q6h^vmL%maw2_AUv`_5Mx&M0?~yOJRIe=0 z7RT0J=#%EU@+a)mQ!Y9T+3^HbhX%352|1HkP^9~ip{v2NS=Djc#pump8Z8@_VPKV z4s(e<^dCr)kvhy7eOwtDCPh^2sxrS)S`6yY0+B%bh@|!jdF?Y6v@clG&ITfiL;A6Z k8fI{p4o_kUvzQ|WjS5yw^H`uYj(aHKChZJDrD>(-FS&*SIsgCw literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/tests/NodeFactoryTest$16.class b/build/classes/nu/xom/tests/NodeFactoryTest$16.class new file mode 100644 index 0000000000000000000000000000000000000000..20f91899287b95bbf8429eaf30d425cc14499d06 GIT binary patch literal 1032 zcmaJ=>uwT36#j;#ENs_;v{bFN1&fxO-Bufah-uOgNSZ)nY}2Gaw=kB~Wv1C(O#2ol z-d;n0D2XIKfDdIn0}C2MI?2wQbLKnWxy}Ci{o^NqUF;?>kNXBpZ0N`v(A4=Lj)w;3 zu&MNdj-r7CN;=9qDh#op>-hO?hP9^XTbHtD1>6sO>ri%h!)^!CyE-OAm5|RJ;RJgO zxx)3(;t4~vM*a-RrX%=azjwyHWBbe{OS&oBwtHfGj?zP8R3*V+9tgp`nrr($CpC9% znEcLDN^0>HCFOTp_8Ui>*InM@B4Ef)GYn1D;)F}p<&&e%C*aiNYJ#OwJ?{5iB3hW- zP27wl+4tJK;iy`c{%g|KYx}KDMb|~!l|CiZ;z3t-bUZS#1D`GN2Quip+FDHQMt!}@g-q<&HN{|OsT_b>I# z!81Hl_L?pBJDq(`_C=?D>G%O9@KVYk>{-K;y~eCbXa)5eGKHpUjpf?n+&T(8a;_?S z%)YqbqQj7y4oo#x^yW=^{w$SJ4JIiV zm^AG%!a#e6MUt1tC@hhrmN8;ugt&#w2qJ2V9`#cJs9n(&OmvILx6f}+v+{G#}Xt+0G UTEjY>Q7Su!3`qu|=INyM2hO?rw*UYD literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/tests/NodeFactoryTest$17.class b/build/classes/nu/xom/tests/NodeFactoryTest$17.class new file mode 100644 index 0000000000000000000000000000000000000000..5c7993583b81fd20b7ea346715b650e6513a1bc3 GIT binary patch literal 1004 zcmaJ=T~8B16g|@|-F8`_7V0NLs}n(@l=04Xw;I!QlXmX8bMCocyT5+__z7SOTLnzxz6Bc(Ow=qG`h1wjvV|$E zXk0hZuuwqL#HxukhMem9QEh`^u_Fe~TiJ6Ik5uFw$aB8ydP;`xj>*v?<}+XTYMY^2 zpG<9>Fl5^l&oJBZ1wR<{&Ukq2o(1G7b)@G8CvNC#oH(<(2nKsk2p+ZrH;OpH>clj) z&8Jk<+)FAd>h8O*eQ~i9@E#Y6p)#s4akUz$mhLN}K$EBBVtSQwsnw82gMfr)(z`Uw zI+BCX+wS=RWhm}8LEwPF5@=tP_8@{aTf~|ZQx$4vWO)*v(#-BWwI<{P18xw EAA?!(qyPW_ literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/tests/NodeFactoryTest$18.class b/build/classes/nu/xom/tests/NodeFactoryTest$18.class new file mode 100644 index 0000000000000000000000000000000000000000..4e9902544219cfd47826f6f00920b18a3a7a6fb0 GIT binary patch literal 736 zcmaJJ>%TJ3b}svQcp13O_4LdBP&Ak;p%(y_;DytC2t-jUpj{;Ta_ z2nrK?^hb@IWT_S(8c6OrC*S$bcfRED@9iG|pYfr98N79n!#f)l2UdK(w^1eJ%)pP< zKM|@OHELezVbjRSM9r=~l?TEzI=t?2p~c8uU-@Q_P}`git@jDpHun?eI=+(K(eOlu zJ#i9nWuc=z5%fjq$9Y=J#v%yrp;9tz2O^3jr?shL*0*<9ROvg5iUvpG!dGYefgDO@ z2y0IY(^9KG=8E@3#FDOLkX+X9Oix4or#9jeL`hg8(|~FFA-l6h|CK&S zz=A)(cfnsHo=MXhtYl$l?!9yFnRD-*AHTnS2k;aRizs5%g^M)@>keuKY!q@M>KF8BOM?PlRXB?od4FjDEoo(%dj@r^$X+D3#x>2(yTq#MXIRdj1}uc<$IMp28Q ze6PB37^+AMY&3fB%@diWnLOt%~bc)hd_E~?Rx9$(TpIgVdl2^pAKGZltGry zUx6vI)9hoNPtN7*6XmjL1+5(aC0qiVMr)hCiMXw;uMn*n|JJ~N!dU(#PQq}6lMDz4)O$t>KQ V;BVnJcN;6XgDNeTXGQM({s5s@$lw3~ literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/tests/NodeFactoryTest$2.class b/build/classes/nu/xom/tests/NodeFactoryTest$2.class new file mode 100644 index 0000000000000000000000000000000000000000..0a5d898d803dd8edf44734b2543cc35f919248d0 GIT binary patch literal 1199 zcma)5ZBNrs7(MOQ)|G;D^5PH?w90s$qWHoHi7X(P$p@JveDFirZdkQz$=VV5EB*@o zAS9Cb1N>3ObGr@|%|es*KKHq&&pFS_{qghLcL0yEl0gOq6DFn&%$P{Yb=HK2Ir*8F zo&^I%0}mAFQNs;w8sineUHlj{i;)PUu(%!6#fDRhg4S`BjuMe8uJ1;x3MLD^q0_qx z(q;ay;A+M7#df>7FIrV+-=im232KhF>$F_CPn>BfLV>mA`=V9$oG=vJPV}U)7pbYg z>3k3`qNwHWwURNgyBPSJDxgW?VW{z2(do}>!rL2VhYG~LxA>k^VV z?K{mloqt#qq~5;SQlK38wj?L`U(x{xl3Aw1C(K9929qxFu5 z3e26L-KvQVSC)0~LcbQIEY^SBuX#bp&_+2xQwL=53Z66+JvFnDI4 zA+>jk^s;saZEtq&6#BBB*XK`Q;%P?;~)D2|Rml#wxF{*B1QhgP( zZg%3Gv)agGRH}lFo3x~I8)LLmv2_dM)R5uXZDy`fYZjBZ!%R}RON2`FJ+5io#}uy9 Q%F&nMDxYCI#0c`g0Kp(1bN~PV literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/tests/NodeFactoryTest$20.class b/build/classes/nu/xom/tests/NodeFactoryTest$20.class new file mode 100644 index 0000000000000000000000000000000000000000..34f4a104c620f8f20a7be75f83c18dfc8d63c18f GIT binary patch literal 918 zcma)4>rN9v6#k~Wbj!Nv(&7b0Xr)3g!V<&38WKY!A*n=!)bMk|B<}7BhV#7w+LdC+90#*yC+A#6d zhJiH;>lSJZc{K=<>K4OtTc+McH1w26RN@^(J+a4q6~!Np$ z%w39SxYG`#I7o+G5g+qzNS0zd@_E?daiH~y(bP>aIQvqHcsJxpB1o-FEmM8IMNKUZ z`KfplN4`jsK=$`#qT^oO>aceeX!c$n5wYz11=9%Lo&W#< literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/tests/NodeFactoryTest$21.class b/build/classes/nu/xom/tests/NodeFactoryTest$21.class new file mode 100644 index 0000000000000000000000000000000000000000..2826fc627b54bddbbf26594391efe6cd5a6b57a0 GIT binary patch literal 866 zcmaJ81QkWtbV@gkRl~O#z7R=B#$sR@;p3&d*)DdsroA)xuk?di zB8fl1|D^HUQWqm+P1<|T)91WA=boRxzyAQRi{=6xJaRFQEeG2UsxEBQTv&MQ;E97e zLs<`HR)58?)mG#1G8u(hWI78wNnafBNGIv%E&(lKzLiSqJ%(B^o7(6x*!vXEu-ukP zbjG7|k#_lcOvq|GiFn-OsWg58+NKEx?@%d`?#DdK1i7`DW$JHUQ&Srw{z1G?lSpKl zRD(m6>2w@vnJ9+cpnbtV@i69U5T59i{_sOJRJyaTD=iY!uniu2KOWx zr;#|2M$_7V!+L4rNc=k$#ffR_Na$hGcktB1GwgU+fa{@x9goU=sVGI3!2!ZO)+uUYW1N*?j^ zY>XIpuvX;iV2(B#@ULKk56UQZl0O=!5Af+vkhuf$-s%%gqRe~%Mlqk!InFP0HjO?M0ziJmP zQX};P`cYNyfQ_myKr(aBopT@O-ueCK=Pv+n@OlLnY8FL(F>H6`#Q7YI93>(ZIcLE@oN`wM;g>!++QfY8NlzU!)EdjF%}WNO zL-7o2T~CU$$>>IeeSYJUr`Qc#?qBlI(>Qh-x(NpRos=T%_&kaP!P?R?^`j$dYU7rg ziiSP@!IO6@eUcbmW44i_1ZgJs&g^+xhu|tK*il$d1~1I6vT)Y zlWyo0mm6KJ*YUZ$b1qa!dfVEc<>Rr?jTRPL&?@N;ReG7L^hjBupl3l=ioQJ7Ky0#( z32Tql+WQXHnj-aW##yKTEOB70qeymT&cX)Dgor=O#)whDW}HjHpr;Ph?~%Tq!szYa zBXeEMPGSB)ZVpyr@RfKe=qg2uf>A=wD5Gptuxo72Dpu#Y78UDwh(}Z@hb=tD6N*lg WrL#Q6Hk}4`NNtHMi&z$&^!p2qkk}Uh literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/tests/NodeFactoryTest$23.class b/build/classes/nu/xom/tests/NodeFactoryTest$23.class new file mode 100644 index 0000000000000000000000000000000000000000..a150b8b4a7705d042e3241954562bca934b4be01 GIT binary patch literal 1619 zcmaJ>>r&G|6#ljiq||^lP!MkgMQu|c?w_~^y$6ef$#`|$RKp}>QF)YNeC{2%4JXZ09fq4viahJd6rp4`TbJce% z_I943+ux~pV>yNkMZ0DmI91aZp6{8vBh$&x+&Z>zG;y~FFGY|t#h{`_n}ddNib+nY+JYmi+i4+?Py1uvFRB~Dpuut zVoeVYw^<^3mvK){66+iIRJ)gV&65pdHELfr{Yyz134?qXVFiwKnyh^3@$UkqN zmuTI)>hsdx65kIi?BAm1c{|4#N1B~%fVCy8ibi^gMlB6rhWM&eb4y~eB0D>9Y-E#i z+BAX%yJR^Y6}lw+9jC0~sfK4LYB-Hy4QEhffQCT~F$^6S$b(wNDhpS`5|%Zr;JJp2 zxWv#^+Mxp#FEqTwD#>d~#hQj!Sl4hFS5$0hc#Y=_k*R6wkLx(p-{{ozj0OW)hEyl5 z(PT*{p+`$m4Cgw9F(jmPS6oVXWvjp~i!>BXOEq@${e5AV87BTS;!N9&!>XmceCOz< zPHr{@PatJj`&mm%jZ)EG}`Ap%hBqoNHTnkC6Ns_`C6(LDTZ6QgSNTP%= zoYmupIPnRZt{KGh9lb_&ypBEzA8o1O8gykYfFC&uY{C!YY5-4QNYg5UOc2X-;vxZu zjPL0{HY-PUoNSUNLeju^D$GQ0W>QE0zeM^Zv2qJZB@aWHZ>crcOeTkMOc0|?K|v%r ZKrxfFj=)4NuuWl_1}sNIGNpg|{RMY@Ye)b9 literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/tests/NodeFactoryTest$24.class b/build/classes/nu/xom/tests/NodeFactoryTest$24.class new file mode 100644 index 0000000000000000000000000000000000000000..e80faec75b5e32de3fbbb83f8e944b60652c9881 GIT binary patch literal 1231 zcma)5ZBNrs6n-w0b=1nsL|zmfAj;Ya1)Pe4Xj~v*#!IpVqTept1qxk@?XBX!(nP>W z;t%jg8PDA&A&e|Gxwk#{KIb_v&*|?!KYszp;Y}yHkTK8=D+yZ}SsjlJbih*Eq}ry` zwUflOff>x|cw)eWr6Z@~DFcfbQiTKli8~(m_ne{(U4L($AtCqOXndOCe!-8O5;dq+3Z$4JCOlIW**F7;fdp(61(DBcxn5cJtixqHS01Z>}=5XVfSp z_|cK@D-4s@G}dbBY@;lttqkg8O8v_;0|_yB5YtFHyCJWN&p~+Ts8P-Hh&pbnAqwTv z$C!IGOB2_`ZVEkbZDE9du5SA2+GtVVY3os9kkwAB54S-D)7J_iY8X3v0%o6~%}%wS zK>J2_Z5W{4Q1{vjI<7`_Aux?Wn#hN}x$NMuR><{0eokGvj zIz^KB?d%ykzY^qfkTncx>z9J0FM_0TpNddH(o|YK^&+^wkhh|sG?F(m~+z)m?dV8WC`F0783n-DR)p-T5XHbpW?rm z9~_A${s4cJ@!YNuG80J3J?Gr#ygkqT^Y`a(0Q-2g3=Me=D=;)Xz{4dJG(5s%={!+U z)S${66{d=kiZVkg9N0l&mtni%j;mk1Q8g4n7*tzcPt>^;dj8i_3e*Vs+IH>mkijS} zhMFA)<%seb(hb`ct?{TU{8Qd_D3Wb>7I!+_x1}Ek6*&Zhe&V{qKXP~w2y%^uZ3=q_ zG}QVT4HXQU{M>f?$Bq~YH)P1qCX7Qhb6QL86;Prorr^xZG@S&$WY}4x2W?MW&Sk|~F>EbLV<<^^>;IGXR?x}=5uAj<8^1rk;xSp2 zTCT+vXY}fQ5k@k$_(&2`G#iqGs>9uWwH^Az0WqAp9-}8DK8?9U(ry;#GTwEE!U}8V z2Lkk>F(`SaproW`c95r+y+V(iWipacvJ&*IAq_&4bx6L9Xr=NGu+jvH?-AxE{ig^6 zrHd@tx2{lF$0m7%kE)R-baOkdHAFH(8RbhPv&toId_$V-)Gw&-Cs?|WXL*9<3o4L= zY>|aREzY1TePomYa>{V7oiS@?Acs5D=5Cb9a+H3L%mlV@pH2na*ue%_8i6!A>Guyw C9_XzA literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/tests/NodeFactoryTest$3.class b/build/classes/nu/xom/tests/NodeFactoryTest$3.class new file mode 100644 index 0000000000000000000000000000000000000000..7eb779656e4b9da27f2e0bf054ce979c678cb21c GIT binary patch literal 1027 zcmaJ=U2hUW6g{&n1-7&zwzX(Qs;GPvsKS$IUuc_600j5muMkW_K|9>Mzm< zOJWm$fIrH32WVqjWs{w~_s+TZ+^_xh`^QfJHB@7W;jsk^*$AFk2FdM^0Pv z5fmaQGOVbP6J+-p(k(fueDcN>B?1*x+TK7Mb6a`-=PnuQgnZ>lM;$Qi=9WYAJqEKu z{tUNTjuh?5xG(%J@4IA4wmh4=J?=YNUl>iD1Vg+jrSKar4+24IW+_d!N;##*{DXMo zd$tGyM-H1ZQ2xYLjwcyvxz>CBkyl(Uhn0@<>Arr`lD{O;1qZrJ#r>gBI@25ORM&tj zP9R?d6PG~i*WDS_n7A|^xneA&Zl2ZgCcZ6>9o>VCYt1cdB5Kx=v|UfvaU#^n8$_@d zM+xOPRuPY54P|Q4Vo0-WiGHCjh%~4Y$G$$E;{^?M*-W!lD)O&*i*YmTEc0S`tv7^P zEqt}$4VtnkmGC8Z54(q*Nk0&(=kWgwCX~|~viXnkX!|NZI}>uy7&-2M8a>yjr&mj- zORwj9$k3KswD2sE&24nD3;i--y31T3bUKCEzCif2IK|3KGiC0bBT`D4=ZKcR62^dL*wT2T zkEmfIX$-J!2&9d7GsfCH))iw0cd@NAqMA}98Hmz(5BG^RN%idD0Zfw9v*-^A1=4r% O2zN-b$TLP?dj0^a1~4l~!2t?#}qkIWx24U%!9+1W-qP4F;YXNTQ(QnSq$vpC|CbzzT|L zDJf@JM@2`KVKo@reqo;>-eN^9mOd1 z^o{e#5lWEAjIKNNhT_;(rET2m*`89M9vsP`^`fy&Og72Gnsg_rGO}Dn}(h7B|^DpM;J{fY$q8P#vXh~mqL6m{tk}Vks zPjct5ci5c{d=d0*zUX|cs4ji;fBh$alAoUoIckn=XGBe&Th!0R9I3J!r9QT4c9S&5 zES*-(p_4{$3hN*=oes!W9;00O4pyE)`xavE(z;3*XqU*)IXg$;9yZA$e8|QKk;PWT zbwPhYoWA9+5j&kBesYbK)6D7&dZ$*oLZVu`!dmq!Av7rdEd4srJ|LleL`M6BE$uUM y+LutTJZD6&7G6-+bPR}0_z2YPQ literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/tests/NodeFactoryTest$5.class b/build/classes/nu/xom/tests/NodeFactoryTest$5.class new file mode 100644 index 0000000000000000000000000000000000000000..cec72f902208bcdd3c57cdb64738f953fce7179b GIT binary patch literal 1120 zcmaJ=-%ry}6#m)`x{U&rUn(LFov^{6sNgS<#2_RljF>={@OT;5tztsxLTvFJh|Dquh3?Xp?`@n!~FIV1N}8d zxE?!2VHD?S5g)T@L|nkdgzFW0co_lv1gX7aq$_jZ(DM_$@6lK0()fh2@(D6~Txa>_ zj?qVN`7=XOPzlFK0R0Uz`dbX??=Y@^z`Xu37LY%s?g+3kj!R6O!3-vFnerydkcIvm c;|8t_5uT?*#}uyepT;#5m_R9Kzxn6$7pOZ7mjD0& literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/tests/NodeFactoryTest$6.class b/build/classes/nu/xom/tests/NodeFactoryTest$6.class new file mode 100644 index 0000000000000000000000000000000000000000..2884e40b9456b5af4a3d259e3874feed1000601d GIT binary patch literal 902 zcmaJ<$!^n76g|%gb>fChZ5c~RAwY%_vp`)C)J;gGN=X)NDPrj)uZh92E88LPRm@ZZ zi4WkT5a*?KKrCX(_PzI=bMEwh{r>(Fz+*Jh(6MD8h1)tx1`^m#;f{g3DC@YVn>tfID866Vsf{W{rNs^GBwR zdImdq$mJ~CO?j7N{YNlbo51EW7Wb$&-5Oi&NIsLN7CY`)6B(RYY|!^6ZFeY;sHkn` z7XwuVH&rMbX)lJJ3*=w^^TPvyjY>yN#B`X!JPLhIgjAGxCsVwhBA-_ip{4O%MFs+w zY3?#F_tE{!8@lreIxBof z;%)sB$!@)P<112opD1WROJ+5|_=Sw|8#&`@E?v4NEu;%rr*w=Gq;VT}h?&6Mxxzi% Sr+*R~cz^;~7NJb~)AJX&>(&AQ literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/tests/NodeFactoryTest$8.class b/build/classes/nu/xom/tests/NodeFactoryTest$8.class new file mode 100644 index 0000000000000000000000000000000000000000..792571a244e75a3450e5456d292a822506e86f0b GIT binary patch literal 856 zcma)4T~8B16g{(DyRfZL3gQ=HkzxxZT_21gV0f_7q)F)mU=tsw%h0au&XC=y)qkZA zhC~y8fIq>6zhbtT>opD?U;biWc{^ir=45V2_Z`0~zRjhPC>1=w^?>Zc;wO zd?%2iJBs=uKH>e4BISIIng}Uhx|N{ zgM&~+LTZL*_0AjqmU|(W1MgVJw6}LXvN>VT@`%lrJW3^AODAn+FS+2pcy-ifaF5j} z_C+f&H7)(qtcJ;_;t!-Bs)UX|6ndym9jq5o#YPb`m@UG^2E$y|^xxtahFV|jdfs?E zZuA=}9(V@%nC3HWJk=fQMF#jw48zK_EQXpHc5_IL5--IWABDO*DYF%;=!F;rQc?j7 zxw;u^?f+WaLxt{~L)VX$N~T(}Ec#p&K&>QepZqkkwfzZf>k`(-6mygQdBVW@jybZI zv%GmMkY^YS8>6?3#f+;Bn{G5v_=4Q)?XrD|8z0EE@Q3UwC9C{`VM12G5^kBpNY*l0 Z7VY0oGw z!9ge@AvMFZTKkk=a4+O?;2mj2dwbU-TNCyykJxO%}jjKz$r1)HukQ2318+nus~g&Xh5weW}RdrDUM1;d1_fF;~Ahmovh avMk!aoo3#_U0QA2!+n&_TYdlCKAz3hSk%I|`-m!xNvpqP|Yv8B& zFYqFX2Y-M+%J_O169S2GI@4A4s^6=s?q9!u`~>hEy#P(zZsAUVySV3qB_9ubJQOrv zCML131htj^uAsi2ebj>4VPf?5bTZcYjvA*#I>Rhh>8{EX7gu22eo6{~|9T@ytDMH^7PuqwA(>M$|;Z&E$c#tL4n z3=h;76{X7TM`~&TtyK630BM0w1d$Oe~pLW=QY4(p7s5iAwd9 zAzAm&1w+2+O3~_d+afsOZI3F2rtfg?ln1Up$Hk<&a6^Vny}=#j2X_qFLn%d2_jniz zLN=PRXW#l=TZvGGc8kPg>IoIrhJ7{C4;2s85x;U}XWtWDAr-?;rFqG3xb1P-v5!?i zSZ%aY9bOXmky=vnZZz~rJHwd9!fWmj!8-PPfg>8Owpjcz-j;68!bFB47yI}ejX4@w zF|o?9{x6f{G3I@{hoSU~NBt5@e~F@u6g{Z^4EkwAkTJ?^ii~1vvk&$Z!4~c5sCbWY z%CiFk#XOxDCNV|3ZazSQ7GS-=I4dOjNIufZz?Z^9q);BPVg=%(fayrpLXk3qRw?S7 OK~C!tmck+?kof>rCYO2u literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/tests/NodeFactoryTest$CallsMakeRoot.class b/build/classes/nu/xom/tests/NodeFactoryTest$CallsMakeRoot.class new file mode 100644 index 0000000000000000000000000000000000000000..2c315c9b687bda836786ac5d74356715e0594ec9 GIT binary patch literal 684 zcma)3O)tbi7=EVP-LBdX);EhiI3S`IaUm`nLQ)PEiR0{=tkO=UoeKYolY~SZoct*9 zPSv-FnasSO&-1?T%+vGjJ%A-NY!onJVYG-b8wSQL)GbUh6jnXysWpb&?A!r^*$j^a zL)G=9*hzx6i1v8fCr`}{JKR6uk*Dp6v>gli`TUlsrg#{{d&{)v+_)5&OGa40bC_qK??`^sIy5T3pod5-b!MD%mXW%~c|r zxs|kI<*7sooi=4*nqmI`Q`7Ti$)74S4E-{<{-pX}%xEA_f3256hnfaM4`~ibMlwHt z19qK;ZMqAo^8vl2mtGS{R>@-MLqFXbyonrbz`lcVSj*kOydulM2g75^(0I+t7$_e# Z45X|!21zq$52eUqR5TyO@|Zvw#TP?}qJ97X literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/tests/NodeFactoryTest$CommentFilter.class b/build/classes/nu/xom/tests/NodeFactoryTest$CommentFilter.class new file mode 100644 index 0000000000000000000000000000000000000000..68ac06236e47f208bf20dbf2451863417d9c3f31 GIT binary patch literal 583 zcma)3%SyvQ6g`usO-)-LwbtiC#6?@gY}~cDDHMdzO-n_0({`xQWCF=V^sjVPQ1Ao% zDDh4j@de_*%$+%N&beop*SDt^04LbBk;jIGx(!3uEeqQWg^njZb;^)yG)D|(H=GKF z71xuZp9EtO4f)t7r0Rwf?vHrn>2n5}>dK25D%~&$gj7AxS0Z94pGhgAuFvCG(Djik z6Xz}r93^5EJALZV;}aD|_d@~>zlqxnrGVdv3}iTHxYzuaJ3f~)XP_ca&f3inLv0n4 z#GG=)b;e&*273@D(L_)-dPVI=^J6V2qX3IiXDL%t3p)(0|L3OWU*y^I820{hz&=d+ z56aoxI+BsAi)qHCX_LyETr34M0!=|;oAfSY0I?;z=l~yh8keRkCKEt?}Blkdi h1E2cKR9|0kGCi@ehAKH2SWo#iRH)j7nAoIl#diP-g^mCK literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/tests/NodeFactoryTest$Counter.class b/build/classes/nu/xom/tests/NodeFactoryTest$Counter.class new file mode 100644 index 0000000000000000000000000000000000000000..0957731c9e15e8f29b4de88a697d4e27a7c7d8fc GIT binary patch literal 1163 zcmai!T~E_c7{~vot1T-<<`^h~2sm*sNFm`4V`4T*78gP?LgJl=aWbphY0{pW`K=_` zvdD!Wzz=17PS+7K$mqp+>;L&ZZ>RnH@A?mbL+m@Sv7M#im4i7Hvv`diy?UdEl8v&B z3WKG-`cZM8VWTNyuOIY0B_b7hC&8sS=4};(KhNmkh?MVq>8tk)Tcz33@;QT9BYTF$ zrZ2@w+`ACr8NcY#Nxm7hdH0-$zTT&YrVfI^t_87FB4ltIQi`zFLcA`|nHE3V_ZBV}0CxYICbizp)F zhKy7gx0N4AhQoW{cNurwS&D}pp=?wcK0Hv!NvQXBq#-HF0fi}!Q^T+G9Dk%gmIyEfjs*n{VyfMplYu*y)Jbx$U}pn|5dTVCjr&9tQ? zG9{hV`Yaj4nv8o%`x^3NpYDs~-TGO*6<a(TiDoJpmQ%tfWD>$sWdP&z0H*n62(wZh z;n6RWj!}-5umKj>Fu|>iO;Q{Px`L+!WV)~^_?*BQA{3^;yCYcBU0^qfT*XAC``nQk}$mJM}Tqx*=M4L50xeu&{xbL@=n#+Hwndn)9@> N=@;{a<~7>W^B+k>1cLwo literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/tests/NodeFactoryTest$DocumentFilter.class b/build/classes/nu/xom/tests/NodeFactoryTest$DocumentFilter.class new file mode 100644 index 0000000000000000000000000000000000000000..fd8eddbb384ccbc987736eec4e3f8d803d2eb94d GIT binary patch literal 820 zcmb7CU2oGc6g^JUq|3UjbPUEAFvftk+sF%VMZ6RyA(g&tn$$Y9l&X=#BJ_C4+!vFzp`?%A>RsaXPKJNP13*g~ifF|~R zJn->Qz2uFR=CPl$BKdM1-IX=1ytK(}}m=K|p~ zJu6d{6~}Q>C?n8*k!8w^5}D_U`J-`GM(=bQ6)G?CXu>0o<*d-={V9R{U)e)})l|Mw zBb{1FfxX`NwR|h1L}r)KNnzsba@emGtyWHodm#&HZ}~?lb4F|GbZXA?L|J9BJfre5 zVJ@t5wO(+dOEXh6MDJIByYVA?NE@qYlUZ4-L@nB511fYW$97iR+NrJT+iBqAK;Ym% z+Cz^&X;*C&*!)|Q_?{cR+F9+|zH;XWYA-mn(9XZ^fL*Ncf3ly$LE{iu<0@hljLpFu z;^Q(Ma`r6ftYDpMTgn1-u))Y?Vw1BaCq!Mwoduj}w=swNa)IV_0dIOZhd)Paa`b_D z2UasggUdDoXARprZ1#T-~6Xz4z#p!E%{ CRJ|+! literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/tests/NodeFactoryTest$MinimizingFactory.class b/build/classes/nu/xom/tests/NodeFactoryTest$MinimizingFactory.class new file mode 100644 index 0000000000000000000000000000000000000000..917831552cfd3b729537707d743f8e2a2d6bf64c GIT binary patch literal 1779 zcmbVMYfltO6g@T5Ll5KdP<-I7uBfQPpbWa>D&pc}kOTw72x|P`hhi+TWxAVm*BIG< z;=gc{bxAZa@$3GmjioV7ty|}u`>6Ww{|}!4)G=Q|Kd!j=y@VXD^rMJ+ z30Luli)+Sn-Ng+TvjPR>Z|QA;!KI*GdlUM#mGHTWxzTsi3p_n1kgrTU7H}H$3k)rJ zfm&(%>nd84>n(Z?Erm_ldMqQ)d|RiZUwW~?!DV9j-d|p@u^^i|j7U^o3<4E3S~89m zL#8{tTE#j}*lp+<9pgO!RCd6Zo2n7|z6!L!naa{D`C8UmGT5lC>WEyk6CKb_GR||U zrP10GDo`|VYw8WLMqW^F{Bl`t66Ib?r3_=04xDxM;=8I7JiI$rU|?0t=H{~8vc(xN zgm*NhuD7*v@h7Xhw!Ot#KHDRgUdelSZ0|DAaf8<@(+Q+cn*5`Oi|qVs*=qC4(Fyml zj__R|w%+q$Gu628lNx(^G8BcOwiR7TbQQPS>n*RjIM1fU+p$)@F^`*aJ&3}liet8A zF^F~4ZfY+K1nT<|(?i{-WwfE#@X~77j+$!0Gn0JSOiS1BO_^aSql^JgZTIacql5!4 z=E}H*+q@HYk0olGi7`j+xVS4YWq0;D+Z@m?8mq)bL*R(@f8872TIUBHk1)m4Xm+vGl%;i>{^{NH(IMAowwQ5KEwq5rG8fFS@mCHu!D-d@aQbLAXy4QYgp9Dh{Q~wp`|VO~Sdu-U tNxlCizyftdVhPJst$BygnmK#272nRsIVUfj9IcsZ1&f3pIXT*d%}>5Od&2+# literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/tests/NodeFactoryTest$NullElementFactory.class b/build/classes/nu/xom/tests/NodeFactoryTest$NullElementFactory.class new file mode 100644 index 0000000000000000000000000000000000000000..ed14e1cc5f2ea5d359f8a88edc68e4f0a4571df9 GIT binary patch literal 621 zcmb7BO-sW-5Ph4bO|8{xtzY1w2M^VQK@cw$5sHOE?7`OFw{fY_WTTsn=)cmF;K3i@ zj}m9oh+;u7Av2Tr=Dpcr-#=d8032Ymf&v;2)*P%el#T)!=wpULYiGn@cj8OIQ1b#Q z`e`&4YRJbSA$2eIc{t)KF#kDd>#HCkuAheCX(%Egb(i}(R*wwTo|Hm$LY^dodiFh; zx({*WYLV!~?bDdNbx6SWtV5f86U~)A<2Qkv z9B@NZn-Zm&U*}%L7XaIbOW<_Tdk(C~7BVu+jFV=uE}~p?Z7}P$bt5Js$MA@}vDcm^4?uW++~$&=Xqwa@3OAsEKF%P&WO* z2*r?XNhwq#;93g;pSI=1y$i>#5!z_CL;0HAGoiZgQQ&ZiRGpzP<~QO`2t%#XzUH^w z4Y(Y+U8AU)`eLN|l|D+WNiuD?8VM71xIm|Q47uS9PB)w=Pf$xViu|vdk0V|dX=JF` zd2_LP21SOG|F%mN{Sceah@rGh4`n3jmZQg_k4gfAO`c1Zkxd;a+AaoWbJ2Ims2Dj$hYv#e~(E&e`;^!I1nkDB6aWAK literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/tests/NodeFactoryTest$SingleAttributeFactory.class b/build/classes/nu/xom/tests/NodeFactoryTest$SingleAttributeFactory.class new file mode 100644 index 0000000000000000000000000000000000000000..16e69c7dd5d0acde0fc0bce85c0f9ae7872544cb GIT binary patch literal 1176 zcma)5ZEMp|7(KU5nl8(>TDwl?+@`DcMO`-6DN+%1Fc_45uywEx3TX{BnkLewWBU!m z5B?JcrQpCH;Exi|P188jT=kNvj!JP0~YqZil}fy7hCbAo3A@j~%{&>jD+ zEihMgJbS;_ZrGi=)o__fSN*2t9#|bm&ZDFfymPt&*_z|ETzf>jYc&JEb0T2ud7jrCV(vrywq+xl?MuHhkd)=xfC)$2m_y>(=nuI07N8c*f5 zDwoQPJCT!omEusKl-6Zs#k1Ps;s2>Z!(P4I6Ht#Vw?~K)gn|}^3?nlJ%o?@?)(XSb z$p&mzWD8=)8v?8ULkj3Mzt?HnyN+y6X3WfWDcHa)<^)pXhox%QX*;|UisyT_gp~~# zm|@2vY-oU7*w*k!U@J;b);+m#yfc51jqb$!xSG%)f#nNJ@D}GU)obZsYUHy#Mri8N zMeJDRUo*`gi{?uk7{wXUJZ4gyy@1LH$dvksmCg`9Oe<-%kHmLqUzv~L3cor>;1miz zAxJITKjGV#N1`{HjEpA$eH(2Ca{&FPsl}65+njAxC+Di#R0b4J?K025vHv z6%8yiiign(&!SNBEN(}&o^X^XmDN<1)@}ZpTXjEd`@vWgK`J5?2SVMKj)5QF>SIRJ7fDi?-^O77 zkAgB#@DK2h63v^}_3^-#g8i)IB?4>s;p3Ep+F z?qWm0NmY^x)CYRvA4FsSMX1I~r#*r4(?AF5GXcBR-eqAwdZPs92Z2`GlkuL4cjR73 zq%nv_GTfDMVD35Sq;G>nU||^O{ZQqiTXK{}@h5@WORZJh4`q@lRy~|GH(nBdn~n0) z9Rlk`8uZslQW>YOpXzYw)B$G685IArA!<{23-8K~nbPi_y3xmus6O{~=Uit=X$ zQ-N|uIL^q;X+chADVA_8%a?GSkyQun-OL4_@HEX!v?op-O$84#!KG=z8Npjv&SyMpHFuYE#6wh%eQGkQAYz;N$J~+OFN~hTVxrAU$8n z1EZ0HRor&4W@4Ix_5YQI=W(i9sv~Ee2xFRPnFF0p<0j&j_I2`DMQlQ_P@4!hf`)0l z1#Z|sVvXPKQ{t(9Uk7`FrGGWedsgHHG*5;f1p*b~s!1!f`IQsMo1uG(|AGPK6Q;R# zMgf??9Cy3~nCIUlzeJWVaN`{^PZ}r4b{faXeL(&d%Bx%!j$uvgYX)Xv$j>29p3f-B z7nJ2I7UdhN@_ksWG9pZiks1q(7xun{^SHq7FOs1M`GKm1OWZGVpP~0GtijKg&4^J5gJ5-|TU`udyv7RYU;;>?n1R zl;Z}|V@uv@gIG3U#l|Y3_8+g@@HgS~rw}H7bIju@Rlggkn~BZ;RSp&8dHUwD_+=*` zIJ|RtC0=tIUC=}Fd5~k?B1ncw(nbXZKvR&j3 xYWJMAV3f=y1HGX{?}=Ka$4J$fi!qF|UWsuNnB-_-Dq&A!B;oVeu!vz4J^|-~r(gg8 literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/tests/NodeFactoryTest.class b/build/classes/nu/xom/tests/NodeFactoryTest.class new file mode 100644 index 0000000000000000000000000000000000000000..fdf8572aaa46b4ddd3e7e234f901291ad5f4f181 GIT binary patch literal 17423 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 RT-Bbb+FsS3rrIvI{eP4L$)NxM literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/tests/NodesTest.class b/build/classes/nu/xom/tests/NodesTest.class new file mode 100644 index 0000000000000000000000000000000000000000..84aab88ca235dc593aeaea26ca84ed50ee08facf GIT binary patch literal 4884 zcmb7I3vg6t75@I+yL*?-29kw@0C_B>G>;_>AW$JK5JG@R9<)gbp_FFXy-5}}cf;;o zNU41(rBtcbTBvtM$ADak z1+YuL<0=wCOot{1gK~K&fMNMg2CzGTJt|T`ltWW75`Z2=1^NR>2QaE)Zx9(|gQ$|m z7s8Mxa`NcDBJ9VQeB7zxKmd0I@L@TBcL4XOxL3t}0{$D5deT@eP*&HmJGL*@l#1y? zO+7{?sSh>RZxv8l(+N#rMn_WDx^g4Cv`lYoSBf*?j&wYh+8WCw<=CE7jNxQfprS+1 zHI1c5nhY&#WShEZBHPQ)X4=zZBj)KVyb*)k-j#023}su>de+F~;zl|n;H{HLe7ZCv zFx@e3nx#qeShue6T){C#niD8-_P3?95lxpM{8`%7682H|P+!s*?#iW70%|rFk84?a zi%g(Ju5C#tbwkUvjm5Q5BbnA|t+>aC#rJf^Moki!Nn(_)TSiAUJs}U7Y;U)x+x4uL zF)Tk-a$mci(8juRMtX2vI;SVHu5E#Z6TCayx+iaIrZ^a8-errP*r+jbk>uZYhMUs# zA!AseNbcE?&a}ni94V4e!gu(QeA3qa%(ewf;%qV6bOsd6&j@(5F?(jcmeLH`@MpAa zE@e<=Tms!S|YNaIg9AzIE0T$)75!hO_i-^>iV&W_ zlOa5Xr}HOd{#ck&8%tTHiQbkFK8wRt-C}|Ase1ssL&3tf?oKI>)>xLN!X{xkBbjPy zXO+dw@>jCS16*Slpq4|itdwQj-drrj^7PfUx69^GQdgj2IprOaHBeI5USF`pthjrf zj^J!?#%ALij3rrCL0ai#Cx{IZ!mPUb2?c3)NvRVjU{y=%7?H_Tq@x3tD` zvGgTS>OwJ#&k7|k8p{LQ(Y{zJr*#if`!bx#8`y&agt=925~!V`lV5p6h8Re?9G5$h z6>v|(>ToREMPZoyvxO{`H!)T3A{b<2uf5 z;L3W0(S|wLNm>sufO7tc((6jTThKxB%ef*8?n6ugFw9-M8SEbJO>=F8!RiclFHdC|n!&SU zJiU(^-A@Y#un~9B&b_pBH`90z`f$HV%rcg)Ma)J@WR*#g{MzDvFB&D5iXccpGyrVw!w0(oQE*;bWY# zjSM&fdC!qD5biw~`yl!V=oF zcw5VzvkUmRgc+>rEpOAcz|9UNOp3yTz#4}VGv%^5*O>LKMr93I!C6b!aY>P-e_fO> zduei`ewGn`k@`D<1$d6QzQktu6*DsH4qD?zaf2O&VBfOuAkSt?k;1)$-X%{~Mc1;T z{REaD1qwzbAr34319|!SDe)=g8?KGgk{ufdG?X7mJNBr=&pPo99LL-$Ut)?nc*CNM zy{4vQI=%B+>1sR$y9-sBU8trMN3OP1^3TE**@0fhPw+ER`zro|zwv!R_(XtjtK9BE z4YT<*(tnbId!B;(ItBMUv;GZU@~6;>(KPRT)=b{9^G*i5TH)CGB zJKYlg0(+n(&vWcwJ?wg3+OfZA!I^__e@AfE`W;au{IOmADD@Rq#W(O(#p`vG zdW(FRUsQXqD=hxTkVPbxJ8FM7VNv{bU6o40)=apb*^^z)yFw{b%IaQzNukU`W@d3$uzlO?1P|Os(21P{FiUwDF$M_#5z^^I3-%xz7QGCCp_+F#B>k@F>m%q4F0leo3`v3p{ literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/tests/NullLocator.class b/build/classes/nu/xom/tests/NullLocator.class new file mode 100644 index 0000000000000000000000000000000000000000..73ca8404437aad5bd6f97ee42f1cfe4a06525d10 GIT binary patch literal 788 zcmZ{hT}$Io5Qg7LYolrWtk#e9TadPjfyKLByb%N;=t8CUX*|@(Ndn1<)c@p#3oD`* z`vdkziF4A@F5T?SoV=5nXWldC&&T_30H-*xU}9qi6?~~;8k;(7>9B2L$HcCQJwi#w zK4Ha;lb+KLrIUJn$BlhY#mP59<$DkX>Wr|~D6D992<7wmRuJahAQD$u*cHi**Oi=E z(j3|Gl0fTWvaIfcl(6AOnbVI$M~PIW&Q&JmcxtsLRQoYiBD}mMEH|3&z4zcb(u;ad zTO~o%V{5wTN7j{ruZw0`jScX%8qG`g#M$#$W??iiMzGp(miXc#7#ZY?vQi>B(L-!h zv2SArmW^f9Y^BMfJIi^ms5#pOQ;Q*$9xk1 zGw4~zoK?v)hdD#f*7*U0*FNez9d=r+UohTKB1{rme-W$^fmg?!`aTncGT~{$!$~25 S_g+kx`j_yvkg%4|S^oqtzJj^{ literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/tests/ParentNodeTest.class b/build/classes/nu/xom/tests/ParentNodeTest.class new file mode 100644 index 0000000000000000000000000000000000000000..9df5728300c4b3e88879652c3a6709f448c1ca53 GIT binary patch literal 8902 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 E8OX*P!T^3 z3?YoH!y$}DaXE%a@ntNED>10JDo?IO@uiA$F;qzpLq(^G@fbEBCAZR1h!|>cUD{7b zgvlt(7`EYt#QsX2Wu)Ph3QKy~F=A#_+?1=MqDMteg)2?+^3PT=t)gGWEfoVQW>m~Z zF&D+x3ZmnAGm{pMg6eK7-#BAWHMSOania%y!ZD4EIVaC+O6SR7lO`2*-c_)t`-U-X zG@5o}>#QqU9LJdL&AZvWn{*vvOwq3Lv}u{{83kJsMca&FO*Hb@v?iMChZKa{?6gp@ zvD>snPkw4#I0MFbhC6zb}H|mL`;jdD+pa~RiHW|YdATPCYEE{+^%qqaaotocMy@7vG$pi$hxL&<)XM9 z#WxD-WCCr5!}=#gzi{)8wKB_4iZ`^qoaKO+VaUjIv6`LIL{i)#M7j)2=DLC%(k7WT zQX;p~qB7?lFw>-+cT%Fm^zLXG^G77z8e8JHf(^?>C3nwgID$q6yWXl94M%Z|^!omb zqLUEcYWNTzEu#CE!fSeyQw7R8>2XKHcle(A@(oJ2CC`4)a2NM9+{Xh-YDHFE@-mA; zmevn7Ji=oQPw-U3k9elws8@ZQy2-}VW;$-!ZanJ@x%iaS!u9;CGTg=MT)aOQ1+Qocmuj;E2bL4TE3a_oPi zxSoQP81)oG0TlI7COU=Xb1(JvMzd19%MOQoFAR9H&#?t-N>|d=8?*;o)_3S* zb8(0)IjQwW49C&3DI^>HK$-DVejsT^Ivup8H@EzN!KOF2Y7(VBE=>j*CUVO$U8cS{ zI4udzbE=k%bQa zhl#kGI4!I=i7n_PZYB5R4TOl@P5%q4^Rid3TRSg%i@dzA*R94&A9=Y*Uiw*6lDrJC zqCr+M#EdU-XPCTU%J9+~@bV9NlzJImq8GicfDhInlLGcpB>N>9 zWfgLSItC0fF^+1a$cF4nfqGo0wkLR#le|TfJWY`eYY79Rfin7hWk`h;l~Knkz>9tm64}1rm2iuRK^UI zF-v94k&D~Z#2l6JEm^v=gqO*Hm$h_Ee;H*YSLdZ(Z&*7o^+jF|>J6*$a+kc^BQN*K r%LDTAki0x1FOSK~6Y}zuygVZ>Psz)3^77)H@iI_`mq98wgrR={_je?l literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/tests/SAXConverterTest$1.class b/build/classes/nu/xom/tests/SAXConverterTest$1.class new file mode 100644 index 0000000000000000000000000000000000000000..0853d35ae8ddbabfea1af572efdfdcfbc0b2caaf GIT binary patch literal 222 zcmaJ*F$%&!5S-O$ViZhcDOy;22!23B!9pxzVH@M3hVu^YPVsFPKEOwbYir>SyTicD zu&?*y31EtLj5tP%kmmO4N?x8d6MFe1>l-l~UIj%XJ>>)-< z7?`WN+lva~^XfM_nh-iGW7y4g;XQjo_AOJoFh{l7S6l`{|8GY&1feGXey1*gs0a<= P2u-<(Y$_fSMwWa4pXWGC literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/tests/SAXConverterTest$BaseChecker.class b/build/classes/nu/xom/tests/SAXConverterTest$BaseChecker.class new file mode 100644 index 0000000000000000000000000000000000000000..608cd58666351bbec6c88f77a0311ffea3ab34d7 GIT binary patch literal 1980 zcma)7TUQ%Z6#h<<$%HUiIuHufDy?V(C{x;sTq>3ZYLP%&(jwM+IV1-%Fqw%nCn3K3 z8(hn!PjzXR*R^zcDL(k%qd&>zQumo;g^^w#GPBP<`||B??>+hRpWl85a2`Aj6O(E5 zV6uaxS5lZtV;VCl%y!}eUQJ^TucdG?h51fgLQXvv)MGJ)%PG8`!WCt*q+?k}o|D^_cjP2u0*n+|DA z<>IV!-&W_AG9k-$z|eV#2O?J%#a%&}j3w6q#2I*5*#xSa=|OsD#Dd5kPQ9V2ZC34jX-i=E;>RvFkH{(cX*v!4tKY$g7j^7 zd-jPmQF7)bId0Yn?`%kk%PS!P_eCbsYQD|D)NhHQL$U3whUQ%#)VO1B*`m}u8!Adr zhF}~+X9ajxp!*zyZWhtvOmbJIHH!d2y zQgy_5k-D#A%|HP+blfzsjyD;!V%c^|496ehQR0&8*f8)GZW(wRw++05+mvvVrj5#u zxb2-AR}I`)$y*cC(^FQPYZ||6Y7R?7H2E{iai~rFknKuuC|aL^ck!N%_Zdc>cxD++ zMQ5ihoT~5xYe8)Bnj^1px8$g$=#Lsv&UPCao_%z#35M>&lSgYS)O^u;Wug;*b#q4) zKW1g0qlQ>0p1cVLKJ4LbTV`kzP zXrG|hOpgA9_&#)odq~~I;F#GF-W)a4(VO_BmeB?h`{-o2gQi3xm#i})JL_=a}- zTa@q}cJMts(%0}~SmPR@U&b)m4^w^X6zh4Cbo$eq#|t=3Tu^Oo2I|Ptts=gT5t0;( zb&Qe}52Z1@NR4`lWCPk@-`|L_nZGE;IB62(XLW;`I6;zX;xxTg1l2@#(1vIen=R1V g05qJ%xzO!AImYlZPSD#V$;2CY8fl!NGrCj%0cx_~Y5)KL literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/tests/SAXConverterTest$XMLPrefixMapCounter.class b/build/classes/nu/xom/tests/SAXConverterTest$XMLPrefixMapCounter.class new file mode 100644 index 0000000000000000000000000000000000000000..37188aeaa51b119cb706f00729b2afaa8d70655e GIT binary patch literal 1184 zcmb7??Q7Fe6vm(1rs=D;R_ps*w^i3wFc$qH6=77xK{tesF~6CucU>e+N|KKLQGO8= zLB<3Hg?;c(5}%ufwX|hJ0{5PCIM46A)}+ z4@@uc1;mDcCY=sV{ki2>!3%+Sb!~^yrrVJM`IhC#twC>3dfVon%}BB3?wj_G=~>E; zf?9BBk#Ti*v-Q@K2i9=Y>^I$k6G%@Wx8XR_YucvoOC~*OIRj(p_KZOKfp4_ecbl$r zBt7PCGg6tuXs~S9@)^8uIbDI(YU_h}WE!^Vbd7f4G1!=msB!du*p_O4M!}H>o+VJ= zBBlzI<`hu4bXNxLXouVvx0qz)6&IPrnn0y`!Ho5WqQr)e1(q&M709&RfwwPTTj~dt zrZ@ITO_W0h*&LEc<&Z`yiJCzDzZWgA?0Q{e*s~4a92$qx?n}=%UdjV=U%> zx_CagF0gQJ2FiSmI^SuGA6pJBcFkL7gp+{JhTb|o!8>33idZfF1#xv~pF+VUo|h2^ z#4$8+g0%Qf9~6cXGabQxE8 Q7l}#W0SbgK&_^Nl54Gd$*8l(j literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/tests/SAXConverterTest$XMLPrefixTester.class b/build/classes/nu/xom/tests/SAXConverterTest$XMLPrefixTester.class new file mode 100644 index 0000000000000000000000000000000000000000..518d16b4284d1e5ea15478317cdd81076407de3b GIT binary patch literal 1175 zcmb7DU2hUW6g{(iEl2@TY^~NpMGLL%wCan&m>7)FgsKTm)A(RAz(AKSyEwbs5)+Mo z!=K@c8Xx=t{wU*}1vRCljW2s=?%Z?Ey=P{B{`&p{zH{748iJN0(Th7m2+m=uIWaM(H2AL+){>A(+yNxKDD&jg=vR2^%QBOaDloX z4eSIq(%1xNm=BFA4#*?fDUQc3h80_BY-5qgXs%i3cNsSR>slCAT-D=5&OI^W2htu& z8YHs{AH^z+kUP3nqi?I&^zT_B?j`? zRfk+ZoVxJHt!P(RHp9}Hx+v1$kfa|2h{DiLf}SbuI7LP=w)PdqXIdDTqi$LttMHbREI3+aku$b^o- zDBwyck;Oc%3@n6rmZ9WTEKam`>A*Otm6)ORp0qxY*2hpwvuTBK>;JU=DC9s^T%RaB pps35W6>PQ@W4_p5z8IEpBZMsz&>)vDQq0rJL=`zAzeaoH%->ks2J!#^ literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/tests/SAXConverterTest$XMLPrefixTester2.class b/build/classes/nu/xom/tests/SAXConverterTest$XMLPrefixTester2.class new file mode 100644 index 0000000000000000000000000000000000000000..d92494a5e9baf0d4bc9d25b1c4095aa8f84ae142 GIT binary patch literal 1217 zcmb7D?M@Rx6g|_gt;;G{`BFqmt6EKYJZP(u_*>pO)G6Vng1c4;PYTb)?$8YhF00G~5wpa7LQ>h57 zQ_B4Wny;yaWFt*E)PV9 zA4|6_Rlr}!rik2dOL(@c%bFX`x<>2K6?-U*LQJ1T z^rCrFiI#ltt9M+#u$l;H%9*BcT!z`9<)KJl&rjR{DZi)V9Fuoz*qcaHz_7Rip zQ%qS*(z=vHK)>l&W(qgSOObyIGhLZC6jOdr=E_i+&m{9DmYGb_`empo3$tDF$7D6R o9suiA{Oz$VxKO`cUrC?pRuTwY%P3 zS#oJh=moT;ZQ4R0DWwHM2uEo|ESU(>Ba~7~NW#^o9BH|F0KEc)9tr)vnc3M{?XF|` z@gLfm`OWY5-uHdq@q54d=rb=r{sa*nZU4sTE@R7KVO$O&%5aF-BcsPRrC)s z{0({hru=+Me!lG{FMUT;|D(+RCl~#*i~dES@4Bgho|2c}lkx8>^kIcQ;-Y_5=-(9j zcZHsGQw#lv%=%CHc|oBc$m}1w=tpkqq8DBCV>cZzMf{hG{#&8{QRsi&)J-qRyq6XF z3Dz;Wm=!K@Q@>1YpqJ(43ksLY!%GU6DO|4bI+^NHSW$Sr!fu5t6!s{*LE()GZ(7fl zTm?9+DqJlOn-$(7*BXUu72Ybhbqa5j>vn~A$aSZ}^>W>%uve}P3OCAix57i;udv&&B;ro{?BgPYy=4R7y`VRi7IToJ#8FBa5S2VlX}* zOY2FdN_#Y@r!cagY15h^ri$ZQN*|onLld}Or zO!k zr(sfBk7a1E#wYW~H%z9r&_(#ffP}#jkepE%2CObYw0Oh4IF`Mj3%n ziN1j}Ts%FGk%6QhoK40TVgn1>6*wjtj{{?628utb8@9nMp;^QcW=vzf_}_{=TNEs) z5EW1sypq6_I(a&kjED483h^`&OQn0!srS`jc14o5_~7InPwnjj=m;l6@X+FohOa56r3N=v3BiFV55L7~E;1|m}q zebojMn_gi88L=^U)H1}s2*(5NP;h8+ei~s9mLsvmJgytY)$1IYV z4w(xk^zeKvti=!)R_!c=-2wYRCeCDiel2OTF=fjz%;>%$Z3O~Jm4$|T9T?n`@%dy( z7f--*&iWd}>`tj+9(oJ?2Ha*Pg;il?^nMTh8vQy`<0}_;551Az#8iL%nhv2n;Gs9u zuX^}64|;fr{T?3X0aUtyXf(c{hrO}+Xw;kQTo0cRg!S2qJBCT8;t|=o>=J_VWNA0~ zwu_L5Px6R|U&FV0IKZRGSwqm8Dkj**JUmYC^6)8`&aai937+(DP<~GH8NquNxoxRI zJR8+B%bguEEUfZ55B)y93nj@~VAmTa1$$+$S&o`!)9FO7&$qC!(7AA+Q@93nUsq4h z5nn7*QAFI7hi~KCJ$wh>$yDoDr7vnyEIUKkO+9>g5>f0_QqiO-X!On^W%?VlGdmYk(MHic z?corkG;jBMv74n+-uQWM68%Da4%Kfl?RLApURdl5P`mV2rQ&nC7e-4Z_L?=K%0Y`} z5Q5!U>xC($P>Dvx@!ldEMECg4O$K~HC{CS^C+9q@^R)OV740=Ql@ooF;{&I9{penz z@r3S$QL$97jP*BL5>ugBeNIbt;#DfHB@92xjZe20`I>#`R@Ntydatz~74o6DA0=}! z8i`%(6*l37BAAUi*qu|xR*3>UI5ZF(aPfH$Z{k^DH^NjhGUVZEp2h2tp$Y%6hqu@d zlUQsFBD}ZCsB3IAIDV@iX|PQm73&eNcgVB*^w^MpFfcIThs=5zS?3=Mjsy{yUSq!T zG~&nnMsxjma3DA`4tY)TT;U%(J?fvZhW5%(ndB-u)J#U2g{m5^&ulL;K}Z#QE=U?) z%w;5`o!3M0SX2l92I(>fo_6(#>xA;o>*s=9hNU7JeeVuqsW`dU*v|W#;WTWamvzI1 z`+h2UFuPAAWzZb8g~w8M@JQ}wvKSH2e9YUY;cwc9AD`U$WI1f64h9w&vr$;0oj+*b%hoG_H#h?PAXRbK|2z{h-mD z?aFzTSGc-xe96Sh$hf2SZ*}fbSmttHhBh=(xmzDHr;(X|{%pDLG+ToKhH45s67WTL`Oc1O~zpp{W+8+Fj?69*Sy7WA{hg zthKqxJYkiYm)bl|jQ8-{u^VH)pm@VRWJjh_7zdHUPHI>)t4IhLl5)Ni2}er>JYWb7WEEW)O{8K-AwPz?x#_GPMWmz!nU46`j%)%M7iED_si9ae_`Dh!A^ zZKoO9O|!I*E;t<7X~9rvFQVq1Ku{tl3;arqJPXrA%IGT907EUVRZ|@+v~^50?lW1E ztAPOE>N=Kb8~(QAZ%1z#W|h^IVSMK@)%TXS*OgzRU0H~o;EE^#h?fvYNvb0W<21F> zJYZf1a|?7BpCO0o3OxEc1NJc!kOf=9_z&n00b3zIy_?wHB-@fHYyPRreuV-Djb$9O}*(s`f%vz_@G19e8e;Qd`wF6`DK7I>7M0 zc2J5~@J?ZOco7 zK{veIkKhf!hSNys`!MsMiORCXjdvfWKLSykQ3O7MyAnt^NPkR^g2Zlo_ZBZIHL9w4vw2_j;o7s{83x~ zjHSQ1kpAu|^{{$mg?dVtskb16CjsG;h{#VNIX>-x&}>7fDFWd!8^XI`j8xH+SvR+< zN0+HDGa@w&h!F%gyx6w|vD0 zl5hFY^)3G{EdL%X|Nc*9`88W%+_JpEX}Mn=UZxXvs1&sQ1=#)rQt5{%@jpUB{@7uA zgKc}QW&3u^3Hi2v%!YEwirL);l#}W(JRWFQZv@QCkAfSR;Yq2sFFPn2W0w`ilU*nYAbpWbENPb%{suidyRT4 z?3Xk}O%AA|>KJPB+JZdRwl60+>#^Ywt7saksfIUGGjE{|uAxI*OTAoYKx{MB*)Hm` zTvS7QtV(;xf?b-Iq94z|W(icunCG*wQ9}TATpeGcQ!DgZ!D@_5s1r*x=^P2F!6iEF z966(&U7~Z&ktua*iEeX_+^*ifM0Yqx?o{twqPv_UcdK_V(LK%)P1TlY+Bp(ZLrWBP zj_9hsMCZ+sVo`sLHi6n*pw>$}xPdykk#6DLG{H?2<~?+o_u_+bGd;j9^dWAgC%BEC z;dXkCJLv1&=?KW@ZR)>dQU9Dpcz$+#B11SnT$_aVtR=iromrt-n{7v^6f?!>(fA-t zIYf2bO>KOH4sZ`0;iC?iJvL0A1@k~rm`~U+lU5RA?~P1JA8`TwMP#a7y|_YAX(pvF zn;R`uQg{G}j?)$%qNV8GAc3`auD^mhRf+%JnW4J> E13Z8B!vFvP literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/tests/SerializerTest$AvoidWriteElement.class b/build/classes/nu/xom/tests/SerializerTest$AvoidWriteElement.class new file mode 100644 index 0000000000000000000000000000000000000000..2a763f5b528227437dc6b2cb93ed882d4a9d6a31 GIT binary patch literal 984 zcmb7C%Wl&^6g`uNow`nwHcg>zC-CwJ_9 zhMBN)ayjXJF6&C6y6daUikHeHN-BynWJ}F*Buprs5r76=Dt{uYUe0uoZ#cbxa`_+2 zVU?a`ik>bI15;YjIZfUc`7yFurTP)ViY4R6-S78RTY z^T<<9VI7lAkS$iKW5nWj3uNN&FkdZB`qbpb$xKBoVVPzsP;?5h@6g#dDmzDR Xg2F3QexCkgBu193l0ZiZ85q9+Dm&xu literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/tests/SerializerTest$ColumnSerializer.class b/build/classes/nu/xom/tests/SerializerTest$ColumnSerializer.class new file mode 100644 index 0000000000000000000000000000000000000000..123bd94016eeb9a68bedd39167b1d1b3e3ecc8ec GIT binary patch literal 1008 zcmb7CO>fgc5Pj=7bz)qaCZ#lxLV(hIm6-3ZLNB36kwSY&)w`Q)h+D_o+G|>I=D_dZ z!kJ5oIPe4bE&Kw6fLS+bRX|9{^6t*e+qW}s=F5*yp8?#(vWYZ`Ca&PBTCSN$VL|z> zt7Xwd31tIS12u+;C$8_x6^4Adwe7reY**Opk=%`BTL#=2ELS!e^oHnihN+h8^R;NO z#e)rJ%Og*=B|46`=>)E_M^0V#-H;*E5MDI!+nj=)`$0uV1d3BDY-osh4XOB>rLY6GTBZnE{U^o}Y6xUlM=mScLr-j-O+B;;c zwZagIA@nyId;jxm^$^KSiq?sbFw|oSsK+i49rPWTx`!O?dELjnF0iQY#g7*Wr{O$( ub8#A_<1|Xh(`~AqSklnGLu214p8}aWx#tOfiOw1>W14o8d?{3rf%yyP1nMaO literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/tests/SerializerTest$ElementSerializer.class b/build/classes/nu/xom/tests/SerializerTest$ElementSerializer.class new file mode 100644 index 0000000000000000000000000000000000000000..4db92f021d2285aafe6843750a1a9907eeedac14 GIT binary patch literal 788 zcmb7COK;Oa7@W<+ZcLz$&_F5B2R)Pq@TIp@y;P|bDWZpz-rZzX7CF0-y@skks7P?) zREYyWpg#&R>o{#Gs8TI?zi;Q8%+9QH`TOTD0NZ#Kpn_$$JooX^$BKZrqqMTS0M>kVi_uvM)e1`$7qRhXFdrEw1oYiO7C0=Irw_0y5Ab#Y88LojN*3RM8 zzM{sx6CjQFLsr*4!YoAI&0(JG1@;vp4X(VD;(%RNcciz~x-*f%ugmtCw bp@}C{o2owLe2F?GJVTu|;H-=nc!1y!IR?Fh literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/tests/SerializerTest$ExposingSerializer.class b/build/classes/nu/xom/tests/SerializerTest$ExposingSerializer.class new file mode 100644 index 0000000000000000000000000000000000000000..16aded733af6c6eb2aa94ed087e7bde15e0aa261 GIT binary patch literal 1342 zcmb7@ZEMp|6vzL!?UJ@;?xA+-rn9=LeVEJC>6^(wN|i#{z|Q(QZ7*FUO-gd(il0D1 zK|w*mkKh~MIPirZzz-#!o1|OURhg9Bd(OEhzkAOAG{1iT_z7SG_tQw?hFor@p`sv{ zqFhQcR#tIK#TtY1z_d-V#ZWHPpBpa>-E{OFU-W&^5H2@*n_<*4?5<7`)9!8-n+%DH z)8Py=b<^hCey_#dU87}DWWMgS4XbImru3s=LhP9yomB^Y$0J08)3If~PakLqRbal|? zePKGbr$Q%x-!%oV?3q@FVYV==XPX)hTepMOr;i<|SSK~O?2mVzNx&0hpCKEGI(lOu zpWp+kIWzh~)oUAlPIoSjMjr|iz2ytuG%TO=&4!1Mxu`gn-?KyUNrXbNMzuE_-)-|p zrW{4)xOi)_cMU0|HC(_&4Y#qZ;f`GH%4Gv9D(*3?{oeo>=Asvlu4Bj#|4UD8&+qpg zSMW|Xn!};qRN^qLwlfk<1``Mb)B^KRnoQu1YckUvS7YfPJs?LPL6W`^K%s~9Kn!Qd zsv%B4KrNM555T^W8G}ZvOhD=tl+SI1}w17Axqf{WG5(co*vRV zx?qcTB0(vY4-gyYzrj@MZNOg$vkv(R7O_O6A@vg3m$7`3`W^9c>N}#o{|ogB)q0$o zn@mlNQ$G;(PrCB!h}fD T81g~Zb literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/tests/SerializerTest$TextSerializer.class b/build/classes/nu/xom/tests/SerializerTest$TextSerializer.class new file mode 100644 index 0000000000000000000000000000000000000000..1b8d443c322ec2e0fb23243748db05af9d1d1bd6 GIT binary patch literal 657 zcma)(OHaZ;6ot<~p;F6RQN-861){MVcNjKA6O%?35cUNpaZG7RJ5}PpGMZpq_yhb= z#ybKMe8nc6nRD*#*E2J(Z_h6PjQ$t`i8Qr=}*aM97fo@R4kDD!Jm0 z2Mp%9=kcKFI$_AEerS7U|czrU84 literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/tests/SerializerTest.class b/build/classes/nu/xom/tests/SerializerTest.class new file mode 100644 index 0000000000000000000000000000000000000000..70dabef45467d76cda6fef3f780e25f8d2f2f4e0 GIT binary patch literal 52344 zcmchA2YeGp_W#?Rm9&x#7@20Oa0tydrWa!jZh*lygkC~G*aDmwo48QYd+*7m_ePSt zOOGKT?P8qXE&6_uGMo<6nz{5l| z-dYwi&N43um>1f{B_T4+3xdX(=0ySXHzD$y7xR}(0_LS53YwSkm&*g@6(LGhU#_&x zt88-%zjk%Nye33p^IHCM9sjwW|J=YY-pGG$3Ya&CD8t;&Z{Na|-x@-Fx7p@zZS!}0 z^Y^xSJHK-W7r)as?+TcAhv-1_GTXd|Kkwx~_t|EPZSLSEciQItw)uceAzZ%vCUUQG}C;In|a+f-(ci#`lo;SEdTeMZT^Y7cmu!l7XNwMHs7($ zcWv{}{K9*-`Mzy_V4ElLvkz_aFWk~!ZS!xo`FGp=hi$xOoBy=Uk8JZ_w)wGbeqx)S z+U94r`MGU=VVnQ9&Hvcum$vzpZT_Ea{?|7DXPaL$o4(-+zvbG#v(4`V<_{s-VqU;E zf3yV!gkcLaL`Q-$LfFD$2)Ojw!f%U!Eo@r^Z4t6XiY-#nQ_;m1UHP+{Ezjw5d|OPjMS(4*+hT?-3T;tji(*@p*rL=HGi@=;7PDYZ&J@dSvBDNB zZE-MnXchlC#1^XqVoiwN6H)%N7Jtn9_^Qel)wYP)Vx2A4+hT()YHV?+Ee^9qtt~bN zL|s7Chv;)KjXo2b_|M_CXs|`2Z30b`Et&&jbBMm?E^i5lt+v=^iz94tq%DrJ#nHAn z#x|a?#j&_a ze+Y=jLdHQb6vjcK)fT%0qAg@B=Gq?*h$lkEG8i=RWXMDv{tS+HQxOD5L`o@~N_1^co7jCYvsb1Pp(-hOM7?f6AU)#K~&RfjC zDcaD;?WC30HRo=v-}q-0cz@qC1||3zB*=5xAL00SY`9ZwXudp(Y3X>6E3f>iqQ!#BB_S zPXppJgNAY&70tCxHJfT<#q}H4f+Th8i#J3YqE$^`#L}9k4d?>8R#OMsfoLGEx@d?s z;yr@~%WeWx9Ovl^nC!J=MVWPX%u-wEGbz}#qEU9>v7q<%}Rc45u>x_R>q>d%FWO9~eiE(BFkM`>MkNvyVJBlHeh)n#E* zwCb>m=q4$daGWsw7}SGbLPr)do92Uiu?9xz+Z?TJ#%EW4ym-;fF%!#6i{@2m@sl^? z*ZA`y^*r5MUrB6TR7!)t3f!w613{4&kXyfOz`YoU{%z8&7p{k#un{sZ)N<6me@wix zx@xchVrkF_Z<~wj8X;6Q>uO@vOBh4DvXaA2eY$_`iuhz74I1sOHd#?66m`|IJOl!2 z&aad}(p}9%Q!XRn-fAu-6sfFtOK5)R#yq#vxZ=`{o0_({7F1GNe`5q}t+f<_(@iy1 z9iAO1D^a56ya5cw(FWL#^|1x9rsjq^NLwG*m_U{rV_c+uBaAcTp~rB?wz#I1jZ#pl zJ?=r3VXnM3)g?uT$}Ct7NI(jA3oWpr)&S-=)K|qCc^oOLYiw$0mg0<#amk`+V-jpq z5DS}Nrq?#36vhBqD~p|`1MVs58)wjj_7BVIJX}y3TvpeS4MrnoB?d{-FRa-J>*%7> zchU3O`t=}L>82W77}Q%eg+V07BgVoQ2pMgvZ@@?fVi{%lSA(9d#qe_&x;To9FN`&@ zWP7PtQXgxqtmh}~YMHwC7Zzj4W>R8Qs@+!FSQXtQyHTW@>&I`iG{U%d>|uA+tJ%@o zb?Q}E>MrV@`(4t)F&y9x+j@+%TyML(EON!n5UDEGT9h?tc#?vQwXSI%hWv(_>KHVL ztJSK}kpO|l<|;;L&=AjnT~=EgTOX}0Y*;TH0C$X=3Wr_~aOE(b4drYe=dD{@S5t*y zy{yh1{Q8zT3ZyK+BYwyj0I_CELPpVcg!a(`ty`u z0I#72)~%=OJeBdV@48HO0}FK{kK^OHr-=roGu4kw_8J?sWPOq59)nnCDwg84hqCPy zX$bdW4@EtLjpSVMg7TTknop>*c!B0~r%IR4)TKLBGnba018hQEtJg_xGTqqQlIB#J z%w!$V!{+?vx~isT3>Wov?E%4;(Q09k96FL!!wm9JXRqZdqFc*jb?cip=)M;(D68Yn zy5n>L`Jm5IDkQ1*R!llxPF?N#)8Uz?4|`*{r`X;^Xhp1{-cwYL(y&mTTU{6fZYdh+ zL1IBHT3frVs0O1OJ25c-(o2R3>*FzDYR*yA%)Tu9@2kg;9?c)))W=x$F@`_v`i2^K zmC;%R2m<1B?YA*G9fDi6_=1LNH3R$@05G+3TjUv$Y$;Za-tnXhVFT@c#p_|}0*rTH z!ic~SVe6Zkw!v2OqyRGwV9DkCu58R~sNcA-d2JOt;)^y!o8+9ZF>z8T$Flu+gS+GS za(c{BZ)5zGBV18zU3~-3V3cnm&CI;ITJEQGcpWoZ#M7Z@!?vXx(0BO+Q_k2%eKm%G z#2*#KnzqDZu(Gn&#F>bLEV>u)+%+OcRT-KE#iB*=|1C=FKZ&kXxOu4v8Evhgt*VZO#Y{X+_D)DrFeGP_-2CtKJ znPxmc_P)%0aQYq)UpOQ}Dd(=|mNQ2{w1gqdiZ#Ux$!RR^v~v=5v#wi>Di&?qBxi98 zw>84Bl$Gd_D_N2G&1-9Gsvv;zveL@CWuf@)tg0jiV+3CuL*HJiEMv;RvX(3=f%$5T zL7D?=Ra^>-5(ecnGPnK?rJ4Dd&VsFr>kxUWspZ&^>$1TUb>>sa9(x&bhsPL8{e{|e zXiX2SZg&o9u5W^pyMnI0Pw7Fqg_5;gR#y>iIIJ1*mWhmS5hjLgFOs>7=tR9VluI|n z&?9$Nu3Ulox^>`4leU@ezz7zpqL}`y#Xq=?UAZAS9>_@oyPG-;2&Tm;fqA)epI>Mn zOB>QL0Ev<8F~n*Ol9L_Tf|}vgvX#=-0(}5yyEs+zZH&3u&iABEaUqwAJVZ;n?x6vU z1+`#>OCD(^WZC#A!9$bNq4wov+)tKY5Z$6@FzuSswjBI+WgMgMULojL5>NzUDny_i zFi#^fXD*%Cgb~#`FN?Y}Lut3=t@iR)$>h{cOJ{(AYfWh^tdnsLhYkxj)Hl~w7sZqj zV)LU5==T>zG0869ON9xwHFm1H(!EqyxDdLGc|XU?RAq3%i}1a+hJAzt9xvNVJq|t@ z3GC=W?5pb8d#PG?krkF_1pBH=#<>tH1SK2it#jDIl-48HtonHf)XTVq3?4vN)LzbfcQ6@;QXpu9lH-LtZ#0pigCx#uefw`>!Uct zo@!iWTn!A`!sz-^jY|xKce?4j&hUl8;yxHcswVl^`aqA^*m$u#BdU@NOtyi{Q)pmXBHEq9X zy{`58-6yu*xchY9d9SnG%`>8uQ zSy5#4=!s(#Sx1kSpA!m8`EyLkn29Cc8z?wd(|y7IG%uM~D(iOZ)NSy`T4p5^t*7eG z_zAi<<8@T}{0 z{ap&&m@yM2urXr_<@3>_$4=25n7Mx)7>@22PaHd*YcHNWdi>N84h3^nSH^Uv3krM5 z{x#jcWvAhkd}v7?)=p!&(8|gCQ)&D~CT3PXSEA%Kk9BKU61}T$Sf4wL8(OwM4YjK{ znL=~LS~;}XfRr7%_fy42GRq;EBbUX+6Pb~ti;Ksq&yop0S?-IAi{!gZNwI1bDCEN$ zUC!iGD`cf0>a%Lq(DhAIQ>|31ORCjX{1^V3w%@L6hD99aNF)0-@ccZ~-!g6#(+6I7 z?wBc4Cg;{U3zM*&oAjC;Tc>WRR+`lv%vppb?+E^nMr!Np)<+r<;(}?8MYh1Dh`@f< zHEoO3uZz@g+Em}r)Qr{knpk55@sY?{b{XJaMb;r$6j{p)r95+rG}XhlSC2|Heq&tB z1{=_LL6S#_F#kyhM0L}BT2u$SzmdjfDH_yNZ!mTL84W-r}`Zm?XaagnnqSmPAar)4_K%q+-v>dY<3&*fs$ zP6rVri>*UTn60h($*s7RWTMgt{_`}3!8+dDhM(Mo2Z&so^@7wg0d4m*)ur0W7+|N_ zpwoZVrXAb~jk5m$TI9+Od;4Z>Xxdl{_o<^sLK~;#ZdH$M<|F5gz-8u@XjbOB#ytGh zip|faK~Lm;T=eqP=kaR{8na*6mD@82Y-ArDA0g%`{)HkNVhCtOqP&`qb%6$UKm zVUMSJDKez|>sC4q}{@PIQA=>C~iZ-n0pj4kYCZI{6eW)>7U3=Ju zXk+zZdM<@2mYh>zmX#KPp{}ltL{x!D1ku;6FeiuQH6iYnRPM0yFeX=)0h=1@H^w3= z329)+{i1ArD8hD0ne|2tO@r&|w?t}VO~V=^8<0#Bk;OOGY>ib%s?>s2s+D2&g&Unj zIC%_8wfgboBcKH~)#}e*Z7k37OaQ%QHDISb698wk{BeZ?;yl2Vj>CzlVpe^9_1bNG zwcfcJ!=^igd;^R*UARM>uha#pRj52#+oj8vSWQE<7t$!W=b=<K6zG|U_;Gu1l48iH&GZlsH9Bw!tg$)YtBT;RJ({L~|4+Q@>sJ5bA{ zo$I1I7YZfo+zihxyK&*@meRAz_pWJyI)VOTnyc|vw z%t2Ts2)1CU5OW_camOQ{Jn|4q$eS}~&Mc94uf6w^d(lr>v}DYfQryhe)<*_l+NCWH z>6pT4?miC+#kG#%h73$%M_JI?(LAT_hsE^>+#&pH*eG*KtJ$-Y9w(dEZcu|bi55yV zWWN%<-^X-64m9rAaNvL~yKBJ_DvEXqMbQYaW6O@jWIhbMnZS_YBSvOrL#F!5ht1xs znTRgNeT3nZf!GMfA*PAWUczXnz8&b~K18q{ilBL94>gTrdobRDB=&X~S_kpr^{WWW zJ}(OQYEKNcK+7aaVIwmk)tYEcLJAEgfB9=6&jtC_1^KZ8Y4`GTV{jtyJr}og@l=LQ zaymT)sSMNj`SX&*}}tOZLj!#j*P4*baSj=ANEaicS05yw|1oxcjOXPH!Zs z;{8aZ&_Vm#qyuhnPL6CQC#M54@xgH?Vl`sFYsPDuxzUcS?8RJ%$1Az7uK6z}wENwT zZX^K#*OjmDj7@@R@lS1Kx^hh;n>h_v24WK=G{9;u4c*VvgaTcU!}Ru=u%vVS4@qHx zh884fS^Nu>%%fwld0pA-&F4QumH*p$zRM}Ke6zM`s{G{eEMItrt8Vf;|0$3~44T(D zD4>9x+OM6KDPMHjKnO^FcSFIcR-T2ZP(FKPqUzc0294`<3(U7K8YpK`MWi1i;mo>ucq+y+FA2qse&V)oar<7MTq)2g%BLCT)2IcKn=L$YJ{dP|k z!S_!(|NWEidjFJ`{jTVf3$O50)Yo-xkRTGNmzzW)m`7l%jX?wB{!OwZ6s!v*yzQ*+ zz^Zr)OUD)G<|6SUvTXLEit-2+nIelCqIHdWhemF0 zX{AAPyp_#gTvT3G9Len5%b6Wo6>`c`%qu-Xsl-ZTN5|Wc8n6-;yR^KtqO@{RMBXT^ zn7?RwL1a~dOh@4mc~D=mNQB$J&NuyCLaU?*_D)(C7g^T+y`&}4m%>Gh%F7n&zC@9Q zACdb|BEv9sYP~g5)qss+!xFm3uH3Nd$hP`s%wlxbbOc3f>LPOGZaQMn+J#A$pW^0& zfZKSBS4K*wOdVm+%=TU01Eu0f*d#_e($9A2hqcc3bz)X;bpi3<6~^oF@FDSmrvW_h zP}}!*Q)$cED1t1`j5H4ciV~|7 z2`_SR9utxua7t%&+H6E4szFtO8sx`j7F0Q7!j>d8bqP62j3wH*b>$g?^g?*Tz!&_V zoN=g2e5sa0pqga3H!-!*FD6ViM(xe@?U){vYR%-;fqq+ff1*r1!t{XcOVl+kFgdzT zZz>X!0Xn`@7h&<2n@}K{`Yq1&Flgl7`o%>&Yw6;D)-0%~^7?rGRWLMjSgJLfBTZ#I zZQQGWuhSXg^(7DYBBF++dhs7bG4{ILjdB=lQ>{51#ppH^?gXzUsTE~(YA)aIk#Ji{ z;V^^9pna|4#^r!z8#CF2kj#W6V&q-ukH5{6V18B2H}|zp?tRadK7`-BbJkR6W~cH< zc!L^poMEA5Ure0MfVxpnKXEfXm>5-2cMrdso?`N*ohRZc=A`^MiAB(Occ+`aCpZ75AZ~!ANNFap zS50pb#^m3dBP&bxN2;ik_N3!oGSw99*jSA5WB3Z*IOmfV1fT0_n;Wq&!dF#W&*8_E zrh0dSG8Z1fyLNrUR~E^aWd11vKk?>0sWekZI2nHkc00SDZLAT1I=rQ`$M6xJ^j{UU zfYX%9ySf{iN)K<2)*{tvSd!h(9be<#OzK?8B%R8N_dkJr$=k7!W-6ma@XeAmnG}>? zu?yz1Q*Y>G9i|4j=O(K%1Iid`K!&B~A^9!o=5XwL3n?ltYG6uFNz!OXY#r}#Q>oGF zYR}p{+SAhS-jq`28(v&2MPhi76pb{u9H+qRB4x7kK|oUZr&xS)i@7r5)bHNlYP;bb zFKZx}SQ3GlXa(q%1fgt*mP(xRa6E+Mm(L8zl=p|$-6n6+AV6Jep zb0=Huh7%89-_=I5_*+b2LU*W?vT2TSoqQ%MHm)~rz&A8u+-TecpV=~QHn!{L^GM(h zQ7URrMeSYgqptDVafS@Zc6YAkUYb;Y&a~ zl0vBQF!X67>fS_Ykf1@-KnGGIWlN-o2ihtN|Xhv2QJF##yt|HpYn}+ab=>0 zu^QzdIzXeGN&(|OJoUNQ?lSHKwiXxLQyN=EcnBDNAVg*aQ$5L-P4sv}wpq)Sy=|sRgiDuHtR8FUQAXc~#=V*w%X`-=1LL5xl8scag zpdrrO1H_#!#78y65-HFl!%)y{VD&G@MW7;_(~*tYUSZbAa@sLaAv>E==^W}w=TaX! zkA~6tltUNLSh|p=(M8bZi#^CnG_q8hECmw-3gt>XCf?4L7zOHZkWJeIvin_Ralsr_ z9?snxuaYR~9yq21^BQpMI*8nj5UZOYm^VW(w?i;*0rzeN=Wc^w{?3D~$mQD46U+ya zU>jW?9`j3Ly9e0r1-2Go+W~Alf$e@^djQyW0o#MX_VBNOZC4U(W6Q(ieo1U?!1g$> zJqc`20o&8S_6)E+3vAB;+w;Ko;;(@1!6ewmmxm|(lGt7cwl{$7Pr&vTu)Pgz?*QAo z!1iZgdk@$?_!Y1{lmy$v^6;cz65Bt4?IU3O7}!1mwoifWGhq81*uDU^e*@c>zXG<0 zlVF=%9-i_`V*3Wzz6G}Lf$axi`;iWWtCD44lcr(PR6|gq;rkV^J(2`lUU_)xFNn=Z zAz`G#b?8cIMmHK{qybxZU<(6VI;R^) z^b}2_74>x?^Lxq<7`p>T+YCcGj~3iUb{!vg5xfGOworHpBiKbVmxpJCXNSvn(3}>U z8!j*B+iK$A!Bvqhab>s1RhfgL$ze%P%a}twjCp{y0Nh%LInW{+XDkM%mQbm&lolJy zCA^WUM=reS#uM-y5!#~>5~zuJRd)$gNT6wC{y+x-Wm2q9x>$E=Zq2=$Dtb`mC09_f zGYtyQm+aDjfI?4!4#J(3_)MrGEk9d~yI?0R5ZTtaowP8{H|cU2G0a2OQy*gkMT{C6 zVjK!_JPba4E#(;-so1E)Eaq^RhkfvbXEIoOhX~(_nG8?y<|^P4nvaZc@d{6B9Nsq6(I0duP(_COg^ELOnQjvx@8YGbr6VN>EqS+gF7IzMpP_O{Ra)BtV zQrx)++_@OsxddIg6x_KC+_@Y)xB}d{62{^xkosB|#82YRGY%f(LueMyn?7I_YOh&x z76UD_%ERRqS(vI|W{zOP65Ofe@{qu#{E6w=(j50?JeF#?EYHf}d3yKo@;p?H7gylL zJb#8i!-u;$JU3sO;dkHD>gZn5R5D!$z09ikx9{&E7jEZlMb$ zJ85NH2@b*hkf5tQjN51+1beV?I}HWDN5kq&#)N!^aW@@g?0}{}Kr4+0X|?eP9cnyE zhZ}#OEyiPXy73fUYCKKX7|+lx#FIuw)f7xC0jmxA4_egV1%=9Q<|wp6+i zWc^5wQZQirF<`tT?TY#f1A_~;rvxZz?TqTn4(l-qQATw^GS;hm;Mu|88Th0lNtn!r zmuJagqar)dLaQ9>ihCF`D#M55B*Y$+L46&hejAef4yK;((j?>0h$Os6CC2-(A|HU% zf0HCr26cfZSvFY0(Jw6MP(N)@=jv&yNKnpKTtv445f60p6A-Od^4JlPXRIjCVlW+t z!0?*v1U#8!Lv%g{o-css-_Xkc0MC~&FJDoa@qaKc-?*9?H!ou$X3Bz=X#{2hf>&Jx zFKC`rB_MD$1N?}xI%c^_qm_+|voS0s&Zw!)ObzGcBzJGB8;YjL5Yld-G%v{hPmk{!UTSh zqRJ6X4C`CJ-UQgvMw?qFz zhnPDnBzHu(HX(YK(8~_Y00+2_UJl?^ZM$3XF-RH`b3T}I5cM<{&;jN`8g9bVG#BId z3YO4vbD4)BD_n+Dy79bH&5#3NfY`VQ8b`xC3>m=VLs3$ObZ<8%yzP>pS@UFA0@@@z z32#g^jB-`MTm^m{0)DIpKh{9dVjg}BbNP{>jbS9IF?`4E%q{2)d;PP+b$jygp$-X7 z0RpTluw1VL%i&Ebx}2!}a{6K(O2RyhQp{TXAjd`;Y}Qehxrt_(&9c{OIGU|{E#UjB zV1I^I0248j0g~Q{R_8LFTbE z!aN=}`xH;_XS%&F*1hlT_I^at-v8O{{RNu(W5b7cn)=~}WOP0YJv|#eJqL6?7j!-k zbiRPb;N;@#KpjQrv2IUuT{LqAk1} zvK8LSNLJq+v@PD@Fbb3TFa+ii{JzVh)Yp7WcCMGI!WEJ}N=W3$5?@FAo7?Cr7;X!a?11W!|c^I1=`1Kei&CT;fbZnKNfC^y?9e2l9zdjk1K zwDuCT;$=wMtDe?+xUF?h+S)(d)?R|FaBG`EMm2X^5k7VY9mg?|j_tdJj&}+nf;gk6p&+D9Lai%F>>MbzrZH#5_z{z+Qzftrar0ac}ZhlAynSa4}_BYyS z{)3v$k33j6YpgvfniT6l9juu2DqH%#)4Uv`r8DeaLGQJLT^;c`4skBi0SiLZ%<5{~&LlFH_ z%lbJSh=C2XXfnJlw(vfzDyJJC!@~QeBOGBzcm)Lr;nV^q{u> zGZe9SqUX>CqAh777m#ywIt0%LWFmwf3Vh*>$yScVFbav`lrBbq8YA&LRaulJvT3x) z0Yyg9bTL{|L5*oMG!;Tv3Fl-KXu*V8uvH$(aha#GW{&8OCQ0DkBpV z!XgYk=*T-1$L2vGd>%#tXw>$498_qNJ7Ko!{*F9qvF2 zU7Xmy+l=Q)Tq%pNvP5Dc;rF;GLrkH;B9F$3sgx)3sX`RcVlkc8i5XNU3ZZ<(P`(nn zOq9}fVkX@qX45XAGHp~|%!7*B?bPRUhj!)$&<_59Q5n7@KAyP79>W>iddFALQJL_i zEp%Bn6B*)x4?XG6Nei{m(a9ttl9ep#hkQ(#_!bm=&FV-0TgDbc{My1w4VazqYgk8Z_4P%J3H74TJsd~=A zizlmTvJlGaBrHIRwBh|tfF(Lnk69h&?O0rDBfH-zCsX|EHz|H_d+ z;`AjBs1?b29Q2EA~^;st-SCp>eLlr(7-Erw?TGHi||8pVSryz+ zASVOJsQ_{shMLnUTbw}?#hElqob3VQOeA}0cN!-dcLY-Nls@5m(U&aW#z>*HEdr4vSaUQ&ijl-Ma}U|7PgjcKVID1$uWY z-6ekO=n$myZbx6}4y`YKWDNZenqwjgf2;8|t^`Hs0x3lnLRiO09rDTXI+Nohh2(cM z21?`T4Z`Q&(xL5wB{)G;&DMdlcd`~5^sQrSrq9ocFW*d* zG!Zbu10_wYggEE-4sI;;t5u2Zj7|pb??$Zm9w*kD5c~Z9COY@D6CK1@S(*~o$&?Vi zN5-3(*fCrc8b|4}d1~i1d&ih~NsBXNB1X316MnLid96S<(XvU+R8k z%O1YQ{dymNZP`gXwo^(4r0xEl^gvd4SJJx=CcXPm(z_2Qz558?eKfJPyC4yhk+M0H zb|O`JIhISHiBylA+?{j;olK|U^E|qOuEyuhbQe+xcWQ^_a?H6%d;~fE7)J9G$nmEz zoS(sPeh$`r0jK3lOmV)#6z6N2DgKKw=zmlxz5}bir{$IbcKt}J;A2!<79DE&sNM=v zgB74g%cdi&R65S;LML0@=`^bwoo=Pkc~%d)*y>4FSQ&J+)tj!d`q0f*fBKy@fbOye z)4kRpy3dNxgVq6(2FpWUPp8k+wnvV9-Bicw8u=a)jpj{YzNeMsZU9VSnWZFx1xD~39 zmKtA8xhig1`?5oBji8`4l6qTNG|0-P5mpXOvvNH`?}X8M>q#Qc;-{V-p&!AM#3>{c z9*j7Eq&zpzE$Oy-jfyGu0Bd*WV72WF)&#(s2w0N0j#ONJ}h|UIsp14>@KVK zEZ0`92tVF#nzLuNJ+Uu*g%q}mC}I`UaI1vITBS4-`bVTKFO^+s1ZeThV^A z&FA7vl*Y$9$F_aKDhJytz_v=TZ64S*A8cFTf%Pj%qu+&fDwqbVLTX+1KDk64R_Ju@ z`!0G?@8oz&t(~z(sii!nQOhV~EvGbV1!Y((=|JmX8fhIOF{n6{vz!+~x`ql&WWaw` zfr$eVdXd5Q1Z4@h-Frey)l836J>6-kdInNu@5L5ugHRm-p*j*mbrgi^Xezai^-$kg zgoz-dK}}=EB7Y$6MM+ywxZgr$yYOJxB=zHI4=ci#?h78$#dxm_o4pq5gAFq>=MlqU zKb}+ASjTxb<P0u}%iiQvmc-06mRzt<$N%I)i3eXVN@NO?4EjoUEz{Rznov0t7IF z$SN=~I0VfUtbw!(8JekkMRWD9faXG=xd>=}12h){%_Tr{DbQR7G*@^8@28;Y;-cXh zG;4-5XzZuNXKEen*iT@|bZCS4Iu zjz;>G-pSd1aY!z{S*^gd8<^U_oyTdE^#nNdB&KaoQHAw1Ewpvd6Yh1iDG~N;{<|w=sSp8FYmm!ia*NNpI7RMX@DR?;s+)2Up;=ME} z-mii88{qvGc)tVQ?}7IR;Qf&*AaBcj2CepqUkq>hUhyU$*E$66Iw{fDB*W{oDeMbU z#22EWz7!heOQi|EE>z;{3cTHbHw}2hzZl*gF5ZhUQn2%@rYe}V?rW@g$r-+RD)Vwh z_?5(*92sf#^`W3IgL?b=(jZ?yAnH$(d;@5jFH<5=es4zyk=ON=i{iQSC$|*pZ2b5ER zG9M_X0c8O#_s#G~o|6|AL29_;X*rfoBWIl-H~PaVNIGQr zYG8B@h0!^T2KZ{h*Nv3ztD~vDdZ64yb9{%>LB2*0LMQ9HHx}BJAWubFn;IfZkW{FK z$T@ou%7W>V~cRFC60hnh}f8SX&#CJAj`Ocv{-?>oG^Qg>s zKF#-CKnwA?#CM4Y?outXy|IR(7@CK?AjQyPuLDc32TN}NOK${AZvxKk9*m1zjOwTdoGph8J4a~K zT%k>_mIGXj$qZ3)$9+G+?#pxc{T_zsb{L{NV2JL7A-W5O=x(6B2Zrcg7^3@Nh}6VO znckmghz7cdJ3m#yT2;c%#7IM_^Aprdafc^?Ve*>Vu1JFMJ=$`=e5di%QfIb1B zPida--vISF)btBl;ZvK~*}+}tSzlP`!kn*Q%JqeGj>j)ZTwlO4!eB>Yv2Dp>dr0r= zL@c!tN~Y(gRRhc}dT)7m0|`a%SIVRmTwhOVIcbp=`e3O{JNXn7tq-v!PINaAW&2B> z4_EXgU-@$c*YTIUK=`kDHVj?Y3|l5C2eUJRaGjlX&c9)KGb4aM;lJ~hpRfMGS1|d* z!~f(fP<(j!Blq6F_)3=hn6Ko!pWq59K6#;C^wM(hqYvkDyquSk5z0u>>!vC>My5LX zz9!rE4XFPu4C8l{>H8kW{|B1k`;q2gA!L!?pjCcBQNKkS{XS~+`{`+ah~D(4(0l$a zQoK&W9^V5IM%U>r*dnKn7qaP1nNEg-!*Eap4-zJ2(j#W3T;!WU*O^1G=4#O#y2LyX zS3aEDbGA7YSAMK4on#KfRe+*&oH-mSb2u^n*qw zU84SM`7ZigW;1@VgZ|A~7%lW4-URtL}2WDjRQR#3i~EeQ*pnmvL}HFJ=Wgp=y%B$=ISQh%)6GNS}?D3+*^8>@09<|s24 zIYh1KS0QIpRLJZ{UeQv^x&d=EFLBXEy~f44ksY4O)vj-lBJZ7Wj&a#=hGxT69oe8& zQYZX-Il&xBL4wl%f6mA(7k@T5lLO9-0cWuH-#?Cq_{USWe*!o&k@EeMXqJBpE%#5Q zHU4~x`3vYo|8zPRpBMP2(UpF+DT_I@#>1hjTn=6Ca_ADxp+U%PQ5>4(a%g!+4z2mc zI5gJb5dBTNY8zxy$5(O@NM8OQE|Jus7Ki?;zIYN_RRRP|X=M%!)GE2n4YpG6ZO1A_ zJj@|2l@V&8Nc{kKWc9|H8Nfqo6p zM}dAV&{qL{HPFX^ejU)S2l@>_uM!ZIOZRioPjJzH3nRfZ%WVm~pP6t@gW~@_gkk^5 z@qIgT$OGQ|%^=5S>gV4=!~I)nf`1!GaRi2}BWa%hC=6Ri(=z`twAz0x9qvDlw))k% zV2UK$T!E<90#V=!#2i;34o@l&6J3%x6E%2D$g*?P0h!960B?TzSsL&|JJ2%6$$u?C zUk4euo+5s1TJqmWWBfPKME}i{=ig3+{#$6F|5jSzztiL4I{r`y+ccE)M_jPkF4%=h z!A^3)ehJ-&J3t%4Kf*K8c-3Kk+46f@Tz z{+$%|-w#8(3v7Q7Y=4NR`X2`JM=%UNiec~%VEJRvfF~sdRLZ-f0fJ)YWDJ5h78QFb zm=-2Z@tdI&|0f_pmkld#(htg^FCl5xoZ`}glXzJdHo1i0ExvKaXQks*JnF9Q!5scv zVMsSHe^1DGQ#5%V((nSL;YAwg|08AjU&5gCGR^S6LZ$v!p$f0TBD_wE{cnICZ&JPg zJ&#m4o18_m0W|4R2r5i{}<^6d-4>)q46O=)k#y~F$1$tB8Kp#3FfE~Pnz5w2jrUd%aw7>u= z2@Iqq0qh70D4$pXc6J1XXt7p;16(D@b(LUAQYDz?0{%*y#+tY}kXJkqstr)t=>{eR zm{GXc(OFoN1P9vde{r`&=t zW>InjRNPIJkzD00I$l2JWj*)mIi?$dcG4kdP=R8Qs03%q3bi?+{G-awQpyFOn%-i?mA6KLs!NheynY|M@7#LSkufQQRFtD0318X1# zQOXOf1@(B276s)|2Z7@#C2%~211CUiP6SC$mYqj!lWv56akz347`Pg6uK`@_01I44g9F$Y7Px`N25zKDftvvLW*EHf z(B@m{;K1!3$g5oKb#{Lab|L4ukQXI|T9tMzS_V}u=Rf6e1OUVyKvOpA3XS+1-2BLrCHh!=Y}YUZWs<2Q`V_0DZZI5 zzB{z0^MP}6D+4e}n8$hcAxce`n$sy`)gw{ew`tRZMylO!^a$ zyagmULN@RYjSRd?qXK`X34!-8w7ieKdw=&Z$;oTO(My_1JzODlk|_nJP$tp4*)F;_ zG`d6Ldgp?}WNZrrvzOj1%O-EX?M&}>Se2dm7>f4^r3XI6(DE5DehveT{Rn}7V<`O( z6$ieAjrj`0=XV~whqy|&NaIbXxmqYkxk|SFCn3U@yx4!jcroI#{Os9q8!^tV{B~Xumefu#HF2xvX2x zc%aymZe+-<3j0eo){vP#-0jH z`PAQ@MuY7F8evZdGiJ~fyO5^YMaViYrX_X>t+Lggab@ltrRndUS-B)Bwh9;9qZ*sc zVM;d!XvV}n>UNsZDPVHd2!b{+kyi@WeBe3=f`*gP?1d1tMc~t7nrJVfJbNh>+RJEx zy_}ZYt35n(a+xx4#HDhYoV=w(zq1m!l;^fpdv5($$qmTZ(sW~xT2m~C*ESgYy721? zkjQS>DFEV0?@jJDuwxXo*HJh7Pzu`{DBZ51zILtb40~|ta=mb#e7B#nQF3u3K<0vg zIX?h*?jV=CM}WHQKMs(+kc$v|>PL6@gNglUq!hb}y4zb}S~gQpdkci&2)7?SJ^dJv z*pEfr4`-daIoO)pQE1}1iCafdD~a=U~Bgr#l3_@hi(SRDe3Kv>Uo;{e$+i`UkmQGF^z z_QP@`B7V~t_NTwwK{=7J&!MjNdEnXk5U}5XUKgT+7sDi9LRq%*VwIv~>(QmFKAz9O z(jO-^;{004O}XaV-@E2JpB#!BIDgn&>~?>lrYII8)b@9oyB~fGQZ^)E-0#XL?Q5uu zeJx0Ly~|0$ilkcg?1C&UDc(*~l6gzqrubAm7N?wqrqIxVdO~umyr$ADEU2q%!}I$q zGUwC21s%8*B6S-`^jnNrzXN%GPucbzo(|=>9f~CB&{DTUufXTy9Sk@X&vh2c*`aXV z3;t~jF!lm9?;En2M&qaHU|Z#)D7elP zB^7&+m4O_fBQPfM3EZ4WUk0jIpaZYcAp12MZodwP2`MP{oA5Q>l1x-{P47>YC{(>@ zl=KEU`rco%Q_y6r3nwOusb%^V4i;kwu<#1R7B7}`V=Vl6`71@(b;idrM)IsI&XVw; zz><8rF;1c2%khcq;#(6s-kO+rE50VLM!@$0{{u?1KcqhPUog7=72e9Orx!eICpnXSy+IPxMPS zCQGZ`U#2t|dFjSfP{ez@0gt^0;I#L#1~$6Nu~*KD?0>_4{|5uie=+9%502b7aD2X{ zT>D3m=X)A&|3GtthD(<5u$(`dI9L?Cb%HxxsX!AbjKpU?meHiXIG|4v=l~qRE(s*9 z{7_Cv7^`zW;$-11#H+u<+{|1p&p?sZI8K>0E@dv!a`lWu8R^T)4JGMD{+_7ABbhp_ zN=nzXxO5dH$_-1`^p3Y?bi7rVc+2EBi*OGkrMzZ=bgqj%*Cpz z!Cv%8us1y&%y1b-^o%RVk7zdaqWd(P2GZ{}n}*SiT8<~vg&sK`N|1yFdza&mq8xP? zM(64n#WXL&zz-d-C`~tJs>q}>6LfjS25(k-YbO20;2@AFLXqHL8XnA~u|X^Z1P_#u zm9H_)h3rf?#wI&#)?Dj!(+EN1Sf;TopfyhV6e=Pyo7V;st**`$N;k@oBqY217&fb` zTrR1I^3<0WBM)A0cPj%n&X|K=00=+ES97J4Vj#V0CeAc-)KOZhU>0=^W>ZEmhcbh? z;QVNs8JtLE!Ld-oakMZvo(>64pbf#v5-uCn}L5PpzGtrXR{jt0j`W&BVDnZe3(C&=t> zGj_-A%AD@>6=!92E~zBlsAw10kgF%b0tyAEQ}^Hukf)Hcf<-hbSWE@M5-3I~%?{3_ z^586xX*R`zWwbFk&m|KfSxWhQA*$B#jVvq#sG!><_t;*{MLc7h6GyFf=wQqNp>gUs z$1|l(36Ff}Pkk66;YL|v*X&UyV<8iPs z3s{6U`s`Zq^)--jGsbM3m9{D83)OD z7-n(C0)6VSu`u0Oq^3?dEyP&Dr-iukhZW1?TDZj0#1c5x6?2#VI3as64Wj}~X%}mg zE5p>mO;EZ9nA=9`AKXHNg3X}TW+>k_nA{^EUB^&i@F*$@9t}l2PEvz?z=)iDZ9v^c z?iJzZ0hE+=MXc~Pvj&r6c6s~oxHm}6Zp=ej(GCkN=RXPBR_oH>A=J3m)t6L87tua87uiPUE^SQyQ|>a_u*q2UqWJ|3 zxxrT;?ypj5@O8;N<=;660fdmt=b#(!qC*T`qRVyBrZw~v+S#-T|L!Vdl)`uJZ6%l{xOUqcPPp;5ta zX+rROSHsbg+sEPPV-xz=kNV+Aa<+G)bZ-+595*++y=5I{ftb6O-s+)4^<5st!NWv5 z0C^ZK4sACaN*M{ktc3*i3t2QGN?GWX~|^du(WRNgoNWqY?$Qv-!Fp`wTYaNv(r!-V05RR zp)d^$rBi082aOE%qzR#3lppHj;fCYab>&PrDKR5)*sJ;tqzOs6vCZxH8R$3*_xNNT z-E{Sx92B5zK(O>*nMJR&);^#?2nIQcTk93cBC5H_qOLuls={~~k;qTBt zmTt`d*>hljf?ce7_z95ZgdY^atj9gC9@by_1#*G|3U5lJuo9GPN(tpqI5dj-hDOuS z&=?5II2s#59$9Dt6^E2N$)g(D*F8_6Df%ZhhPpqgk)M>xN4gk32L?9r)k-HBLL3cy z^o%C)L%K!Il9)VEmo*m%IiSH24h|5+5$0i{b$GJOn$Qdig$luyBI+M1rUODHkfKt` z4b7s-p)x8B&7rxWa+)8ipp~IYstO$>xuph!YK=KWD_p;3o<3V{GLjeAOT>>3z=L47 z+JZNu)Fg4JJIbZQhnfy5-Bhl&d+89yNoL*m3Oq@gk)FY(8x8H?OD86@9Pn2F{z}+` zgDDa^1U6v}O$xe=6Xg z2Kc9g|7U{#XM_Le0RFjve?Its0r-C*`0xF7;$MdU$GXt}4)tIYfRsY-R9gL{6I2;d za_Y6qXoQKIx5H?HKijw*7NL2EvDp*9X)(6!AvUO~pQy=Eq}HC%6Q7M-u=c`HlpTdqz8=-_{xQ>BbT5G+)LHLU#cAoq&E9WrXgb zA)$L|G=5}$GESJDjzOd}v=c_}eh9rfOjN;M?ha3Lb);YjP0+9><5xjc@NPPOy-&fO zpCIsY*e5!$&2fN@_!YgSL%ck89ob?Wg=v~Rw8l8P5|fodIFq@>Ms!I*G5+`08Z7>YkC>Vw{rEC8H~j z)ye3Z(GBybjI@mI_&5oN#{F0x4=W#v2q%ShlO1ZKv=B}(4Lt#sdlDng(->8sfeZF5 zWbQePIM36P&(4C<->4DID z^myofdM@;LdIk0UDfEdPrCLDRv2?w865KUEoU{wflOc@(1b0@Mfg#gn~+t1CFJ~6Ty@2%mZQwmaMcYz-ZI2I9am}e z0DkS`3|w_b)Od&7Ey%v?TUu;vdRdByfs)Ti5#gBcAIvjN{Jaci#=Fe3v02UYTU3aL z^XZBz2yUKZU?KP$YLOEWN0@B$TnLj|Odg9`_`h(vaq3RvGywrQeW!5-c0w}`4C7k- zm8*MLYr)S51K|uZ>^AG|xBA!RJU!LsuAQ8fRnL7Q^Sc_&f-o=TV5h#^?F?d=j4* HQ0V^wTpD2g literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/tests/SubclassTest$AttributeSubclass.class b/build/classes/nu/xom/tests/SubclassTest$AttributeSubclass.class new file mode 100644 index 0000000000000000000000000000000000000000..fe10e4770a687eeac2b604222f7d4ca323b03eb6 GIT binary patch literal 916 zcmaJ<+iuf95Iq~)N$Q%$DdAGerO-f=lwuxuE2>nA3Wz8Yq>9AjCfx=j$3~7_!C!#{ zR04?);G+;{U8f*Sc<}7Z?3puX#{1*v*Y5xxptAr2S8Y^rt%4gigEeh#YI94Q^$MB- z<>a+LZQd7H?S`rME}D3WnkG}PpAJWXoK6oIZ4>s;5B+3E;8E*8xb7QyEWJR6W3QjY zemLGPX4(S*b1ynn0=2Fms$M!7s`x+-17?=H(MSdZ8T&fUlcts^P~T0+IZP8Z(>0_jNOmY zc%=4yJx_fmRj=`$?&4qpwu437uHcTq=G>(h1v}V4%RyV4MK}&F;j)06Gn}as*k~2k zUd-qpu`;(kiN`8=k;=e7^3`E4Ta;eHKUP&Ga-g?`HNLAVpPz7DJtsYbAH_o89KDx} zO`c9?^D{)}?$!xP9~qahM7xxs_hI1)Yo4OUoMt2*F3$7RUIQ11*WN{X2AR2soPUS4 zf_IH?FkW>&vBejd{Hzm{KQMpVr<`G)XZ%_j@u&RmF1c26E{~_avshpGyZG@R#a1q^ TgezH#RV?$gS#01s>L~pJDlX2h literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/tests/SubclassTest$CommentSubclass.class b/build/classes/nu/xom/tests/SubclassTest$CommentSubclass.class new file mode 100644 index 0000000000000000000000000000000000000000..4cdbd4e8e4281ca863df9aea720fb48df644fdeb GIT binary patch literal 819 zcmaJ<+iDY06kTVM$+Xk4nbuyct-WZJBo(I*KD9+C0i_W7k`n0SBt68D$&AdU=&PvU z-?X3>6#RgGROvc16Dq0kVb0lm?Y-7ndox$pzy1JtiD%O&;h_lws|FsKOxD%0p^i-h zTLP8jtrypJ1XkNY>U;=?P9ozZb~@~Uq~4j_p$w9d4*}~i z2xQb!rX(A-C;O<4ap_+_bNw`X@{UCs?1%3^(oVfG(kui>tlol09D|723Dc-AUwdkV zxsgA$rx!}w!W5=0n0Rd9i9loGR>xghsNtz1&RAH)lE7?1EzAfk*8jcWm5FZ%Obukx z&1YZWD0imECVvHkKZ~$!<(yH8A6YIhRig#e?T;!UqZVetl=Kd*{t*#I=+zd zl{v7)8c45Vp0|qbV}Tg`vzCkQqRa@ef1`BL{7&7UC|^Q9CzdN$vii47(aL*sMf-p{ k%Y}}^Tj}WJSH{Vo{vzvz9Ssk%Csr`W+a$S!HQ3N@04NW$vH$=8 literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/tests/SubclassTest$DocTypeSubclass.class b/build/classes/nu/xom/tests/SubclassTest$DocTypeSubclass.class new file mode 100644 index 0000000000000000000000000000000000000000..e22f0bfd00694d1fd59b6e7b4e00c0ecf0e88ac1 GIT binary patch literal 828 zcmaJ<+iuf96r6ROn7XEMOIs-AQc6Hcit0Y_L=h4y7a>J?AWEdXcEq58$H^vyLNBN_g<@Idjg;&N=eWUq3DYJjTNrlyK9Ag;fi;Z6i_9$eegf}UgWF3 z*U6Gl^|u?l0_INqUJ6uOp_1+4peK{A-;0PWwBo>zcKsyOc>$W*5W8Q*LHA%^j(rF? zZOD{ongtnfeBY>dWkH z9A_^hIgl!A=UZ6hP?z?AHU1M8{~F=CdIm-%e)I$Gp3!Rq{EgBme(1ksw#4B zy|xpIAPk&ZRByOE2#zV*BH6Z7GTdQUFaD#eUhr$~xLh@zTIfsF+$x;Ntzmn)Vr=|3l(-a7_0-OgkRB6f&?qwIhKF)62GZ< zJ3jFjAww=`ao6>3D&FNCK^03K=fsBEvo$aB8)8pt>_Uf0&t^>3J~Q z*6_45=pzu&Kxns5af<%A^2!rfdG!UxLoBjr9f(UFdT>b%SLmnQXl#(9T7e>J#2|(! zXJQy5iRmUqjB559Qm5rd@_mN+f}VTI8PLw@xE{ui*0(lsH0T7a8iP%LjhVtE@-V&t D7+SC7 literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/tests/SubclassTest$ProcessingInstructionSubclass.class b/build/classes/nu/xom/tests/SubclassTest$ProcessingInstructionSubclass.class new file mode 100644 index 0000000000000000000000000000000000000000..e9d84f6c8789a3f6cf3ab2dd99bc288261b086d3 GIT binary patch literal 1093 zcmbtSU279T6g`vgY}Y2O@uRlYsL>{=x;_XZk%AV4LMYONfRB^y5LdRlvO6pKdqf4F z6x4!(KfoU)o=LZ~n2!g2m^=60Irq%Dcjnjc?>_9QPS@2z%%Sp5798+?<2!zL2k^=k1tGzHd)+u1`1c8dSjc7&qgV{P8bD2!HT1F$Kjh=~)sD58+$;}O4z7gm&TmNxW z{iJ%TD2l0^Kz2DcW~^SAM6P<`nUO6WPr$s?9V6F98J3L&+$>^4;Qq{aoj(T~O|)#Z zjah(g;{q-Utfg#I)e3AhCnq+UF@IoXrYwOnhu=@1pvLy6JVJ?(f%FrFI{$KsUtUzJ zrg3^XE-Hq=8G3s>HyOLV`4ysl>-HfspBZPcNV~{W;2rXK&zcXYFfxq9ql&ZKjaS1t z;*EEnUY5)pM#{g%TEVm04`iRUzwnB0$Z_QlQTW9C(LIGR=Hr;(C?o!ezxs$=D=C-5 l-JIoFU;MlH?WyAYsp3ojSWIQBV|{B_=5CQDiz`?{<_~;@0yY2u literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/tests/SubclassTest$TextSubclass.class b/build/classes/nu/xom/tests/SubclassTest$TextSubclass.class new file mode 100644 index 0000000000000000000000000000000000000000..c9c525b353b7189447c4e550f2c5d52291655ac0 GIT binary patch literal 804 zcmaKqYfIZu9LArMHjSp4#_Q(X)VV6Pit&Z7<`6{K7!=>M6hwuk_0UCPBDsiuD+Mp$ zU?0E_HTImOg>Ft?04M8-+%G}3m*cjLIpY>US}dx4j13v5(hI9ES$&t1oNgRav^A}{D} zt+WJ+yWz1E7_EDO+)Gc}GHSYQpOx`?*m3=q8+j`46^jZ;z-r2iqz}RLTM)=-R}o2e zUx26#3Q4Y?yMCIz>(4XO32WHg~jl=<~Yg<8W6#I`yT0 a#rp$dy@#XWP4^ArprYfhYy66_H8^Em%}+i=v3qQt?s=DE2xeBa96*G?`fK^+kGn z1zUku+O9?0KU&*o>gu;AlgUg1y0X@sIs5Fh_ita$nLPRL@oxZ5qNw30rd7O=#E^y< zruU!^Z>pG4@s-#5D~qxGu_X%j1T|#W{J)s+iZ1l07jj$m3=b zw^S@@=)z?cx085BLyzoD;9Ys#l*fB2-q+A88Xu_mP{E<=MJH#LN|wDmW0%}wIpb;P$ZuAnLJs(2kv24qoP;ekUd)K&Uj1&xe zd1Q{Z?d9>o1qJabCvOsZ*0Rm3<<%v#m^GFPlyuBGIis*(6fK$i!nnI)F~oyaPp(;V zOC!~|vYgRp+%{G{0}T!pG?z?wevQOwao#2SgGjv$&e&7V+5;NNc|%GxBsG!6PLcZI zH!k?ujx6-cjveb5Q5$%c8dF>0>lV$Zr|4P~FS^E=bcpXdSkva5axrI4Tay3wa8`#U zl{!XnTtREJ{5pp45)0@{Y0Rs=f-Y!-9jmZuGq++D@=pz-;ZU94-OY}buSJk> zjpDND>Ts|o%pX71v0uH~_U>IBv&AE8x2|2uO5YeIR`_`ODaUB%)0zslR1d!#qYHtw zvDV{_h8iVeL^tyJpt#JMT;;XoIZV=Fuv&BGt#i9&iyvR1J-U5Hyrybit7l^zNBE66 z$`4MAKOP_Njh)Z21&SOqK40N%!qWf}nW0T64?Vq6KILNqjG`GQ_yannG4alDGxd|UJUd?{Qzn%P2Zv7>1d@pUwFl33h=x|k73aua*jq17CC{B%w*Q|B;<^K>>Diu9E4e2_5X%%8&FnE>Mx?iY*`6^s-y2(f2&r6XFX(d`XC}o&j+#fT-(=9zqgs9i{M#5~kRxC_keEkNID5 z3u*j9f?q=vCw+=^h~h#(vBVQ1XQZzdx!;XppV4mY;C%vYqZPmN#`%L)_%r0M&-d3E z@;4RutBbmqQS-hz5{b$OS|c^a={-ASm$>0Z9}kPyvllkr)%NGZz>gX3Wf;NZO`J zAL(|}q|J7_+jrB(X5FTb)TAnH8k6*$^s(*sxlP-2yW2ELo2KtJX`=nkxp(e>VBP)k zV_>P=? zSHt%-d|$&4H2jx_A8L3ah#!Tp2|xA&KLCd(ZszbKIeYS)-1vzco|MB+HT<`RpK16X z4gag*brS#Qa(GI^FM@bFh-X6BAuX~4&q{8;l(VPg@GCj|T*Gr3el1TB>Uj;n(ePUh zztix04S&$^M-6|{@MjHw(eQ$X7eff*uNqzo;^iRD3KVw4lX1IKprWzwka>gIk}#8F zErWJCo*Zjmxkn(-m5NycC4KRvHISV+Xr+hDg9)zaeW|FK*kh*S^4(nw*yHhxKxJPt z+cKG&XtAw~ooN}eChZ~4+6k95C)D9t%*aDhccqdUJDrW%skDHP@g03O(d)_Gto6}rp+jcx0$xNeKKktvE!*EaSGJ7 zK-s?8`V-V9=Rw?{Cq@&Wa1d5~xL#e&}eVkin=Eg2FW0AKZ*LP);Q9El& zE*|epZ_*_X#3+WV#P;j6W`f6jQL5>Sw#wmB0nM?-ksAdT<)-n>K+4|IwQbALmO(2j zu?6PK&B1Isof@|vcS#d+i?sek>L_PjyqNL$*f?2d z4hk%sWs0rY(NQbiPQMliONQH%v7MuYa$ox_b96vCO3+GJ6IPP=rMWBE?yT_i)5Mmi zhf0M*iZp+T5scQY-Cy+>^3aRXTLSCIKX^yok$xIf! zA7z?=tsef#iG)C%1nZ%7JR?i72U2~hquvUwKA6g;qgHoZ#zwJ(8RD^K>E>Zr7!{c3 z#)QWrV~25$VhJI9G}rkNLEr4L5{VQ~j?1TEk6USiR~kmLtvwfu9Y0+$#W#&W_k8XCN{I_F$0m1XD}W-^wescopuQ!V6x4dt9tqBdjye&&@bqhr}VwYYEAUeKsFCyumJ z@+yN9E1jVz8Kd5)ujjwa6zxV>_@(hcqsn_Q9=Sz@7tgF798YBvF(YlpGg1lT?}mC< z1SHOK_Yy8bTOhBcP!2(%g+&o#1ilWcqtz$u_WCiIfeMuH41M*KiZGs(&V~E;J0yZ8 zDXg!XT(gaP)tj4|mt9e}e8sh^8dqMoy0&58{#V}=X}P?;?Z9iUA*;;cLy72k#ylK5 zG%kH26c+PDSk8*WV!kK|i&Ak(Sm>fGEXqYiSS%1)P*jG+LQy5D7P&FrW;}Q7lK zjM6s4UB4#jPWI=|6kqI2I)A3rofhXAEt{KBdM;K)7h`4TSp_}X1#@zs1|4hGwqCk! z{f3Q~ZTkPu1lG^Q{Xal7H#d`Qk*s;ePT`v`A5LJ%Sy(iRl>)1_xW3|MELoC_Os=#U z@gzfk(l(N+&{WLXVlb`GSl_#|U*=(1nHjuemc7SJWH~KwTsf=AF-F5?CS#@TTuE-2 zqbZtwO!g>R#)A%wnsJtmAen7*syt`?>`GY4n8h3ptm6aO1j%OfT{{mRVp&&xSAp#` z7B!8zGAcc?ea4GoJZ@&jogR(d5__P6#@=4JQE0h+#he_jX5>4PbhG)OT!F@`<*7h4 zB|DB1n#Z-HV$0j361&W_s_Yf{U1v#_ep)Z&>ZTN%R?i%@9Q$`ANW^w)hW*td0cP0S zHO4f1Pcu)TQ7KtOv-eraF`KPRp?Ty8>yE(U#;)0y>vk{{!giYv-UW5)8jmMpT}&M8 zSfqcbcUEKK=PclAH_yTM2xe?c+vTrHotU*hFKtbvZm?W{YVTqd_4(X_H%Ld_$&ytu zmpbiO#*cv*Kc9dyCR=tG9LM<9$xi_vpM|R;ry)*o=EFEg`H29mLIEO(bGE>t!XX^y z8|jh21Th4XNV!*R{csfL=5>QlN(hM+)_MgCjvbJUF81#e*ZIh5CGs zLA`{drk8Ro(l6l{(shpW^s>P|C1p9K?Du!pM9$oeMNN^K)>Ek9*yKq4M!z_I?x`lh zMSX$0)}&X;*M(E4I*#%Iy>j&_ET)EhR8OJi1jW7?x8V+cMf#N~J5fThH=qaGv76)7 z=!J<_(2zY;vL82KfG4iPt=zp0!#p{Jx8jv}C-&e27*PgV$BP(34KlEa`c{-8%X89@ zcezIE;mMKUevG` zGujdw!Zn({RGP`4)#PH6zD&N>darfzb-DMtLcZ3!uhLnj+wNK{r?WOw(+#xRX1d;$ zocB|MK|0Dc)b$|J+{tieQ|l~lp>_U|){w5diu$ebYzNxdapL6+f}54anu(FOSP6V* zu_kXZ8tayvv2Yy)lV-2b8th^ zQ5Z$81Ol)3m>navbotBk0p-p5)vS^12O^OvtYn<6lK)ePNO78`usTAQl+S$Z)j}iO zn;2jJK#|^zB{W7I-a^yfPSf7OZ16VEWMu1#8NzzJfu?00WHxxCa;+uID7SLv=jxk0 zfp6uzif&KLKFZYGFOvX;l8GQfiB{$$qDbRU_ookDA#xpQeBBJ;b+{1_JEo@orL zJccD6!;r`D%{hj?QZkg6_67aYnI0mwS3<1gr44%t#LXM}Iya9flJzr4^!4tZORGpW z%pl33Y?zL+aZZwr(@8FyL84#g?zwO$n0CtPA@#h7jp%0LTEz4fX83P8033*n)?M`8lFK%yjq&gZWXS ze~ESK%jEGja(Rrw@i;^JtK{}gUh22V^Lym_ZQP0P&^^9O?mxu6c!Dzgl$?G{PCp^? zllVM-rtWSxJ-)$9m#^?<6W;7jZ}xyUmOr?> z$J2XeAGllUtq)T4A>L^|+p0cLG*Z}=C==7W%;|lg^gY>SzV`y?uTSakfIgzouf72K6-3`c^sbAbf51ae(G6r! z)hU15r_ir)(IfhP*Hrk`K2<{I*Em5y`vqdPf%e@QArb^gURT<*pC+MD;xtx(~}LQa-E*jvFcr755c* zj4cH{@mwjomCr^Obu+V)u^wICN*U``CZC&7(3sLoMMFVnd`;ibqo$rqN8RUY)+iSB zG&O=QjRmWa$)#!DbS{(2SZ~sPVDItZq5|(!euc(u@l4K`EoGODLQ-EgZFl*UZZ7JD zjJ!KSueF*fD(J5uwv3`x+>1L&F@t+^dRBak)>J-Mva=!ww`e*n48+HFC6z!6R4Qy)I5?`Zu zq~S5%S8#CNkoL;3Zyy^gy>_ei<~NK&Z;s4q_&}07Tmv67O(U(FWZN>T)mG3oX`1;> zIw+FW5%Ho+_bcezV_Inkm$fS7GkK~zG>TxKuB>rgR$7`%NHL~#nX#cdGvb<>>BXW^ur5C==_VU8^8b+?GYRXXbEj*=yIj}M zz2I(cdCf>!6D|~vMBE*(6F3%g>DJtedJCi1ZVG#lW4xmVVlg>w6Kt+YQ!rS^z00|i zSJ~QMrbK4$h3m?Nw}~Nc&NF7Xj_0zUW@$->qFw2^QgLCZNqJ%2*E1%L@XC(xW75DY zoa4uxN5;&4aVgBRlXa+o z8n1%-$^mt$0xH;P!`eod8&a8wa2+_oPM&p>uNtmHP>Jg@Ixvkxn88uRSnsPO!ao zz?vn|hnu8ok=0vrK^d#o?kJ%Q+O<1Zh4QD}(7+r+HYX9J|ePeS8*2Y%dTx8I}IGP^iMd79b=wb#h- z1KQ+imBlQ-NYaQJffJ^a%z0blEvf8@MOk=yp_`VJ$wS&BwI* zh*qCq3ZK&U3tE4UIKH$~>$006;iTrLPPtB!WH-G3(%7ToqRJjqB-ezK@TF?P20cha zbxA$S^r{-r@x}j>iex^H2-wNw4x9XMu0Km43yZ`_I literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/tests/VerifierTest.class b/build/classes/nu/xom/tests/VerifierTest.class new file mode 100644 index 0000000000000000000000000000000000000000..6edeba6629c7136a1789ba6da8061b847c48599f GIT binary patch literal 22293 zcmcg!34D~*wLka!CYhVbBuqXCkPtBJOZEhij6f0s!C(kT5_W_k8IsXtg2@Deb=S7m zF3+WEZEb7SN?Wy6uz;f0rD|Q?!@5+TTDR8P#ii}5R2AM8}SK#3pfuhi(^}-C~P}Qp83k0T2C2Y;}uP52cAV zDT4gF%_G`HhZG^cIg?AA3o&WS6;sI@>K?3W$;y|4_BGTj!F?>3HqeiA;nI&=$B%b6z6zoq4*rbajq2S@$dPP zj!AI=UtY-MA}KDG;u0w?mE!YKd_jsYN^zMKUy|Z-DZcC$U-3|r=uJc0UzOr(Qv4TR zULnPmQd}j))lz(2ifg2}R*LJS_=XhMOYu!9z9q#CQhZyA??~}oDQ@IS-;?4dDQ=eH z7QXww6t_yTTZ$h@@k1$oB*kq~+%Cl(Qv6topGa}16n9CnM~b~t+%3gVrPwFMez!Q_ zp$l2X7m9nB+{@%lDGoB9T~gd9#r;w|AjQw5cur7_{>$5Tkck zqaR-n@)F-3eS`J*DXS1fKZdt1zH;$Z3SXu2m7A|5U#0PthenuS)T2DPEJ}b=K7zQoJd}TT=W+ir-4{wiLgU;vFe|FU23E_@fkmlHy${-jm{e zDL#9P!Z0VxwDp;-OdB5ndI&Qv5>- z7}cj~;+O_wT&M}DX;Rar=3-W!kXnk=QX%P!54ttCTa$v)di%Dv1v^7sy@Iq2wSqi- z;htb`uxCfGO;BoIcxR|Lhzq++dc%^M#hX{u)io_?79{HNYJMmjiYyeAJ)_~wz>Yvk zXCT~O(iG_lh1*Y=xmu8`wz~~YjA#gjgN=P%TZ28#fvuf*Vl;HO20B*66}rimaGo;gtmoH6L+Ve$#9@cH87w{j$EZGuR&JWzyK8?y$s>YR6yF0tX z$=^>+b0awzaIPk1Ctp zLp+T@Drgcny;!-(;LR!BwJ2v9PVY(=Lltj<2iAwTbz2AyRrJ*E-iV;wXIjI5Ghq)!o@>FxcRe_yg9x=rVj<1uL(s7N(TYr5x3dcl?$ELomrcNCF zM$0H`!t;S;OgYP~Hd>rWx3VwX8tDtzMjEyDXrs!)(C9EK#eklaix?L|&>2)9vza-= zXEv_g$yU8&N{UJ~i%SjdH_^f>EGX2I+jz93iovm-Ww}vZPoSOSa{c1k?r25@y6r;TuU9GoQz~8{8{nGOIBO1>-jF%!T0>+u7cfWEm2+#_+K`m=@>rf0h+dD} zFaU92z04BQZauDgW@~qNN3bW-+}+Y#$0Kz1jQW`aTS1D^5o$+F)G>Nsyzbp9D3dvg z8pptVVR0Z5h#5k}CxT2bj361>+80qHcXLM|vLxKryCxLrsIhF?47`b@)Q}%nP6fZ1 z)FxS5s>g&f16z{wTRW|^&C}G~*V7uT3vo)86`N=kb9UvWztM+oEzPTWv~;iL)iQvD zm77`Jc(qJzgje%vSwKE9BU{uL>TC=4a5JA`h_M_t-%@sd7&%scZzRwY$#1oX?R>9h zXd~U)D6f_+8j-Z+hr;<*vZN|_wb9(+82S*2qQ#4)?YNxHtBvJyIa;n)8>gis5esnB zc^EDe`H1a&yfy;*XKj1Ma%NymMxxhYB&_je(|^I1&P z*g6y>@xs3~z{&HJnCd$#io1K-OV%!LC@HI~oL9o~_ihig1_6*Z!K@I2RZtVp!L)WO{8dYYEDy~s=yc&M4;WumexrYCE zHLAHrwOON@Yg7xGX6wB#98}SeSJi~(9UcQ$9muuh)J2+FgR96PQ#?(U_( zaJz6%yo0@GJPtKj42=}-NL^Qc_DKG zwN)otW0kF}4E7f;iC<13rCk|fC04>)F5RJ$$kB;!C#xpmWP_-mwU5b8iTSHqGB4d$ z1-!I8#byF?D8(Q5sGJ!Sq3Tvu$V=}r4zl06#|+VGrqwK9bywUG>FV^-2VB_QZ@v;V z+AeEXw~af^c>zDMOXs#)otUMRoRha9Jz;H)wKbu%6{m>Fc4L!=YM|V1U>K$I1HJi} zdk8AE3lHA1a+?y{Z;(?YRYt<(KxuSgkG+ZHwb8;zSD9#&2wlHI-!RPfjxp z`()^N;ON2$76+W~`({^*Sd> zca!3~Zgw6jtoK2 z$QzvC*RrZXnGYrhh`O*KuQr`WIA5ab0dC!r#kKxLHI?)HC(oTT+h0*$R_agk48wg{ z>AX_E`TsnaY^lGzw6xYgyX@pL7;Xh@$X_17(=z-!xwN9p@8{>Ll0UwD4Pr6gR+bl6 z7SAc3gZYTxUx)wvW#yH{WwU1&&nYjimJJm3Xxotdki@!1gaqP)a_ug2k<5MUy?7T`{M85Y;BOtQt;g1PLXVs*=OuHlJ;1 zc!CuSzbKbPs2L6e73KNNz*x&*VK<59nx)J&-c-{ZlSCj*W{|~mFx5$7sx`2K?12@E z%AtcK&kJIom(Q7NTfBO1H_x#mu-(d$kvk0Vy&~KBVD5P_Z;MP>V_#3Oqq#j_s9(8b zZoV}I^kbqhQ=8@03baBm{gwWYV@Z)$D`wkFx3ab7U_X|}5@rdUK0B!!#vqRW@Dw|R zh>vz%D4<3lw+6#6*DAQ1*%&)*-U4>kuf~!52xfTsK}_-TcVdp0A3!+6_=86L+zqnU zq3Uhrb>7691$K?tNzkDEID8E(owW5F3n?Dj;#b;XhjH)Lu2|lTLbYmEGlAzZ0Zd&w zf+hVy%piM9`XZrDp4#vf9%){vx3RVnB4fF{2Rd+JroA2+o5FI%HW=89+N4u&Z+kGJ zwg$K|%yvfdQUNRo^hB1N-52P@y1?`#yAK2B^}G*|3gd)&pui+m26kmlV6pz66&-@= z!;xS+r{d|6ZY*k;E9XdEBi;6;Yg-_MnV|==EJq#^Xon;jCd*jBY3%F7%eWnr6M7(gQn5iq+vefQ8RBUCTGv~=_o*4?7 zH>_?4q6#=i^ajlpWuE@340r@OV4bT8v3fEtx%g^T4TDOO&Ifj7Ey5{XwLtYfrv0*J z7QvELKkiVtE!b}wWfF{#oqJ5(HTSU8t{Lmq&K4{h@WBQeu*YDvo1KW=6f4^?f4ueK zFp6T!L@j5f)Gn@RMjRY70CH6gFs)1?s^TZoNwY@Ji;tb+X{?!uKue+Tubr^d!o;K6Hw1pm`06mRs-F`R;)|$&0n!D)wgiPI=64qignU=?uvE3RNqEWx9<#4 z>DvOD=DPsYlSMb;C*8;a^%}XL8OAu!Od}6;gfSk}XG{RiGEM?DjC|0M#zfFj2HN*! z8bc)|)TkCvSUXBaISO5Z>^`{gbq=oB@Dx*3lWk1#IqeVF>g+{GWy^j{d z{4UkS?qmDdFHK{c#z$0WhsdN0#8@05$ft|M47ym<(j}sqJ}&}z)(%r*pIu0)cvmST z48(;J6GR%WQbaB-7aqg{93BuImf|yMy+{{cXn8EH6j&FA1+X!UAnj9;>lT@AF+v=H z1q*sYMgT-HKJ&g^N}3-8lu$|G?K2Q>2wtp($#ReD`-Al3unBJ*1@69pzCQH?!$D0 zjSR|Wh%Dg2hj~hrBWNgVKdW+{K|Cw!c_^q{OekbVlItjCn6MPH!yKVvpkp}bQyJY#mAJ2=AJQ`V5w+57)Iqn? zcHH;VPaK>I9ZWK4wuMux^K7y6jA)mGQ}-Y^RhT$c0H=`-PQ$Pw<1`B=c+Bvu==e1f z(YYJw>;pRcVHpR2&^@rCeXyeYU`6-CG9G{x{R~!gAFSwM2c6HzibmP!KoO>oEr_wA zQg}>d)MJc>u$odpyeHyYp0a*_zxn{wslgUN2z|n9;jE23W zMLhwk&32d%fq6t38W&~WSiZafL+jE)D-1AeX38;|Pw8QF^U$y0k}r~hlVQ2^vSKOU zY{uz6%j_QIGL;i+=yvpw-8K&0?d$0FO*Howy8R8heS6s5jv2Ds6+?IXM|Arxx_uAb zzK?GIJnU}A4%zL>p}YMM-TocjevEDpquWo0-EEHDt#CsntgtOn-Dc5+3-YpD2r5X$ z%ye#mY1%JNwjgNgdRCJodxbWYw;k-$rd`z zL_97QsZ@_qtpTH5BgVNkc(+-wggC4UJ{xhMRAA(Dp{-&o?q|~JRy>0gj%lrqvFynl z(qO}6kR zJZlv^PQ15Yu2aaEFO4CQxs8!2M4sK0fXoyiGY!a0r$R9k=*&XGQ;f5#g+Qo?&cM4i zQRX1i;viEPgUnh7nV^HrhCz_28yuN@2bmlTnVuxbz{5;uS|2O?hM1e7VcLk2C^5jp zlmUH*VXDM*V@m=|a{<#lz*LD0@)Vq6o)2@VrY&M2Z535?7T%qWoYMBMtqx3;F)#%k zn0g$TItGEMX>gb(Ixr39Uu-~*e{G6E#yGvZ_nGWb`E1J7sbUz3gtPh9vh;jzNcmcik%O8j&1tOq$Asz|#$Q&IUX^ zfM*Bbi2$BHz!L^MyZ$rQGsS^tI1`pOh^rpDMR!Z*7(6$W=r>MXxFBexr;Hx3<*CFsV5cpaMd^H5VUJ1u}c{1U^i4$Gt#YETG zyqu?8(`*Sul5seiyREW^o8&UsZ=D3LeGm_@Cir*o08@+v?DT{I<~tDfMkH|GLqc{l zRft=VxcvYr*{v9hchg3^YZJFQKG5V0FvT$m+-fI*8)6)`61YXl2N+)2n(iQTLO#&( z&-uWR)uJY0`oNt)X%9?dFHGWYn8Z(E5_@102jBzuzz6Py4;+LK9Dolz@LBu73>zoR zl1(3267>O<=!P&5SJ)=P2DFDlW}Eo2(+wVh%#R{EJ_dOoM|AuJ(vL@J zv3SaHgEGg*y)@5BGL|@Qa7v;Zs4VpeoeX8qJTW28iWQ=B_(D7bA)bX0&q0XiA;b$1 z;#mms(!WKBS;rNkYoI2&PFNGah7hkqh&Ld_n-JnH2=O|EFsH{Hkp2}-6#NT9gom#Q zOtQth5aK-u@jit307ASAA^!Sr(L|vm#2(8hs+0IcvF#JxG3Ml??e_So+~Ju-clbN_ zKMeklAOw5@8UBF~aG0tv>Ei4=epK)xkn-9tPui*-cbGefJCws6iX8rj<^xLi{V1RO|>bm=f3>5|m)N!|;@#Wr6>Z;C~bq zYM4`Nqp1@2Ra%b2e{^U*aV3;E{0}F&aI&dkjwC9oY7-zqK8@5SLV`(gYW2dac8_^= zkdw*`8iTbR z*S$@~H-qs27~cxUTfuaY#%tSXlGaYMw2)%lTmvs~*q!LGJHccZEX^L0rT%ypj9u~f zi7bV|(sr1o&gR!7?1GMwN!UbUALa=ZVShysn z+i?yO$#w(g+TA9LM@UtD`8QLG?NLJqqtC(L@~k5XgSnd(kJO@L!NAFmb{Pb@9724V z3be0MsrEI*qpJ{)u0TAx67lGA#G`AJaPje|0OP&6OXdmU%-JL4&`MytWe{Ga<##uJpl24M)}%4@(|f@~(fi`4lSp2!n6w#@@e9;y~uf{#9xQI3k6W^m8v4Ia46DM+qI9wUTU73T5f z;Rv&b zber}(?bTkOL)weVvp7fV|MG(kDvpA%?ta$ zS_*80ck9lG(dPyX4S}1S4NMjL>XLal!vqWV8nGxP*}&Y(FippKuw@QRrhWewF#Qhs z_&c!gKOiUnBQ4e5r)Ao^)S$hGeC&6$N&Aaq-wn?C*K}w7D+4=M_K6okP!tm5OXe6IYafM)f=4qm_)NZoe{gx4Gyx58aMOw-eB9{;<2PwYwF`Y~!mZq-^7BCsh16 zwxLgjP&lZf&xBC3s7NoSQoWGM^di_sDQu%0wow7wI2pDv2evU6wo&=n+Q#A`(fY3w z65qJugo+;zt%X3V8fYy7TD3rHDbQL1wCaFXJl|ZWzXsrNRt3F$_me^?F zM3otL{4sIo$`cfyXM|=TG0%ypiIu(vVsC)hn;`ZX5PK`c-U6`$5W7u@XD(w#XM}zy zJd`@&p(rLis2SnF@PNorXUik*H&X>V>>I-;WOW>nHLk(|S>tLPkTt%J1G2_7I3R0W zi?gGth6d_3t^<|EH$c;j>p?xnH$l^lZ-IJ^8$dIRZ-ZtU-vJ$Ad>7Pb@EO`H<9ncn zaTDlB<7UuN#x0=P#`i%-8@I0DB#lp`o>h>?CsG^jU_Rd-#q9H_II6mPo$-Sl*M9n; zb13^qD5WamSoUqWwoem`K~jK}fU~%m`5M8E+=?cA@uG6>E zU3w4QulLf^dW2rW?<@K)dQIO!Z|FPe_j*756=grx&sA#TMBxo}dYKc2>u7^I*s7zK z8XWk+5m&mL!p`F7VM~dZ;h4fw%mK|qo6~qayd1|_ktH-)SwbeK2==`GFpV^`gi{?= zE=IqcB|PLCc|YC0PB-pof--;H1ZDoD3Cg^)3Cg^y3Ci5l#LCNf5MmBvp&(~Nzf z9%DafI+unr4}fME_kd;^_kxZv4ublO`#`gd`#}w~Ht;}km94i2SR>=hL5-i83h}$5 zCyAjvb&{A5u|H^^B7P{ku55b7Mbx8TN9XF_piA}Z=|=sVP{$2)FYX`IzpEhUx#*3Q zYr%UyEwVE*97Z=YvL0F?>T%dX(7CiE;oLBt?-r-=>7XOX?S*J?5WNPvVnpAv5q*de zeV7q_gc1EYBl;*K`WTQ%H690b8@~XR#uK1v#*?5P<0;T|<7rT@@!y~s#{Yn38iznf z7|($Ej9-Fg8P9?m#&b=!8{mAFd89_+m=}v9T!S5;DAxX;XS7u*+wv#-U7RDEmXD-Z zy?x^2FHkA6ehZ+#m(I}-(lz@1fc^nGpg%$f^#|b% z57DFg!|;TA=}rAHM-BJm__ld~?Ha063ehP-U5>rKn1L z)0~v}AB6L4J0B;wc^a6qpI&M#!dDm%XLY{aKo+ zKSw3{^E5|)K}pGB7)MNTppQRO8BNp03iWkDK8+VE)xyV2%<)dgEKXOl;SUC~Td#7= zikDY;(zgNMpZL;lqDcqoW$XKrSJqfxko=mzATdAbc+HoIa``t=QG7Y|6^JSanc+aM z!GWwQ&+!<`UtNsBA2CFRK~?xQB~;m}{whiRH5#SAMPu|gXsrGworGu8^|vwTyn`|J zU8>UmK-D^Oef>|g3eVT;A2>Q5t7fiE=tHBaDzg{Z#NqEX_}MHfwVpvlQ`-uMT=RR4 zX18c|<2)iCvF2;*qm33D9(-GpidNJjB|bQK-FU-za}T|>pMJB(_^q1ajcULQojs2IB(aM8kh1iTpvX@sNB99Jsky4*C;C25u`{X{rh<8byx z*d${%O{FEkY(KrT#`t{#Q}J+R!9L>Pa%GX%Wq`3UG|DxKvR&Df=Ne5@UFO~q!<8Ka z*R*)Jpi2eU+Ci8CT#BhbBrp|^)Ig?A0#o^5YATqT1g0i~sVQJ;x`WhlnUZ3i!_=F~ zdx`GxrK=SYEM&NN3B+0v!77HE7crz-!$?(Y8*X0#Rw>Lik~CgI!HUR&mc0~ms}_p5 zg<@_2OCzaR6WL2$KB-<|r32p^@hhyI;QJzO94j7DEr+D41DAf64^B{T!)#BvvCYpoqOXkvE{VDj>dX&+`2J9yB O7jgnYbs=__a{eDrD>Rh= literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/tests/XIncludeExceptionTest.class b/build/classes/nu/xom/tests/XIncludeExceptionTest.class new file mode 100644 index 0000000000000000000000000000000000000000..d644e4a317b4818a3e42ab184e768cb50bb65e44 GIT binary patch literal 3023 zcma)8+fx&F6#g~@64n4E0^Vu`r3#6V)>{=U6)8|OAu1QeT6L4GVR6}DvQgSTw0-F1 zp_hkVUV52crtPycRh&B0=|g8a(?6>1cXrp!a#P3gx4+9d-*?V;&hHog`SBxO>>*Ug)ME-L8`^(F*ERrZqFA;WU^|SzL_K7xaT~jn>QS1mO(+Y7?10AMz2(~BBOdvHZn$K$<*?C zVN5g490`2w6hszRnV!}P=d^M~AGRr8u4?N6(=NfH!Kjrl6{ze+HV)rp$*ZA=D`#SK)wZL8vT=LlDS}EAR+9;cm&KS|;6*#cLCExhk ztHr=oXzS@+Z>1onRVsSf9=%o7OkVm1y3AadP!Q$ByOrBkc}~xlOLye8;N4eUXStqh z9-nM(LC@RH2CiP&2Do~B zu46ve-;7LB&$@6e5Acl4_&<6dtrz9%pTuRR$aZ||1GN2$a9Vb}gjem~C1^JlJBoH3 z!x?1wt%E3i2r-VsRQxnm9--A3_s6+^mev!rp2R4!^qHd9d9G#xDP+AAULr;e;c5iO zF)ITFJK-nw7iAE^2i(Faf1+Up9qHtKNPfu;0?s5?<&}~6+u2FI921P|xF3H%I|{gr zW?bRkHFn;0+7)P-qh*o(GEaMx9t(`f3Ut$e7ke{1QF>H?WU(FL|L7cwU?9SZDUwXa z%jl$^kG%#xxN}v@$AtcbWS^4kv!^CIy$RVKA?w_f>~oTRL9#DN_SI99jre50u+!zr4>>Y+3E5tU zOx_5pDgExX`{YCLyl(~je1ksMB3^qy_ESILYew}AqxzOneaDC7d+fsxyrn;~vwvcr h{Om-UaGCuY4Ei-V2mHOQkYTnEv(0a}YZGV_RKr@D-rP7@*HP L~_v{+Eix|JC#P#Qqi$WLt777zyW}JjLu9JoE*fhsN2>}R_jl)x}exmKiKXW!CI|xseY;;Qt^3kWYRD` zuWvriIi$gi{Cry(Wqm!PdRP@yYmb5#F5ar!tWNKnVEIiNvmGVfw56Gjk1#cA|Es;Y z1P?`q9)|3j^H3G6zb7{Bj|lfw%OvSOFq3;c2LhaKw-f)A_$q7dCL zH*o%N|~t0CjMXat;@t45v(E=BC?xSurCFF#>)UAEN)7oG`E zMe!eka~_5=J_$sUNCxax!_;j@Emz4zCGORE=mlw0);ZdKRa+tCTjGw-+KRAQs@{s< z!VN@tV^5y> z+K$;#4L@{a2UDD?m0KgQ|R(6^ss#%uEHnGgNopB yKKB_9dDo1392!4PkH#F*nD~OlZrM_|?J^SsORUqv8Vam*fnyZ&{By0b5B4(>8Ogf< literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/tests/XIncludeTest$TextSubclass.class b/build/classes/nu/xom/tests/XIncludeTest$TextSubclass.class new file mode 100644 index 0000000000000000000000000000000000000000..223b3e919ba192a0df1c68c46fbeb959c6965608 GIT binary patch literal 677 zcma)3O-sW-6r7D|T4VfZZLQz-AW{{~MZ7422!c?&v=lvV)1^jYB55M^U+G0b!5`p{ z5@*{8MG-vY?aq5|=FR4Pe7?Q|*hkHVg=!v4HViE5P}N~2k5vKd$PfMGSfEmG+{k}Hh4o;vfj@#6TvJuU1Yk3t@60$cxc=3pHg4oa98n93-6xWLk% z0s@7eO4>3=>7h*1zn+8|*J5#Jzyv>fg1{ti$IKf%t)1N$h$r3**o=COFssO!ih2W)^m5OZ->* z6lg)gM}L%fXR|HArsB)oxp&Sz_uRw$`hEEwz#Hs(a8dWLfVzX{4%S^XJlJ^Qp@aAfrw5-5}Ldy+WJ$NGP)o|afFp4%?@nNSn~a&?yJXU?=TMHfum zheuce`Cvg$3eLA24W|)A^94<5$~QD+tm0|jww8~5h9x74Xusf0TPZazo z`V>&`2l%6;cQ#8%sY+kw&b@Q)x#wKw@#*dXz-QDvxLEQ~!jg++7au&>Sn*Im)y0~N zbwcrLpaT7kP^fL65bQ?Wk%Y-+pyW|EI+ICDoP}JOZpLj9o`@tcd0w>jMUWE4Te7EH zaU+f*sWhQ{sFX|^p-5B7&)ZGa^?PyTYnkfQ|8c0=VYee&oNfHoStpbt@snSA%(7N% zUWzN>heDnE$2tkrd3{KA#4=OH+Z~~WQTGqj#Rj2Tdu8^1-8i;-p9ssZ!U*1R+)di@ zATX<#8g;N|2A44gkA3GpGjW88Jk9rFKp)Mq>0*np^`A>&b0f3|qOIfP7h&<`_I<+K zbGu`+mEk^&#?1f1i$tjM?XFf;b=24oRAH^H4jq+S)Jo5u=&cj`p#T=*tmh(1e{KC;N m8aed5oB@q9q;c>L3t8LyZ0sTvgGE~GcomcEbe?lmN`C-ZX1X2# literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/tests/XIncludeTest$TextToElements.class b/build/classes/nu/xom/tests/XIncludeTest$TextToElements.class new file mode 100644 index 0000000000000000000000000000000000000000..8c38dde785b959be84cdcaaea419ac61dd4c9dd6 GIT binary patch literal 952 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 literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/tests/XMLExceptionTest.class b/build/classes/nu/xom/tests/XMLExceptionTest.class new file mode 100644 index 0000000000000000000000000000000000000000..174b7693cf5c32d295e6a36acc84066b84d43269 GIT binary patch literal 4776 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 S$zMFnE-%m1@wc^RU-&=Yo%&?} literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/tests/XOMTestCase.class b/build/classes/nu/xom/tests/XOMTestCase.class new file mode 100644 index 0000000000000000000000000000000000000000..a676621f7aae9008f4868f7e592e2eda86a2cd75 GIT binary patch literal 8364 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 literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/tests/XOMTestCaseTest.class b/build/classes/nu/xom/tests/XOMTestCaseTest.class new file mode 100644 index 0000000000000000000000000000000000000000..0b432105f0bc2b4bab7a0e2c5984805f07709afb GIT binary patch literal 4606 zcma)9S#TUz6+JzYJ?-(xOIw!ZEgmn}l1I{5vb;!+9m|SM6h;z6iX$Zk(i*ijW6ku8 zXR)M^kc}ma31m+QWCugYf$p|NZL;ll9e0FqCq5&S z&+7P`j?ag17w*<^kC@)8RH1jE$yNlJuWU8RyvbbG}O=dH(qE#psVnhACgB*051xtPp z(W`A{l#c~5HPpzazEV2fowO3;8mbxuN5GoYuswdJM7Xijd2XM`=Eq~**-VZQ3R(Mv znM#-P7M*KFIw>O4yut7vpr$e9W2_}P+~F7y-&R{At! z+T>!CJ@u81=x&4Ta$;M(sY1pqCX&|Z;8f1yj)2|j1JFnPxFcY<1@V-IHNKtQ$y9nY zZxKbn;+9qANXOGw#BnK#JwALlh3HD$P|v0xYAV+6MvFQt4D)PUfuz8y9`YV2bdywk68C#0Vx>C?yhBfg?Za zq2Bm07D)xXhNFVDt5C4=MJGAOGr8ha*-@80>JA5!X0hK+6S-Y9Q+7d_2{{d+fov(C zuui0;Y-{HgV~bQ^7@eTn%iXGVwjqqC@eBcWrPJ97Yc!JZYArI6DkdW~5?Tz-_)>9z(8)xF`62+cNHTn^u#!yPU!~mQW!7el`y`Ii##m;=%SAVu9g(C_``S=-=X7Hi9pdb zR!5+*vIc!JlkOa0 z5oy@rQ*&ak2pMC6#acT$4#uSas1vVYnUb%C`$WQ7mO)F*TVneETPxF0|3R-ST zL)i2cv4+hJ<o>%W=yF z<+*IHVZs!ci9p@m6lJc+Hm+&lO!0<3^0{ff`aIXl9y zkpO>}?vBntyU0-hhxwGR6rdSP&_Wxa&auK3ILaCA(S>95p`jaBy1w2)75@SI<`5V* zs%B6fk7~16Vl16S@B)_iHceyMEJ9jzk;RJ)5JD~1@k?k68qvhxDg~vDtrXbHxBIXX z`)SijtAqUH>7eCd>>$8Kf@vbOX2NPGAc4Fa)wBuXI8M-avo|6@mqrLtd>B^|eh=;I zQT;w02ZFddh>t)EA|8y(gYSDilKqrKq**hE<-^ds4ILY} z+Na_b0YYxt<={GWteP3K(|1?RA8*jQ~CGg#9bZDJIo)={&s*-^7eLeF0m zHR}+;cB&X9--Di-`w0pR9Aely3}7P$X*Wd6lT`9LTHk;@IK@3Ta@QxQ@h7S4h@#L= z?mJu^tN6yzR#M*ODS7}O!!;yTM^6JB1(dcMMceJDQrZp%8S@f(uk{po*HvI(4r_;v zx*4pCd!ydy9O{RS^)uKojg3uevdNhkn?;+;j4eu=dZmrAc^Z)?5Ujp{>Z)06y+~9o ztewN27Q3keFve$+Rh8l)IYWyyxo2p1mX=xa&XGmItEeNil!_A810L%hs&^gz*K%cu zBZqZFSRZy-4+k-7gJtg~DL(y9ApcZZdd=KM3JY zEF;S&iSQ{Re3}TKA;M>g@Hw`+=h4cq!46#Tq3rZf?(tA=@KE09p*-)RZ1bb6C>ULU zwWW-5zJ4zf-AhFGGSR()P5g@6iHk({D$%{>L)PXYYjBb6WEGV4Gd*N)xurDZM;4Rl zYGDtP&6Dp>_d>8@&fdW8;hHxYf?wKXV+Fr+sk~17ZxH_+HsgEjPv7T7@+M;V0hRqB zG5-j0yu}LqF^}|5e0+vHK7B49xr)ksMm#?6k&hhdQ+_^5X|=%SX5kWC`twz*Rt+iP65&D5yU=2VjynBV5)+8p(iNb~BDQ_JSY`)se# zZtR=Ee)e{Ec7U_R`^6v0=1;7xKeLJb#g~I}>srB+==6(CzJ6hOk!`oyBT7$R9`21I sBld`@g|%HrtiWHX-{0u_?`)v|V0Pd2#g2H2)VYe(`s(>KcLy-`A7XCWc>n+a literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/tests/XOMTests.class b/build/classes/nu/xom/tests/XOMTests.class new file mode 100644 index 0000000000000000000000000000000000000000..76bd0dec0a5828cf09daca50f724034d2efe41fc GIT binary patch literal 2500 zcmZ{lOHf-?7{~u7JU)ulR$5vpeF!Q7t&O(6DwIMKXj1}7Nk~I|-rStPrOCa_eN=G9 zgF6}~@(U~q>>Y`g0Ze6%_;m*f*NJ7rZM3}kv{?7OR9_O6zoc#RH4^IHR zj%q(HW0r8eA2IcwQ|}wn~);hAuJQpgcZUnAw$R#?h@7r zIYOSWPACxW5sHKj!hOQKg!c&V6Fwk(Ncf0g5lRG`P$o!1g|JE366l?ETqm3p=$f1= z2y`dBvJ^O$c3im{)k@OOTcs+G45mHXsunEYQQR2p4!4{@;B?xJ;=5ig9?BpL;>FBL zUVY36^aPO;viI=hRC+t&MDdDm)#S&X{~)e3Ea~-S5LH8g(T<9o3c#}doEQ1FTyj(~ zN1G`wZ(9$o0mSjDz(}VA2krtVi~>_hm0P(v1K7p`fwP9a5j|lAQV9i4ndYn&Zq*GV zhM^#Rr{YMzlPwpO?5Y(6b(@)QmDha9vO~|`tD8rSuvyEOZm2|^0;i=_Y1z&go(o~< zJEbUWIG+C>q$*h!mG7R6NXpT_iPyij=Pz1gV2xc(DB?(g(p2b zY6UrG1S#gydEat_is#ol^R zw%lpW>4I6eYK=C$wgeZ08e8&8banW=td(@(VesZK!!J)RHz<&1}OpFLKLy-`S~er5QDuSxI>AL+OXj zgEM55m~ty#-Ot>p93M7Schk$#;^y(CN~hDqsw+@Q;!Di$SRSlb^3K#rZAx-7~!p@%X@eg`d3#gA=&Q-vf04|De6W zbOdkWEgoU#*i&>XTde0t#5VTPwLGmp2D|ss^F4a&Ksxm4a72fG9iGu)K!>9`JgdVo z9ggepoDPFJoY3K<4nsPe(&4lY!#a%Ua7Kqw9nR`-PKWb4T+rcp9maGR*Wm>nUew_w z9VT>mS%*m-rgY%;Xzl4m9WFKC+r#Gh8dLmz5q-FXA?~|z?z}kv&vMtLxN!=&0-IZF zhg<4XZmQ3@rM~2*`U-F3F|OeWD}TXt{E9jJjvM$B^Y|ADF@U5P!J?SJlDLXnVjj1} z3R2=e?ubn+ix8{gGi1aU$cjg}E55;+_zpSo6Y}CI*2Qlqh(B;o{Doqy3mb>c-6#I$ O%-6VPfp-pOG4mf#*}fM5 literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/tests/XPathExceptionTest.class b/build/classes/nu/xom/tests/XPathExceptionTest.class new file mode 100644 index 0000000000000000000000000000000000000000..da079964c73db99d2ef7b3dfd972693b676aca94 GIT binary patch literal 3479 zcmaJ^ZF3XX6@IQPdxb0;5XK;bu>c2b3*&|)w8a797(*Ono5aGzEhJgXYg@3=iq#6g zBu&~ReMw5wrcK&rrk%D)rv1!JVl!c;KcGJ)-}0efOP_OhwUS@j5v=aZx#v93bIv_0 z|LZ?r{}aH|_%x2wxM|>43>V{w;N~t2;#C8$#nFP-Wn#uKCp&q0%*VN6$!I}Fi}F~K zX(5K&GPz^Gj$@y1Sd5`$z=`7k=3^*}%XtHLV{qd*gqtx`LGT0}H|ub;a_o zmlc}&1j26dGU}8no?Fd(j;ml4txCmQpn=a2Q?5Qa@W?SNMm{p_DBj=_qN^Td{EPXn+TCP)~VosH( zdwggGXXyD=&l+)Eb9Jifm8%{Vw#=fa?`Xrrq2birZ7c6>u{u;ku|1Tot)+s^a$Zx2 zhm0!*-r;=Osk(XVazQHPz~-vVk;7@pO#)BhX;$8jwVS{YzN4_Op2RSOoI9VulQM{Vr!@bS94~E_(pt&1wNk5?1U|xVsc;QO(Xwp z{)qFW7sL@Ac4`v}2RBwAofp{(t|$F=<;dIuBaKflaRK7`C|c6&r3q5v+*2xQ|0x z)0S@CV6;mrI|zEJ5otYUBgy(ID{jFo&~sZ5#?eiXtytFx3%b!;jb*JpO!6I3VMh?k zx zJz@{w_bw=DC44@^*#&I@vnI1IAZ~)!nzFiH2vD!$Al|Xf?+! z9gBT$a#vPdX9m7RYyK14$19JW0~dp$-c!GrLc=w}bT9mGW($8$PX+V>a4 zYNA{hHJv@^&~inH<+(8ax3m^HZZS@?4zpWE5_KJWZpe4I(=H<=%gMbDk^Bc*vzc{t zJj8z42(;K~`vF7rQtfB>bLc!?LXO`}ln7M1ozF4m|n2zPw0lCnzy!#Guxd!Tp*EQRP}I*u5)Y*0`olqUk- zFND0;h*f-aOXmCpGpn8TtR5uI2XqMn?%=OhKV4nC9KbRKUE%th+*!kZyn(}bOM{iG zv|mFU*JryMl3*Z`Gw`D5e>KE$EhxVZeXUZwR@r2V8R%l=*UR0%Qtwc|cd6-nVJHxQ ze{1918)DTcezKwW&*@!Xu17dDn>@UM?rbJ`WF0*>l4(t~Z6bMuIX$YQv9+=WQ>5UN zJ;8ng{&I-(H^W}s=iW!${SaOJWzfynewyFM`C^p|E|;#0_9M)L=-M0N>S7g75|!j; zA0m$>fX>a7g z{z@JHMo6C#^XGU0f7c8z5ZD=N@Dguhl)J6GkvX=Ze)N#_b$Tc4koCoYbq8t9kbgw; gXL&qkkS3qM!8=X~ZuH<4o|XPO%l|*%ID#Mk7uK{4s{jB1 literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/tests/XPathTest.class b/build/classes/nu/xom/tests/XPathTest.class new file mode 100644 index 0000000000000000000000000000000000000000..06e93df053971abd9ad01179c13a74ab61dcd28b GIT binary patch literal 44033 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

    1wl5$GUNL=e90Y9(R;-U-^>S*802Nfd=wCt1E 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 L7Snk78pZz)-|oHZ literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/tests/XSLExceptionTest.class b/build/classes/nu/xom/tests/XSLExceptionTest.class new file mode 100644 index 0000000000000000000000000000000000000000..98f6714de9c2a411be5ca8587368c26bdb5a86c3 GIT binary patch literal 2330 zcmaJ?Yf}?v6n@?i*sw0Qa1pSSNKt`+6tsBZq7)FR37`R#7OG2FLv%?nyFtcj`*-@8 znF>rh(;v{E(r^6(ZJ)DQl0|4IGn@B4*XKOvoZUbE{`GeNw_)n|3=#MaNQTO}sJp&0*Wj zWrlrT<2FJO^!3)x&|ynjt0Jd@w1e?uahvlffHTUKGk+_EW&Ch}?1UNZ|> zx%jkO6@{rqx{Yz;PS zg_bvk5d&AzqtO0eoi)&f&lQ?0?3I&>YurFLVm@egGBz?aI&@1kDFZ85HL!-~23}xY zPTv~%4jTqG@x4OVGfO5SYuecl)^^MrpICn1D#UW6#lTC!)m_PZA-M)#VMC#9+P3rW z8O0%QLlVlAu~g_e=YNhnwpn;1cw+Kt5W)Frs?g-?my*Kcj45+JaKTsyI=8Y@$iJ8Q z^=4hq1iIAH*B>w}52s-|j#Y5y-W5%ob9UqZ;|CTg!r=bwTs{-Jg5i^JOIvTOv^yR^ z;Y(yK^b!u4)lZf;U{ouu^pg$-kydgBi>yewps&p=BiH$9^4?Z(fUfXW2fnL9GJ)QS zlg57yTsrjd`2}xNo&``d5I=@G^z3^1l(!rZM>PgKNgVsOF_+{TSLuL@MFyQANWqQHK$dCE`bDIYFx^ zd@e3hA2LNRsrZWDlSw?~_BS)F^x{71`Fy~hJY*lHDVrtSIodAJdY&adrbmLmB>lv5 z39DGf22$l>$JqB_60WE@J+UC4fDy(w5H4yM)z;)eyIbY*|Mcmf74DTH@lq1WZirt{ zPm+z0a^q)6IF(A3O`N`KtZs-+8825k#$f>8bUM~)vB5TM($1viOQL*5>uqAP=#!;a z1{2u94BmPb4iMpWzb<~&%O{Yj(!}ayQT9M_;J{G`|3k-+hT9q`R8n%%uY0Uq_XlE? zws%SAN)2XL+bUgcXPJi#2?B26XTQ|0PA&)Vj)WE{->1e!D~epl0}rgMQmY5?j_2DI zXo7~&iiW$w|GhGfWq_8_czcTKnmHL7F{YXxKDyISgoN}O(gBW8g KvLWw=@qYoC<>^%-J(D`}5cL9{`@C+e8DKHkx>7VavjH13NZM?AkE! z$U@7)V}`mqO^VhthOJOcgL9b$iWjN~-VVc29*N?e%(D?idIWuuh(sMQ?E3$~w#N)+ zUmkOY)i4qKaGFhcK8hwOMZ8ePQ96$DMEh0H)G`>H*Fx~TpGHN&$=z9gPivo4tz^*| z@5?OXLY30qM<1dfjl@YXRCyv!dhI_fhoq>W6jSkY!g}EXl))a#X&&=IqNT3?+l8k( zgNk}3;#3wy{)Veld2Hc{gFW~TY^*qN;8Q78JIhNM+{%5aNYrGih~0`WRqKSdUOF^| zr9)`#lj=F%K@=;Qe`MHPn#Qo9hgvmhBdzu1b!h(@|sqt|uy+GA|fD{In& zW3H>KC#y!kgX>_pLDm8JI%0Ra-@rOE)V`LORa)zWf$<40*&CNAtl=hkgfH0`A#P#4 z;u=tf=JGCJ99_UX@~(AfsLx=1p=b?%IX};FdRLqt-Ey0_4BRR4cX5w)6Zi1|9$7Y> IHE5^rHyRtoTL1t6 literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/tests/XSLTransformTest$AttributeFactory.class b/build/classes/nu/xom/tests/XSLTransformTest$AttributeFactory.class new file mode 100644 index 0000000000000000000000000000000000000000..24976bb1add237b56eecf594c01a9f60403e78e2 GIT binary patch literal 1267 zcmb7ETTj$L6#k}V8K`Aht_q5PA}$vx2wreeLqHs6(^ z;R`p|_5At)lo+~psjy&gP3+rl?TI7n(hV7gifN<-DvhPUI#nK!kjs=@NRLu3 zUH3rvaiFgB>{$#bF=^W>p{VY!iG%op9J3sA3?o_6Gs>SSsorUa(+tCD+%u^RM#YQ# zs$8*EV|z|FW9qedBTVGc&T+@YUEHI2B<)-Fs!^j6W`e}GR_yyUhXl*IX<{A^Oq7&a zP$rLu95n2y{~HI5|F?A6kUJ9aq=gKF=SQAl$R~_!rmZy@HbPK0irG9*qS9ar*$CoE zsdg7g_B;}fy=%*zby25Yc9U9l&vJxYvnupbx-}BF6H^g_15uSPU#v2;5V9$ph&1!X zKPCTys1gRwy)1_4qimz^6VRclfr6^0okgCJ&y62J`$!879b}sos4t?E)@Fl%F7(ol z;UX@PRpNK__3Gs3CO$!5oBRf2@++F&!#qZAYvKsaAJpO)e2WMz^t94r9N~gGBAp)R z>Zfebmyy$-prEgyPk)M0eHF9hRsNwyjwHYOi|Io@Rgk0NFXIYTJV2~gXg^TkxY|OM v^DMx54dW?pg92Io06I z@)>4c27zbQJAv?P?!GNr!U@QoJaio4?b+P-1szKjr(<5YEtA3m-+WW8)I9F^?_94{ zBTsG=NSR@x#XpF_oeq;o)-Kyb>21F9o}Y8m=FX{EB?iuE`9_|I#{e`Z@ydlhzax^s zr^>3sTVdyaR3WuqzdB^l&bi&85H$!1&G!+8kQsE*Q-vYjkBM|D>v+hJ%lF-X5vN#| z(}`VB?8-kw8H}pi@fza5lB1g)mU>$@l!Soil; z|360heSX0@;EllbE_FO1Zjt|Y#Tb^ZFH&S!yV3NeY6;$M3#YkvX4y@uI3iC*r3WLB z{f2TcUQ2>fhRI`@9^E*-KhRT)Nr6E!(#fQq(XOp`5&KL=3=_1*rBiW`B0Jd&z$B*W z#Bd99w94d#F$zznuAOH`J~Pv1|DaV8G{ literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/tests/XSLTransformTest.class b/build/classes/nu/xom/tests/XSLTransformTest.class new file mode 100644 index 0000000000000000000000000000000000000000..fa1924f792efe269d9c35d53c543ee0269f862c5 GIT binary patch literal 23853 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 Y&(GuYAU+?(=WY1>B|h)L=Nn}HKfdrmlK=n! literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/tests/package.html b/build/classes/nu/xom/tests/package.html new file mode 100644 index 0000000..e314029 --- /dev/null +++ b/build/classes/nu/xom/tests/package.html @@ -0,0 +1,83 @@ + + + + + + +

    +nu.xom.tests contains the JUnit +based test suite for XOM. JUnit 3.7 or later is required. +(Earlier versions don't have the assertTrue method +these tests depend on.) +

    + +

    +The XOMTests class runs all the tests, except those that +take an exceptionally long time to run (MegaTest). +Some of the tests, especially the builder tests, +depend on the underlying parser, and are known to fail +if the parser is buggy. Xerces 2.6.1 is the only parser which is currently +known to be able to run all the tests. Earlier versions of Xerces 2.x +cannot successfully run all the tests. +The IBM JVM 1.4.1 bundles an earlier version of Xerces which overrides the one bundled with XOM. +For this reason, a few tests will fail when using that VM. These tests should pass when using the +Sun VMs. +

    + +

    +Similarly some of the XSLT tests depend on the +underlying TrAX engine. Xalan 2.7 and Saxon 6.5.4 both do a pretty good job, +but neither can pass all thne tests included with XOM. +testOASISMicrosoftConformanceSuite +is the most likely test to fail. +

    + + +

    +A few tests rely on access to external network resources, + and may fail sproadically if the remote server is down or unreachable. +However, in general, they pass. +

    + +

    +The only other test in this class which is known to fail is +testBuildFromFileThatContainsPlane1CharacterInName. This test fails on Mac OS X due to bugs in that platform's VM. However, it passes on other platforms. +

    + +

    +There are numerous public classes and methods in this package because JUnit requires +all test cases to be public. However, the only published class is +XOMTestCase which provides methods for asserting the equality +of documents, elements, processing instructions, and other node types. +This may be useful for your own tests. The remainder of this package +should be treated as unstable and your code should not depend on it. +

    + +@since 1.0 + + + diff --git a/build/classes/nu/xom/tools/XHTMLJavaDoc$1.class b/build/classes/nu/xom/tools/XHTMLJavaDoc$1.class new file mode 100644 index 0000000000000000000000000000000000000000..91a11dbe7bc75cd88a488d5644e4b9206f118961 GIT binary patch literal 210 zcmX^0Z`VEs1_l!bZgvKCb_PyH2Cm@z(xT*4x6GVWMh3wMj}TuUuf(!Mm;7YCEFjIu z!0(!uoReRinV0UHT9T2U!p^|O$RM3ps$Y?xt6!3zpHr-ltV_j^k%7lEFE6#oIVZ8W zIJKCOK>%cseokUuy1su>R%&tyBZDMP{Q?RMj0{XbH-Z2&5HbLHEDWqbnuUQ4NOAx% N7f=l&kY*6z002psG2Z|H literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/tools/XHTMLJavaDoc$HTMLFilter.class b/build/classes/nu/xom/tools/XHTMLJavaDoc$HTMLFilter.class new file mode 100644 index 0000000000000000000000000000000000000000..6fad3e76e00e788b47129f3fc43e89b114d3105f GIT binary patch literal 950 zcma)4%Wl&^6g}hEv2oor4Sf(wAwVce3vmN%AW#;q5TLXyQWeAoP25p3#jzuMg371x z58bdS5(_?nk3!sW0tAUxZ00`Z-gD1AGv9xF{RZFx?z^yY)rF&vYp6K5?&1b+>Y!?4 z&BkpTHHQ3S8A-LpkgL@X7|iYXP%zB3WhC}SgRV&Sc{ij?sT~JAJm87c@g!-gzDyb1 z=li?uXEIbGVeno=kw~^fo~DBC*4oj?KaK~!isLZ#-{{hp`~%;KgX*8Q77^h=Acl%z zzScgX0$;{HVTxA$ErT=Usvq$I5h?tA%P`wfJb1s$hZ!jwb%sjqoT<&0Ueq|=WLQ17 zj27#}qa+ZT2JxEa-p~|1xR|lA?qLIW7_3HL4MH1tJv88Z*hJIDJ%;N4D8Nw6whnpJ z^Ivt3L_kuMriJ(=@XFb6O_G1Qg~9F#wU_C(P^*uX%O-SGBBLJ3J~efmBe6wvn7)>( z&#*Xoi|HQy9y2oCkqL2(laJZgpU|rkS#TGaUn3zX2^=#!A+9g&Ga4 TVxDY?JR7%AB=AcVqv-qu5W~la literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/tools/XHTMLJavaDoc$HTMLFixFactory.class b/build/classes/nu/xom/tools/XHTMLJavaDoc$HTMLFixFactory.class new file mode 100644 index 0000000000000000000000000000000000000000..da10d135a07e6457fd5db8616ce345959993be00 GIT binary patch literal 1301 zcma)5O;giQ6g{sWB*p+GAc6>1tm+!Uc(GNxtRmN28B8+RnkN|O^Rq)yTHv&UNOYo4~ExOU}`L0z>hy|QR>$I@G7LA^PJ}Ti>vpwL&@+ax zsi(ELiCrpEMKAg!%&WKuvY8nY9;kST1v1;&=az~`SY(L!7A?4L#mHA(;tD(Np=bTJ zZMoy#WiD#C+%yV;bV4G3=c0MrEK(DEM$aFS@L0kML;QbxK|OC_ zX!dsK^!zCr1^>0#IJKm^-st3bnQH2bC$hqol*z49YMpAeO4N(UzcCt2{Y90VG(G3y zEg4VqyL!P*C!SM%P8)L@%qJE-9Boba<1K_ONR=0hE%pSqVepw)hfEWc(MRZHi&S(#h9U<_Y4gvJj+TjIX<2;?!IsgM0q8r0S4AUx-zaUI_ zAeXEmsD)|>f5uQ!>!?9W9z)*J&S=UJ&eouQMa;u=dYFNBOjjLqjP9-E5hCA+G3con zfkfvwq~I-ye}@>}V-g=QgO423;RRO36nWSU;dLkz7}It((mul<`hmU^SZ9X71cM_uez--uB1OZ{GpDz*7&`aMzl9*4%gT(8U5n>!r-3 zS!L+;qxbxfhf;-Gg&7xSXtdzz%Cl_-XHD%1hUrLV;$4yM3O(Yx2_bWlig~ilwY29N zbj*Rw8QO0~o6%5ED3Kq9W(YR2Oz5?Q=eeNxQj`_pu}VXuRFa1~HvKIrSXc4l@1-jY zO;s3%`M*nK=p1Qj#Li~4F5-l1ZlubnsBgu0%h(_tn-7CTq#`pTzE36ni~DP|?9>%i z3d78wVFqt-9E-8#&RsmB^H3E!7O$io#q4AQ&+RA!wBQDKjBbD@=(<>B=>KoeL|CUz zj<9p_i8i(_wJ=PT9S#lGRcn6Lakf`B+WY5~Sb#(Sf_<8_NG%wyledZny@V_+pM#x| z*T4<3UWtC9n$P3|^HmmnwCQ9SrZGbt4rVb|b1f6j?7E_9mB5#hBB+8jg@X>dL~2F6 zrrnD0t%!y-%`1v_srV}?IU$qacI9ybUpGLxPvL$J;EG3KmhL- D;Igij literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/tools/XHTMLJavaDoc.class b/build/classes/nu/xom/tools/XHTMLJavaDoc.class new file mode 100644 index 0000000000000000000000000000000000000000..c1d9236fcc8571e02ffbf64197284009a13de1f4 GIT binary patch literal 3984 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 LH?x=7cSHFP35Op; literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/tools/package.html b/build/classes/nu/xom/tools/package.html new file mode 100644 index 0000000..6e8648c --- /dev/null +++ b/build/classes/nu/xom/tools/package.html @@ -0,0 +1,41 @@ + + + + + + + +

    +nu.xom.tools contains +classes that are used to build XOM. +They are not part of the published API. +

    + +@since 1.0 + + + \ No newline at end of file diff --git a/build/classes/nu/xom/xinclude/BadEncodingAttributeException.class b/build/classes/nu/xom/xinclude/BadEncodingAttributeException.class new file mode 100644 index 0000000000000000000000000000000000000000..3181b15ec6fecce8a91332d89df7c5bd8c3ea583 GIT binary patch literal 634 zcmb7>O-=$q5QSg!HwHuy1Qa*mMg?`EF>WwKL`*Qm4NBZLLkHUoJ!XCk7w{t9z!eD# z58yF8h_MC;3nV5kraG_Y)mN|6pWovT0H@fFB7~e@HZ^Q%$TK7zVM}gygzZR6-8Zio z*bPIxZYjs*%I$E|6XDm~*V|dTFlLCHOC{YZLuRk_#9z2>a@E({t}Ru+vfp6{)vcaj zNVKF9x89&D>^t5ysgr711});Wj`XYNhmO&S$PJ=lrsxp9BOH|Z6cHWC{2 z0MRq_DAB$u4$47;yANyMwZFX%=i~eB9l#NCVFa+|m30jp8nO%tTUe4C9bwthRM(AD z26n*^tC`AnxNGxq z1DZX=GU?3Jg*XzV$d45yiQ%zQGggCk`strmEbWcq`wcsxq3Q&-HMPxQ+6+dcSS73% JF-;&JGCyuWhoAre literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/xinclude/BadHrefAttributeException.class b/build/classes/nu/xom/xinclude/BadHrefAttributeException.class new file mode 100644 index 0000000000000000000000000000000000000000..bbf86685e25fad1ad3241f5877bccfb43fa4d4e5 GIT binary patch literal 622 zcma)(%T5A85JhkE8Z&?*FBKCbD+P=bABhVH5m6(CxIu{<7Y(%7X6P~VFnor;;)=wD zAK*tBYk;soOk7NLZq2Pzx6@zWAD;k@v8f}3HNUKD*wBz;NI1fl+-wWmk(Rn^oHMX% zhIrjlj?0zX=B6jYm50<@`s66e5UEHd-6}(7xB1LpxnXkEH(IVORli(lGlc3^PcW=B zr4l#Zpd;*C-Z80@Y+7Af#AzMrX*1+LNr$1-RGu-k2F6gTuIcrJamIUBws@?$MDBR5 zxEOZDi%Sv94B9|A4)2edzcMiFEJ(IEYtXYLgWj?{yDKiGpK|NZ#ryuM7<5Gal42>-v(XD2SKAVG@!SVfW;J}Wh4HE3s${%OV1-Uxwt*eMOw#<1;)Z3fe(Fd9Xguzti0 Ifda_>0MW;aLjV8( literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/xinclude/BadParseAttributeException.class b/build/classes/nu/xom/xinclude/BadParseAttributeException.class new file mode 100644 index 0000000000000000000000000000000000000000..4c156fbcbcd53b8d6d1bcdfb516d848b14dca255 GIT binary patch literal 625 zcma)(OH0E*6ot?2E7DqD)v9%2U8z-!f*YySw@@h}sKuR3)1i)}6GS1=3f-txG>pp1!S~66k`E zJ@Is4gf?~IhBEomHedI}J%^CmS6Z10VQ#JVD4vDw3f;1=jjwd8yj~|HPQ0chOxBc^ zmtngh{TtD6xieGq99|@N9qWEGVIEXK*sST$?s#pxqqO6OO=%yC=9TaRd1M&f2#q}J zIP%GG#xlWb%ODV~F7#Ij!s>`PRT%T3topJtWl{Plt5F3Ua@X%owR_xgZFsl#aQOq(nia5uh G81g?!V~tS& literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/xinclude/EncodingHeuristics.class b/build/classes/nu/xom/xinclude/EncodingHeuristics.class new file mode 100644 index 0000000000000000000000000000000000000000..6503d205fd0192a249fb6f517e1f45637fbf7a53 GIT binary patch literal 2932 zcma)8ZETxY6@E_q@xJk##&+tYuAMGTyEO?})30S|KDsSg+J(8zSkr82#@aXboAhnt z51iLs$~v@(0o$Y%3P_bAm6&}HCJ+iAN($*ZBtU{qV$ueJP2y|(*)R43m_TF;&wcGQ z?pg^f`JQ{vz2`aSJm=hNzxL5jt^nABvp(F6xDN+$NDYHJ?$_}t9S`U@tYb*&j_4TH z@t~SMq+vwIQ6H)>>cb{HtmD&a@rZ_F8jkB|Q!7IaV>%K()F7$B^kJj&u@U2Hn9y)S zgC+2GTWQPQCvY`24-4FVnWQPGj$3K-P(C$g=7x;1NoqpzOv0EPHgc9)my~Y%gf%7D z8c*ljrZcIwX)B$W%qPvZ{&XUfw9*szn)#eHWm}1OXrqd71aT(~+YnTY_A>TKGd*FS5XgYwrlgseG;)S* zWzs5K2k}~4-7JMgiv+(a{jvP`xS11p#?IL0&XU|!lDkWC4;`A*g78|LgZQ+m-R_yJHd_NknnaTKZ3X^`MKoWdM(iS_z?6oGCd1(>1Y+C_&jtuR;twW$u z?%vhgzdDQe`;ozB7In?~NIGTYPMJyadJW2|Uir%k0w3=JR=lI*_Pxh;s^~uBM=!{O zr!Tu>_Z><@B5JO-;#!FX8`pq7USL0_VEd8BX+g9vlb=kcx7iIzleDIIiyBsmRrrtc z7OWwa6YIw`&iL_JJSJ%T-`DAy)CR{+nhCo+^tBG-jZsWdGz~ZxTwOxvw)4(-a^{pt zL#=7FcVM8Inus&IuJ~Qbw2pf>-qF~Z=Jen=t8t@NB(67c!pPlCS1Jrre#3M0;We^Z zGtB^_O?}t5aH$6cx}7PKCs^Nf{kbkuy@_wC`nXWR)n<@ZuG%Pt($lgC`93EV*uv54 zC|j|WbARywZo#cwRbm^Oc#6jE0iLS_*dT+=xua51b_Wc6ZQ@g~?`XF9an_g$L-5s4lBsPOE^)y-+c9+n!`LZ3NIy(-m zzP2-iVAvh#F2F}ml_A=wMIG^K>Ss_LcF&Y{9GhrN?S{FdtnOU3@)fO$Q7Cnp-A%>4 zv6#|7TpyS&V3@Y40tU&(WxdlA@wlSrhaw(#OpTs;HG0GD^Fu8Wj}~+-FTk%IZ}%>t zYJ^9_-q5;5RCj6-?Ggf=x(YkO-~#aij>ZC+0)|M$Tmc6a5nnH$Kj`|Ga^q_EhP@Hb zmFLl*NNv}`S_NsGLonhAd&AoKp{KkuxAI2?So9SNE+RxH^K+<)JM?(xS7m95T4;P? zmiS}Yh+U4{jX_7=kBlR;c*c>>;sr;(h*unW1#dg@9sJXgAIMQ9WmFcOwZi=w);qXL zDqJNMu96B@NrkJV!c|h?DyeXlRJfE5u3EdC&V3gh?9#g!eLp*QKY}>G#{VR}#jyi} z^ftm1qx|3S2t67Y#u%T*33|2gC8X)q#zmaQH}NPvpTQy?!xGNY`{(c$p1|+$BqR7d z-p3d4cYF~a;AyGGb5e_Q62q6J5nqueoR>CyReEqi4&tH=;T!T0W@Qx5%W1qMkK}LFxAGj; z(PuxJ(E<-WZNewe3NQVw!)@3Ojb1fg@9og(C&=7ese6LAc&BsjadcsqvV~v1OIBKW z%Y!n5rj6ALuVuKXhwsGME-TP#pDyYgNv;G11}cMh&tpl23U?75N9 zhDAhP{l9GX)d#lC!e1X~oORxyC2Sm7M6}brW?k=GtEz}Qwt(1!b1Uyf+zY6mXMn}t zQ{4vqf+_rpiM+;&yiWdqO-|onwr{Z$e}iNAEj#Y_NaAhw+&lcO`~$n|Pwb{Y<2n2V z7w}i7zI%#^JGBH6zlM998r85vcd+giMA*yz?x*jwe9jNxUJ|IPumSoqd?b5|RBu^E UfHPggK%laWWZ{iaZ~cA$1?W1slmGw# literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/xinclude/InclusionLoopException.class b/build/classes/nu/xom/xinclude/InclusionLoopException.class new file mode 100644 index 0000000000000000000000000000000000000000..339399af5c86d2a831fda2aac2767ef0f15c1120 GIT binary patch literal 613 zcma)(J5K^p5QWe1TEj!|fe&b;P!Wi$iJeAGd?c8S4N7b;aD&$@cga2sKgd5|MPj0z zjg`O1IJ*)Hq_EhToY|T0oXviGy}SW9Ksk*BR>QJpVBJ88Q1GOygx!{|ryPCTJSWf< zA$R6z&llQn3pdK+d z;*ekrr00o#1pE_#ustKz?4a?$RfKfQ3EZx{RAI#OY}eh;CyO*PVOb(<{Vy${GQD^g zIrQZ37ombU{||srLJ}$N2CEq6_~CfSaLw}Mn01VMs#YI^o}--{*K{=915kkQ)>l!vR6d6*Euq8LU!gi#kZdzvy z?1CZDu$1F+<#xI0iSTp!>9f#JelSFirIK!)A-CJU=Z{=BxfJnD{t5n_BHRB)XB6hgBEdGM|$22xp&fGsJ4}-kFB9TmdY@_zR)kFGcvg$`b}<{ zJ#IXlj}0+$DPxU68w$tagDLb!2!@?sqWvB==-HAX+Oa&_5KZaF-1>X-zP~GuC}Mue zGL-+HnxV98zHQC!y0{ri2-4dC3`K+yp;IFjzzTg76EUiB>eNXG=!}#r6R?-5CqXqj z?HB1L|rks*f9%FbCW+8LyOTDel4Ag~NOp`p_$Yf}Q6YvN; zMK^4y)CCW~k`;?SOT`)6O05zQ8_zjs{`uxV$MfUt3+-;zSn+;TP%S5+r+_AAt zn2TkkM9`N}tU~>!`;0&@2-BTV$BEEMUj(VF{rYnB`SfSx66%kXR>>Bjxz>9xJ_;`o zdf@FQk->_2Wl{8X>#%`EKzT8%(=jFe*ao-qsa2bw+6|_vW$kzXV4usZ4@w;MX<5>}= z%iBFIa>TyYW#KxOQ^%<^%7?mrcmLSee-vkD_dN%c&)eBk3nBE%?y7V zS@i)k9%7dBgT;YuMn8#IS7}m9d^_n#m-g7cm+x-UX?KyEK+k4E!Cw%5*2Zz>% literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/xinclude/XIncludeException.class b/build/classes/nu/xom/xinclude/XIncludeException.class new file mode 100644 index 0000000000000000000000000000000000000000..58a002f15268d66e05acacf1494752547d398ac6 GIT binary patch literal 1419 zcmah|T~8B16g|`LmKNIbrHD{K1h$0=qC99!w4kX$A1I)}i%S{H>UNiGXMq=A{0kZt zefL=tkwg=D_fPmwjCZ!1?g~U7dgsp0z2}@cclz`9w;upzF_%CJg*cL!Oi-ATSy6^7 zQB21$lRyN8D6YmZn?PKq*P^)2kZy2GQ}tDDH8j&$DbF*o`wXd)X*7gth*ed$c{pYN zn%k(q8)XREmd21>e5UTIMO`&&#fq>rqjrNKTvhD`XXy4FE^k}rp1P@XI*&Q$6)uQk zogs2lGc<9F>c@QzF51K@L#SkK5nRWjX7Go0eUn?u5=pX~Rg$cdaN4^cLt=!4YfvC${FN7Ii7y0Xzbs`KJAej7b{C; zhOY4mA6(0;7@e1zcJ=;M;Sn;#r9p00!B6cK`N6%fG{1^atIr>(JFYv4ifLO_epi$J z^_;R~N{%y$^XN;W7w3`~!*~*%=t`m+Jq&~1xXZfEYpPE8g10QCA(m8QOyta6ZtYo` z;5qj(44>+}ZPn~LH-xXbzryvWld?mV1Iihu$n9{et_i`n7>52kQie{Crv;25djBc< zdxX%3c65NAJM99<(2p+Xqq+dCZA$(KObH$$@YyNur`V1l z#c71`0tw9EBJFyb;X#C0cih(te^tgii7-xjYqeXxYb;ZtDqG zMtZ}_7lb}h5`gqaS_wRH0F%uE7m#5Tm+6bN*xwvJ*}QjVGrdi>`6c-w=Tji&zyVU< z5n0Pdj}SY~D<9=HzLrfKq3u}7ACkNf%}S_{=atogoA=?W9DZJb?TknZ+Hd literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/xinclude/XIncluder.class b/build/classes/nu/xom/xinclude/XIncluder.class new file mode 100644 index 0000000000000000000000000000000000000000..26670f9fe5c3dcc9465a7979dc30a2ae1af0e987 GIT binary patch literal 18431 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 literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/xinclude/XPointer.class b/build/classes/nu/xom/xinclude/XPointer.class new file mode 100644 index 0000000000000000000000000000000000000000..9d3dadcd903d9c9928f82303d0f2fea17d9a7f29 GIT binary patch literal 6520 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 XuD+i-9e9AAf>>1ie}kXX%fKH2(DY=^ literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/xinclude/XPointerException.class b/build/classes/nu/xom/xinclude/XPointerException.class new file mode 100644 index 0000000000000000000000000000000000000000..13d4d078755b9b7317fc7989210b6aab5ece9f9c GIT binary patch literal 1174 zcmah{&rcIU6#i!0(z0$9iWGk!6cB-JVIlE`QAyPVQw>QGh1<}MWp%q-cc;*^{{>eM zo;49kG?BX(68{x1o{Vp{n|31@4>R-T&3oVXzW3()?=Rl~+{TIy19OA8fO)ko=(L$> z6f-F4NMSCG#SE5oWRz*8vCJ?O2;b(;mhc1HbDyn0U|^3KvSrT=BzNT&ciJNL>u~km z>FJN345=D#2ZCX=@`~?s%i(Ur+T8QK1HS8sRfbIXUKNsXb{Gck*sd+_Qv7@@p(=gb zZLBVAF(k`go!~B2Y*##OH+O}>81z*-UzY!#YWr=<~bF`YV3QlV;EFg zqo`6GJ%;s<)NEDq+UqC06$#a=UfZvUN49E3{*1aMrN=-XV+L{o292BtB?Fxi*7 z?l_{s9fFpk*BBZ_nY(kc;Oz_lz_+C+L?fL(ley+M+D+lgSn_yPI4>7f23Kt^xSlMu zgx|EK6m^EF{|<&>q|eg>MuEPnL7zB*Yslgv=u6N|!w@Y}7}_L~&Lk}oP7zng+Msuh z&LOjS3}&(;XlCLFiI3#daFzC4n6XU%e-=7+2zP}z6h@eb>lmjSA>alk2vxmIM(A~l zNhr@>=w|6NlF$1bc|r&7#W?O$t!p7qp^MPt$;Wt9>aEgCRZxP6(c*QRE_F5Cm24sfkNzT0f(L&< zZ~h^18pVqgyv)pdym@b!@#ot+fCH?0n8AvRnu|K2YGtBC+?9z{Mqi&E5$KFi=@@NO zq0_F2Gg%xzhaXSj{Vt(&sI*GkgxXecCmw|#3qA0|G*NocYIF&Oj_FInLZGxf&xSpj zT!~)H%u-+?4iX&4_=hZ{x5^TlfzJGq8Tuooqd4nJ|K`Fdoyz1=T9YM_JRU{zA>|WW z1a~N{6@%RK&kY1G%r{QdBz1kN(avPGj9GYu#=qkUo71$vCt?E*{}>>wp@1Uy5+esL yPnaZfpW~^`oWs4e-FyYTl_^FH>EPnxYDtVs( literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/xinclude/XPointerSyntaxException.class b/build/classes/nu/xom/xinclude/XPointerSyntaxException.class new file mode 100644 index 0000000000000000000000000000000000000000..94463eeb7afba2972071dd64ea63f24b1b81bbf0 GIT binary patch literal 645 zcma)($w~u36h&`kYH5vAj6)2fD>aI(;4Tsc2T&RjqQPyYEn-QhN_x=v9d2Fu2d)BH z_yK-_Un9P1gNOsT=vUm=x9&TSdU@+T1K32_Mh4TS%vdN{m}M9ZMIgD~5J4!lIy>BB zU`Gu3U9G~1tEj>KSY#jPUS97XdLct@ODY-F7>Y~o6~Ez*&sEE*M}btW>T-i2v#T!z z!-y-DIF8#*5uEa-Pn~gBdo+mCIMV%QCc2cAy6mdh>FBo8k;?Pq3*nrfXsIF*)bCWp zJNq3^Tt@_0Ww6>J40$U_{6WA_`GxBX>FH&lZw){KoAr1cQd03(2ZLSLao~vqX%;B| z!L(*5hG1jRlmf%zKbK)x_{;gZFy@h__W~G7$RbClMJj~>`Unt%REMZjBb}l%S6S(T zJtdwzRXgdf)7=dWlb-x=VFaV3$d56MQ-;YZ^s^3WW}5z4m6g>lQs2Y2>C#RTX5h&7 T8D{mvOm6~38pUX!!JT>su<@0) literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/xinclude/package.html b/build/classes/nu/xom/xinclude/package.html new file mode 100644 index 0000000..9643751 --- /dev/null +++ b/build/classes/nu/xom/xinclude/package.html @@ -0,0 +1,44 @@ + + + + + + + +

    +nu.xom.xinclude supports XInclude processing of XOM documents and elements. +It is conformant to the +final recommendation of XML Inclusions (XInclude) Version 1.0 +including fallbacks, XPointer bare names, the accept +and accept-language attributes, and the XPointer +element() scheme. +

    + +@since 1.0 + + + diff --git a/build/classes/nu/xom/xslt/XOMInputSource.class b/build/classes/nu/xom/xslt/XOMInputSource.class new file mode 100644 index 0000000000000000000000000000000000000000..9d796610752381a6c3c43affc9e66c3a7f14b44b GIT binary patch literal 475 zcmZut!AiqG6r4@drj4<+t+pT@1QnVH?!}wpMJNch9+cuWt)WKJq@;=DvlJA;gCF2W ziL+@67Ch|ko7s6YyYKtw^9#T!x+ZkAYAB&?VB5fsKsk&ayoEr05T-JVW^$- z5B=mqpxIl$vPS~?B~=2AfggIqbT;uE-@x)_bb?PN~xWHD=UTIg)F_*f&uTt9U%_mRpvunXX$3htuf!*RS{{#ti zqj)N_Ss)i~CjUG^pwzQR*ynU~PK$q0W~?+W1649LR()c{n9c`;bNmkNHP@Kj>+A&% z*nddv5jKcAOEoNP60t9L6|CxL6rPf&V#>}Nv=?cBy%3JM#JC|5Oq0kA1u A4FCWD literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/xslt/XOMReader.class b/build/classes/nu/xom/xslt/XOMReader.class new file mode 100644 index 0000000000000000000000000000000000000000..153f937a1035ca3c84289054f5b35ed38a1c706c GIT binary patch literal 3662 zcma)8TXz#x6#h=zbka1vBtW5UD3M$59Th7|5#&}ulLngtZ9!z3OxuBUCd^D~ilE{J z6`yqZ;N=JS;sdUw=~{Lz<=G$Qa-W$=a%LK+56PT4XYc)e`+DY|fB*aoz$^GBg8jH2 z#*GNx!_6?>mvAc#C4vx^!pOBDfqVqJp-Nch&lL%)5p-}BjX!VmzdI5N5yWv_!WtLS zB^VL(pvYhDN-!hng~eZN2_*^Z5s*|!AyJq_$Ha;HM;N})sq7=g7DqsyjIqzG*=9BrU7 zED*^o-BxopInqu*b+y3;YTLCqw6v<|`GT7Z!(5TeYX#X-%CcIv<&;|1D5e)(2?7yA zP(zLp@&(=2?2TE~G76Mhh66(>(U|{uisOdOnsDNIvJ5c_h~EIiz|ipXl{$#j`ML8R zb%uUFq56?SW}nZPa7YOz^!f%vUjVs&M)BdHpxU)n-L4@ALg7Dl(Ea`g1YH09o7?^O zpA(ZVo)ptG%o>mV{s@SN4vRit41C9Aj_1HcuM%8qGDQ9(Fs55CB0a(vqs6x zsSaqX<*XTcedwYXwO^U{a3a{lqL73Q3HP4S!BlP3!3AnY7kOIa^P;mh1}1o4NAUs< zGEA)6cJYMF+hBrMFmHurcRpCMuB@q6QOT*}MN?hY$}08vhf&8>*!b+DWTaEE=YrtP-k;r9x$4BuA9@6v-&b_lGtw7_Gn&H_Tqxck`Me#YlU>K|$xT1Sb zsQCVcT;(jnBP=nkIomKdoVgJuKlvVoqMLNRBsx1}S<1QJ4-^ld z1&ef%q)(gupxUQj6YmB39_peWwLk3~qLoZfM$gd5CfIMZXu@IoZ*?}0;Rvmxt^kf= zklq-EFicWhc@@pH0tVtuqZKs&glN2lFMdR*<<~8Q7UHd&kp3o#X2)tAEwnyC7N_V- z>Lh(DopKO*Tq_;|*~17%32BU^ThJ7zc+;wtq(YdWeHkx$3{E??iB*vLv|~6)z?`y6 zov&QOeDxAuCL?~s$LMR(b?uhtT7Nw3delDhJK8FUJZa!prvsBh8`6m3vfxvH4Z2_O zsTnL5K%T^_9`euRG7r;a6?xa#Q?$=;`0)y&zhHnf*h0s`COYF?6?7B9?h0Z&)I1Ds ztotZlzyfhvL?^D(m+B4rK)p#0E;;;02&5BX9LH-8`^g&9$y$&mDM+sqEiVtfeF4%Z zD35L#og^t<3E?foiPtWkbFq?^g_wkENMWjhH^>5`IymiF8gt%Bn>`8GxZ~JxtFg(m z!SE)|c$$-re1lrJD}al&a2#hHuyZ)SBY3GHI89$qbDkF}5lD<|VfVu5CieV+HlDLB z^enJV?5$v5y++-Fxkrs~AN_bB@JrN!lc3bR}1UYQujYXeLAT-I>Sz>0xY z!q}GUyK0+IsXKnpj)Sg!5S+<9(NRHoy}5o&DDE;qm~vcS9`w4WGHi=ekC9m?=m_sv zgs%28u&6HGi0N{+IP#Qza=3paqn@WW3C4NKO_I zfv=>m@^17am^_rs=c;%r&%_gEBB_QeC9Dl=JZ**_8!NmZC0-z)z#k8Tcg5nrHi`*S zZTtcKXx^f5i;+g7kK#Q>`xv`PGRkZxF~YXVSszis2K&IvG#68tW)G-h2D8jedUBb< qH$JsyIVB~h*@y9qfdVwGnOuJ6^1cjEmNUw6jAEW;G-V~pss0C=W4Dq3 literal 0 HcmV?d00001 diff --git a/build/classes/nu/xom/xslt/XOMSource.class b/build/classes/nu/xom/xslt/XOMSource.class new file mode 100644 index 0000000000000000000000000000000000000000..5dc0dcc209987efecf6904a8eab84babba6aedfa GIT binary patch literal 1101 zcmZuwZEw<06n-u*D|Cf{ZjMcz+kE3iSDg$$%$RI5GbCk;pbLELpkkG_OIr;3Q~VV( zQAi~52l%6m=k|qEl!Tjm&w0*sp7Y$>U%!9+1W?6O5gBX?xGf@vJ84L`E8w1pB<>3+ ziV(2PGbO&1MO5%Wz(WB$3=57mw7VKZRxyY2$TH*;tEV~plzFC`y8E1AxfrHiSlywa zneKk+j3IGEK!znnH?@ilY}VNq#oK24np-z7hOS@9aJ^VkEW0m{jDhT^BRP^JT8ZH8hSJp3o@O(w z&4Fhrf-VxY^~rHHqu%3Fmz4MFM^zrEW?yc(wr=(blxewY_d`Ry2<8%f8OJ)GJUbg` zhnGsoK_&@NV|nb6ge;aA)`LXoMMOD=6&P0L5dIhv9%EO+6I2E4NvL7tUu5Xc78f$| z%yDhiblzIF;n~zWXor}1ksF))%WVIO9sM(nMfp0dWDiHxOW{rNOX0pn=3@{EcBneq zn`RxG^bj-jKYbHKz&RYPAz)WmH%)a5a%J%IErwJGfvSH%sJPv9&px3 z3nNZxie#t!4T%XBF6rzmUGQ=*JvPb51#I+2aUaCsFA`cOv-~&ht@qsHGN2UscQ{5C1bfK4WDbX}xl3QTyM)$7# z1Fm&OWJYIX@76!zuW&p!jcsOxMefbH=bZ0xZu0Zbw;uo&aKnI!>2CC3#-L?311pQ7 z0UgsBl(LvJkZI-TGq5wb&d?u8k^!87D5evQO(l1y=YsLNd^WEib@P5aPq*oUDj z?Tu>X!C_78NpeN9G7S-iydFVxgvUC_%_-`XkZz^E+;Ut>)+F?v>VhU%mAv-)5#Nhd zX6xb~((uge8BIpjZw587>?pIjq=$K>+QbkhOq|D{i2^1WCOTeLU02k(ORQ3~os+XN z_oijR-xt9_;7C!3?{DhA&ZVHJ0K>>x zQ)d|HaI`@&Ngt+3Uuz1Z=tUpsnkmxIPY(e`?_`j6opM7Mrgem(=af&;p0kR_U{>k~ znpOIW)YH#V%LQ8V)B@Z@8p|+niy#+>1T?Cs5f?E=5kcS*#tBxvDM~FOnuv*3DAp*~ z(;@0m^hHqjVv3~*eyCO6hVO~tbLx&_&^OelKugvaXdlq~9r|WIeT=S?qV-XQnazCm z7~LmU@d$>cD|er$J_TiLoPJy21+6bh@)k{{M%X*JiaIRpCRCK#DoT+xhAHaQq2<)1 jFXKwA$EIBkAWeDzytd#3QxCT!c_+|(mJQ2h`d`pLK8~9ELetYFf15bqz zv~T{wz|$d^_RTXP?)}F~Jd1zQ;h%N*t`7gA!*e?Ps}laV5Wa`+D~*3w>Oaug4|RCn zz>h+hg%|YZ#{v8#gg(3&z)$u1A3^*KFX_>rt1LX=$IAwO5yEQx(!j3_{AUQQcp`xR z((A87ScL!9*(*BxA07Tz_mX%YextMh)8W+sejC6U<@h@TXO;eIO8RwuIKe?c1x0l5 zsH!CQJ*l9o;?=M+;LpUf`D9K|-J8mHjHO39wxkE+*);-V zG?s~{a#=xr@4?vNSVumWNOr8xWMVh;CbBv12=*loq++>zCN5Z1ZqKDfn%&9x2x+ZZ zr*wy61G#kO2Ep{=V`X`zy~+*#{rRDxct$YOrkRYT4s`V8GKtiIE379)Y! zB1ImQHx(O+XGdcL@eT2TWGoZQCDJMCF~2vRInXgSlI+OF#yW=M$ZI<-;y8MAJ6p1_9wX#?oAKGlDlG= zgx))g-rR74>?1{LW7%Y`V^3di|HfEqFd5H~XYfEgx5M#7ZOh`~&Y~~E*?4Y4dLXYx zR1UNs&C_y@KF<0YmCr~PH`SNjBdE&eVi}jFprS?3RmM|;MLq)4+T++&u)XCxf2?^+ zE2UPSw6-OvBm~k!o7=%As4J?z&5AxX0L|lxj|t`#A8jeRw=10*N+t$!-D3msQFXFf zGFJ4bdc%onmn%1TQwkxr0^-svDE`qpIukv1DQ>5g)YgL$0jk zJM$bbyV`0R`;+n1f!r`-B5o%k#p}z(1`chGjausL)I}He?R5kFZC0Vl=qk zU;PJqfAKD2FKG=7GZ6=J%s7hDvahSB$4aDJe2hu#+1EuQTH!{mhm!g1aKH7SjtG;^ zXfEv@PB29^shQxTne;$Bn{!E;_r=0@|Su2WbX(-#XH0mg&qKIWgu^5j!5fHYtopP!opJ75& zWe+7rN8^LtsazsQob+l`(1g~Bt_|z^*Hd7!EKaTU4gDKbjr@KlM2}9hH)M115uN%q ztMzfEwX;YzsP1)G7z61M1s8JZOXo8K@hcMwXSF3LwN!J##0YK_%q>mn@<3|h5RxY1 z7-F=SmKGCl$2*wlWon9v81@Sqre>WD`z8)x*u=fK(U4|S=E^)%=F0+87V5A_hZY?c z>(DA~hAc6qU6z{CA(?IR!JvgkyRpsM@PvcAEGKj++?A7d1R%W9d}%NGy}GLVadw zF!Zh88a3IY9OXcGlHzx#bs_ocP+H+|x6H8@>(wUK^gNdvrczZgZ8_nn1sgM>owh(QyYwOPu%kCVmS9D2H#C%Pn1p?1dNK5@TBqGbuTutC=(XN*#VRrN zEj^3NRXJ0SOje%&Z@1fp$KbU8hEbw}E;gmBJwbkmwrB{ zMpz}Hf(DvW6`aFL+#lEK&U#~Ge0Fz2D|Xo$!rk?b_O8L_+y3y{7qcgoif6i#v20d* zlF-gA8@juC*YD_N1(;Fndd@vd4XQ?BhvM$)|HFl4gHuAse)GtZCSEAkeCl3;f6B*241h_OxUQFoyOnfALm#?%D2 z3z=HceLPv)xW_*jXWPfHnr(Mq=NZ9$xe90~*!|bsvf7on&3aQzqz2<-TUqGKMl9i` zp@r?C3L|JVs-_5No!~1+1uCm3z($2_9pjj!gw=I`a}Nf2&xXdDvlp?hY%8oQyB%H|_&0(SPoz2fV^r`I(Ajzd z(s~M>y(ds{9NyNp*19y)ZhwC zM;Dr~fuA6{u?kn>dOmHiIU$2A@5s}Vl&t}B$UE{}%&V>hDNjx7R}grBH#Q6Xd;(kz zFE&}^Onak4PH-4U93rc%Gi*5~oz{9OsePR>HeT zLh!CPKe)Z@!FS_5Hb>tbwBR)Ax=b~Xcu%5M(CMq_^w<0AeaEnt#_&eG_5PEX=7Bv4 z7bkVa$@Jo+-h%=fZuZOg#Mks>)XUM=-d4bjsGrly0%jhC*&6X5M`RpLx@0e3;2kXB z!qb=)H6ljDUqGX->ty!s60wYWL&bWSk{BmJ0iuCOAly_y^R4(iXLAaedlde0eA3=Q zp`$^2O4*_z67@v_kzgeBWy~vJezY<|nHF@K6`fV}RrTgE?2A;^R~4`@S{@e;anAL!PiWR8jJ*Lsr`zmQVA8XlA;DxSZX>=mmp|@%8)ZN;tzL*JITiqzNaKpA4G-B z;cG=37R!B7kdwT=B4=d+i<#TZO=)G)0DG7?`%py$nLz|;B8IdOL6#FkqC}7`V#r3W zZ{qXq%*)+;lew1ov7gwHIXWl%?1b7gQsTV*>wEB&}# z_VHW{hvfk7lI!sWc^CdlZo#;GoQ>_RxLa<=m*h^|Blm&&LN$IsPT{Na2u{iqcu1bb zDR~YL%lGk!Jda1^CpaxH;W2pyUzf9ZnwI&7NAMj_4W9JW<0;Q<+~t{%?|M4$oaQKH zorPLr*&k64KeaY+6LHd@%yaN3xS5GL2g~rM)UbxS_27MYKdQw;Ykq(ys%Voh^3V@8ISbJ;34DZBucUqUXt87XbDjvuK|EwrR%9t69%rBAQ{o$Ef591nm9tNA zR$>l=Aub#`_d*WhD6Nd*C-{c#TGoGr0UL<{9|xg=bMu%eEM}ZGsV1 zZQyoJi~m0-+`>P1;SM3^*D%|_=M5a|F>vP@EbyFxe}b$6S(rcZG@N#L?ua~tJY&qwb z^Q@r=N$9l@vM%B$??`|!X#u2SO@Ih)fyTGB8d``zBi3>L1N!)fh~Rn5!;cuzFJLWx z%(!}i2=F3yF=qGjXNN(&#P1|O$0%NAe*c0I{Y%F0uW&nlO>4Z2dl(PQ8a}VY3L4PP znq^pMMY)gro2(28uUUD>P7(wVp$Um_)4yNCIqZ3(|aaHhQ!KmIsej#&3Vt8yvevfD9Nooe4* zUcma^*0vM4VoE<{d#HgDD~k|wB|>bOEfvIdpCym>Q4u24-9!De_!O#-yR-*?o3wweJ~&48h`zeN|f5Xk&}g%>MLypT-Ebbx(6+MT+Uwh%a(^ztOttUcKn6ltDAN>*Ttti&!^g}7XTLtML_wOK=A9TDHe1y4sMhCA(mYoH~)?46qUZ!06GVH~UMrDEgAhEMxq zZ5qog*@q@g8K#RNS24^sVisGMEo@!XRLwS9SGcW?a1@0lSWB#?rx%`C%PHz9SLfZj z#wSgh7pJgoZ&8NrlVY8~jz_tr1f66D-?aKltkUE%ZolwT0H%Y0FwVdyF#z8ou`C@?2Y1p0B%R ze#6kZ&N^tnnw~F@;P2j?#M|G5#NQXeSgINDTIp`Go!z&Wn0x~Ln$6*zM8r;Cy^ol9 zu@eIkZ$&*j1q&5-F&y|t;VZ$#l4Ii&-z16M1?)MBhyX62#P};Tw{&3B~2h{rQhb@2l!E9++2K*cgKUf z<6iH_6TlLE2FB1MM2V?wDmS^3gwZM49j(yrYHT?##9a`EYn>|Na-HpEbu$-+h%nJL zZ%oM>rz-wynk@)}t=7`6lyFWuLj29)O6ZIq&V@`X!uX#(o`TTw4ZqX&3;zY^cn;9@ M82XF6hCCwEI(pamOlL1<@zC3?p^6~DI^o`AnGx!qCu3!ggHF7FT4?$=DAuFKOp3N zt$zWRaG4Q5NYr`uhXbOg&Z5CH;VMgrLgFbSFCp&#D8$Y`{^^2- z7;1=t#x=g}9bM-!H-Xyc$<*rJ`aElTd(Q^F`E35N*w3bKu`+D0f}2<N^kPIi+3`Tch0&3oVb?(hG; znY?)N=raK3%Ax=+!2u0l2p|s!3a}232XGKisL>%cdQx4UQjdo<^l9k#F@O>r2$0}Q8lDMYG>)l<<7&&ZYV>7w`HCN34PY+5rXHTt@O%K1aKMkRtNROn zyr}NqD8x7MEp_>J0Mqat4c}FFG9QiaY50Bs)%Zaneu$Se{K$_V`|*8bDOiq)IRNIbPQ zk?g8ppCM=zXj>!gW+K@u7}Fe2*Y_m4>RS_Aj8*n52?E`bWTeY5jU@RMTm0(NW;9mc z66xl0L0hyV9x>BNLohFQb&>rTi^MzX+stG%-qDyfvv%_~qup#=LLQ@xWHOOVElW}? zO%Y7B=!UAjQc-2>8{O2a(!fT zj5ERJM0+H5StJ=%&(7q??AB-eXCw!`oXh@GZ*|@vYI0bTVQ_6LHHR8j@%9 z2o{`;L+;j|cB5PI;C8Rs8D(rHWa8J8ikbBr+L~SI%2z4N8G=b8ig8@UWNI@b?b{hz z%M~i=M63cm%1l_95lU%esx#tNB-+zmM%=U<9Y~qIF(cJ!7^a|f7>S^I_`VEPcHYgr zj3q)8Q0biUyI{)j%`>xir<60Jah6ERFxO@BRxrbkX#JT}(Mps!b6ur{s+$~ofp2Fd zmL9U`WU=7#$R2!LAzWm%zH-Ka$EW@RT62|B+d z6PcAU!>TkUxLR44NJSL}(E6km4jwXeubHs3O%>_PEN?hDNQy=6F$X$5)hYo4b_%SD zDo8BNhK^Y(K02>&JaePP)L5QK$F_vx2{RNUfSX1r5?Y^Jwt*MtkaIzr%8Smk z=`4EWDv$v>qH0@j+>G>uESwKTfjA`jYE(DV7orcI()O{hE~O5|q)pjFB>3#>zP6 zMwV7env5T=k6pfMv0Rf0x=fTwf{KxRv^H&ar_D7+FY_^4BRKHiMb(tw)!RSGAl$tfyo;Y@63X?Az9XJ}1?RI!B& zyZDsP&gHa2oT*+leV#6}WsWX$WgY``r7rVjfhG%eIUoCZFg1i!>>9Xqm0-HFKa(P16pSX4LYWTr)5rGNUgA^ znQmI0M3c|xWmU9{*&xRARpIQ}LZ_56O>7`rmrQgUNt2DuS(?I(tEp5z8=*FgGdgxj@h7tD0t%uw*+V!EQLkVoOgD|udoSd}6F?7vo z%&fkWJnTmn3e|F8A=BUKT+RZzaT&#}b7wGJRk*Ul>~5h=0kUW@QmIG><6TnSlp}-d zIoX}%(a~-KWLg@fZ(%f^Vr-Uo@~|74IE_sfgO9THh6>sMNS1B${m11O>CrTqv# z!G%0aclBurc!4f^(b8+I^<3miE`o_P8RjzI=`g>=acvddI(lPGKgRTcjXU{**#! zqG#FUcWv_HdfZ^i_2a$FmpqQyx;lLCqz9C57S=uoJy=fG6ZSw4*VOf6;yx%ZPhtco z2P^te$;HZGh!>`AWgn&}njt=xN)$;5V`VB9$aG7eu+87qXQ8XlLcE`vsccwiY38xK z?SrStAL2AyYDdWwl~naqX$ATzcx#T}9G2uh6o=JQ?Ga4%;PRom)W9pX2ueLFWOkNs zDqUSFU0o_2->7_N*#}ih_u^(JtX+-*6|}tSZWIn+8oe_8pcCX7Enya{6=yr&RGmUD zLb0sIcxlR#UE#{Ez;*ZlrHn^DYwZ?FQnEde;;h`tQ?DAf67mWa(@1h?0M#35_t<{S zY~j7&2-u6a)&*;Wb$yssX8}yTa?BC1<2?d)j}6{(KFt>F!3;H;BRGz^4L(Ix?mL2c zd1%P5EBBQXo96F3^-^68gS~*ig$G+|TkD=h!ycSlcN`6hQ6Cx`eC599P*LtvYm4?^ z4A&O*VX-}*&v}Ju+`oiLQgn=Fg675fS=DQSz|{m5$R@r$MVKt@43L3ovK8~B152e7 zYb1&dvJIPLJK80NsBpI=kdPhNAxZQ|3fGH?nn`NU9#+`h7$x_hT<*n0xerxxKW1>QPWCeI z9zdf!gcY(ME%Gon@_vmxhAugPUA*s>gI1tcGp+OK_Rrw6lyDI%LXEsUr^=lXjmPI$ zE$R%}jlFn)uJO|8pC?@&Kf^cTL1L$kA0hZpB45KpBB2xT6}^K(qPCpCxVNe8333

    >{2Mm@5|`p_I~v0-lroXazo5i0Iv zL=_cNfBFDgH%dR&K8bt + + + + + + +

    +nu.xom.xslt connects +XOM documents to TrAX-based XSLT processors. +

    + +@since 1.0 + + + diff --git a/dist/xom.jar b/dist/xom.jar new file mode 100644 index 0000000000000000000000000000000000000000..9260563cde841a9c56ba70ccba2e5f1c965e1f33 GIT binary patch literal 1697619 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%0DxLP$i==4Ji{y8p7}}=@0ai68Bv!=BSH#O# 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 @wcBSpJd#M9*wi@h7lN{eh)N%sBj>a&C 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�iQeE^ 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

    >thVFMH~n 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%0D4kkj_&Mz|a1MobBr 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 ahO@Zmk3qCpEIzBx`qf&CWlaH0vHS-SF3^+! literal 0 HcmV?d00001 diff --git a/nbproject/build-impl.xml b/nbproject/build-impl.xml new file mode 100644 index 0000000..8b15145 --- /dev/null +++ b/nbproject/build-impl.xml @@ -0,0 +1,1420 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set src.dir + Must set test.src.dir + Must set build.dir + Must set dist.dir + Must set build.classes.dir + Must set dist.javadoc.dir + Must set build.test.classes.dir + Must set build.test.results.dir + Must set build.classes.excludes + Must set dist.jar + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set javac.includes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + No tests executed. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set JVM to use for profiling in profiler.info.jvm + Must set profiler agent JVM arguments in profiler.info.jvmargs.agent + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select some files in the IDE or set javac.includes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + To run this application from the command line without Ant, try: + + java -jar "${dist.jar.resolved}" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set run.class + + + + Must select one file in the IDE or set run.class + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set debug.class + + + + + Must select one file in the IDE or set debug.class + + + + + Must set fix.includes + + + + + + + + + + This target only works when run from inside the NetBeans IDE. + + + + + + + + + Must select one file in the IDE or set profile.class + This target only works when run from inside the NetBeans IDE. + + + + + + + + + This target only works when run from inside the NetBeans IDE. + + + + + + + + + + + + + This target only works when run from inside the NetBeans IDE. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set run.class + + + + + + Must select some files in the IDE or set test.includes + + + + + Must select one file in the IDE or set run.class + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select some files in the IDE or set javac.includes + + + + + + + + + + + + + + + + + + + + Some tests failed; see details above. + + + + + + + + + Must select some files in the IDE or set test.includes + + + + Some tests failed; see details above. + + + + Must select some files in the IDE or set test.class + Must select some method in the IDE or set test.method + + + + Some tests failed; see details above. + + + + + Must select one file in the IDE or set test.class + + + + Must select one file in the IDE or set test.class + Must select some method in the IDE or set test.method + + + + + + + + + + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/nbproject/genfiles.properties b/nbproject/genfiles.properties new file mode 100644 index 0000000..5bb7ed1 --- /dev/null +++ b/nbproject/genfiles.properties @@ -0,0 +1,8 @@ +build.xml.data.CRC32=1b0fca79 +build.xml.script.CRC32=356c1df9 +build.xml.stylesheet.CRC32=8064a381@1.80.1.48 +# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml. +# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you. +nbproject/build-impl.xml.data.CRC32=1b0fca79 +nbproject/build-impl.xml.script.CRC32=0b296cfb +nbproject/build-impl.xml.stylesheet.CRC32=830a3534@1.80.1.48 diff --git a/nbproject/private/config.properties b/nbproject/private/config.properties new file mode 100644 index 0000000..e69de29 diff --git a/nbproject/private/private.properties b/nbproject/private/private.properties new file mode 100644 index 0000000..1a8881e --- /dev/null +++ b/nbproject/private/private.properties @@ -0,0 +1,6 @@ +compile.on.save=true +do.depend=false +do.jar=true +javac.debug=true +javadoc.preview=true +user.properties.file=C:\\Users\\sgarces\\AppData\\Roaming\\NetBeans\\8.2\\build.properties diff --git a/nbproject/private/private.xml b/nbproject/private/private.xml new file mode 100644 index 0000000..90967e7 --- /dev/null +++ b/nbproject/private/private.xml @@ -0,0 +1,9 @@ + + + + + + file:/C:/Users/sgarces/Documents/NetBeansProjects/xom/src/nu/xom/Builder.java + + + diff --git a/nbproject/project.properties b/nbproject/project.properties new file mode 100644 index 0000000..921120a --- /dev/null +++ b/nbproject/project.properties @@ -0,0 +1,60 @@ +#Wed Nov 13 09:49:41 CLST 2019 +excludes= +javac.deprecation=false +build.test.results.dir=${build.dir}/test/results +run.classpath=${javac.classpath}\:${build.classes.dir} +javadoc.nonavbar=false +javac.external.vm=true +run.test.classpath=${javac.test.classpath}\:${build.test.classes.dir} +annotation.processing.source.output=${build.generated.sources.dir}/ap-source-output +javac.processorpath=${javac.classpath} +javac.target=1.8 +annotation.processing.processors.list= +javadoc.noindex=false +javadoc.additionalparam= +includes=** +build.classes.dir=${build.dir}/classes +source.encoding=UTF-8 +javadoc.author=false +test.src.dir=test +build.dir=build +build.test.classes.dir=${build.dir}/test/classes +platform.active=default_platform +javac.compilerargs= +dist.jar=${dist.dir}/xom.jar +javac.test.processorpath=${javac.test.classpath} +javadoc.use=true +dist.archive.excludes= +build.sysclasspath=ignore +debug.test.classpath=${run.test.classpath} +dist.dir=dist +build.classes.excludes=**/*.java,**/*.form +javadoc.splitindex=true +javadoc.encoding=${source.encoding} +javac.source=1.8 +application.vendor=sgarces +junit.selected.version=4 +debug.classpath=${run.classpath} +run.jvmargs= +build.generated.dir=${build.dir}/generated +jar.compress=false +javac.test.classpath=\ + ${javac.classpath}:\ + ${build.classes.dir}:\ + ${libs.junit_4.classpath}:\ + ${libs.hamcrest.classpath} +javadoc.private=false +annotation.processing.run.all.processors=true +application.title=xom +meta.inf.dir=${src.dir}/META-INF +annotation.processing.enabled=true +dist.javadoc.dir=${dist.dir}/javadoc +src.dir=src +mkdist.disabled=true +endorsed.classpath= +javac.classpath=${libs.xerces-2.12.0.classpath}\:${libs.jaxen-1.2.0.classpath}\:${libs.junit_4.classpath}\:${libs.javaee-api-7.0.classpath}\:${libs.TagSoup-1.2.classpath} +annotation.processing.enabled.in.editor=false +build.generated.sources.dir=${build.dir}/generated-sources +javadoc.version=false +javadoc.windowtitle= +javadoc.notree=false diff --git a/nbproject/project.xml b/nbproject/project.xml new file mode 100644 index 0000000..86a0a80 --- /dev/null +++ b/nbproject/project.xml @@ -0,0 +1,15 @@ + + + org.netbeans.modules.java.j2seproject + + + xom + + + + + + + + + diff --git a/src/nu/xom/ASCIIWriter.java b/src/nu/xom/ASCIIWriter.java new file mode 100644 index 0000000..abe974f --- /dev/null +++ b/src/nu/xom/ASCIIWriter.java @@ -0,0 +1,58 @@ +/* Copyright 2002, 2003 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom; + +import java.io.Writer; + +/** + * @author Elliotte Rusty Harold + * @version 1.0 + * + */ +class ASCIIWriter extends TextWriter { + + /** + *

    + * Constructor for ASCIIWriter. + *

    + * + * @param out the Writer to write to + * @param encoding the encoding the writer uses + */ + ASCIIWriter(Writer out, String encoding) { + super(out, encoding); + } + + /** + * + * @param c the character tested for availability in the ASCII character set + * + * @return true if this character must be escaped + * with a numeric character reference in ASCII + * + * @see nu.xom.TextWriter#needsEscaping(char) + */ + boolean needsEscaping(char c) { + return c > 127; + } + +} diff --git a/src/nu/xom/Attribute.java b/src/nu/xom/Attribute.java new file mode 100644 index 0000000..4aca73d --- /dev/null +++ b/src/nu/xom/Attribute.java @@ -0,0 +1,1079 @@ +/* Copyright 2002-2006 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom; + +/** + *

    + * This class represents an attribute such as + * type="empty" or + * xlink:href="http://www.example.com". + *

    + * + *

    + * Attributes that declare namespaces such as + * xmlns="http://www.w3.org/TR/1999/xhtml" + * or xmlns:xlink="http://www.w3.org/TR/1999/xlink" + * are stored separately on the elements where they + * appear. They are never represented as Attribute + * objects. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.3.0 + * + */ +public class Attribute extends Node { + + private String localName; + private String prefix; + private String URI; + private String value = ""; + private Type type; + + + /** + *

    + * Creates a new attribute in no namespace with the + * specified name and value and undeclared type. + *

    + * + * @param localName the unprefixed attribute name + * @param value the attribute value + * + * @throws IllegalNameException if the local name is not + * a namespace well-formed, non-colonized name + * @throws IllegalDataException if the value contains characters + * which are not legal in XML such as vertical tab or a null. + * Characters such as " and & are legal, but will be + * automatically escaped when the attribute is serialized. + */ + public Attribute(String localName, String value) { + this(localName, "", value, Type.UNDECLARED); + } + + + /** + *

    + * Creates a new attribute in no namespace with the + * specified name, value, and type. + *

    + * + * @param localName the unprefixed attribute name + * @param value the attribute value + * @param type the attribute type + * + * @throws IllegalNameException if the local name is + * not a namespace well-formed non-colonized name + * @throws IllegalDataException if the value contains + * characters which are not legal in + * XML such as vertical tab or a null. Note that + * characters such as " and & are legal, + * but will be automatically escaped when the + * attribute is serialized. + */ + public Attribute(String localName, String value, Type type) { + this(localName, "", value, type); + } + + + /** + *

    + * Creates a new attribute in the specified namespace with the + * specified name and value and undeclared type. + *

    + * + * @param name the prefixed attribute name + * @param URI the namespace URI + * @param value the attribute value + * + * @throws IllegalNameException if the name is not a namespace + * well-formed name + * @throws IllegalDataException if the value contains characters + * which are not legal in XML such as vertical tab or a null. + * Note that characters such as " and & are legal, but will + * be automatically escaped when the attribute is serialized. + * @throws MalformedURIException if URI is not + * an RFC 3986 URI reference + * @throws NamespaceConflictException if there's no prefix, + * but the URI is not the empty string, or the prefix is + * xml and the URI is not + * http://www.w3.org/XML/1998/namespace + */ + public Attribute(String name, String URI, String value) { + this(name, URI, value, Type.UNDECLARED); + } + + + /** + *

    + * Creates a new attribute in the specified namespace with the + * specified name, value, and type. + *

    + * + * @param name the prefixed attribute name + * @param URI the namespace URI + * @param value the attribute value + * @param type the attribute type + * + * @throws IllegalNameException if the name is not a namespace + * well-formed prefixed name + * @throws IllegalDataException if the value contains + * characters which are not legal in XML such as + * vertical tab or a null. Note that characters such as + * " and & are legal, but will be automatically escaped + * when the attribute is serialized. + * @throws MalformedURIException if URI is not + * an RFC 3986 absolute URI reference + */ + public Attribute( + String name, String URI, String value, Type type) { + + prefix = ""; + String localName = name; + int prefixPosition = name.indexOf(':'); + if (prefixPosition > 0) { + prefix = name.substring(0, prefixPosition); + localName = name.substring(prefixPosition + 1); + } + + try { + _setLocalName(localName); + } + catch (IllegalNameException ex) { + ex.setData(name); + throw ex; + } + _setNamespace(prefix, URI); + _setValue(value); + if (isXMLID()) { + _setType(Attribute.Type.ID); + } + else { + _setType(type); + } + + } + + + /** + *

    + * Creates a copy of the specified attribute. + *

    + * + * @param attribute the attribute to copy + * + */ + public Attribute(Attribute attribute) { + + // These are all immutable types + this.localName = attribute.localName; + this.prefix = attribute.prefix; + this.URI = attribute.URI; + this.value = attribute.value; + this.type = attribute.type; + + } + + + private Attribute() {} + + static Attribute build( + String qualifiedName, String URI, String value, Type type, String localName) { + + Attribute result = new Attribute(); + String prefix = ""; + int prefixPosition = qualifiedName.indexOf(':'); + if (prefixPosition >= 0) { + prefix = qualifiedName.substring(0, prefixPosition); + if ("xml:id".equals(qualifiedName)) { + type = Attribute.Type.ID; + value = normalize(value); + } + } + + result.localName = localName; + result.prefix = prefix; + result.type = type; + result.URI = URI; + result.value = value; + + return result; + + } + + + private static String normalize(String s) { + + int length = s.length(); + int pos = 0; + while (pos < length && s.charAt(pos) == ' ') pos++; + s = s.substring(pos); + int end = s.length()-1; + while (end > 0 && s.charAt(end) == ' ') end--; + s = s.substring(0, end+1); + + length = s.length(); + StringBuffer sb = new StringBuffer(length); + boolean wasSpace = false; + for (int i = 0; i < length; i++) { + char c = s.charAt(i); + if (c == ' ') { + if (wasSpace) continue; + sb.append(' '); + wasSpace = true; + } + else { + sb.append(c); + wasSpace = false; + } + } + return sb.toString(); + + } + + + /** + *

    + * Returns the DTD type of this attribute. + * If this attribute does not have a type, then + * Type.UNDECLARED is returned. + *

    + * + * @return the DTD type of this attribute + */ + public final Type getType() { + return type; + } + + + /** + *

    + * Sets the type of this attribute to one of the ten + * DTD types or Type.UNDECLARED. + *

    + * + * @param type the DTD type of this attribute + * @throws NullPointerException if type is null + * @throws IllegalDataException if this is an xml:id + * attribute and the type is not ID + */ + public void setType(Type type) { + + if (type == null) { + throw new NullPointerException("Null attribute type"); + } + if (isXMLID() && ! Type.ID.equals(type)) { + throw new IllegalDataException( + "Can't change type of xml:id attribute to " + type); + } + _setType(type); + + } + + + private boolean isXMLID() { + return "xml".equals(this.prefix) && "id".equals(this.localName); + } + + + private void _setType(Type type) { + this.type = type; + } + + + /** + *

    + * Returns the attribute value. If the attribute was + * originally created by a parser, it will have been + * normalized according to its type. + * However, attributes created in memory are not normalized. + *

    + * + * @return the value of the attribute + * + */ + public final String getValue() { + return value; + } + + + /** + *

    + * Sets the attribute's value to the specified string, + * replacing any previous value. The value is not normalized + * automatically. + *

    + * + * @param value the value assigned to the attribute + * + * @throws IllegalDataException if the value contains characters + * which are not legal in XML such as vertical tab or a null. + * Characters such as " and & are legal, but will be + * automatically escaped when the attribute is serialized. + */ + public void setValue(String value) { + _setValue(value); + } + + + private void _setValue(String value) { + Verifier.checkPCDATA(value); + if (this.isXMLID()) { + value = normalize(value); + } + this.value = value; + } + + + /** + *

    + * Returns the local name of this attribute, + * not including the prefix. + *

    + * + * @return the attribute's local name + */ + public final String getLocalName() { + return localName; + } + + + /** + *

    + * Sets the local name of the attribute. + *

    + * + * @param localName the new local name + * + * @throws IllegalNameException if localName + * is not a namespace well-formed, non-colonized name + * + */ + public void setLocalName(String localName) { + + if ("id".equals(localName) && + "http://www.w3.org/XML/1998/namespace".equals(this.URI)) { + Verifier.checkNCName(this.value); + } + _setLocalName(localName); + if (isXMLID()) { + this.setType(Attribute.Type.ID); + } + + } + + + private void _setLocalName(String localName) { + Verifier.checkNCName(localName); + if (localName.equals("xmlns")) { + throw new IllegalNameException("The Attribute class is not" + + " used for namespace declaration attributes."); + } + this.localName = localName; + } + + + /** + *

    + * Returns the qualified name of this attribute, + * including the prefix if this attribute is in a namespace. + *

    + * + * @return the attribute's qualified name + */ + public final String getQualifiedName() { + if (prefix.length() == 0) return localName; + else return prefix + ":" + localName; + } + + + /** + *

    + * Returns the namespace URI of this attribute, or the empty string + * if this attribute is not in a namespace. + *

    + * + * @return the attribute's namespace URI + */ + public final String getNamespaceURI() { + return URI; + } + + + /** + *

    + * Returns the prefix of this attribute, + * or the empty string if this attribute + * is not in a namespace. + *

    + * + * @return the attribute's prefix + */ + public final String getNamespacePrefix() { + return prefix; + } + + + /** + *

    + * Sets the attribute's namespace prefix and URI. + * Because attributes must be prefixed in order to have a + * namespace URI (and vice versa) this must be done + * simultaneously. + *

    + * + * @param prefix the new namespace prefix + * @param URI the new namespace URI + * + * @throws MalformedURIException if URI is + * not an RFC 3986 URI reference + * @throws IllegalNameException if + *
      + *
    • The prefix is xmlns.
    • + *
    • The prefix is null or the empty string.
    • + *
    • The URI is null or the empty string.
    • + *
    + * @throws NamespaceConflictException if + *
      + *
    • The prefix is xml and the namespace URI is + * not http://www.w3.org/XML/1998/namespace.
    • + *
    • The prefix conflicts with an existing declaration + * on the attribute's parent element.
    • + *
    + */ + public void setNamespace(String prefix, String URI) { + + _setNamespace(prefix, URI); + if (isXMLID()) { + this.setType(Attribute.Type.ID); + } + + } + + + private void _setNamespace(String prefix, String URI) { + + if (URI == null) URI = ""; + if (prefix == null) prefix = ""; + + if (prefix.equals("xmlns")) { + throw new IllegalNameException( + "Attribute objects are not used to represent " + + " namespace declarations"); + } + else if (prefix.equals("xml") + && !(URI.equals("http://www.w3.org/XML/1998/namespace"))) { + throw new NamespaceConflictException( + "Wrong namespace URI for xml prefix: " + URI); + } + else if (URI.equals("http://www.w3.org/XML/1998/namespace") + && !prefix.equals("xml")) { + throw new NamespaceConflictException( + "Wrong prefix for the XML namespace: " + prefix); + } + else if (prefix.length() == 0) { + if (URI.length() == 0) { + this.prefix = ""; + this.URI = ""; + return; + } + else { + throw new NamespaceConflictException( + "Unprefixed attribute " + this.localName + + " cannot be in default namespace " + URI); + } + } + else if (URI.length() == 0) { + throw new NamespaceConflictException( + "Attribute prefixes must be declared."); + } + + ParentNode parent = this.getParent(); + if (parent != null) { + // test for namespace conflicts + Element element = (Element) parent; + String currentURI = element.getLocalNamespaceURI(prefix); + if (currentURI != null && !currentURI.equals(URI)) { + throw new NamespaceConflictException( + "New prefix " + prefix + + "conflicts with existing namespace declaration" + ); + } + } + + + Verifier.checkAbsoluteURIReference(URI); + Verifier.checkNCName(prefix); + + this.URI = URI; + this.prefix = prefix; + + } + + + /** + *

    + * Throws IndexOutOfBoundsException + * because attributes do not have children. + *

    + * + * @param position the child to return + * + * @return nothing. This method always throws an exception. + * + * @throws IndexOutOfBoundsException because attributes do + * not have children + */ + public final Node getChild(int position) { + throw new IndexOutOfBoundsException( + "Attributes do not have children" + ); + } + + + /** + *

    + * Returns 0 because attributes do not have children. + *

    + * + * @return zero + */ + public final int getChildCount() { + return 0; + } + + + /** + *

    + * Creates a deep copy of this attribute that + * is not attached to an element. + *

    + * + * @return a copy of this attribute + * + */ + public Attribute copy() { + return new Attribute(this); + } + + + /** + *

    + * Returns a string representation of the attribute + * that is a well-formed XML attribute. + *

    + * + * @return a string containing the XML form of this attribute + */ + public final String toXML() { + // It's a common belief that methods like this one should be + // implemented using StringBuffers rather than String + // concatenation for maximum performance. However, + // disassembling the code shows that today's compilers are + // smart enough to figure this out for themselves. The compiled + // version of this class only uses a single StringBuffer. No + // benefit would be gained by making the code more opaque here. + return getQualifiedName() + "=\"" + escapeText(value) + "\""; + } + + + /** + *

    + * Returns a string representation of the attribute suitable for + * debugging and diagnosis. However, this is not necessarily + * a well-formed XML attribute. + *

    + * + * @return a non-XML string representation of this attribute + * + * @see java.lang.Object#toString() + */ + public final String toString() { + return "[" + getClass().getName() + ": " + + getQualifiedName() + "=\"" + + Text.escapeLineBreaksAndTruncate(getValue()) + "\"]"; + } + + + private static String escapeText(String s) { + + int length = s.length(); + // Give the string buffer enough room for a couple of escaped characters + StringBuffer result = new StringBuffer(length+12); + for (int i = 0; i < length; i++) { + char c = s.charAt(i); + switch (c) { + case '\t': + result.append(" "); + break; + case '\n': + result.append(" "); + break; + case 11: + // impossible + break; + case 12: + // impossible + break; + case '\r': + result.append(" "); + break; + case 14: + // impossible + break; + case 15: + // impossible + break; + case 16: + // impossible + break; + case 17: + // impossible + break; + case 18: + // impossible + break; + case 19: + // impossible + break; + case 20: + // impossible + break; + case 21: + // impossible + break; + case 22: + // impossible + break; + case 23: + // impossible + break; + case 24: + // impossible + break; + case 25: + // impossible + break; + case 26: + // impossible + break; + case 27: + // impossible + break; + case 28: + // impossible + break; + case 29: + // impossible + break; + case 30: + // impossible + break; + case 31: + // impossible + break; + case ' ': + result.append(' '); + break; + case '!': + result.append('!'); + break; + case '"': + result.append("""); + break; + case '#': + result.append('#'); + break; + case '$': + result.append('$'); + break; + case '%': + result.append('%'); + break; + case '&': + result.append("&"); + break; + case '\'': + result.append('\''); + break; + case '(': + result.append('('); + break; + case ')': + result.append(')'); + break; + case '*': + result.append('*'); + break; + case '+': + result.append('+'); + break; + case ',': + result.append(','); + break; + case '-': + result.append('-'); + break; + case '.': + result.append('.'); + break; + case '/': + result.append('/'); + break; + case '0': + result.append('0'); + break; + case '1': + result.append('1'); + break; + case '2': + result.append('2'); + break; + case '3': + result.append('3'); + break; + case '4': + result.append('4'); + break; + case '5': + result.append('5'); + break; + case '6': + result.append('6'); + break; + case '7': + result.append('7'); + break; + case '8': + result.append('8'); + break; + case '9': + result.append('9'); + break; + case ':': + result.append(':'); + break; + case ';': + result.append(';'); + break; + case '<': + result.append("<"); + break; + case '=': + result.append('='); + break; + case '>': + result.append(">"); + break; + default: + result.append(c); + } + } + return result.toString(); + + } + + + boolean isAttribute() { + return true; + } + + + /** + *

    + * Uses the type-safe enumeration + * design pattern to represent attribute types, + * as specified by XML DTDs. + *

    + * + *

    + * XOM enforces well-formedness, but it does not enforce + * validity. Thus it is possible for a single element to have + * multiple ID type attributes, or ID type attributes + * on different elements to have the same value, + * or NMTOKEN type attributes that don't contain legal + * XML name tokens, and so forth. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.0 + * + */ + public static final class Type { + + /** + *

    + * The type of attributes declared to have type CDATA + * in the DTD. The most general attribute type. + * All well-formed attribute values are valid for + * attributes of type CDATA. + *

    + */ + public static final Type CDATA = new Type(1); + + /** + *

    + * The type of attributes declared to have type ID + * in the DTD. In order to be valid, an ID type attribute + * must contain an XML name which is unique among other + * ID type attributes in the document. + * Furthermore, each element may contain no more than one + * ID type attribute. However, XOM does not enforce + * such validity constraints. + *

    + */ + public static final Type ID = new Type(2); + + /** + *

    + * The type of attributes declared to have type IDREF + * in the DTD. In order to be valid, an IDREF type attribute + * must contain an XML name which is also the value of + * ID type attribute of some element in the document. + * However, XOM does not enforce such validity constraints. + *

    + * + */ + public static final Type IDREF = new Type(3); + + /** + *

    + * The type of attributes declared to have type IDREFS + * in the DTD. In order to be valid, an IDREFS type attribute + * must contain a white space separated list of + * XML names, each of which is also the value of + * ID type attribute of some element in the document. + * However, XOM does not enforce such validity constraints. + *

    + * + */ + public static final Type IDREFS = new Type(4); + + /** + *

    + * The type of attributes declared to have type NMTOKEN + * in the DTD. In order to be valid, a NMTOKEN type + * attribute must contain a single XML name token. However, + * XOM does not enforce such validity constraints. + *

    + * + */ + public static final Type NMTOKEN = new Type(5); + + /** + *

    + * The type of attributes declared to have type NMTOKENS + * in the DTD. In order to be valid, a NMTOKENS type attribute + * must contain a white space separated list of XML name + * tokens. However, XOM does not enforce such validity + * constraints. + *

    + * + */ + public static final Type NMTOKENS = new Type(6); + + + /** + *

    + * The type of attributes declared to have type NOTATION + * in the DTD. In order to be valid, a NOTATION type + * attribute must contain the name of a notation declared + * in the DTD. However, XOM does not enforce such + * validity constraints. + *

    + * + */ + public static final Type NOTATION = new Type(7); + + /** + *

    + * The type of attributes declared to have type ENTITY + * in the DTD. In order to be valid, a ENTITY type attribute + * must contain the name of an unparsed entity declared in + * the DTD. However, XOM does not enforce such + * validity constraints. + *

    + * + */ + public static final Type ENTITY = new Type(8); + + /** + *

    + * The type of attributes declared to have type ENTITIES + * in the DTD. In order to be valid, an ENTITIES type + * attribute must contain a white space separated list of + * names of unparsed entities declared in the DTD. + * However, XOM does not enforce such validity constraints. + *

    + * + */ + public static final Type ENTITIES = new Type(9); + + /** + *

    + * The type of attributes declared by an enumeration + * in the DTD. In order to be valid, a enumeration type + * attribute must contain exactly one of the names given + * in the enumeration in the DTD. However, XOM does not + * enforce such validity constraints. + *

    + * + *

    + * Most parsers report attributes of type enumeration as + * having type NMTOKEN. In this case, XOM will not + * distinguish NMTOKEN and enumerated attributes. + *

    + * + */ + public static final Type ENUMERATION = new Type(10); + + /** + *

    + * The type of attributes not declared in the DTD. + * This type only appears in invalid documents. + * This is the default type for all attributes in + * documents without DTDs. + *

    + * + *

    + * Most parsers report attributes of undeclared + * type as having type CDATA. In this case, XOM + * will not distinguish CDATA and undeclared attributes. + *

    + */ + public static final Type UNDECLARED = new Type(0); + + + /** + *

    + * Returns the string name of this type as might + * be used in a DTD; for example, "ID", "CDATA", etc. + *

    + * + * @return an XML string representation of this type + */ + public String getName() { + + switch (type) { + case 0: + return "UNDECLARED"; + case 1: + return "CDATA"; + case 2: + return "ID"; + case 3: + return "IDREF"; + case 4: + return "IDREFS"; + case 5: + return "NMTOKEN"; + case 6: + return "NMTOKENS"; + case 7: + return "NOTATION"; + case 8: + return "ENTITY"; + case 9: + return "ENTITIES"; + case 10: + return "ENUMERATION"; + default: + throw new RuntimeException( + "Bug in XOM: unexpected attribute type: " + type); + } + + } + + + private final int type; + + private Type(int type) { + this.type = type; + } + + + /** + *

    + * Returns a unique identifier for this type. + *

    + * + * @return a unique identifier for this type + * + * @see java.lang.Object#hashCode() + */ + public int hashCode() { + return this.type; + } + + + /** + *

    + * Tests for type equality. This is only necessary, + * to handle the case where two Type objects + * are loaded by different class loaders. + *

    + * + * @param o the object compared for equality to this type + * + * @return true if and only if o represents + * the same type + * + * @see java.lang.Object#equals(Object) + */ + public boolean equals(Object o) { + + if (o == this) return true; + if (o == null) return false; + if (this.hashCode() != o.hashCode()) return false; + if (!o.getClass().getName().equals("nu.xom.Attribute.Type")) { + return false; + } + return true; + + } + + + /** + *

    + * Returns a string representation of the type + * suitable for debugging and diagnosis. + *

    + * + * @return a non-XML string representation of this type + * + * @see java.lang.Object#toString() + */ + public String toString() { + + StringBuffer result + = new StringBuffer("[Attribute.Type: "); + result.append(getName()); + result.append(']'); + return result.toString(); + + } + + + } + + +} diff --git a/src/nu/xom/Builder.java b/src/nu/xom/Builder.java new file mode 100644 index 0000000..1eee166 --- /dev/null +++ b/src/nu/xom/Builder.java @@ -0,0 +1,1332 @@ +/* Copyright 2002-2006, 2009, 2010, 2013, 2018 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom; + +import java.io.CharConversionException; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; +import java.io.StringReader; +import java.io.UTFDataFormatException; +import java.net.MalformedURLException; +import java.net.URL; + +import org.xml.sax.ErrorHandler; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; +import org.xml.sax.SAXNotRecognizedException; +import org.xml.sax.SAXNotSupportedException; +import org.xml.sax.SAXParseException; +import org.xml.sax.XMLFilter; +import org.xml.sax.XMLReader; +import org.xml.sax.helpers.XMLReaderFactory; + +import org.apache.xerces.impl.Version; + +/** + *

    + * This class is responsible for creating XOM Document + * objects from a URL, file, string, or input stream by reading + * an XML document. A SAX parser is used to read the + * document and report any well-formedness errors. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.2.11 + * + */ +public class Builder { + + + private XMLReader parser; + private NodeFactory factory; + + private static double xercesVersion = 2.6; + + static { + + try { + String x = Version.getVersion(); + String versionString = x.substring(9); + int firstPeriod = versionString.indexOf("."); + int secondPeriod = versionString.lastIndexOf("."); + String major = versionString.substring(0, firstPeriod); + String minor = versionString.substring(firstPeriod+1, secondPeriod); + if (Integer.parseInt(minor) < 10 && Integer.parseInt(major) < 3) { + xercesVersion = Double.parseDouble(x.substring(9,12)); + } + // else it's 2.6 or later which is all we really need to know + } + catch (Exception ex) { + // The version string format changed so presumably it's + // 2.6 or later + } + catch (Error err) { + // Xerces not installed, so none of this matters + } + + } + + + /** + *

    + * Creates a Builder that uses the default node + * factory and chooses among any available SAX2 parsers. + * In order of preference, it looks for: + *

    + * + *
      + *
    1. Xerces 2.x (a.k.a. IBM XML parser for Java)
    2. + *
    3. GNU Ælfred
    4. + *
    5. Crimson
    6. + *
    7. Piccolo
    8. + *
    9. Oracle
    10. + *
    11. XP
    12. + *
    13. Saxon's Ælfred
    14. + *
    15. dom4j's Ælfred
    16. + *
    17. The platform default specified by the + * org.xml.sax.driver system property
    18. + *
    + * + *

    + * Parsers must implicitly or explicitly support the + * http://xml.org/sax/features/external-general-entities + * and + * http://xml.org/sax/features/external-parameter-entities + * features XOM requires. Parsers that don't are rejected + * automatically. + *

    + * + * @throws XMLException if no satisfactory parser is + * installed in the local class path + */ + public Builder() { + this(false); + } + + + /** + *

    + * Creates a Builder based on an optionally validating + * parser. If the validate argument + * is true, then a validity error while + * parsing will cause a fatal error; that is, + * it will throw a ValidityException. + *

    + * + * @param validate true if the parser should + * validate the document while parsing + * + * @throws XMLException if no satisfactory parser + * is installed in the local class path + */ + public Builder(boolean validate) { + this(findParser(validate), validate, null); + } + + + /** + *

    + * Creates a Builder based on an optionally + * validating parser that builds node objects with the supplied + * factory. If the validate argument is true, then + * a validity error while parsing will cause a fatal error; that + * is, it will throw a ValidityException. + *

    + * + * @param validate true if the parser should + * validate the document while parsing + * @param factory the NodeFactory that creates + * the node objects for this Builder + * + * @throws XMLException if no satisfactory parser + * is installed in the local class path + */ + public Builder(boolean validate, NodeFactory factory) { + this(findParser(validate), validate, factory); + } + + + // These are stored in the order of preference. + private static String[] parsers = { + /*"nu.xom.XML1_0Parser",*/ + /*"nu.xom.JDK15XML1_0Parser",*/ + "org.apache.xerces.parsers.SAXParser", + "org.apache.xerces.jaxp.SAXParserImpl$JAXPSAXParser", // xerces-2.9.x + "com.sun.org.apache.xerces.internal.jaxp.SAXParserImpl$JAXPSAXParser", // JDK 1.6 + "com.sun.org.apache.xerces.internal.parsers.SAXParser", + "gnu.xml.aelfred2.XmlReader", + "org.apache.crimson.parser.XMLReaderImpl", + "com.bluecast.xml.Piccolo", + "oracle.xml.parser.v2.SAXParser", + "com.jclark.xml.sax.SAX2Driver", + "net.sf.saxon.aelfred.SAXDriver", + "com.icl.saxon.aelfred.SAXDriver", + "org.dom4j.io.aelfred2.SAXDriver", + "org.dom4j.io.aelfred.SAXDriver", + "org.xmlpull.v1.sax2.Driver" // android + }; + + + static XMLReader findParser(boolean validate) { + + // first look for Xerces; we only trust Xerces if + // we set it up; and we need to configure it specially + // so we can't load it with the XMLReaderFactory + XMLReader parser; + /*try { + parser = new XML1_0Parser(); + setupParser(parser, validate); + System.out.println("parser JXML1_0Parser"); + return parser; + } + catch (SAXException ex) { + // look for next one + } + catch (NoClassDefFoundError err) { + // Xerces is not available; look for next one + } + + try { + parser = (XMLReader) Class.forName( + "nu.xom.JDK15XML1_0Parser").newInstance(); + setupParser(parser, validate); + System.out.println("parser JDK15XML1_0Parser"); + return parser; + } + catch (SAXException ex) { + // look for next one + } + catch (InstantiationException ex) { + // look for next one + } + catch (ClassNotFoundException ex) { + // look for next one + } + catch (IllegalAccessException ex) { + // look for next one + } + catch (NoClassDefFoundError err) { + // Xerces is not available; look for next one + } + */ + // XMLReaderFactory.createXMLReader never returns + // null. If it can't locate the parser, it throws + // a SAXException. + for (int i = 2; i < parsers.length; i++) { + try { + parser = XMLReaderFactory.createXMLReader(parsers[i]); + setupParser(parser, validate); + return parser; + } + catch (SAXException ex) { + // try the next one + } + catch (NoClassDefFoundError err) { + // try the next one + } + } + + try { // default + parser = XMLReaderFactory.createXMLReader(); + setupParser(parser, validate); + return parser; + } + catch (SAXException ex) { + throw new XMLException( + "Could not find a suitable SAX2 parser", ex); + } + + } + + + private static void setupParser(XMLReader parser, boolean validate) + throws SAXNotRecognizedException, SAXNotSupportedException { + + // General configuration for all parsers + parser.setFeature( + "http://xml.org/sax/features/namespace-prefixes", true); + parser.setFeature( + "http://xml.org/sax/features/namespaces", true); + + // Parser specific configuration + XMLReader baseParser = parser; + while (baseParser instanceof XMLFilter) { + XMLReader parent = ((XMLFilter) baseParser).getParent(); + if (parent == null) break; + baseParser = parent; + } + + String parserName = baseParser.getClass().getName(); + if (!validate) { + if (parserName.equals( // Crimson workaround + "org.apache.crimson.parser.XMLReaderImpl")) { + parser.setErrorHandler( + new NamespaceWellformednessRequired() + ); + } + else { + parser.setFeature( + "http://xml.org/sax/features/external-general-entities", + true + ); + parser.setFeature( + "http://xml.org/sax/features/external-parameter-entities", + true + ); + } + } + else { + parser.setFeature( + "http://xml.org/sax/features/validation", true); + parser.setErrorHandler(new ValidityRequired()); + } + + try { + parser.setFeature( + "http://xml.org/sax/features/string-interning", true); + } + catch (SAXException ex) { + // This parser does not support string interning. + // We can live without that. + } + + // A couple of Xerces specific properties + /*if (parserName.equals("nu.xom.XML1_0Parser") + || parserName.equals("nu.xom.JDK15XML1_0Parser") + || parserName.equals("org.apache.xerces.parsers.SAXParser") + || parserName.equals("com.sun.org.apache.xerces.internal.parsers.SAXParser") + || parserName.equals("org.apache.xerces.jaxp.SAXParserImpl$JAXPSAXParser") // xerces-2.9.x + || parserName.equals("com.sun.org.apache.xerces.internal.jaxp.SAXParserImpl$JAXPSAXParser")) // JDK 1.6 + {*/ + if (parserName.equals("org.apache.xerces.parsers.SAXParser") + || parserName.equals("com.sun.org.apache.xerces.internal.parsers.SAXParser") + || parserName.equals("org.apache.xerces.jaxp.SAXParserImpl$JAXPSAXParser") // xerces-2.9.x + || parserName.equals("com.sun.org.apache.xerces.internal.jaxp.SAXParserImpl$JAXPSAXParser")) // JDK 1.6 + { + try { + parser.setFeature( + "http://apache.org/xml/features/allow-java-encodings", true); + } + catch (SAXException ex) { + // Possibly an earlier version of Xerces; no big deal. + // We can live without this feature. + } + // See http://nagoya.apache.org/bugzilla/show_bug.cgi?id=23768 + // if you care to know why this line breaks unit tests on + // versions of Xerces prior to 2.6.1 + try { + parser.setFeature( + "http://apache.org/xml/features/standard-uri-conformant", + true); + } + catch (SAXException ex) { + // Possibly an earlier version of Xerces, or a + // or a non-Xerces parser; no big deal. + // We can live without this. + } + } + + } + + + /** + *

    + * Creates a Builder that uses + * the specified SAX XMLReader. + * Custom SAX features and properties such as + * schema validation can be set on this XMLReader + * before passing it to this method. + *

    + * + * @param parser the SAX2 XMLReader that + * parses the document + * + * @throws XMLException if parser does not support the + * features XOM requires + */ + public Builder(XMLReader parser) { + this(parser, false); + } + + + /** + *

    + * Creates a Builder that uses + * the specified NodeFactory to create + * node objects. + *

    + * + * @param factory the NodeFactory that creates + * the node objects for this Builder + * + * @throws XMLException if no satisfactory parser is + * installed in the local class path + */ + public Builder(NodeFactory factory) { + this(findParser(false), false, factory); + } + + + /** + *

    + * Creates a optionally validating Builder based + * on the specified parser object. Custom SAX features and + * properties such as schema validation can be set on this + * XMLReader before passing it to this method. + *

    + * + *

    + * If the validate argument is true, then a validity error + * while parsing will cause a fatal error; that is, it + * will throw a ParsingException + *

    + * + * @param parser the SAX2 XMLReader that parses + * the document + * @param validate true if the parser should validate + * the document while parsing + * + */ + public Builder(XMLReader parser, boolean validate) { + this(parser, validate, null); + } + + + /** + *

    + * Creates an optionally validating Builder that reads + * data from the specified parser object and constructs new nodes + * using the specified factory object. Custom SAX features and + * properties such as schema validation can be set on this + * XMLReader before passing it to this method. + *

    + * + *

    + * If the validate argument is true, then a validity + * error while parsing will throw a ParsingException. + *

    + * + * @param parser the SAX2 XMLReader that parses + * the document + * @param validate true if the parser should validate the + * document while parsing + * @param factory the NodeFactory + * this builder uses to create objects in the tree + * + * @throws XMLException if parser does not support + * the features XOM requires + * + */ + public Builder( + XMLReader parser, boolean validate, NodeFactory factory) { + + try { + setupParser(parser, validate); + } + catch (SAXException ex) { + if (validate) { + throw new XMLException(parser.getClass().getName() + + " does not support validation.", ex); + } + else { + throw new XMLException(parser.getClass().getName() + + " does not support the entity resolution" + + " features XOM requires.", ex); + } + } + + // setup the handlers + this.parser = parser; + this.factory = factory; + setHandlers(); + + } + + + private static boolean knownGoodParser(XMLReader parser) { + + String parserName = parser.getClass().getName(); + + // In general, a filter may violate the constraints of XML 1.0. + // However, I specifically trust Norm Walsh not to do that, so + // if his filters are being used we look at the parent instead. + if (parserName.equals("org.apache.xml.resolver.tools.ResolvingXMLFilter")) { + XMLFilter filter = (XMLFilter) parser; + parserName = filter.getParent().getClass().getName(); + } + + // These parsers are known to not make all the checks + // they're supposed to. :-( + if (parserName.equals("gnu.xml.aelfred2.XmlReader")) return false; + if (parserName.equals("net.sf.saxon.aelfred.SAXDriver")) return false; + if (parserName.equals("com.icl.saxon.aelfred.SAXDriver")) return false; + + if (parserName.equals("org.apache.xerces.parsers.SAXParser") + && xercesVersion >= 2.4) { + return false; + } + + for (int i = 0; i < parsers.length; i++) { + if (parserName.equals(parsers[i])) return true; + } + return false; + + } + + + private void setHandlers() { + XOMHandler handler; + if ((factory == null + || factory.getClass().getName().equals("nu.xom.NodeFactory")) + && knownGoodParser(parser)) { + // If no factory is supplied by user, don't + // return one + NodeFactory tempFactory = factory; + if (tempFactory == null) tempFactory = new NodeFactory(); + handler = new NonVerifyingHandler(tempFactory); + } + else { + if (factory == null) factory = new NodeFactory(); + handler = new XOMHandler(factory); + } + + parser.setContentHandler(handler); + parser.setDTDHandler(handler); + + try { + parser.setProperty( + "http://xml.org/sax/properties/lexical-handler", + handler); + } + catch (SAXException ex) { + // This parser does not support lexical events. + // We can live without them, though it does mean + // there won't be any comments or a DOCTYPE declaration + // in the tree. + } + + try { + parser.setProperty( + "http://xml.org/sax/properties/declaration-handler", + handler); + // Due to Crimson bugs in misidentifying the internal and + // external DTD subsets, we only build the internal DTD + // subset if there is no external DTD subset. + if (parser.getClass().getName().equals( + "org.apache.crimson.parser.XMLReaderImpl")) { + handler.usingCrimson = true; + } + } + catch (SAXException ex) { + // This parser does not support declaration events. + // We can live without them, though it does mean + // they won't be any internal DTD subset. + } + + } + + + /** + *

    + * Parses the document at the specified URL. + *

    + * + *

    + * Note that relative URLs generally do not work here, as + * there's no base to resolve them against. This includes + * relative URLs that point into the file system, though this + * is somewhat platform dependent. Furthermore, file + * URLs often only work when they adhere exactly to RFC 2396 + * syntax. URLs that work in Internet Explorer often fail when + * used in Java. If you're reading XML from a file, more reliable + * results are obtained by using the build method + * that takes a java.io.File object as an argument. + *

    + * + * @param systemID an absolute URL from which the document is read. + * The URL's scheme must be one supported by the Java VM. + * + * @return the parsed Document + * + * @throws ValidityException if a validity error is detected. This + * is only thrown if the builder has been instructed to validate. + * @throws ParsingException if a well-formedness error is detected + * @throws IOException if an I/O error such as a broken socket + * prevents the document from being fully read + */ + public Document build(String systemID) + throws ParsingException, ValidityException, IOException { + + systemID = canonicalizeURL(systemID); + InputSource source = new InputSource(systemID); + return build(source); + + } + + + /** + *

    + * Reads the document from an input stream. + *

    + * + * @param in the input stream from which the document is read + * + * @return the parsed Document + * + * @throws ValidityException if a validity error is detected; + * only thrown if the builder has been instructed to validate + * @throws ParsingException if a well-formedness error is detected + * @throws IOException if an I/O error such as a broken + * socket prevents the document from being fully read + * @throws NullPointerException if in is null + */ + public Document build(InputStream in) + throws ParsingException, ValidityException, IOException { + + if (in == null) throw new NullPointerException("Null InputStream"); + InputSource source = new InputSource(in); + return build(source); + + } + + + /** + *

    + * Reads the document from an input stream while specifying + * a base URI (which need not be the stream's actual URI). + *

    + * + * @param in the input stream from which the document is read + * @param baseURI an absolute URI for this document; may be null + * + * @return the parsed Document + * + * @throws ValidityException if a validity error is detected; + * only thrown if the builder has been instructed to validate + * @throws ParsingException if a well-formedness error is detected + * @throws IOException if an I/O error such as a broken + * socket prevents the document from being fully read + */ + public Document build(InputStream in, String baseURI) + throws ParsingException, ValidityException, IOException { + + InputSource source = new InputSource(in); + if (baseURI != null) { + baseURI = canonicalizeURL(baseURI); + source.setSystemId(baseURI); + } + return build(source); + + } + + + // Nasty hack to make sure we get the right form + // of file URLs on Windows + private static String fileURLPrefix = "file://"; + + static { + String os = System.getProperty("os.name", "Unix"); + // I could do System.setProperty("os.name" "Windows") to test + // this, but I'd need to use a fresh ClassLoader to rerun the + // static initializer block. + if (os.indexOf("Windows") >= 0) { + fileURLPrefix = "file:/"; + } + } + + + /** + *

    + * Reads the document from a file. + * The base URI of the document is set to the + * location of the file. + *

    + * + * @param in the file from which the document is read + * + * @return the parsed Document + * + * @throws ValidityException if a validity error is detected. This + * is only thrown if the builder has been instructed to validate. + * @throws ParsingException if a well-formedness error is detected + * @throws IOException if an I/O error such as a bad disk + * prevents the file from being read + */ + public Document build(File in) + throws ParsingException, ValidityException, IOException { + + InputStream fin = new FileInputStream(in); + // Java's toURL method doesn't properly escape file + // names so we have to do it manually + String absolute = in.getAbsolutePath(); + StringBuffer url = new StringBuffer(fileURLPrefix); + int length = absolute.length(); + char separatorChar = File.separatorChar; + for (int i = 0; i < length; i++) { + char c = absolute.charAt(i); + if (c == separatorChar) url.append('/'); + else { + switch(c) { + case ' ': + url.append("%20"); + break; + case '!': + url.append(c); + break; + case '"': + url.append("%22"); + break; + case '#': + url.append("%23"); + break; + case '$': + url.append(c); + break; + case '%': + url.append("%25"); + break; + case '&': + // ampersand does not need to be encoded in + // path part of URL + url.append('&'); + break; + case '\'': + url.append(c); + break; + case '(': + url.append(c); + break; + case ')': + url.append(c); + break; + case '*': + url.append(c); + break; + case '+': + url.append("%2B"); + break; + case ',': + url.append(c); + break; + case '-': + url.append(c); + break; + case '.': + url.append(c); + break; + case '/': + url.append("%2F"); + break; + case '0': + url.append(c); + break; + case '1': + url.append(c); + break; + case '2': + url.append(c); + break; + case '3': + url.append(c); + break; + case '4': + url.append(c); + break; + case '5': + url.append(c); + break; + case '6': + url.append(c); + break; + case '7': + url.append(c); + break; + case '8': + url.append(c); + break; + case '9': + url.append(c); + break; + case ':': + url.append(c); + break; + case ';': + url.append(c); + break; + case '<': + url.append("%3C"); + break; + case '=': + url.append(c); + break; + case '>': + url.append("%3E"); + break; + case '?': + url.append("%3F"); + break; + case '@': + url.append("%40"); + break; + case 'A': + url.append(c); + break; + case 'B': + url.append(c); + break; + case 'C': + url.append(c); + break; + case 'D': + url.append(c); + break; + case 'E': + url.append(c); + break; + case 'F': + url.append(c); + break; + case 'G': + url.append(c); + break; + case 'H': + url.append(c); + break; + case 'I': + url.append(c); + break; + case 'J': + url.append(c); + break; + case 'K': + url.append(c); + break; + case 'L': + url.append(c); + break; + case 'M': + url.append(c); + break; + case 'N': + url.append(c); + break; + case 'O': + url.append(c); + break; + case 'P': + url.append(c); + break; + case 'Q': + url.append(c); + break; + case 'R': + url.append(c); + break; + case 'S': + url.append(c); + break; + case 'T': + url.append(c); + break; + case 'U': + url.append(c); + break; + case 'V': + url.append(c); + break; + case 'W': + url.append(c); + break; + case 'X': + url.append(c); + break; + case 'Y': + url.append(c); + break; + case 'Z': + url.append(c); + break; + case '[': + url.append("%5B"); + break; + case '\\': + url.append("%5C"); + break; + case ']': + url.append("%5D"); + break; + case '^': + url.append("%5E"); + break; + case '_': + url.append(c); + break; + case '`': + url.append("%60"); + break; + case 'a': + url.append(c); + break; + case 'b': + url.append(c); + break; + case 'c': + url.append(c); + break; + case 'd': + url.append(c); + break; + case 'e': + url.append(c); + break; + case 'f': + url.append(c); + break; + case 'g': + url.append(c); + break; + case 'h': + url.append(c); + break; + case 'i': + url.append(c); + break; + case 'j': + url.append(c); + break; + case 'k': + url.append(c); + break; + case 'l': + url.append(c); + break; + case 'm': + url.append(c); + break; + case 'n': + url.append(c); + break; + case 'o': + url.append(c); + break; + case 'p': + url.append(c); + break; + case 'q': + url.append(c); + break; + case 'r': + url.append(c); + break; + case 's': + url.append(c); + break; + case 't': + url.append(c); + break; + case 'u': + url.append(c); + break; + case 'v': + url.append(c); + break; + case 'w': + url.append(c); + break; + case 'x': + url.append(c); + break; + case 'y': + url.append(c); + break; + case 'z': + url.append(c); + break; + case '{': + url.append("%7B"); + break; + case '|': + url.append("%7C"); + break; + case '}': + url.append("%7D"); + break; + case '~': + url.append(c); + break; + default: + if (c < 0xD800 || c > 0xDFFF) { + url.append(URIUtil.percentEscape(c)); + } + else if (c <= 0xDBFF) { + // high surrogate; therefore we need to + // grab the next half before encoding + i++; + try { + char low = absolute.charAt(i); + String character = String.valueOf(c)+low; + byte[] data = character.getBytes("UTF8"); + // Always exactly 4 bytes, unless the encoder is buggy + for (int j=0; j < 4; j++) { + url.append('%'); + String hex = Integer.toHexString(data[j]).toUpperCase(); + url.append(hex.substring(hex.length()-2)); + } + } + catch (IndexOutOfBoundsException ex) { + // file name contains a high half and not a low half + url = new StringBuffer(0); + break; + } + } + else { + // low half not preceded by high half + // Can't create a base URI + url = new StringBuffer(0); + break; + } + } + } + } + + String base = url.toString(); + try { + Document doc = build(fin, base); + return doc; + } + finally { + fin.close(); + } + + } + + + /** + *

    + * Reads the document from a reader. + *

    + * + * @param in the reader from which the document is read + * + * @return the parsed Document + * + * @throws ValidityException if a validity error is detected. This + * is only thrown if the builder has been instructed to validate. + * @throws ParsingException if a well-formedness error is detected + * @throws IOException if an I/O error such as a bad disk + * prevents the document from being fully read + */ + public Document build(Reader in) + throws ParsingException, ValidityException, IOException { + + if (in == null) throw new NullPointerException("Attempted to build from null reader"); + InputSource source = new InputSource(in); + return build(source); + + } + + + /** + *

    + * Reads the document from a character stream while + * specifying a base URI. + *

    + * + * @param in the reader from which the document + * is read + * @param baseURI the base URI for this document; may be null + * + * @return the parsed Document + * + * @throws ValidityException if a validity error is detected. This + * is only thrown if the builder has been instructed to + * validate. + * @throws ParsingException if a well-formedness error is detected + * @throws IOException if an I/O error such as a bad disk + * prevents the document from being completely read + */ + public Document build(Reader in, String baseURI) + throws ParsingException, ValidityException, IOException { + + InputSource source = new InputSource(in); + if (baseURI != null) { + baseURI = canonicalizeURL(baseURI); + source.setSystemId(baseURI); + } + return build(source); + + } + + + /** + *

    + * Reads the document from the contents of a string. + *

    + * + * @param document the string that contains the XML document + * @param baseURI the base URI for this document; may be null + * + * @return the parsed Document + * + * @throws ValidityException if a validity error is detected. This + * is only thrown if the builder has been instructed to + * validate. + * @throws ParsingException if a well-formedness error is detected + * @throws IOException if an I/O error such as a bad disk + * prevents the document's external DTD subset from being read + */ + public Document build(String document, String baseURI) + throws ParsingException, ValidityException, IOException { + + Reader reader = new StringReader(document); + return build(reader, baseURI); + + } + + // needed to work around a bug in Xerces and Crimson + // for URLs with no trailing slashes (no path part) + // such as http://www.cafeconleche.org. + // Also needed to work around a VM bug involving file URLs such as + // file:///tmp/nosuchdirectory/../foo.xml + // where "nosuchdirectory" does not exist. + private String canonicalizeURL(String uri) { + + try { + URL u = new URL(uri); + String path = u.getPath(); + String scheme = u.getProtocol(); + String authority = u.getHost(); + String query = u.getQuery(); + int port = u.getPort(); + // fragment ID not needed + if (path == null || path.length() == 0) { + // We handle here the case where we have a URL such as + // http://www.cafeaulait.org with no trailing slash. + path = "/"; + } + // If this proves to be a hot spot we could probably take this path + // only if the scheme is file; not in the more common case where + // it's http + path = URIUtil.removeDotSegments(path); + StringBuffer canonicalForm = new StringBuffer(uri.length()); + canonicalForm.append(scheme); + canonicalForm.append("://"); + if (authority != null) canonicalForm.append(authority); + if (port >= 0) canonicalForm.append(":" + port); + canonicalForm.append(path); + if (query != null) canonicalForm.append("?" + query); + return canonicalForm.toString(); + } + catch (MalformedURLException ex) { + return uri; + } + } + + + /** + *

    + * Reads the document from a SAX InputSource. + *

    + * + * @param in the input source from which the document is read + * + * @return the parsed Document + * + * @throws ValidityException if a validity error is detected. This + * is only thrown if the builder has been instructed to + * validate. + * @throws ParsingException if a well-formedness error is detected + * @throws IOException if an I/O error such as a bad disk + * prevents the document from being read + */ + private Document build(InputSource in) + throws ParsingException, ValidityException, IOException { + + XOMHandler handler = (XOMHandler) parser.getContentHandler(); + Document result = null; + try { + parser.parse(in); + result = handler.getDocument(); + } + catch (SAXParseException ex) { + ParsingException pex = new ParsingException( + ex.getMessage(), + ex.getSystemId(), + ex.getLineNumber(), + ex.getColumnNumber(), + ex); + throw pex; + } + catch (SAXException ex) { + ParsingException pex + = new ParsingException(ex.getMessage(), in.getSystemId(), ex); + throw pex; + } + catch (XMLException ex) { + throw new ParsingException(ex.getMessage(), ex); + } + catch (RuntimeException ex) { + // Work-around for non-conformant parsers, especially Piccolo + ParsingException pex + = new ParsingException(ex.getMessage(), in.getSystemId(), ex); + throw pex; + } + catch (UTFDataFormatException ex) { + // Work-around for non-conformant parsers, especially Xerces + // http://nagoya.apache.org/bugzilla/show_bug.cgi?id=27583 + ParsingException pex + = new ParsingException(ex.getMessage(), in.getSystemId(), ex); + throw pex; + } + catch (CharConversionException ex) { + // Work-around for non-conformant parsers, especially Xerces + // http://nagoya.apache.org/bugzilla/show_bug.cgi?id=27583 + ParsingException pex + = new ParsingException(ex.getMessage(), in.getSystemId(), ex); + throw pex; + } + catch (IOException ex) { + // Work-around for Xerces; I don't want to just catch + // org.apache.xerces.util.URI.MalformedURIException + // because that would introduce a dependence on Xerces + if (ex.getClass().getName().equals( + "org.apache.xerces.util.URI$MalformedURIException")) { + throw new ParsingException(ex.getMessage(), in.getSystemId(), ex); + } + else { + throw ex; + } + } + finally { + handler.freeMemory(); + } + + if (result == null) { + ParsingException ex = new ParsingException( + "Parser did not build document", + in.getSystemId(), -1, -1 + ); + throw ex; + } + + if ("".equals(result.getBaseURI())) { + result.setBaseURI(in.getSystemId()); + } + + ErrorHandler errorHandler = parser.getErrorHandler(); + if (errorHandler instanceof ValidityRequired) { + ValidityRequired validityHandler + = (ValidityRequired) errorHandler; + if (!validityHandler.isValid()) { + ValidityException vex = validityHandler.vexception; + vex.setDocument(result); + validityHandler.reset(); + throw vex; + } + } + return result; + + } + + + private static class ValidityRequired implements ErrorHandler { + + ValidityException vexception = null; + + void reset() { + vexception = null; + } + + public void warning(SAXParseException exception) { + // ignore warnings + } + + public void error(SAXParseException exception) { + + if (vexception == null) { + vexception = new ValidityException( + exception.getMessage(), + exception.getSystemId(), + exception.getLineNumber(), + exception.getColumnNumber(), + exception); + } + vexception.addError(exception); + } + + public void fatalError(SAXParseException exception) + throws SAXParseException { + throw exception; + } + + boolean isValid() { + return vexception == null; + } + + } + + + // Because Crimson doesn't report namespace errors as fatal + private static class NamespaceWellformednessRequired + implements ErrorHandler { + + public void warning(SAXParseException exception) { + // ignore warnings + } + + public void error(SAXParseException exception) + throws SAXParseException { + + if (exception.getMessage().equals("Illegal Namespace prefix: \"xml\".")) { + return; + } + + throw exception; + + } + + public void fatalError(SAXParseException exception) + throws SAXParseException { + throw exception; + } + + } + + + // I added this because XIncluder needed it. + /** + *

    + * Returns this builder's NodeFactory. It returns + * null if a factory was not supplied when the builder was created. + *

    + * + * @return the node factory that was specified in the constructor + */ + public NodeFactory getNodeFactory() { + return factory; + } + + +} \ No newline at end of file diff --git a/src/nu/xom/CDATASection.java b/src/nu/xom/CDATASection.java new file mode 100644 index 0000000..ca2535d --- /dev/null +++ b/src/nu/xom/CDATASection.java @@ -0,0 +1,66 @@ +/* Copyright 2003-2005 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom; + +/** + *

    + * This class represents a CDATA section. + * Builders will sometimes use this class to represent + * a CDATA section. However, they are not required to do so. + * This class is used solely for preservation of CDATA sections + * from input to output. + *

    + + * @author Elliotte Rusty Harold + * @version 1.1b4 + * + */ +class CDATASection extends Text { + + + CDATASection(Text text) { + super(text); + } + + + CDATASection(String data) { + super(data); + } + + + boolean isCDATASection() { + return true; + } + + + static Text build(String data) { + return new CDATASection(data); + } + + String escapeText() { + String s = this.getValue(); + if (s.indexOf("]]>") != -1) return super.escapeText(); + else return ""; + } + + +} diff --git a/src/nu/xom/Comment.java b/src/nu/xom/Comment.java new file mode 100644 index 0000000..4138f4f --- /dev/null +++ b/src/nu/xom/Comment.java @@ -0,0 +1,243 @@ +/* Copyright 2002-2005 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom; + +/** + *

    + * This class represents an XML comment such as + * <-- This is a comment-->. + * A comment node cannot have any child nodes. + * It can be a child of an Element + * or a Document. + * It has essentially no internal substructure. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.3.0 + * + */ +public class Comment extends Node { + + + private String data; + + /** + *

    + * Creates a new Comment object from string data. + * The data is checked for legality according to XML 1.0 rules. + * Illegal characters such as the form feed and null are not + * allowed. Furthermore, the two hyphen string "--" is not allowed; + * and the last character of the comment must not be a hyphen. + *

    + * + * @param data the initial text of the comment + */ + public Comment(String data) { + _setValue(data); + } + + + /** + *

    + * Creates a new comment that's a copy of its argument. + * The copy has the same data but no parent node. + *

    + * + * @param comment the comment to copy + */ + public Comment(Comment comment) { + this.data = comment.data; + } + + + private Comment() {} + + static Comment build(String data) { + Comment result = new Comment(); + result.data = data; + return result; + } + + + /** + *

    + * Returns the value of this comment as defined by XPath 1.0. + * The XPath string-value of a comment node is the string + * content of the node, not including the initial + * <-- and closing -->. + *

    + * + * @return the content of the comment + */ + public final String getValue() { + return data; + } + + + /** + *

    + * Sets the content of this Comment object + * to the specified string. + * This string is checked for legality according to XML 1.0 rules. + * Characters that can be serialized such as < and & + * are allowed. However, illegal characters such as the form feed + * and unmatched halves of surrogate pairs are not allowed. + * Furthermore, the string may not contain a double hyphen + * (--) and may not end with a hyphen. + *

    + * + * @param data the text to install in the comment + */ + public void setValue(String data) { + _setValue(data); + } + + + private void _setValue(String data) { + + if (data == null) data = ""; + else { + Verifier.checkPCDATA(data); + + if (data.indexOf("--") != -1) { + IllegalDataException ex = new IllegalDataException( + "Comment data contains a double hyphen (--)."); + ex.setData(data); + throw ex; + } + + if (data.indexOf('\r') != -1) { + IllegalDataException ex = new IllegalDataException( + "Comment data cannot contain carriage returns."); + ex.setData(data); + throw ex; + } + + if (data.endsWith("-")) { + IllegalDataException ex = new IllegalDataException( + "Comment data ends with a hyphen."); + ex.setData(data); + throw ex; + } + + } + this.data = data; + + } + + + /** + *

    + * Throws IndexOutOfBoundsException because + * comments do not have children. + *

    + * + * @return never returns because comments do not have children; + * Always throws an exception. + * + * @param position the index of the child node to return + * + * @throws IndexOutOfBoundsException because comments + * do not have children + */ + public final Node getChild(int position) { + throw new IndexOutOfBoundsException( + "LeafNodes do not have children"); + } + + + /** + *

    + * Returns 0 because comments do not have children. + *

    + * + * @return zero + */ + public final int getChildCount() { + return 0; + } + + + /** + *

    + * Returns a deep copy of this Comment object + * which contains the same text, but does not have any parent. + * Thus, it can be inserted into a different document. + *

    + * + * @return a deep copy of this Comment + * that is not part of a document + * + */ + public Comment copy() { + return new Comment(data); + } + + + /** + *

    + * Returns a String containing the actual XML + * form of the comment; + * for example, <--This is a comment-->. + *

    + * + * @return a String containing a well-formed + * XML comment + */ + public final String toXML() { + StringBuffer result = new StringBuffer(""); + return result.toString(); + } + + + /** + *

    + * Returns a string form of the comment suitable for debugging + * and diagnosis. It deliberately does not return an actual + * XML comment. + *

    + * + * @return a representation of the Comment + * as a String + */ + public final String toString() { + + String value = getValue(); + if (value.length() <= 40) { + return "[" + getClass().getName() + ": " + + Text.escapeLineBreaksAndTruncate(value) + "]"; + } + + return "[" + getClass().getName() + ": " + + Text.escapeLineBreaksAndTruncate(value.substring(0, 35)) + "...]"; + + } + + + boolean isComment() { + return true; + } + + +} diff --git a/src/nu/xom/CycleException.java b/src/nu/xom/CycleException.java new file mode 100644 index 0000000..0808b78 --- /dev/null +++ b/src/nu/xom/CycleException.java @@ -0,0 +1,67 @@ +/* Copyright 2002-2005 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom; + +/** + *

    + * Indicates an attempt to + * insert an ancestor element as one of its own descendants. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.1b3 + * + */ +public class CycleException extends IllegalAddException { + + + private static final long serialVersionUID = 6690940405434358625L; + + + /** + *

    + * Creates a new CycleException + * with a detail message. + *

    + * + * @param message a string indicating the specific problem + */ + public CycleException(String message) { + super(message); + } + + + /** + *

    + * Creates a new CycleException with a detail message + * and an underlying root cause. + *

    + * + * @param message a string indicating the specific problem + * @param cause the original cause of this exception + */ + public CycleException(String message, Throwable cause) { + super(message, cause); + } + + +} \ No newline at end of file diff --git a/src/nu/xom/DocType.java b/src/nu/xom/DocType.java new file mode 100644 index 0000000..0b744c2 --- /dev/null +++ b/src/nu/xom/DocType.java @@ -0,0 +1,613 @@ +/* Copyright 2002-2005 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom; + + +/** + *

    + * Represents an XML document type declaration such as + * <!DOCTYPE book SYSTEM "docbookx.dtd">. + * Note that this is not the same thing as a document + * type definition (DTD). XOM does not currently model + * the DTD. The document type declaration contains or points to + * the DTD, but it is not the DTD. + *

    + * + *

    + * A DocType object does not have any child + * nodes. It can be a child of a Document. + *

    + * + *

    + * Each DocType object has four String + * properties, some of which may be null: + *

    + * + *
      + *
    • The declared name of the root element (which + * does not necessarily match the actual root element name + * in an invalid document)
    • + *
    • The public identifier (which may be null)
    • + *
    • The system identifier (which may be null)
    • + *
    • The internal DTD subset (which may be null)
    • + * + *
    + * + * @author Elliotte Rusty Harold + * @version 1.1d1 + * + */ +public class DocType extends Node { + + + private String rootName; + private String systemID; + private String publicID; + private String internalDTDSubset = ""; + + + /** + *

    + * Creates a new document type declaration with a public ID + * and a system ID. It has the general form + * <!DOCTYPE rootElementName PUBLIC + * "publicID" "systemID">. + *

    + * + * @param rootElementName the name specified for the root element + * @param publicID the public ID of the external DTD subset + * @param systemID the URL of the external DTD subset + * + * @throws IllegalNameException if rootElementName + * is not a legal XML 1.0 name + * @throws IllegalDataException if publicID is not a + * legal XML 1.0 public identifier + * @throws MalformedURIException if the system ID is not a + * syntactically correct URI, or if it contains a fragment + * identifier + */ + public DocType( + String rootElementName, String publicID, String systemID) { + _setRootElementName(rootElementName); + _setSystemID(systemID); + _setPublicID(publicID); + } + + + /** + *

    + * Creates a new document type declaration with a system ID + * but no public ID. It has the general form + * <!DOCTYPE rootElementName SYSTEM "systemID">. + *

    + * + * @param rootElementName the name specified for the root element + * @param systemID the URL of the external DTD subset + * + * @throws IllegalNameException if the rootElementName is not + * a legal XML 1.0 name + * @throws MalformedURIException if the system ID is not a + * syntactically correct URI, or if it contains a fragment + * identifier + */ + public DocType(String rootElementName, String systemID) { + this(rootElementName, null, systemID); + } + + + /** + *

    + * Creates a new document type declaration with + * no public or system ID. It has the general form + * <!DOCTYPE rootElementName>. + *

    + * + * @param rootElementName the name specified for the root element + * + * @throws IllegalNameException if the rootElementName is not + * a legal XML 1.0 name + */ + public DocType(String rootElementName) { + this(rootElementName, null, null); + } + + + /** + *

    + * Creates a new DocType that's a copy of its + * argument. The copy has the same data but no parent document. + *

    + * + * @param doctype the DocType to copy + */ + public DocType(DocType doctype) { + this.internalDTDSubset = doctype.internalDTDSubset; + this.publicID = doctype.publicID; + this.systemID = doctype.systemID; + this.rootName = doctype.rootName; + } + + + private DocType() {} + + static DocType build( + String rootElementName, String publicID, String systemID) { + DocType result = new DocType(); + result.publicID = publicID; + result.systemID = systemID; + result.rootName = rootElementName; + return result; + } + + + /** + *

    + * Returns the name the document type declaration specifies + * for the root element. In an invalid document, this may + * not be the same as the actual root element name. + *

    + * + * @return the declared name of the root element + */ + public final String getRootElementName() { + return rootName; + } + + + /** + *

    + * Sets the name the document type declaration specifies + * for the root element. In an invalid document, this may + * not be the same as the actual root element name. + *

    + * + * @param name the root element name given by + * the document type declaration + * + * @throws IllegalNameException if the root element name is not + * a legal XML 1.0 name + */ + public void setRootElementName(String name) { + _setRootElementName(name); + } + + + private void _setRootElementName(String name) { + Verifier.checkXMLName(name); + this.rootName = name; + } + + + /** + *

    + * Returns the complete internal DTD subset. + * White space may not be preserved completely accurately, + * but all declarations should be in place. + *

    + * + * @return the internal DTD subset + */ + public final String getInternalDTDSubset() { + return internalDTDSubset; + } + + + /** + *

    + * Sets the internal DTD subset; that is the part of the DTD + * between [ and ]. Changing the + * internal DTD subset does not affect the instance document. + * That is, default attribute values and attribute types + * specified in the new internal DTD subset are not applied to the + * corresponding elements in the instance document. Furthermore, + * there's no guarantee that the instance document is or is not + * valid with respect to the declarations in the new internal + * DTD subset. + *

    + * + * @param subset the internal DTD subset + * + * @throws IllegalDataException if subset is not + * a legal XML 1.0 internal DTD subset + * + * @since 1.1 + */ + public final void setInternalDTDSubset(String subset) { + + if (subset != null && subset.length() > 0) { + Verifier.checkInternalDTDSubset(subset); + fastSetInternalDTDSubset(subset); + } + else { + this.internalDTDSubset = ""; + } + + } + + + final void fastSetInternalDTDSubset(String internalSubset) { + this.internalDTDSubset = internalSubset; + } + + + /** + *

    + * Returns the public ID of the external DTD subset. + * This is null if there is no external DTD subset + * or if it does not have a public identifier. + *

    + * + * @return the public ID of the external DTD subset. + */ + public final String getPublicID() { + return publicID; + } + + + /** + *

    + * Sets the public ID for the external DTD subset. + * This can only be set after a system ID has been set, + * because XML requires that all document type declarations + * with public IDs have system IDs. Passing null removes + * the public ID. + *

    + * + * @param id the public identifier of the external DTD subset + * + * @throws IllegalDataException if the public ID does not satisfy + * the rules for public IDs in XML 1.0 + * @throws WellformednessException if no system ID has been set + */ + public void setPublicID(String id) { + _setPublicID(id); + } + + + private void _setPublicID(String id) { + + if (systemID == null && id != null) { + throw new WellformednessException( + "Cannot have a public ID without a system ID" + ); + } + + if (id != null) { + int length = id.length(); + if (length != 0) { + if (Verifier.isXMLSpaceCharacter(id.charAt(0))) { + throw new IllegalDataException("Initial white space " + + "in public IDs is not round trippable."); + } + if (Verifier.isXMLSpaceCharacter(id.charAt(length - 1))) { + throw new IllegalDataException("Trailing white space " + + "in public IDs is not round trippable."); + } + + for (int i = 0; i < length; i++) { + char c = id.charAt(i); + if (!isXMLPublicIDCharacter(c)) { + throw new IllegalDataException("The character 0x" + + Integer.toHexString(c) + + " is not allowed in public IDs"); + } + if (c == ' ' && id.charAt(i-1) == ' ') { + throw new IllegalDataException("Multiple consecutive " + + "spaces in public IDs are not round trippable."); + } + } + } + } + this.publicID = id; + + } + + + /** + *

    + * Returns the system ID of the external DTD subset. + * This is a URL. It is null if there is no external DTD subset. + *

    + * + * @return the URL for the external DTD subset. + */ + public final String getSystemID() { + return systemID; + } + + + /** + *

    + * Sets the system ID for the external DTD subset. + * This must be a a relative or absolute URI with no fragment + * identifier. Passing null removes the system ID, but only if + * the public ID has been removed first. Otherwise, + * passing null causes a WellformednessException. + *

    + * + * @param id the URL of the external DTD subset + * + * @throws MalformedURIException if the system ID is not a + * syntactically correct URI, or if it contains a fragment + * identifier + * @throws WellformednessException if the public ID is non-null + * and you attempt to remove the system ID + */ + public void setSystemID(String id) { + _setSystemID(id); + } + + + private void _setSystemID(String id) { + + if (id == null && publicID != null) { + throw new WellformednessException( + "Cannot remove system ID without removing public ID first" + ); + } + + if (id != null) { + + Verifier.checkURIReference(id); + + if (id.indexOf('#') != -1) { + MalformedURIException ex = new MalformedURIException( + "System literals cannot contain fragment identifiers" + ); + ex.setData(id); + throw ex; + } + } + + this.systemID = id; + + } + + + /** + *

    + * Returns the empty string. XPath 1.0 does not define a value + * for document type declarations. + *

    + * + * @return an empty string + */ + public final String getValue() { + return ""; + } + + + /** + *

    + * Throws IndexOutOfBoundsException because + * document type declarations do not have children. + *

    + * + * @return never returns because document type declarations do not + * have children. Always throws an exception. + * + * @param position the index of the child node to return + * + * @throws IndexOutOfBoundsException because document type declarations + * do not have children + */ + public final Node getChild(int position) { + throw new IndexOutOfBoundsException( + "Document type declarations do not have children"); + } + + + /** + *

    + * Returns 0 because document type declarations do not have + * children. + *

    + * + * @return zero + */ + public final int getChildCount() { + return 0; + } + + + /** + *

    + * Returns a string form of the + * DocType suitable for debugging + * and diagnosis. It deliberately does not return + * an actual XML document type declaration. + *

    + * + * @return a string representation of this object + */ + public final String toString() { + return "[" + getClass().getName() + ": " + rootName + "]"; + } + + + /** + *

    + * Returns a copy of this DocType + * which has the same system ID, public ID, root element name, + * and internal DTD subset, but does not belong to a document. + * Thus, it can be inserted into a different document. + *

    + * + * @return a deep copy of this DocType + * that is not part of a document + */ + public Node copy() { + return new DocType(this); + } + + + /** + *

    + * Returns a string containing the actual XML + * form of the document type declaration represented + * by this object. For example, + * <!DOCTYPE book SYSTEM "docbookx.dtd">. + *

    + * + * @return a String containing + * an XML document type declaration + */ + public final String toXML() { + + StringBuffer result = new StringBuffer(); + result.append(""); + return result.toString(); + } + + + boolean isDocType() { + return true; + } + + + private static boolean isXMLPublicIDCharacter(char c) { + + // PubidChar ::= #x20 | #xD | #xA | [a-zA-Z0-9] | [-'()+,./:=?;!*#@$_%] + // but I'm deliberately not allowing carriage return and linefeed + // because the parser normalizes them to a space. They are not + // roundtrippable. + switch(c) { + case ' ': return true; + case '!': return true; + case '"': return false; + case '#': return true; + case '$': return true; + case '%': return true; + case '&': return false; + case '\'': return true; + case '(': return true; + case ')': return true; + case '*': return true; + case '+': return true; + case ',': return true; + case '-': return true; + case '.': return true; + case '/': return true; + case '0': return true; + case '1': return true; + case '2': return true; + case '3': return true; + case '4': return true; + case '5': return true; + case '6': return true; + case '7': return true; + case '8': return true; + case '9': return true; + case ':': return true; + case ';': return true; + case '<': return false; + case '=': return true; + case '>': return false; + case '?': return true; + case '@': return true; + case 'A': return true; + case 'B': return true; + case 'C': return true; + case 'D': return true; + case 'E': return true; + case 'F': return true; + case 'G': return true; + case 'H': return true; + case 'I': return true; + case 'J': return true; + case 'K': return true; + case 'L': return true; + case 'M': return true; + case 'N': return true; + case 'O': return true; + case 'P': return true; + case 'Q': return true; + case 'R': return true; + case 'S': return true; + case 'T': return true; + case 'U': return true; + case 'V': return true; + case 'W': return true; + case 'X': return true; + case 'Y': return true; + case 'Z': return true; + case '[': return false; + case '\\': return false; + case ']': return false; + case '^': return false; + case '_': return true; + case '`': return false; + case 'a': return true; + case 'b': return true; + case 'c': return true; + case 'd': return true; + case 'e': return true; + case 'f': return true; + case 'g': return true; + case 'h': return true; + case 'i': return true; + case 'j': return true; + case 'k': return true; + case 'l': return true; + case 'm': return true; + case 'n': return true; + case 'o': return true; + case 'p': return true; + case 'q': return true; + case 'r': return true; + case 's': return true; + case 't': return true; + case 'u': return true; + case 'v': return true; + case 'w': return true; + case 'x': return true; + case 'y': return true; + case 'z': return true; + } + + return false; + + } + + +} \ No newline at end of file diff --git a/src/nu/xom/Document.java b/src/nu/xom/Document.java new file mode 100644 index 0000000..9bea48c --- /dev/null +++ b/src/nu/xom/Document.java @@ -0,0 +1,461 @@ +/* Copyright 2002-2004 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom; + +/** + *

    + * The Document class represents + * a complete XML document including its root element, + * prolog, and epilog. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.3.0 + * + */ +public class Document extends ParentNode { + + /** + *

    + * Creates a new Document object with the + * specified root element. + *

    + * + * @param root the root element of this document + * + * @throws NullPointerException if root is null + * @throws MultipleParentException if root already + * has a parent + */ + public Document(Element root) { + _insertChild(root, 0); + } + + + /** + *

    + * Creates a copy of this document. + *

    + * + * @param doc the document to copy + * + * @throws NullPointerException if doc is null + */ + public Document(Document doc) { + + insertChild(doc.getRootElement().copy(), 0); + int count = doc.getChildCount(); + for (int i = 0; i < count; i++) { + Node child = doc.getChild(i); + if (!(child.isElement())) { + this.insertChild(child.copy(), i); + } + } + this.actualBaseURI = doc.actualBaseURI; + + } + + + final void insertionAllowed(Node child, int position) { + + if (child == null) { + throw new NullPointerException( + "Tried to insert a null child in the document"); + } + else if (child.getParent() != null) { + throw new MultipleParentException("Child already has a parent."); + } + else if (child.isComment() || child.isProcessingInstruction()) { + return; + } + else if (child.isDocType()) { + if (position <= getRootPosition()) { + DocType oldDocType = getDocType(); + if (oldDocType != null) { + throw new IllegalAddException( + "Tried to insert a second DOCTYPE" + ); + } + return; + } + else { + throw new IllegalAddException( + "Cannot add a document type declaration " + + "after the root element" + ); + } + } + else if (child.isElement()) { + if (getChildCount() == 0) return; + else { + throw new IllegalAddException( + "Cannot add a second root element to a Document." + ); + } + } + else { + throw new IllegalAddException("Cannot add a " + + child.getClass().getName() + " to a Document."); + } + + } + + + private int getRootPosition() { + + // This looks like an infinite loop but it isn't + // because all documents have root elements + for (int i = 0; ; i++) { + Node child = getChild(i); + if (child.isElement()) { + return i; + } + } + + } + + + /** + *

    + * Returns this document's document type declaration, + * or null if it doesn't have one. + *

    + * + * @return the document type declaration + * + * @see #setDocType + * + */ + public final DocType getDocType() { + + for (int i = 0; i < getChildCount(); i++) { + Node child = getChild(i); + if (child.isDocType()) { + return (DocType) child; + } + } + return null; + + } + + + /** + *

    + * Sets this document's document type declaration. + * If this document already has a document type declaration, + * then it's inserted at that position. Otherwise, it's inserted + * at the beginning of the document. + *

    + * + * @param doctype the document type declaration + * + * @throws MultipleParentException if doctype belongs + * to another document + * @throws NullPointerException if doctype is null + * + */ + public void setDocType(DocType doctype) { + + DocType oldDocType = getDocType(); + if (doctype == null) { + throw new NullPointerException("Null DocType"); + } + else if (doctype == oldDocType) return; + else if (doctype.getParent() != null) { + throw new MultipleParentException("DocType belongs to another document"); + } + + if (oldDocType == null) insertChild(doctype, 0); + else { + int position = indexOf(oldDocType); + super.removeChild(position); + fastInsertChild(doctype, position); + oldDocType.setParent(null); + doctype.setParent(this); + } + + } + + + /** + *

    + * Returns this document's root element. + * This is guaranteed to be non-null. + *

    + * + * @return the root element + */ + public final Element getRootElement() { + + // This looks like an infinite loop but it isn't because + // all documents have root elements. + for (int i = 0; ; i++) { + Node child = getChild(i); + if (child.isElement()) { + return (Element) child; + } + } + + } + + + /** + *

    + * Replaces the current root element with a different root element. + *

    + * + * @param root the new root element + * + * @throws MultipleParentException if root has a parent + * @throws NullPointerException if root is null + */ + public void setRootElement(Element root) { + + Element oldRoot = this.getRootElement(); + if (root == oldRoot) return; + else if (root == null) { + throw new NullPointerException("Root element cannot be null"); + } + else if (root.getParent() != null) { + throw new MultipleParentException(root.getQualifiedName() + + " already has a parent"); + } + + fillInBaseURI(oldRoot); + int index = indexOf(oldRoot); + + oldRoot.setParent(null); + children[index] = root; + root.setParent(this); + + } + + + /** + *

    + * Sets the URI from which this document was loaded, and + * against which relative URLs in this document will be resolved. + * Setting the base URI to null or the empty string removes any + * existing base URI. + *

    + * + * @param URI the base URI of this document + * + * @throws MalformedURIException if URI is + * not a legal absolute URI + */ + public void setBaseURI(String URI) { + setActualBaseURI(URI); + } + + + /** + *

    + * Returns the absolute URI from which this document was loaded. + * This method returns the empty string if the base URI is not + * known; for instance if the document was created in memory with + * a constructor rather than by parsing an existing document. + *

    + * + * @return the base URI of this document + */ + public final String getBaseURI() { + return getActualBaseURI(); + } + + + /** + *

    + * Removes the child of this document at the specified position. + * Indexes begin at 0 and count up to one less than the number + * of children of this document. The root element cannot be + * removed. Instead, use setRootElement to replace + * the existing root element with a different element. + *

    + * + * @param position index of the node to remove + * + * @return the node which was removed + * + * @throws IndexOutOfBoundsException if the index is negative or + * greater than the number of children of this document - 1 + * @throws WellformednessException if the index points + * to the root element + */ + public Node removeChild(int position) { + + if (position == getRootPosition()) { + throw new WellformednessException( + "Cannot remove the root element" + ); + } + return super.removeChild(position); + + } + + + /** + *

    + * Removes the specified child from this document. + * The root element cannot be removed. + * Instead, use setRootElement to replace the + * existing root element with a different element. + *

    + * + * @param child node to remove + * + * @return the node which was removed + * + * @throws NoSuchChildException if the node is not a + * child of this node + * @throws WellformednessException if child is the root element + */ + public Node removeChild(Node child) { + + if (child == getRootElement()) { + throw new WellformednessException( + "Cannot remove the root element"); + } + return super.removeChild(child); + + } + + + /** + *

    + * Replaces an existing child with a new child node. + * If oldChild is not a child of this node, + * then a NoSuchChildException is thrown. + * The root element can only be replaced by another element. + *

    + * + * @param oldChild the node removed from the tree + * @param newChild the node inserted into the tree + * + * @throws MultipleParentException if newChild already + * has a parent + * @throws NoSuchChildException if oldChild + * is not a child of this node + * @throws NullPointerException if either argument is null + * @throws IllegalAddException if newChild is an + * attribute or a text node + * @throws WellformednessException if newChild + * oldChild is an element and + * newChild is not + */ + public void replaceChild(Node oldChild, Node newChild) { + + if (oldChild == getRootElement() + && newChild != null && newChild.isElement()) { + setRootElement((Element) newChild); + } + else if (oldChild == getDocType() + && newChild != null && newChild.isDocType()) { + setDocType((DocType) newChild); + } + else { + super.replaceChild(oldChild, newChild); + } + + } + + + /** + *

    + * Returns the value of the document as defined by XPath 1.0. + * This is the same as the value of the root element, which + * is the complete PCDATA content of the root element, without + * any tags, comments, or processing instructions after all + * entity and character references have been resolved. + *

    + * + * @return value of the root element of this document + * + */ + public final String getValue() { + return getRootElement().getValue(); + } + + + /** + *

    + * Returns the actual complete, well-formed XML document as a + * String. Significant white space is preserved. + * Insignificant white space in tags, the prolog, the epilog, + * and the internal DTD subset is not preserved. + * Entity and character references are not preserved. + * The entire document is contained in this one string. + *

    + * + * @return a string containing this entire XML document + */ + public final String toXML() { + + StringBuffer result = new StringBuffer(64); + + // XML declaration + result.append("\n"); + + // children + for (int i = 0; i < getChildCount(); i++) { + result.append(getChild(i).toXML()); + result.append("\n"); + } + + return result.toString(); + + } + + + /** + *

    + * Returns a complete copy of this document. + *

    + * + * @return a deep copy of this Document object + */ + public Document copy() { + return new Document(this); + } + + + boolean isDocument() { + return true; + } + + + /** + *

    + * Returns a string representation of this document suitable + * for debugging and diagnosis. This is not + * the XML representation of this document. + *

    + * + * @return a non-XML string representation of this document + */ + public final String toString() { + return "[" + getClass().getName() + ": " + + getRootElement().getQualifiedName() + "]"; + } + + +} \ No newline at end of file diff --git a/src/nu/xom/DocumentFragment.java b/src/nu/xom/DocumentFragment.java new file mode 100644 index 0000000..4ea0098 --- /dev/null +++ b/src/nu/xom/DocumentFragment.java @@ -0,0 +1,67 @@ +/* Copyright 2005, 2006 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom; + +/** + * + *

    + * Used in XPath when querying a subtree that is not part of a + * document. This class is purely internal. Instances should never + * be visible in the public API. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.2d1 + * + */ +class DocumentFragment extends ParentNode { + + void insertionAllowed(Node child, int position) { + // Everything can be inserted + } + + + public void setBaseURI(String URI) { + throw new UnsupportedOperationException("XOM bug"); + } + + + public String getValue() { + throw new UnsupportedOperationException("XOM bug"); + } + + + public Node copy() { + throw new UnsupportedOperationException("XOM bug"); + } + + + public String toXML() { + throw new UnsupportedOperationException("XOM bug"); + } + + + boolean isDocumentFragment() { + return true; + } + +} diff --git a/src/nu/xom/EBCDICWriter.java b/src/nu/xom/EBCDICWriter.java new file mode 100644 index 0000000..d6bc2d5 --- /dev/null +++ b/src/nu/xom/EBCDICWriter.java @@ -0,0 +1,57 @@ +/* Copyright 2002-2004 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ +package nu.xom; + +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; + + +class EBCDICWriter extends OutputStreamWriter { + + + private final static int NEL = 0x85; + private OutputStream raw; + + + public EBCDICWriter(OutputStream out) + throws UnsupportedEncodingException { + super(out, "Cp037"); + this.raw = out; + } + + + // work around broken implementation of EBCDIC-37 + public void write(int c) throws IOException { + + if (c == NEL) { + flush(); + raw.write(0x15); + } + else { + super.write(c); + } + + } + + +} diff --git a/src/nu/xom/Element.java b/src/nu/xom/Element.java new file mode 100644 index 0000000..f223ac8 --- /dev/null +++ b/src/nu/xom/Element.java @@ -0,0 +1,1873 @@ +/* Copyright 2002-2006, 2011, 2013, 2019 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Set; +import java.util.LinkedHashSet; + +/** + *

    + * This class represents an XML element. Each + * element has the following properties: + *

    + * + *
      + *
    • Local name
    • + *
    • Prefix (which may be null or the empty string)
    • + *
    • Namespace URI (which may be null or the empty string)
    • + *
    • A list of attributes
    • + *
    • A list of namespace declarations for this element + * (not including those inherited from its parent)
    • + *
    • A list of child nodes
    • + *
    + * + * @author Elliotte Rusty Harold + * @version 1.3.1 + * + */ +public class Element extends ParentNode { + + private String localName; + private String prefix; + private String URI; + + private Attribute[] attributes = null; + private int numAttributes = 0; + Namespaces namespaces = null; + + /** + *

    + * Creates a new element in no namespace. + *

    + * + * @param name the name of the element + * + * @throws IllegalNameException if name + * is not a legal XML 1.0 non-colonized name + */ + public Element(String name) { + this(name, ""); + } + + + /** + *

    + * Creates a new element in a namespace. + *

    + * + * @param name the qualified name of the element + * @param uri the namespace URI of the element + * + * @throws IllegalNameException if name + * is not a legal XML 1.0 name + * @throws NamespaceConflictException if name's prefix + * cannot be used with uri + * @throws MalformedURIException if uri + * is not an RFC 3986 absolute URI reference + */ + public Element(String name, String uri) { + + // The shadowing is important here. + // I don't want to set the prefix field just yet. + String prefix = ""; + String localName = name; + int colon = name.indexOf(':'); + if (colon > 0) { + prefix = name.substring(0, colon); + localName = name.substring(colon + 1); + } + + // The order of these next two calls + // matters a great deal. + _setNamespacePrefix(prefix); + _setNamespaceURI(uri); + try { + _setLocalName(localName); + } + catch (IllegalNameException ex) { + ex.setData(name); + throw ex; + } + + } + + + private Element() {} + + + static Element build(String name, String uri, String localName) { + + Element result = new Element(); + String prefix = ""; + int colon = name.indexOf(':'); + if (colon >= 0) { + prefix = name.substring(0, colon); + } + result.prefix = prefix; + result.localName = localName; + // We do need to verify the URI here because parsers are + // allowing relative URIs which XOM forbids, for reasons + // of canonical XML if nothing else. But we only have to verify + // that it's an absolute base URI. I don't have to verify + // no conflicts. + if (! "".equals(uri)) Verifier.checkAbsoluteURIReference(uri); + result.URI = uri; + return result; + + } + + + /** + *

    + * Creates a deep copy of an element. + * The copy is disconnected from the tree, and does not + * have a parent. + *

    + * + * @param element the element to copy + * + */ + public Element(Element element) { + + this.prefix = element.prefix; + this.localName = element.localName; + this.URI = element.URI; + + // Attach additional namespaces + if (element.namespaces != null) { + this.namespaces = element.namespaces.copy(); + } + + // Attach clones of attributes + if (element.attributes != null) { + this.attributes = element.copyAttributes(this); + this.numAttributes = element.numAttributes; + } + + this.actualBaseURI = element.findActualBaseURI(); + + copyChildren(element, this); + + } + + + private Attribute[] copyAttributes(Element newParent) { + + if (numAttributes == 0) return null; + Attribute[] copy = new Attribute[numAttributes]; + for (int i = 0; i < numAttributes; i++) { + copy[i] = (Attribute) attributes[i].copy(); + copy[i].setParent(newParent); + } + return copy; + + } + + + private static Element copyTag(final Element source) { + + Element result = source.shallowCopy(); + + // Attach additional namespaces + if (source.namespaces != null) { + result.namespaces = source.namespaces.copy(); + } + + // Attach clones of attributes + if (source.attributes != null) { + result.attributes = source.copyAttributes(result); + result.numAttributes = source.numAttributes; + } + + result.actualBaseURI = source.findActualBaseURI(); + + return result; + + } + + + private static void copyChildren(final Element sourceElement, + Element resultElement) { + + if (sourceElement.getChildCount() == 0) return; + ParentNode resultParent = resultElement; + Node sourceCurrent = sourceElement; + int index = 0; + int[] indexes = new int[10]; + int top = 0; + indexes[0] = 0; + + // true if processing the element for the 2nd time; + // i.e. the element's end-tag + boolean endTag = false; + + while (true) { + if (!endTag && sourceCurrent.getChildCount() > 0) { + sourceCurrent = sourceCurrent.getChild(0); + index = 0; + top++; + indexes = grow(indexes, top); + indexes[top] = 0; + } + else { + endTag = false; + ParentNode sourceParent = sourceCurrent.getParent(); + if (sourceParent.getChildCount() - 1 == index) { + sourceCurrent = sourceParent; + top--; + if (sourceCurrent == sourceElement) break; + // switch parent up + resultParent = (Element) resultParent.getParent(); + index = indexes[top]; + endTag = true; + continue; + } + else { + index++; + indexes[top] = index; + sourceCurrent = sourceParent.getChild(index); + } + } + + if (sourceCurrent.isElement()) { + Element child = copyTag((Element) sourceCurrent); + resultParent.appendChild(child); + if (sourceCurrent.getChildCount() > 0) { + resultParent = child; + } + } + else { + Node child = sourceCurrent.copy(); + resultParent.appendChild(child); + } + + } + + } + + + private static int[] grow(int[] indexes, int top) { + + if (top < indexes.length) return indexes; + int[] result = new int[indexes.length*2]; + System.arraycopy(indexes, 0, result, 0, indexes.length); + return result; + + } + + + /** + *

    + * Returns a list of the child elements of + * this element with the specified name in no namespace. + * The elements returned are in document order. + *

    + * + * @param name the name of the elements included in the list + * + * @return a comatose list containing the child elements of this + * element with the specified name + */ + public final Elements getChildElements(String name) { + return getChildElements(name, ""); + } + + + /** + *

    + * Returns a list of the immediate child elements of this + * element with the specified local name and namespace URI. + * Passing the empty string or null as the local name + * returns all elements in the specified namespace. + * Passing null or the empty string as the namespace URI + * returns elements with the specified name in no namespace. + * The elements returned are in document order. + *

    + * + * @param localName the name of the elements included in the list + * @param namespaceURI the namespace URI of the elements included + * in the list + * + * @return a comatose list containing the child + * elements of this element with the specified + * name in the specified namespace + */ + public final Elements getChildElements(String localName, + String namespaceURI) { + + if (namespaceURI == null) namespaceURI = ""; + if (localName == null) localName = ""; + + Elements elements = new Elements(); + for (int i = 0; i < getChildCount(); i++) { + Node child = getChild(i); + if (child.isElement()) { + Element element = (Element) child; + if ((localName.equals(element.getLocalName()) + || localName.length() == 0) + && namespaceURI.equals(element.getNamespaceURI())) { + elements.add(element); + } + } + } + return elements; + + } + + + /** + *

    + * Returns a list of all the child elements + * of this element in document order. + *

    + * + * @return a comatose list containing all + * child elements of this element + */ + public final Elements getChildElements() { + + Elements elements = new Elements(); + for (int i = 0; i < getChildCount(); i++) { + Node child = getChild(i); + if (child.isElement()) { + Element element = (Element) child; + elements.add(element); + } + } + return elements; + + } + + + /** + *

    + * Returns the first child + * element with the specified name in no namespace. + * If there is no such element, it returns null. + *

    + * + * @param name the name of the element to return + * + * @return the first child element with the specified local name + * in no namespace or null if there is no such element + */ + public final Element getFirstChildElement(String name) { + return getFirstChildElement(name, ""); + } + + + /** + *

    + * Returns the first child + * element with the specified local name and namespace URI. + * If there is no such element, it returns null. + *

    + * + * @param localName the local name of the element to return + * @param namespaceURI the namespace URI of the element to return + * + * @return the first child with the specified local name in the + * specified namespace, or null if there is no such element + */ + public final Element getFirstChildElement(String localName, + String namespaceURI) { + + for (int i = 0; i < getChildCount(); i++) { + Node child = getChild(i); + if (child.isElement()) { + Element element = (Element) child; + if (localName.equals(element.getLocalName()) + && namespaceURI.equals(element.getNamespaceURI())) { + return element; + } + } + } + return null; + + } + + + /** + *

    + * Adds an attribute to this element, replacing any existing + * attribute with the same local name and namespace URI. + *

    + * + * @param attribute the attribute to add + * + * @throws MultipleParentException if the attribute is already + * attached to an element + * @throws NamespaceConflictException if the attribute's prefix + * is mapped to a different namespace URI than the same prefix + * is mapped to by this element, another attribute of + * this element, or an additional namespace declaration + * of this element + */ + public void addAttribute(Attribute attribute) { + + if (attribute.getParent() != null) { + throw new MultipleParentException( + "Attribute already has a parent"); + } + + // check for namespace conflicts + String attPrefix = attribute.getNamespacePrefix(); + if (attPrefix.length() != 0 && !"xml".equals(attPrefix)) { + if (prefix.equals(attribute.getNamespacePrefix()) + && !(getNamespaceURI() + .equals(attribute.getNamespaceURI()))) { + throw new NamespaceConflictException("Prefix of " + + attribute.getQualifiedName() + + " conflicts with element prefix " + prefix); + } + // check for conflicts with additional namespaces + if (namespaces != null) { + String existing + = namespaces.getURI(attribute.getNamespacePrefix()); + if (existing != null + && !existing.equals(attribute.getNamespaceURI())) { + throw new NamespaceConflictException("Attribute prefix " + + attPrefix + + " conflicts with namespace declaration."); + } + } + + } + + if (attributes == null) attributes = new Attribute[1]; + checkPrefixConflict(attribute); + + // Is there already an attribute with this local name + // and namespace? If so, remove it. + Attribute oldAttribute = getAttribute(attribute.getLocalName(), + attribute.getNamespaceURI()); + if (oldAttribute != null) remove(oldAttribute); + + add(attribute); + attribute.setParent(this); + + } + + + private void add(Attribute attribute) { + + if (numAttributes == attributes.length) { + Attribute[] newAttributes = new Attribute[attributes.length * 2]; + System.arraycopy(attributes, 0, newAttributes, 0, numAttributes); + this.attributes = newAttributes; + } + attributes[numAttributes] = attribute; + numAttributes++; + + } + + + private boolean remove(Attribute attribute) { + + int index = -1; + for (int i = 0; i < attributes.length; i++) { + if (attributes[i] == attribute) { + index = i; + break; + } + } + + if (index == -1) return false; + + int toCopy = numAttributes-index-1; + if (toCopy > 0) { + System.arraycopy(attributes, index+1, attributes, index, toCopy); + } + numAttributes--; + attributes[numAttributes] = null; + return true; + + } + + + void fastAddAttribute(Attribute attribute) { + if (attributes == null) attributes = new Attribute[1]; + add(attribute); + attribute.setParent(this); + } + + + /** + *

    + * Removes an attribute from this element. + *

    + * + * @param attribute the attribute to remove + * + * @return the attribute that was removed + * + * @throws NoSuchAttributeException if this element is not the + * parent of attribute + * + */ + public Attribute removeAttribute(Attribute attribute) { + + if (attributes == null) { + throw new NoSuchAttributeException( + "Tried to remove attribute " + + attribute.getQualifiedName() + + " from non-parent element"); + } + if (attribute == null) { + throw new NullPointerException( + "Tried to remove null attribute"); + } + if (remove(attribute)) { + attribute.setParent(null); + return attribute; + } + else { + throw new NoSuchAttributeException( + "Tried to remove attribute " + + attribute.getQualifiedName() + + " from non-parent element"); + } + + } + + + /** + *

    + * Returns the attribute with the specified name in no namespace, + * or null if this element does not have an attribute + * with that name in no namespace. + *

    + * + * @param name the name of the attribute + * + * @return the attribute of this element with the specified name + */ + public final Attribute getAttribute(String name) { + return getAttribute(name, ""); + } + + + /** + *

    + * Returns the attribute with the specified name and namespace URI, + * or null if this element does not have an attribute + * with that name in that namespace. + *

    + * + * @param localName the local name of the attribute + * @param namespaceURI the namespace of the attribute + * + * @return the attribute of this element + * with the specified name and namespace + */ + public final Attribute getAttribute(String localName, + String namespaceURI) { + + if (attributes == null) return null; + for (int i = 0; i < numAttributes; i++) { + Attribute a = attributes[i]; + if (a.getLocalName().equals(localName) + && a.getNamespaceURI().equals(namespaceURI)) { + return a; + } + } + + return null; + + } + + + /** + *

    + * Returns the value of the attribute with the specified + * name in no namespace, + * or null if this element does not have an attribute + * with that name. + *

    + * + * @param name the name of the attribute + * + * @return the value of the attribute of this element + * with the specified name + */ + public final String getAttributeValue(String name) { + return getAttributeValue(name, ""); + } + + + /** + * + *

    + * Returns the number of attributes of this Element, + * not counting namespace declarations. + * This is always a non-negative number. + *

    + * + * @return the number of attributes in the container + */ + public final int getAttributeCount() { + return numAttributes; + } + + + /** + * + *

    + * Selects an attribute by index. + * The index is purely for convenience and has no particular + * meaning. In particular, it is not necessarily the + * position of this attribute in the original document from + * which this Element object was read. + * As with most lists in Java, attributes are numbered + * from 0 to one less than the length of the list. + *

    + * + *

    + * In general, you should not add attributes to or remove + * attributes from the list while iterating across it. + * Doing so will change the indexes of the other attributes in + * the list. it is, however, safe to remove an attribute from + * either end of the list (0 or getAttributeCount()-1) + * until there are no attributes left. + *

    + * + * @param index the attribute to return + * + * @return the indexth attribute of this element + * + * @throws IndexOutofBoundsException if the index is negative + * or greater than or equal to the number of attributes + * of this element + * + */ + public final Attribute getAttribute(int index) { + + if (attributes == null) { + throw new IndexOutOfBoundsException( + "Element does not have any attributes" + ); + } + return attributes[index]; + + } + + + /** + *

    + * Returns the value of the attribute with the + * specified name and namespace URI, + * or null if this element does not have such an attribute. + *

    + * + * @param localName the name of the attribute + * @param namespaceURI the namespace of the attribute + * + * @return the value of the attribute of this element + * with the specified name and namespace + */ + public final String getAttributeValue(String localName, + String namespaceURI) { + + Attribute attribute = getAttribute(localName, namespaceURI); + if (attribute == null) return null; + else return attribute.getValue(); + + } + + + /** + *

    + * Returns the local name of this element, not including the + * namespace prefix or colon. + *

    + * + * @return the local name of this element + */ + public final String getLocalName() { + return localName; + } + + + /** + *

    + * Returns the complete name of this element, including the + * namespace prefix if this element has one. + *

    + * + * @return the qualified name of this element + */ + public final String getQualifiedName() { + if (prefix.length() == 0) return localName; + else return prefix + ":" + localName; + } + + + /** + *

    + * Returns the prefix of this element, or the empty string + * if this element does not have a prefix. + *

    + * + * @return the prefix of this element + */ + public final String getNamespacePrefix() { + return prefix; + } + + + /** + *

    + * Returns the namespace URI of this element, + * or the empty string if this element is not + * in a namespace. + *

    + * + * @return the namespace URI of this element + */ + public final String getNamespaceURI() { + return URI; + } + + + /** + *

    + * Returns the namespace URI mapped to the specified + * prefix within this element. Returns null if this prefix + * is not associated with a URI. + *

    + * + * @param prefix the namespace prefix whose URI is desired + * + * @return the namespace URI mapped to prefix + */ + public final String getNamespaceURI(String prefix) { + + Element current = this; + String result = getLocalNamespaceURI(prefix); + while (result == null) { + ParentNode parent = current.getParent(); + if (parent == null || parent.isDocument()) break; + current = (Element) parent; + result = current.getLocalNamespaceURI(prefix); + } + if (result == null && "".equals(prefix)) result = ""; + return result; + + } + + + final String getLocalNamespaceURI(String prefix) { + + if (prefix.equals(this.prefix)) return this.URI; + + if ("xml".equals(prefix)) { + return "http://www.w3.org/XML/1998/namespace"; + } + // This next line uses the original Namespaces 1.0 + // specification rules. + // Namespaces 1.0 + errata is different + if ("xmlns".equals(prefix)) return ""; + // Look in the additional namespace declarations + if (namespaces != null) { + String result = namespaces.getURI(prefix); + if (result != null) return result; + } + // Look in the attributes + if (prefix.length() != 0 && attributes != null) { + for (int i = 0; i < numAttributes; i++) { + Attribute a = attributes[i]; + if (a.getNamespacePrefix().equals(prefix)) { + return a.getNamespaceURI(); + } + } + } + + return null; + + } + + + /** + *

    + * Sets the local name of this element. + *

    + * + * @param localName the new local name + * + * @throws IllegalNameException if localName is not + * a legal, non-colonized name + */ + public void setLocalName(String localName) { + _setLocalName(localName); + } + + + private void _setLocalName(String localName) { + Verifier.checkNCName(localName); + this.localName = localName; + } + + + /** + *

    + * Sets the namespace URI of this element. + *

    + * + * @param uri the new namespace URI + * + * @throws MalformedURIException if uri + * is not an absolute RFC 3986 URI reference + * @throws NamespaceException if this element has a prefix + * and uri is null or the empty string; + * or if the element's prefix is shared by an attribute + * or additional namespace + */ + public void setNamespaceURI(String uri) { + _setNamespaceURI(uri); + } + + + private void _setNamespaceURI(String uri) { + + if (uri == null) uri = ""; + // Next line is needed to avoid unintentional + // exceptions below when checking for conflicts + if (uri.equals(this.URI)) return; + if (uri.length() == 0) { // faster than "".equals(uri) + if (prefix.length() != 0) { + throw new NamespaceConflictException( + "Prefixed elements must have namespace URIs." + ); + } + } + else Verifier.checkAbsoluteURIReference(uri); + // Make sure this doesn't conflict with any local + // attribute prefixes or additional namespace declarations + // Note that if the prefix equals the prefix, then the + // URI must equal the old URI, so the URI can't easily be + // changed. (you'd need to detach everything first; + // change the URIs, then put it all back together + if (namespaces != null) { + String result = namespaces.getURI(prefix); + if (result != null) { + throw new NamespaceConflictException( + "new URI conflicts with existing prefix" + ); + } + } + // Look in the attributes + if (uri.length() > 0 && attributes != null) { + for (int i = 0; i < numAttributes; i++) { + Attribute a = attributes[i]; + String attPrefix = a.getNamespacePrefix(); + if (attPrefix.length() == 0) continue; + if (a.getNamespacePrefix().equals(prefix)) { + throw new NamespaceConflictException( + "new element URI " + uri + + " conflicts with attribute " + + a.getQualifiedName() + ); + } + } + } + + if ("http://www.w3.org/XML/1998/namespace".equals(uri) + && ! "xml".equals(prefix)) { + throw new NamespaceConflictException( + "Wrong prefix " + prefix + + " for the http://www.w3.org/XML/1998/namespace namespace URI" + ); + } + else if ("xml".equals(prefix) && + !"http://www.w3.org/XML/1998/namespace".equals(uri)) { + throw new NamespaceConflictException( + "Wrong namespace URI " + uri + " for the xml prefix" + ); + } + + this.URI = uri; + + } + + + /** + *

    + * Sets the namespace prefix of this element. + * You can pass null or the empty string to remove the prefix. + *

    + * + * @param prefix the new namespace prefix + * + * @throws IllegalNameException if prefix is + * not a legal XML non-colonized name + * @throws NamespaceConflictException if prefix is + * already in use by an attribute or additional + * namespace with a different URI than the element + * itself + */ + public void setNamespacePrefix(String prefix) { + _setNamespacePrefix(prefix); + } + + + private void _setNamespacePrefix(String prefix) { + + if (prefix == null) prefix = ""; + + if (prefix.length() != 0) Verifier.checkNCName(prefix); + + // Check how this affects or conflicts with + // attribute namespaces and additional + // namespace declarations. + String uri = getLocalNamespaceURI(prefix); + if (uri != null) { + if (!uri.equals(this.URI) && !"xml".equals(prefix)) { + throw new NamespaceConflictException(prefix + + " conflicts with existing prefix"); + } + } + else if ("".equals(this.URI) && !"".equals(prefix)) { + throw new NamespaceConflictException( + "Cannot assign prefix to element in no namespace"); + } + + this.prefix = prefix; + + } + + + /** + *

    + * Inserts a child node at the specified position. + * Inserting at position 0 makes the child the first child + * of this node. Inserting at the position + * getChildCount() + * makes the child the last child of the node. + *

    + * + *

    + * All the other methods that add a node to the tree, + * invoke this method ultimately. + *

    + * + * @param position where to insert the child + * @param child the node to insert + * + * @throws IllegalAddException if child + * is a Document + * @throws MultipleParentException if child + * already has a parent + * @throws NullPointerException if child is null + * @throws IndexOutOfBoundsException if the position is negative + * or greater than the number of children of this element. + */ + void insertionAllowed(Node child, int position) { + + if (child == null) { + throw new NullPointerException( + "Tried to insert a null child in the tree"); + } + else if (child.getParent() != null) { + throw new MultipleParentException(child.toString() + + " child already has a parent."); + } + else if (child.isElement()) { + checkCycle(child, this); + return; + } + else if (child.isText() + || child.isProcessingInstruction() + || child.isComment()) { + return; + } + else { + throw new IllegalAddException("Cannot add a " + + child.getClass().getName() + " to an Element."); + } + + } + + + private static void checkCycle(Node child, ParentNode parent) { + + if (child == parent) { + throw new CycleException("Cannot add a node to itself"); + } + if (child.getChildCount() == 0) return; + while ((parent = parent.getParent()) != null) { + if (parent == child) { + throw new CycleException( + "Cannot add an ancestor as a child"); + } + } + + } + + + /** + *

    + * Converts a string to a text node and inserts that + * node at the specified position. + *

    + * + * @param position where to insert the child + * @param text the string to convert to a text node and insert + * + * @throws NullPointerException if text is null + * @throws IndexOutOfBoundsException if the position is negative + * or greater than the number of children of the node + */ + public void insertChild(String text, int position) { + + if (text == null) { + throw new NullPointerException("Inserted null string"); + } + super.fastInsertChild(new Text(text), position); + + } + + + /** + *

    + * Converts a string to a text node + * and appends that node to the children of this node. + *

    + * + * @param text String to add to this node + * + * @throws IllegalAddException if this node cannot + * have children of this type + * @throws NullPointerException if text is null + */ + public void appendChild(String text) { + insertChild(new Text(text), getChildCount()); + } + + + /** + *

    + * Detaches all children from this node. + *

    + * + *

    + * Subclassers should note that the default implementation of this + * method does not call removeChild + * or detach. If you override + * removeChild, you'll probably need to override this + * method as well. + *

    + * + * @return a list of all the children removed in the order they + * appeared in the element + */ + public Nodes removeChildren() { + + int length = this.getChildCount(); + Nodes result = new Nodes(); + for (int i = 0; i < length; i++) { + Node child = getChild(i); + if (child.isElement()) fillInBaseURI((Element) child); + child.setParent(null); + result.append(child); + } + this.children = null; + this.childCount = 0; + + return result; + + } + + + /** + *

    + * Declares a namespace prefix. This is only necessary when + * prefixes are used in element content and attribute values, + * as in XSLT and the W3C XML Schema Language. Do not use + * this method to declare prefixes for element and attribute + * names. + *

    + * + *

    + * You can supply an empty string for the prefix to declare a + * default namespace, provided the element itself has a prefix. + *

    + * + *

    + * If you do redeclare a prefix that is already used + * by an element or attribute name, the additional + * namespace is added if and only if the URI is the same. + * Conflicting namespace declarations will throw an exception. + *

    + * + * @param prefix the prefix to declare + * @param uri the absolute URI reference to map the prefix to + * + * @throws MalformedURIException if URI + * is not an RFC 3986 URI reference + * @throws IllegalNameException if prefix is not + * a legal XML non-colonized name or the empty string + * @throws NamespaceConflictException if the mapping conflicts + * with an existing element, attribute, + * or additional namespace declaration + */ + public void addNamespaceDeclaration(String prefix, String uri) { + + if (prefix == null) prefix = ""; + if (uri == null) uri = ""; + + // check to see if this is the xmlns or xml prefix + if (prefix.equals("xmlns")) { + if (uri.equals("")) { + // This is done automatically + return; + } + throw new NamespaceConflictException( + "The xmlns prefix cannot bound to any URI"); + } + else if (prefix.equals("xml")) { + if (uri.equals("http://www.w3.org/XML/1998/namespace")) { + // This is done automatically + return; + } + throw new NamespaceConflictException( + "Wrong namespace URI for xml prefix: " + uri); + } + else if (uri.equals("http://www.w3.org/XML/1998/namespace")) { + throw new NamespaceConflictException( + "Wrong prefix for http://www.w3.org/XML/1998/namespace namespace: " + + prefix); + } + + if (prefix.length() != 0) { + Verifier.checkNCName(prefix); + Verifier.checkAbsoluteURIReference(uri); + } + else if (uri.length() != 0) { + // Make sure we're not trying to undeclare + // the default namespace; this is legal. + Verifier.checkAbsoluteURIReference(uri); + } + + String currentBinding = getLocalNamespaceURI(prefix); + if (currentBinding != null && !currentBinding.equals(uri)) { + + String message; + if (prefix.equals("")) { + message = "Additional namespace " + uri + + " conflicts with existing default namespace " + + currentBinding; + } + else { + message = "Additional namespace " + uri + + " for the prefix " + prefix + + " conflicts with existing namespace binding " + + currentBinding; + } + throw new NamespaceConflictException(message); + } + + if (namespaces == null) namespaces = new Namespaces(); + namespaces.put(prefix, uri); + + } + + + /** + *

    + * Removes the mapping of the specified prefix. This method only + * removes additional namespaces added with + * addNamespaceDeclaration. + * It has no effect on namespaces of elements and attributes. + * If the prefix is not used on this element, this method + * does nothing. + *

    + * + * @param prefix the prefix whose declaration should be removed + */ + public void removeNamespaceDeclaration(String prefix) { + + if (namespaces != null) { + namespaces.remove(prefix); + } + + } + + + /** + *

    + * Returns the number of namespace declarations on this + * element. This counts the namespace of the element + * itself (which may be the empty string), the namespace + * of each attribute, and each namespace added + * by addNamespaceDeclaration. + * However, prefixes used multiple times are only counted + * once; and the xml prefix used for + * xml:base, xml:lang, and + * xml:space is not counted even if one of these + * attributes is present on the element. + *

    + * + *

    + * The return value is almost always positive. It can be zero + * if and only if the element itself has the prefix + * xml; e.g. <xml:space />. + * This is not endorsed by the XML specification. The prefix + * xml is reserved for use by the W3C, which has only + * used it for attributes to date. You really shouldn't do this. + * Nonetheless, this is not malformed so XOM allows it. + *

    + * + * @return the number of namespaces declared by this element + */ + public final int getNamespaceDeclarationCount() { + + // This seems to be a hot spot for DOM conversion. + // I'm trying to avoid the overhead of creating and adding + // to a HashSet for the simplest case of an element, none + // of whose attributes are in namespaces, and which has no + // additional namespace declarations. In this case, the + // namespace count is exactly one, which is here indicated + // by a null prefix set. + Set allPrefixes = null; + if (namespaces != null) { + allPrefixes = new HashSet(namespaces.getPrefixes()); + allPrefixes.add(prefix); + } + if ("xml".equals(prefix)) allPrefixes = new HashSet(); + // add attribute prefixes + int count = getAttributeCount(); + for (int i = 0; i < count; i++) { + Attribute att = attributes[i]; + String attPrefix = att.getNamespacePrefix(); + if (attPrefix.length() != 0 && !"xml".equals(attPrefix)) { + if (allPrefixes == null) { + allPrefixes = new HashSet(); + allPrefixes.add(prefix); + } + allPrefixes.add(attPrefix); + } + } + + if (allPrefixes == null) return 1; + return allPrefixes.size(); + + } + + + // Used for XPath and serialization + Map getNamespacePrefixesInScope() { + + HashMap namespaces = new HashMap(); + + Element current = this; + while (true) { + + if (!("xml".equals(current.prefix))) { + addPrefixIfNotAlreadyPresent(namespaces, current, current.prefix); + } + + // add attribute prefixes + if (current.attributes != null) { + int count = current.numAttributes; + for (int i = 0; i < count; i++) { + Attribute att = current.attributes[i]; + String attPrefix = att.getNamespacePrefix(); + if (attPrefix.length() != 0 && !("xml".equals(attPrefix))) { + addPrefixIfNotAlreadyPresent(namespaces, current, attPrefix); + } + } + } + + // add additional namespace prefixes + // ???? should add more methods to Namespaces to avoid access to private data + if (current.namespaces != null) { + int namespaceCount = current.namespaces.size(); + for (int i = 0; i < namespaceCount; i++) { + String nsPrefix = current.namespaces.getPrefix(i); + addPrefixIfNotAlreadyPresent(namespaces, current, nsPrefix); + } + } + + ParentNode parent = current.getParent(); + if (parent == null || parent.isDocument() || parent.isDocumentFragment()) { + break; + } + current = (Element) parent; + } + + return namespaces; + + } + + + private void addPrefixIfNotAlreadyPresent(HashMap namespaces, Element current, String prefix) { + if (!namespaces.containsKey(prefix)) { + namespaces.put(prefix, current.getLocalNamespaceURI(prefix)); + } + } + + + /** + *

    + * Returns the indexth namespace prefix declared on + * this element. Namespaces inherited from ancestors are not included. + * The index is purely for convenience, and has no + * meaning in itself. This includes the namespaces of the element + * name and of all attributes' names (except for those with the + * prefix xml such as xml:space) as well + * as additional declarations made for attribute values and element + * content. However, prefixes used multiple times (e.g. on several + * attribute values) are only reported once. The default + * namespace is reported with an empty string prefix if present. + * Like most lists in Java, the first prefix is at index 0. + *

    + * + *

    + * If the namespaces on the element change for any reason + * (adding or removing an attribute in a namespace, adding + * or removing a namespace declaration, changing the prefix + * of an element, etc.) then this method may skip or repeat + * prefixes. Don't change the prefixes of an element while + * iterating across them. + *

    + * + * @param index the prefix to return + * + * @return the prefix + * + * @throws IndexOutOfBoundsException if index is + * negative or greater than or equal to the number of + * namespaces declared by this element. + * + */ + public final String getNamespacePrefix(int index) { + + // XXX can I reuse set? or not use a Set at all? + // suppose the zeroth prefix is the namespace prefix is the first prefix + // found for the element, attributes, and namespace declarations, in that order. + // The second prefix is the next and so on. + + if (index < 0) { + throw new IndexOutOfBoundsException( + "Negative prefix number " + index); + } + else if (index == 0) { + if (!("xml".equals(prefix))) return prefix; + } + + + Set allPrefixes = getNamespacePrefixes(); + Iterator iterator = allPrefixes.iterator(); + try { + for (int i = 0; i < index; i++) { + iterator.next(); + } + return iterator.next(); + } + catch (NoSuchElementException ex) { + throw new IndexOutOfBoundsException( + // ???? fix to use 3rd, 2nd, 1st as appropriate + "No " + index + "th namespace"); + } + + } + + + private Set getNamespacePrefixes() { + + Set allPrefixes = new LinkedHashSet(); + if (!("xml".equals(prefix))) allPrefixes.add(prefix); + if (namespaces != null) { + allPrefixes.addAll(namespaces.getPrefixes()); + } + + if (attributes != null) { + int count = getAttributeCount(); + for (int i = 0; i < count; i++) { + Attribute att = attributes[i]; + String attPrefix = att.getNamespacePrefix(); + if (attPrefix.length() != 0 && !("xml".equals(attPrefix))) { + allPrefixes.add(attPrefix); + } + } + } + return allPrefixes; + + } + + + /** + * + *

    + * Sets the URI from which this element was loaded, + * and against which relative URLs in this element will be + * resolved, unless an xml:base attribute overrides + * this. Setting the base URI to null or the empty string removes + * any existing base URI. + *

    + * + * @param URI the new base URI for this element + * + * @throws MalformedURIException if URI is + * not a legal RFC 3986 absolute URI + */ + public void setBaseURI(String URI) { + setActualBaseURI(URI); + } + + + /** + * + *

    + * Returns the absolute base URI against which relative URIs in + * this element should be resolved. xml:base + * attributes in the same entity take precedence over the + * actual base URI of the document where the element was found + * or which was set by setBaseURI. + *

    + * + *

    + * This URI is made absolute before it is returned + * by resolving the information in this element against the + * information in its parent element and document entity. + * However, it is not always possible to fully absolutize the + * URI in all circumstances. In this case, this method returns the + * empty string to indicate the base URI of the current entity. + *

    + * + *

    + * If the element's xml:base attribute contains a + * value that is a syntactically illegal URI (e.g. %GF.html"), + * then the base URI is application dependent. XOM's choice is + * to behave as if the element did not have an xml:base + * attribute. + *

    + * + * @return the base URI of this element + */ + public String getBaseURI() { + + String baseURI = ""; + String sourceEntity = this.getActualBaseURI(); + + ParentNode current = this; + + while (true) { + String currentEntity = current.getActualBaseURI(); + if (sourceEntity.length() != 0 + && ! sourceEntity.equals(currentEntity)) { + baseURI = URIUtil.absolutize(sourceEntity, baseURI); + break; + } + + if (current.isDocument()) { + baseURI = URIUtil.absolutize(currentEntity, baseURI); + break; + } + Attribute baseAttribute = ((Element) current).getAttribute("base", + "http://www.w3.org/XML/1998/namespace"); + if (baseAttribute != null) { + String baseIRI = baseAttribute.getValue(); + // The base attribute contains an IRI, not a URI. + // Thus the first thing we have to do is escape it + // to convert illegal characters to hexadecimal escapes. + String base = URIUtil.toURI(baseIRI); + if ("".equals(base)) { + baseURI = getEntityURI(); + break; + } + else if (legalURI(base)) { + if ("".equals(baseURI)) baseURI = base; + else if (URIUtil.isOpaque(base)) break; + else baseURI = URIUtil.absolutize(base, baseURI); + if (URIUtil.isAbsolute(base)) break; // ???? base or baseURI + } + } + current = current.getParent(); + if (current == null) { + baseURI = URIUtil.absolutize(currentEntity, baseURI); + break; + } + } + + if (URIUtil.isAbsolute(baseURI)) return baseURI; + return ""; + + } + + + private String getEntityURI() { + + ParentNode current = this; + while (current != null) { + if (current.actualBaseURI != null + && current.actualBaseURI.length() != 0) { + return current.actualBaseURI; + } + current = current.getParent(); + } + return ""; + + } + + + private boolean legalURI(String base) { + + try { + Verifier.checkURIReference(base); + return true; + } + catch (MalformedURIException ex) { + return false; + } + + } + + + /** + *

    + * Returns a string containing the XML serialization of this + * element. This includes the element and all its attributes + * and descendants. However, it does not contain namespace + * declarations for namespaces inherited from ancestor elements. + *

    + * + * @return the XML representation of this element + * + */ + public final String toXML() { + + StringBuffer result = new StringBuffer(1024); + Node current = this; + boolean endTag = false; + int index = -1; + int[] indexes = new int[10]; + int top = 0; + indexes[0] = -1; + + while (true) { + + if (!endTag && current.getChildCount() > 0) { + writeStartTag((Element) current, result); + current = current.getChild(0); + index = 0; + top++; + indexes = grow(indexes, top); + indexes[top] = 0; + } + else { + if (endTag) { + writeEndTag((Element) current, result); + if (current == this) break; + } + else if (current.isElement()) { + writeStartTag((Element) current, result); + if (current == this) break; + } + else { + result.append(current.toXML()); + } + endTag = false; + ParentNode parent = current.getParent(); + if (parent.getChildCount() - 1 == index) { + current = parent; + top--; + if (current != this) { + index = indexes[top]; + } + endTag = true; + } + else { + index++; + indexes[top] = index; + current = parent.getChild(index); + } + + } + + } + + return result.toString(); + + } + + + private static void writeStartTag(Element element, StringBuffer result) { + + result.append('<'); + result.append(element.getQualifiedName()); + + ParentNode parentNode = element.getParent(); + for (int i = 0; i < element.getNamespaceDeclarationCount(); i++) { + String additionalPrefix = element.getNamespacePrefix(i); + String uri = element.getNamespaceURI(additionalPrefix); + if (parentNode != null && parentNode.isElement()) { + Element parentElement = (Element) parentNode; + if (uri.equals( + parentElement.getNamespaceURI(additionalPrefix))) { + continue; + } + } + else if (uri.length() == 0) { + continue; // no need to say xmlns="" + } + + result.append(" xmlns"); + if (additionalPrefix.length() > 0) { + result.append(':'); + result.append(additionalPrefix); + } + result.append("=\""); + result.append(escape(uri)); + result.append('"'); + } + + // attributes + if (element.attributes != null) { + for (int i = 0; i < element.numAttributes; i++) { + Attribute attribute = element.attributes[i]; + result.append(' '); + result.append(attribute.toXML()); + } + } + + if (element.getChildCount() > 0) { + result.append('>'); + } + else { + result.append(" />"); + } + + } + + + private static String escape(String s) { + + int length = s.length(); + // Give the string buffer enough room for a couple of escaped characters + StringBuffer result = new StringBuffer(length+12); + for (int i = 0; i < length; i++) { + char c = s.charAt(i); + if (c == '&') result.append("&"); + else result.append(c); + } + return result.toString(); + + } + + + private static void writeEndTag( + Element element, StringBuffer result) { + + result.append("'); + + } + + + /** + *

    + * Returns the value of the element as defined by XPath 1.0. + * This is the complete PCDATA content of the element, without + * any tags, comments, or processing instructions after all + * entity and character references have been resolved. + *

    + * + * @return XPath string value of this element + * + */ + public final String getValue() { + + // non-recursive algorithm avoids stack size limitations + int childCount = this.getChildCount(); + if (childCount == 0) return ""; + + Node current = this.getChild(0); + // optimization for common case where element + // has a single text node child + if (childCount == 1 && current.isText()) { + return current.getValue(); + } + + StringBuffer result = new StringBuffer(); + int index = 0; + int[] indexes = new int[10]; + int top = 0; + indexes[0] = 0; + + boolean endTag = false; + while (true) { + if (!endTag && current.getChildCount() > 0) { + current = current.getChild(0); + index = 0; + top++; + indexes = grow(indexes, top); + indexes[top] = 0; } + else { + endTag = false; + if (current.isText()) result.append(current.getValue()); + ParentNode parent = current.getParent(); + if (parent.getChildCount() - 1 == index) { + current = parent; + top--; + if (current == this) break; + index = indexes[top]; + endTag = true; + } + else { + index++; + indexes[top] = index; + current = parent.getChild(index); + } + } + } + + return result.toString(); + + } + + /** + *

    + * Creates a deep copy of this element with no parent, + * that can be added to this document or a different one. + *

    + * + *

    + * Subclassers should be wary. Implementing this method is trickier + * than it might seem, especially if you wish to avoid potential + * stack overflows in deep documents. In particular, you should not + * rely on the obvious recursive algorithm. Most subclasses should + * override the {@link nu.xom.Element#shallowCopy() shallowCopy} + * method instead. + *

    + * + * @return a deep copy of this element with no parent + */ + public Element copy() { + Element result = copyTag(this); + copyChildren(this, result); + return result; + } + + + /** + *

    + * Creates a very shallow copy of the element with the same name + * and namespace URI, but no children, attributes, base URI, or + * namespace declaration. This method is invoked as necessary + * by the {@link nu.xom.Element#copy() copy} method + * and the {@link nu.xom.Element#Element(nu.xom.Element) + * copy constructor}. + *

    + * + *

    + * Subclasses should override this method so that it + * returns an instance of the subclass so that types + * are preserved when copying. This method should not add any + * attributes, namespace declarations, or children to the + * shallow copy. Any such items will be overwritten. + *

    + * + * @return an empty element with the same name and + * namespace as this element + */ + protected Element shallowCopy() { + return new Element(getQualifiedName(), getNamespaceURI()); + } + + + /** + *

    + * Returns a string representation of this element suitable + * for debugging and diagnosis. This is not + * the XML representation of the element. + *

    + * + * @return a non-XML string representation of this element + */ + public String toString() { + return + "[" + getClass().getName() + ": " + getQualifiedName() + "]"; + } + + + boolean isElement() { + return true; + } + + + private void checkPrefixConflict(Attribute attribute) { + + String prefix = attribute.getNamespacePrefix(); + String namespaceURI = attribute.getNamespaceURI(); + + // Look for conflicts + for (int i = 0; i < numAttributes; i++) { + Attribute a = attributes[i]; + if (a.getNamespacePrefix().equals(prefix)) { + if (a.getNamespaceURI().equals(namespaceURI)) return; + throw new NamespaceConflictException( + "Prefix of " + attribute.getQualifiedName() + + " conflicts with " + a.getQualifiedName()); + } + } + + } + + + private class AttributeIterator implements Iterator { + + private int next = 0; + + public boolean hasNext() { + return next < numAttributes; + } + + public Attribute next() throws NoSuchElementException { + + if (hasNext()) { + Attribute a = attributes[next]; + next++; + return a; + } + throw new NoSuchElementException("No such attribute"); + + } + + public void remove() { + throw new UnsupportedOperationException(); + } + } + + Iterator attributeIterator() { + + return new AttributeIterator(); + } + + +} \ No newline at end of file diff --git a/src/nu/xom/Elements.java b/src/nu/xom/Elements.java new file mode 100644 index 0000000..eba6aaf --- /dev/null +++ b/src/nu/xom/Elements.java @@ -0,0 +1,92 @@ +/* Copyright 2002-2004 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; + +/** + * + *

    + * A read-only list of elements for traversal purposes. + * Changes to the document from which this list was generated + * are not reflected in this list. Changes to the individual + * Element objects in the list are reflected. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.3.0 + * + * + */ +public final class Elements implements Iterable { + + + private List elements = new ArrayList(1); + + // non-public constructor to prevent instantiation + Elements() {} + + /** + *

    + * Returns the number of elements in the list. + * This is guaranteed non-negative. + *

    + * + * @return the number of elements in the list + */ + public int size() { + return elements.size(); + } + + /** + *

    + * Returns the indexth element in the list. + * The first element has index 0. The last element + * has index size()-1. + *

    + * + * @param index the element to return + * + * @return the element at the specified position + * + * @throws IndexOutOfBoundsException if index is negative + * or greater than or equal to the size of the list + */ + public Element get(int index) { + return (Element) elements.get(index); + } + + + // Add the specified Element object to the list + void add(Element element) { + elements.add(element); + } + + @Override + public Iterator iterator() { + return Collections.unmodifiableList(elements).iterator(); + } + +} \ No newline at end of file diff --git a/src/nu/xom/GenericWriter.java b/src/nu/xom/GenericWriter.java new file mode 100644 index 0000000..75b46a5 --- /dev/null +++ b/src/nu/xom/GenericWriter.java @@ -0,0 +1,80 @@ +/* Copyright 2002-2004, 2018 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom; + +import java.io.UnsupportedEncodingException; +import java.io.Writer; +import java.nio.charset.Charset; +import java.nio.charset.CharsetEncoder; +import java.util.Locale; + +/** + *

    + * GenericWriter figures out whether a + * character is or is not available in a particular encoding. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.2.11 + * + */ +class GenericWriter extends TextWriter { + + private final boolean isJapanese; + private final CharsetEncoder encoder; + + + GenericWriter(Writer out, String encoding) + throws UnsupportedEncodingException { + + super(out, encoding); + encoding = encoding.toUpperCase(Locale.ENGLISH); + if (encoding.indexOf("EUC-JP") > -1 + || encoding.startsWith("EUC_JP") + || encoding.equals("SHIFT_JIS") + || encoding.equals("SJIS") + || encoding.equals("ISO-2022-JP")) { + isJapanese = true; + } + else { + isJapanese = false; + } + encoder = Charset.forName(encoding).newEncoder(); + } + + + boolean needsEscaping(char c) { + + // assume everything has at least the ASCII characters + if (c <= 127) return false; + // work around various bugs in Japanese encodings + if (isJapanese) { + if (c == 0xA5) return true; // Yen symbol + if (c == 0x203E) return true; // Sun bugs in EUC-JP and SJIS + } + + if (encoder.canEncode(c)) return false; + return true; + } + + +} diff --git a/src/nu/xom/ISOArabicWriter.java b/src/nu/xom/ISOArabicWriter.java new file mode 100644 index 0000000..33ed80b --- /dev/null +++ b/src/nu/xom/ISOArabicWriter.java @@ -0,0 +1,128 @@ +/* Copyright 2002, 2003 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom; + +import java.io.Writer; + +/** + *

    + * ISO 8859-6, ASCII plus Arabic + *

    + * + * @author Elliotte Rusty Harold + * @version 1.0 + * + */ +class ISOArabicWriter extends TextWriter { + + ISOArabicWriter(Writer out, String encoding) { + super(out, encoding); + } + + /** + * @see nu.xom.TextWriter#needsEscaping(char) + */ + boolean needsEscaping(char c) { + if (c <= 0xA0) return false; + switch (c) { + case 0x060C: return false; // ARABIC COMMA + case 0x060D: return true; // place holder to allow table lookup + case 0x060E: return true; // place holder to allow table lookup + case 0x060F: return true; // place holder to allow table lookup + case 0x0610: return true; // place holder to allow table lookup + case 0x0611: return true; // place holder to allow table lookup + case 0x0612: return true; // place holder to allow table lookup + case 0x0613: return true; // place holder to allow table lookup + case 0x0614: return true; // place holder to allow table lookup + case 0x0615: return true; // place holder to allow table lookup + case 0x0616: return true; // place holder to allow table lookup + case 0x0617: return true; // place holder to allow table lookup + case 0x0618: return true; // place holder to allow table lookup + case 0x0619: return true; // place holder to allow table lookup + case 0x061A: return true; // place holder to allow table lookup + case 0x061B: return false; // ARABIC SEMICOLON + case 0x061C: return true; // place holder to allow table lookup + case 0x061D: return true; // place holder to allow table lookup + case 0x061E: return true; // place holder to allow table lookup + case 0x061F: return false; // ARABIC QUESTION MARK + case 0x0620: return true; // place holder to allow table lookup + case 0x0621: return false; // ARABIC LETTER HAMZA + case 0x0622: return false; // ARABIC LETTER ALEF WITH MADDA ABOVE + case 0x0623: return false; // ARABIC LETTER ALEF WITH HAMZA ABOVE + case 0x0624: return false; // ARABIC LETTER WAW WITH HAMZA ABOVE + case 0x0625: return false; // ARABIC LETTER ALEF WITH HAMZA BELOW + case 0x0626: return false; // ARABIC LETTER YEH WITH HAMZA ABOVE + case 0x0627: return false; // ARABIC LETTER ALEF + case 0x0628: return false; // ARABIC LETTER BEH + case 0x0629: return false; // ARABIC LETTER TEH MARBUTA + case 0x062A: return false; // ARABIC LETTER TEH + case 0x062B: return false; // ARABIC LETTER THEH + case 0x062C: return false; // ARABIC LETTER JEEM + case 0x062D: return false; // ARABIC LETTER HAH + case 0x062E: return false; // ARABIC LETTER KHAH + case 0x062F: return false; // ARABIC LETTER DAL + case 0x0630: return false; // ARABIC LETTER THAL + case 0x0631: return false; // ARABIC LETTER REH + case 0x0632: return false; // ARABIC LETTER ZAIN + case 0x0633: return false; // ARABIC LETTER SEEN + case 0x0634: return false; // ARABIC LETTER SHEEN + case 0x0635: return false; // ARABIC LETTER SAD + case 0x0636: return false; // ARABIC LETTER DAD + case 0x0637: return false; // ARABIC LETTER TAH + case 0x0638: return false; // ARABIC LETTER ZAH + case 0x0639: return false; // ARABIC LETTER AIN + case 0x063A: return false; // ARABIC LETTER GHAIN + case 0x063B: return true; // place holder to allow table lookup + case 0x063C: return true; // place holder to allow table lookup + case 0x063D: return true; // place holder to allow table lookup + case 0x063E: return true; // place holder to allow table lookup + case 0x063F: return true; // place holder to allow table lookup + case 0x0640: return false; // ARABIC TATWEEL + case 0x0641: return false; // ARABIC LETTER FEH + case 0x0642: return false; // ARABIC LETTER QAF + case 0x0643: return false; // ARABIC LETTER KAF + case 0x0644: return false; // ARABIC LETTER LAM + case 0x0645: return false; // ARABIC LETTER MEEM + case 0x0646: return false; // ARABIC LETTER NOON + case 0x0647: return false; // ARABIC LETTER HEH + case 0x0648: return false; // ARABIC LETTER WAW + case 0x0649: return false; // ARABIC LETTER ALEF MAKSURA + case 0x064A: return false; // ARABIC LETTER YEH + case 0x064B: return false; // ARABIC FATHATAN + case 0x064C: return false; // ARABIC DAMMATAN + case 0x064D: return false; // ARABIC KASRATAN + case 0x064E: return false; // ARABIC FATHA + case 0x064F: return false; // ARABIC DAMMA + case 0x0650: return false; // ARABIC KASRA + case 0x0651: return false; // ARABIC SHADDA + case 0x0652: return false; // ARABIC SUKUN + } + switch (c) { // random overlap with Latin-1 + case 0x00A4: return false; // CURRENCY SIGN + case 0x00AD: return false; // SOFT HYPHEN + } + + return true; + + } + +} \ No newline at end of file diff --git a/src/nu/xom/ISOCyrillicWriter.java b/src/nu/xom/ISOCyrillicWriter.java new file mode 100644 index 0000000..7b6250e --- /dev/null +++ b/src/nu/xom/ISOCyrillicWriter.java @@ -0,0 +1,153 @@ +/* Copyright 2002, 2003 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom; + +import java.io.Writer; + +/** + *

    + * ISO 8859-5, ASCII plus Cyrillic (Russian, Byelorussian, etc.) + *

    + * + * @author Elliotte Rusty Harold + * @version 1.0 + * + */ +class ISOCyrillicWriter extends TextWriter { + + ISOCyrillicWriter(Writer out, String encoding) { + super(out, encoding); + } + + /** + * @see nu.xom.TextWriter#needsEscaping(char) + */ + boolean needsEscaping(char c) { + if (c <= 0xA0) return false; + switch (c) { + case 0x0401: return false; // CYRILLIC CAPITAL LETTER IO + case 0x0402: return false; // CYRILLIC CAPITAL LETTER DJE + case 0x0403: return false; // CYRILLIC CAPITAL LETTER GJE + case 0x0404: return false; // CYRILLIC CAPITAL LETTER UKRAINIAN IE + case 0x0405: return false; // CYRILLIC CAPITAL LETTER DZE + case 0x0406: return false; // CYRILLIC CAPITAL LETTER BYELORUSSIAN-UKRAINIAN I + case 0x0407: return false; // CYRILLIC CAPITAL LETTER YI + case 0x0408: return false; // CYRILLIC CAPITAL LETTER JE + case 0x0409: return false; // CYRILLIC CAPITAL LETTER LJE + case 0x040A: return false; // CYRILLIC CAPITAL LETTER NJE + case 0x040B: return false; // CYRILLIC CAPITAL LETTER TSHE + case 0x040C: return false; // CYRILLIC CAPITAL LETTER KJE + case 0x040D: return true; // place holder to enable table lookup + case 0x040E: return false; // CYRILLIC CAPITAL LETTER SHORT U + case 0x040F: return false; // CYRILLIC CAPITAL LETTER DZHE + case 0x0410: return false; // CYRILLIC CAPITAL LETTER A + case 0x0411: return false; // CYRILLIC CAPITAL LETTER BE + case 0x0412: return false; // CYRILLIC CAPITAL LETTER VE + case 0x0413: return false; // CYRILLIC CAPITAL LETTER GHE + case 0x0414: return false; // CYRILLIC CAPITAL LETTER DE + case 0x0415: return false; // CYRILLIC CAPITAL LETTER IE + case 0x0416: return false; // CYRILLIC CAPITAL LETTER ZHE + case 0x0417: return false; // CYRILLIC CAPITAL LETTER ZE + case 0x0418: return false; // CYRILLIC CAPITAL LETTER I + case 0x0419: return false; // CYRILLIC CAPITAL LETTER SHORT I + case 0x041A: return false; // CYRILLIC CAPITAL LETTER KA + case 0x041B: return false; // CYRILLIC CAPITAL LETTER EL + case 0x041C: return false; // CYRILLIC CAPITAL LETTER EM + case 0x041D: return false; // CYRILLIC CAPITAL LETTER EN + case 0x041E: return false; // CYRILLIC CAPITAL LETTER O + case 0x041F: return false; // CYRILLIC CAPITAL LETTER PE + case 0x0420: return false; // CYRILLIC CAPITAL LETTER ER + case 0x0421: return false; // CYRILLIC CAPITAL LETTER ES + case 0x0422: return false; // CYRILLIC CAPITAL LETTER TE + case 0x0423: return false; // CYRILLIC CAPITAL LETTER U + case 0x0424: return false; // CYRILLIC CAPITAL LETTER EF + case 0x0425: return false; // CYRILLIC CAPITAL LETTER HA + case 0x0426: return false; // CYRILLIC CAPITAL LETTER TSE + case 0x0427: return false; // CYRILLIC CAPITAL LETTER CHE + case 0x0428: return false; // CYRILLIC CAPITAL LETTER SHA + case 0x0429: return false; // CYRILLIC CAPITAL LETTER SHCHA + case 0x042A: return false; // CYRILLIC CAPITAL LETTER HARD SIGN + case 0x042B: return false; // CYRILLIC CAPITAL LETTER YERU + case 0x042C: return false; // CYRILLIC CAPITAL LETTER SOFT SIGN + case 0x042D: return false; // CYRILLIC CAPITAL LETTER E + case 0x042E: return false; // CYRILLIC CAPITAL LETTER YU + case 0x042F: return false; // CYRILLIC CAPITAL LETTER YA + case 0x0430: return false; // CYRILLIC SMALL LETTER A + case 0x0431: return false; // CYRILLIC SMALL LETTER BE + case 0x0432: return false; // CYRILLIC SMALL LETTER VE + case 0x0433: return false; // CYRILLIC SMALL LETTER GHE + case 0x0434: return false; // CYRILLIC SMALL LETTER DE + case 0x0435: return false; // CYRILLIC SMALL LETTER IE + case 0x0436: return false; // CYRILLIC SMALL LETTER ZHE + case 0x0437: return false; // CYRILLIC SMALL LETTER ZE + case 0x0438: return false; // CYRILLIC SMALL LETTER I + case 0x0439: return false; // CYRILLIC SMALL LETTER SHORT I + case 0x043A: return false; // CYRILLIC SMALL LETTER KA + case 0x043B: return false; // CYRILLIC SMALL LETTER EL + case 0x043C: return false; // CYRILLIC SMALL LETTER EM + case 0x043D: return false; // CYRILLIC SMALL LETTER EN + case 0x043E: return false; // CYRILLIC SMALL LETTER O + case 0x043F: return false; // CYRILLIC SMALL LETTER PE + case 0x0440: return false; // CYRILLIC SMALL LETTER ER + case 0x0441: return false; // CYRILLIC SMALL LETTER ES + case 0x0442: return false; // CYRILLIC SMALL LETTER TE + case 0x0443: return false; // CYRILLIC SMALL LETTER U + case 0x0444: return false; // CYRILLIC SMALL LETTER EF + case 0x0445: return false; // CYRILLIC SMALL LETTER HA + case 0x0446: return false; // CYRILLIC SMALL LETTER TSE + case 0x0447: return false; // CYRILLIC SMALL LETTER CHE + case 0x0448: return false; // CYRILLIC SMALL LETTER SHA + case 0x0449: return false; // CYRILLIC SMALL LETTER SHCHA + case 0x044A: return false; // CYRILLIC SMALL LETTER HARD SIGN + case 0x044B: return false; // CYRILLIC SMALL LETTER YERU + case 0x044C: return false; // CYRILLIC SMALL LETTER SOFT SIGN + case 0x044D: return false; // CYRILLIC SMALL LETTER E + case 0x044E: return false; // CYRILLIC SMALL LETTER YU + case 0x044F: return false; // CYRILLIC SMALL LETTER YA + case 0x0450: return true; // place holder to enable table lookup + case 0x0451: return false; // CYRILLIC SMALL LETTER IO + case 0x0452: return false; // CYRILLIC SMALL LETTER DJE + case 0x0453: return false; // CYRILLIC SMALL LETTER GJE + case 0x0454: return false; // CYRILLIC SMALL LETTER UKRAINIAN IE + case 0x0455: return false; // CYRILLIC SMALL LETTER DZE + case 0x0456: return false; // CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I + case 0x0457: return false; // CYRILLIC SMALL LETTER YI + case 0x0458: return false; // CYRILLIC SMALL LETTER JE + case 0x0459: return false; // CYRILLIC SMALL LETTER LJE + case 0x045A: return false; // CYRILLIC SMALL LETTER NJE + case 0x045B: return false; // CYRILLIC SMALL LETTER TSHE + case 0x045C: return false; // CYRILLIC SMALL LETTER KJE + case 0x045D: return true; // place holder to enable table lookup + case 0x045E: return false; // CYRILLIC SMALL LETTER SHORT U + case 0x045F: return false; // CYRILLIC SMALL LETTER DZHE + } + + switch (c) { // assorted leftover characters + case 0x00AD: return false; // SOFT HYPHEN + case 0x00A7: return false; // SECTION SIGN + case 0x2116: return false; // NUMERO SIGN + } + + return true; + } + +} \ No newline at end of file diff --git a/src/nu/xom/ISOGreekWriter.java b/src/nu/xom/ISOGreekWriter.java new file mode 100644 index 0000000..8bf0c58 --- /dev/null +++ b/src/nu/xom/ISOGreekWriter.java @@ -0,0 +1,169 @@ +/* Copyright 2002, 2003 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom; + +import java.io.Writer; + +/** + *

    + * ISO 8859-7, ASCII plus Greek + *

    + * + * @author Elliotte Rusty Harold + * @version 1.1b2 + * + */ +class ISOGreekWriter extends TextWriter { + + ISOGreekWriter(Writer out, String encoding) { + super(out, encoding); + } + + /** + * @see nu.xom.TextWriter#needsEscaping(char) + */ + boolean needsEscaping(char c) { + if (c < 127) return false; + switch (c) { // Greek + case 0x0384: return false; // GREEK TONOS + case 0x0385: return false; // GREEK DIALYTIKA TONOS + case 0x0386: return false; // GREEK CAPITAL LETTER ALPHA WITH TONOS + case 0x0387: return true; // place holder to enable table lookup + case 0x0388: return false; // GREEK CAPITAL LETTER EPSILON WITH TONOS + case 0x0389: return false; // GREEK CAPITAL LETTER ETA WITH TONOS + case 0x038A: return false; // GREEK CAPITAL LETTER IOTA WITH TONOS + case 0x038B: return true; // place holder to enable table lookup + case 0x038C: return false; // GREEK CAPITAL LETTER OMICRON WITH TONOS + case 0x038D: return true; // place holder to enable table lookup + case 0x038E: return false; // GREEK CAPITAL LETTER UPSILON WITH TONOS + case 0x038F: return false; // GREEK CAPITAL LETTER OMEGA WITH TONOS + case 0x0390: return false; // GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TONOS + case 0x0391: return false; // GREEK CAPITAL LETTER ALPHA + case 0x0392: return false; // GREEK CAPITAL LETTER BETA + case 0x0393: return false; // GREEK CAPITAL LETTER GAMMA + case 0x0394: return false; // GREEK CAPITAL LETTER DELTA + case 0x0395: return false; // GREEK CAPITAL LETTER EPSILON + case 0x0396: return false; // GREEK CAPITAL LETTER ZETA + case 0x0397: return false; // GREEK CAPITAL LETTER ETA + case 0x0398: return false; // GREEK CAPITAL LETTER THETA + case 0x0399: return false; // GREEK CAPITAL LETTER IOTA + case 0x039A: return false; // GREEK CAPITAL LETTER KAPPA + case 0x039B: return false; // GREEK CAPITAL LETTER LAMDA + case 0x039C: return false; // GREEK CAPITAL LETTER MU + case 0x039D: return false; // GREEK CAPITAL LETTER NU + case 0x039E: return false; // GREEK CAPITAL LETTER XI + case 0x039F: return false; // GREEK CAPITAL LETTER OMICRON + case 0x03A0: return false; // GREEK CAPITAL LETTER PI + case 0x03A1: return false; // GREEK CAPITAL LETTER RHO + case 0x03A2: return true; // place holder to enable table lookup + case 0x03A3: return false; // GREEK CAPITAL LETTER SIGMA + case 0x03A4: return false; // GREEK CAPITAL LETTER TAU + case 0x03A5: return false; // GREEK CAPITAL LETTER UPSILON + case 0x03A6: return false; // GREEK CAPITAL LETTER PHI + case 0x03A7: return false; // GREEK CAPITAL LETTER CHI + case 0x03A8: return false; // GREEK CAPITAL LETTER PSI + case 0x03A9: return false; // GREEK CAPITAL LETTER OMEGA + case 0x03AA: return false; // GREEK CAPITAL LETTER IOTA WITH DIALYTIKA + case 0x03AB: return false; // GREEK CAPITAL LETTER UPSILON WITH DIALYTIKA + case 0x03AC: return false; // GREEK SMALL LETTER ALPHA WITH TONOS + case 0x03AD: return false; // GREEK SMALL LETTER EPSILON WITH TONOS + case 0x03AE: return false; // GREEK SMALL LETTER ETA WITH TONOS + case 0x03AF: return false; // GREEK SMALL LETTER IOTA WITH TONOS + case 0x03B0: return false; // GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS + case 0x03B1: return false; // GREEK SMALL LETTER ALPHA + case 0x03B2: return false; // GREEK SMALL LETTER BETA + case 0x03B3: return false; // GREEK SMALL LETTER GAMMA + case 0x03B4: return false; // GREEK SMALL LETTER DELTA + case 0x03B5: return false; // GREEK SMALL LETTER EPSILON + case 0x03B6: return false; // GREEK SMALL LETTER ZETA + case 0x03B7: return false; // GREEK SMALL LETTER ETA + case 0x03B8: return false; // GREEK SMALL LETTER THETA + case 0x03B9: return false; // GREEK SMALL LETTER IOTA + case 0x03BA: return false; // GREEK SMALL LETTER KAPPA + case 0x03BB: return false; // GREEK SMALL LETTER LAMDA + case 0x03BC: return false; // GREEK SMALL LETTER MU + case 0x03BD: return false; // GREEK SMALL LETTER NU + case 0x03BE: return false; // GREEK SMALL LETTER XI + case 0x03BF: return false; // GREEK SMALL LETTER OMICRON + case 0x03C0: return false; // GREEK SMALL LETTER PI + case 0x03C1: return false; // GREEK SMALL LETTER RHO + case 0x03C2: return false; // GREEK SMALL LETTER FINAL SIGMA + case 0x03C3: return false; // GREEK SMALL LETTER SIGMA + case 0x03C4: return false; // GREEK SMALL LETTER TAU + case 0x03C5: return false; // GREEK SMALL LETTER UPSILON + case 0x03C6: return false; // GREEK SMALL LETTER PHI + case 0x03C7: return false; // GREEK SMALL LETTER CHI + case 0x03C8: return false; // GREEK SMALL LETTER PSI + case 0x03C9: return false; // GREEK SMALL LETTER OMEGA + case 0x03CA: return false; // GREEK SMALL LETTER IOTA WITH DIALYTIKA + case 0x03CB: return false; // GREEK SMALL LETTER UPSILON WITH DIALYTIKA + case 0x03CC: return false; // GREEK SMALL LETTER OMICRON WITH TONOS + case 0x03CD: return false; // GREEK SMALL LETTER UPSILON WITH TONOS + case 0x03CE: return false; // GREEK SMALL LETTER OMEGA WITH TONOS + } + switch (c) { // overlap with Latin-1 + case 0x00A0: return false; // non-breaking space + case 0x00A1: return true; // place holder to enable table lookup + case 0x00A2: return true; // place holder to enable table lookup + case 0x00A3: return false; // POUND SIGN + case 0x00A4: return true; // place holder to enable table lookup + case 0x00A5: return true; // place holder to enable table lookup + case 0x00A6: return false; // BROKEN BAR + case 0x00A7: return false; // SECTION SIGN + case 0x00A8: return false; // DIAERESIS + case 0x00A9: return false; // COPYRIGHT SIGN + case 0x00AA: return true; // place holder to enable table lookup + case 0x00AB: return false; // LEFT-POINTING DOUBLE ANGLE QUOTATION MARK + case 0x00AC: return false; // NOT SIGN + case 0x00AD: return false; // SOFT HYPHEN + case 0x00AE: return true; // place holder to enable table lookup + case 0x00AF: return true; // place holder to enable table lookup + case 0x00B0: return false; // DEGREE SIGN + case 0x00B1: return false; // PLUS-MINUS SIGN + case 0x00B2: return false; // SUPERSCRIPT TWO + case 0x00B3: return false; // SUPERSCRIPT THREE + case 0x00B4: return true; // place holder to enable table lookup + case 0x00B5: return true; // place holder to enable table lookup + case 0x00B6: return true; // place holder to enable table lookup + case 0x00B7: return false; // MIDDLE DOT + case 0x00B8: return true; // place holder to enable table lookup + case 0x00B9: return true; // place holder to enable table lookup + case 0x00BA: return true; // place holder to enable table lookup + case 0x00BB: return false; // RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK + case 0x00BC: return true; // place holder to enable table lookup + case 0x00BD: return false; // VULGAR FRACTION ONE HALF + } + switch (c) { // assorted characters + case 0x2015: return false; // HORIZONTAL BAR + // A bug in Java 1.4 and 1.3 prevents a LEFT and RIGHT + // SINGLE QUOTATION MARKs + // from being correctly output + // as the actual character in this encoding + // even though it does exist in the + // ISO-8859-7 character set. + // case 0x2018: return false; // LEFT SINGLE QUOTATION MARK + // case 0x2019: return false; // RIGHT SINGLE QUOTATION MARK + } + return true; + } + +} \ No newline at end of file diff --git a/src/nu/xom/ISOHebrewWriter.java b/src/nu/xom/ISOHebrewWriter.java new file mode 100644 index 0000000..60e8d04 --- /dev/null +++ b/src/nu/xom/ISOHebrewWriter.java @@ -0,0 +1,138 @@ +/* Copyright 2002, 2003 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom; + +import java.io.Writer; + +/** + *

    + * ISO 8859-8, ASCII plus Hebrew + *

    + * + * @author Elliotte Rusty Harold + * @version 1.0 + * + */ +class ISOHebrewWriter extends TextWriter { + + ISOHebrewWriter(Writer out, String encoding) { + super(out, encoding); + } + + /** + * @see nu.xom.TextWriter#needsEscaping(char) + */ + boolean needsEscaping(char c) { + if (c <= 0xA0) return false; + switch (c) { // characters shared with Latin-1 + case 0x00A2: return false; // CENT SIGN + case 0x00A3: return false; // POUND SIGN + case 0x00A4: return false; // CURRENCY SIGN + case 0x00A5: return false; // YEN SIGN + case 0x00A6: return false; // BROKEN BAR + case 0x00A7: return false; // SECTION SIGN + case 0x00A8: return false; // DIAERESIS + case 0x00A9: return false; // COPYRIGHT SIGN + case 0x00AB: return false; // LEFT-POINTING DOUBLE ANGLE QUOTATION MARK + case 0x00AC: return false; // NOT SIGN + case 0x00AD: return false; // SOFT HYPHEN + case 0x00AE: return false; // REGISTERED SIGN + // A bug in Java prevents a macron from being correctly + // output as the actual character in this encoding even + // though it does exist in the ISO-8859-8 character set. + // See JDC bug 4760496 + // http://developer.java.sun.com/developer/bugParade/bugs/4760496.html + // They have marked this as fixed in Tiger (i.e. Java 1.5) + // I'm not going to fix it here yet though, because I'd + // prefer XOM to work correctly with earlier versions of + // of Java; and it's not incorrect to output a character + // reference even if you don't have to. It is an issue if a + // macron is used in a a comment or a processing + // instruction though. The macron is not a name character + // so that's not an issue though. + case 0x00AF: return true; // MACRON + case 0x00B0: return false; // DEGREE SIGN + case 0x00B1: return false; // PLUS-MINUS SIGN + case 0x00B2: return false; // SUPERSCRIPT TWO + case 0x00B3: return false; // SUPERSCRIPT THREE + case 0x00B4: return false; // ACUTE ACCENT + case 0x00B5: return false; // MICRO SIGN + case 0x00B6: return false; // PILCROW SIGN + case 0x00B7: return false; // MIDDLE DOT + case 0x00B8: return false; // CEDILLA + case 0x00B9: return false; // SUPERSCRIPT ONE + case 0x00BA: return true; // place holder to allow optimization of switch statement + case 0x00BB: return false; // RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK + case 0x00BC: return false; // VULGAR FRACTION ONE QUARTER + case 0x00BD: return false; // VULGAR FRACTION ONE HALF + case 0x00BE: return false; // VULGAR FRACTION THREE QUARTERS + } + switch (c) { // Unicode Hebrew block + case 0x05D0: return false; // HEBREW LETTER ALEF + case 0x05D1: return false; // HEBREW LETTER BET + case 0x05D2: return false; // HEBREW LETTER GIMEL + case 0x05D3: return false; // HEBREW LETTER DALET + case 0x05D4: return false; // HEBREW LETTER HE + case 0x05D5: return false; // HEBREW LETTER VAV + case 0x05D6: return false; // HEBREW LETTER ZAYIN + case 0x05D7: return false; // HEBREW LETTER HET + case 0x05D8: return false; // HEBREW LETTER TET + case 0x05D9: return false; // HEBREW LETTER YOD + case 0x05DA: return false; // HEBREW LETTER FINAL KAF + case 0x05DB: return false; // HEBREW LETTER KAF + case 0x05DC: return false; // HEBREW LETTER LAMED + case 0x05DD: return false; // HEBREW LETTER FINAL MEM + case 0x05DE: return false; // HEBREW LETTER MEM + case 0x05DF: return false; // HEBREW LETTER FINAL NUN + case 0x05E0: return false; // HEBREW LETTER NUN + case 0x05E1: return false; // HEBREW LETTER SAMEKH + case 0x05E2: return false; // HEBREW LETTER AYIN + case 0x05E3: return false; // HEBREW LETTER FINAL PE + case 0x05E4: return false; // HEBREW LETTER PE + case 0x05E5: return false; // HEBREW LETTER FINAL TSADI + case 0x05E6: return false; // HEBREW LETTER TSADI + case 0x05E7: return false; // HEBREW LETTER QOF + case 0x05E8: return false; // HEBREW LETTER RESH + case 0x05E9: return false; // HEBREW LETTER SHIN + case 0x05EA: return false; // HEBREW LETTER TAV + } + + switch (c) { // a few random, out of order characters + case 0x00D7: return false; // MULTIPLICATION SIGN + case 0x00F7: return false; // DIVISION SIGN + // A bug in Java prevents a LEFT-TO-RIGHT MARK + // and RIGHT-TO-LEFT MARK from being correctly output + // as the actual character in this encoding even + // though it does exist in the ISO-8859-8 character set. + // See JDC bug 4758951 + // http://developer.java.sun.com/developer/bugParade/bugs/4758951.html + // They have marked this as fixed in Tiger (i.e. Java 1.5) + // case 0x200E: return false; // LEFT-TO-RIGHT MARK + // case 0x200F: return false; // RIGHT-TO-LEFT MARK + case 0x2017: return false; // DOUBLE LOW LINE + } + + return true; + + } + +} \ No newline at end of file diff --git a/src/nu/xom/ISOThaiWriter.java b/src/nu/xom/ISOThaiWriter.java new file mode 100644 index 0000000..557608e --- /dev/null +++ b/src/nu/xom/ISOThaiWriter.java @@ -0,0 +1,160 @@ +/* Copyright 2002, 2003 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom; + +import java.io.Writer; + +/** + *

    + * TIS-620, not quite the same as ISO 8859-11. + * TIS-620 does not have the non-breaking space or the C1 controls. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.0 + * + */ +class ISOThaiWriter extends TextWriter { + + ISOThaiWriter(Writer out, String encoding) { + super(out, encoding); + } + + /** + * @see nu.xom.TextWriter#needsEscaping(char) + */ + boolean needsEscaping(char c) { + + if (c < 128) return false; + // C1 controls do not appear to be assigned in this character set + // Also, according to + // http://www.inet.co.th/cyberclub/trin/thairef/tis620-iso10646.html + // "Special attention should be paid on codepoint A0. Contrary + // to many people's belief that TIS 620 defines codepoint A0 + // as no-break space (U+00A0), the standard does not assign any + // character to this codepoint. Codepoints A0 as well as DB-DE + // and FC-FF are not part of the standard. Interpretations of + // these unassigned codepoints are implementation specific and + // may vary from implementation to implementation. To ensure full + // data interchangeability among various applications, it is + // suggested that Thai software implementors follows the + // TIS 620 standard strictly." + + switch (c) { + case 0x0E01: return false; // THAI CHARACTER KO KAI + case 0x0E02: return false; // THAI CHARACTER KHO KHAI + case 0x0E03: return false; // THAI CHARACTER KHO KHUAT + case 0x0E04: return false; // THAI CHARACTER KHO KHWAI + case 0x0E05: return false; // THAI CHARACTER KHO KHON + case 0x0E06: return false; // THAI CHARACTER KHO RAKHANG + case 0x0E07: return false; // THAI CHARACTER NGO NGU + case 0x0E08: return false; // THAI CHARACTER CHO CHAN + case 0x0E09: return false; // THAI CHARACTER CHO CHING + case 0x0E0A: return false; // THAI CHARACTER CHO CHANG + case 0x0E0B: return false; // THAI CHARACTER SO SO + case 0x0E0C: return false; // THAI CHARACTER CHO CHOE + case 0x0E0D: return false; // THAI CHARACTER YO YING + case 0x0E0E: return false; // THAI CHARACTER DO CHADA + case 0x0E0F: return false; // THAI CHARACTER TO PATAK + case 0x0E10: return false; // THAI CHARACTER THO THAN + case 0x0E11: return false; // THAI CHARACTER THO NANGMONTHO + case 0x0E12: return false; // THAI CHARACTER THO PHUTHAO + case 0x0E13: return false; // THAI CHARACTER NO NEN + case 0x0E14: return false; // THAI CHARACTER DO DEK + case 0x0E15: return false; // THAI CHARACTER TO TAO + case 0x0E16: return false; // THAI CHARACTER THO THUNG + case 0x0E17: return false; // THAI CHARACTER THO THAHAN + case 0x0E18: return false; // THAI CHARACTER THO THONG + case 0x0E19: return false; // THAI CHARACTER NO NU + case 0x0E1A: return false; // THAI CHARACTER BO BAIMAI + case 0x0E1B: return false; // THAI CHARACTER PO PLA + case 0x0E1C: return false; // THAI CHARACTER PHO PHUNG + case 0x0E1D: return false; // THAI CHARACTER FO FA + case 0x0E1E: return false; // THAI CHARACTER PHO PHAN + case 0x0E1F: return false; // THAI CHARACTER FO FAN + case 0x0E20: return false; // THAI CHARACTER PHO SAMPHAO + case 0x0E21: return false; // THAI CHARACTER MO MA + case 0x0E22: return false; // THAI CHARACTER YO YAK + case 0x0E23: return false; // THAI CHARACTER RO RUA + case 0x0E24: return false; // THAI CHARACTER RU + case 0x0E25: return false; // THAI CHARACTER LO LING + case 0x0E26: return false; // THAI CHARACTER LU + case 0x0E27: return false; // THAI CHARACTER WO WAEN + case 0x0E28: return false; // THAI CHARACTER SO SALA + case 0x0E29: return false; // THAI CHARACTER SO RUSI + case 0x0E2A: return false; // THAI CHARACTER SO SUA + case 0x0E2B: return false; // THAI CHARACTER HO HIP + case 0x0E2C: return false; // THAI CHARACTER LO CHULA + case 0x0E2D: return false; // THAI CHARACTER O ANG + case 0x0E2E: return false; // THAI CHARACTER HO NOKHUK + case 0x0E2F: return false; // THAI CHARACTER PAIYANNOI + case 0x0E30: return false; // THAI CHARACTER SARA A + case 0x0E31: return false; // THAI CHARACTER MAI HAN-AKAT + case 0x0E32: return false; // THAI CHARACTER SARA AA + case 0x0E33: return false; // THAI CHARACTER SARA AM + case 0x0E34: return false; // THAI CHARACTER SARA I + case 0x0E35: return false; // THAI CHARACTER SARA II + case 0x0E36: return false; // THAI CHARACTER SARA UE + case 0x0E37: return false; // THAI CHARACTER SARA UEE + case 0x0E38: return false; // THAI CHARACTER SARA U + case 0x0E39: return false; // THAI CHARACTER SARA UU + case 0x0E3A: return false; // THAI CHARACTER PHINTHU + } + // optimize by splitting switch into contiguous blocks per + // Chapter 7 of Java Performance Tuning, Jack Shirazi + switch (c) { + case 0x0E3F: return false; // THAI CURRENCY SYMBOL BAHT + case 0x0E40: return false; // THAI CHARACTER SARA E + case 0x0E41: return false; // THAI CHARACTER SARA AE + case 0x0E42: return false; // THAI CHARACTER SARA O + case 0x0E43: return false; // THAI CHARACTER SARA AI MAIMUAN + case 0x0E44: return false; // THAI CHARACTER SARA AI MAIMALAI + case 0x0E45: return false; // THAI CHARACTER LAKKHANGYAO + case 0x0E46: return false; // THAI CHARACTER MAIYAMOK + case 0x0E47: return false; // THAI CHARACTER MAITAIKHU + case 0x0E48: return false; // THAI CHARACTER MAI EK + case 0x0E49: return false; // THAI CHARACTER MAI THO + case 0x0E4A: return false; // THAI CHARACTER MAI TRI + case 0x0E4B: return false; // THAI CHARACTER MAI CHATTAWA + case 0x0E4C: return false; // THAI CHARACTER THANTHAKHAT + case 0x0E4D: return false; // THAI CHARACTER NIKHAHIT + case 0x0E4E: return false; // THAI CHARACTER YAMAKKAN + case 0x0E4F: return false; // THAI CHARACTER FONGMAN + case 0x0E50: return false; // THAI DIGIT ZERO + case 0x0E51: return false; // THAI DIGIT ONE + case 0x0E52: return false; // THAI DIGIT TWO + case 0x0E53: return false; // THAI DIGIT THREE + case 0x0E54: return false; // THAI DIGIT FOUR + case 0x0E55: return false; // THAI DIGIT FIVE + case 0x0E56: return false; // THAI DIGIT SIX + case 0x0E57: return false; // THAI DIGIT SEVEN + case 0x0E58: return false; // THAI DIGIT EIGHT + case 0x0E59: return false; // THAI DIGIT NINE + case 0x0E5A: return false; // THAI CHARACTER ANGKHANKHU + case 0x0E5B: return false; // THAI CHARACTER KHOMUT + } + + return true; + + } + +} \ No newline at end of file diff --git a/src/nu/xom/IllegalAddException.java b/src/nu/xom/IllegalAddException.java new file mode 100644 index 0000000..30fa3a6 --- /dev/null +++ b/src/nu/xom/IllegalAddException.java @@ -0,0 +1,67 @@ +/* Copyright 2002-2005 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom; + +/** + *

    + * Indicates an attempt to add a node in a wrong place; for instance + * adding a Text node to a Document object. + *

    + + * @author Elliotte Rusty Harold + * @version 1.1b3 + * + */ + +public class IllegalAddException extends WellformednessException { + + + private static final long serialVersionUID = 6153993399665387138L; + + + /** + *

    + * Creates a new IllegalAddException + * with a detail message. + *

    + * + * @param message a string indicating the specific problem + */ + public IllegalAddException(String message) { + super(message); + } + + + /** + *

    + * Creates a new IllegalAddException with a detail message + * and an underlying root cause. + *

    + * + * @param message a string indicating the specific problem + * @param cause the original cause of this exception + */ + public IllegalAddException(String message, Throwable cause) { + super(message, cause); + } + +} \ No newline at end of file diff --git a/src/nu/xom/IllegalCharacterDataException.java b/src/nu/xom/IllegalCharacterDataException.java new file mode 100644 index 0000000..c6bdee9 --- /dev/null +++ b/src/nu/xom/IllegalCharacterDataException.java @@ -0,0 +1,69 @@ +/* Copyright 2002-2005 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom; + +/** + *

    + * Indicates an attempt to create text content that is not allowed in + * XML 1.0. For example, this could be a text node that contains a + * vertical tab or an attribute value that contains an unmatched + * surrogate character. + *

    + + * @author Elliotte Rusty Harold + * @version 1.1b3 + * + */ +public class IllegalCharacterDataException extends IllegalDataException { + + + private static final long serialVersionUID = 5097647465152879476L; + + + /** + *

    + * Creates a new IllegalCharacterDataException + * with a detail message. + *

    + * + * @param message a string indicating the specific problem + */ + public IllegalCharacterDataException(String message) { + super(message); + } + + + /** + *

    + * Creates a new IllegalCharacterDataException + * with a detail message and an underlying root cause. + *

    + * + * @param message a string indicating the specific problem + * @param cause the original cause of this exception + */ + public IllegalCharacterDataException(String message, Throwable cause) { + super(message, cause); + } + + +} \ No newline at end of file diff --git a/src/nu/xom/IllegalDataException.java b/src/nu/xom/IllegalDataException.java new file mode 100644 index 0000000..e326254 --- /dev/null +++ b/src/nu/xom/IllegalDataException.java @@ -0,0 +1,97 @@ +/* Copyright 2002-2005 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom; + +/** + *

    + * Indicates an attempt to + * set some value to malformed content; for instance + * by adding a string containing a null or a vertical tab + * to a text node, or using white space in an element name. + *

    + + * @author Elliotte Rusty Harold + * @version 1.1b3 + * + */ +public class IllegalDataException extends WellformednessException { + + /** + * + */ + private static final long serialVersionUID = 5116683358318890040L; + private String data; + + + /** + *

    + * Creates a new IllegalDataException + * with a detail message. + *

    + * + * @param message a string indicating the specific problem + */ + public IllegalDataException(String message) { + super(message); + } + + + /** + *

    + * Creates a new IllegalDataException + * with a detail message and an underlying root cause. + *

    + * + * @param message a string indicating the specific problem + * @param cause the original cause of this exception + */ + public IllegalDataException(String message, Throwable cause) { + super(message, cause); + } + + + /** + *

    + * Stores the illegal text that caused this exception. + *

    + * + * @param data the illegal data that caused this exception + */ + public void setData(String data) { + this.data = data; + } + + + /** + *

    + * Returns a string containing the actual illegal text that + * caused this exception. + *

    + * + * @return the syntactically incorrect data that caused + * this exception + */ + public String getData() { + return data; + } + +} \ No newline at end of file diff --git a/src/nu/xom/IllegalNameException.java b/src/nu/xom/IllegalNameException.java new file mode 100644 index 0000000..81a5ffa --- /dev/null +++ b/src/nu/xom/IllegalNameException.java @@ -0,0 +1,71 @@ +/* Copyright 2002-2005 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom; + +/** + *

    + * Indicates an attempt to assign a name that is not a legal XML name. + * For example, this might be a name that begins with a digit + * such as 7pins or a name that contains an asterisk such + * as pt*. In some contexts, this also includes names that + * are not legal non-colonized names as defined by Namespaces in + * XML. + *

    + + * @author Elliotte Rusty Harold + * @version 1.1b3 + * + */ +public class IllegalNameException extends IllegalDataException { + + + private static final long serialVersionUID = -5050380625643506613L; + + + /** + *

    + * Creates a new IllegalNameException + * with a detail message. + *

    + * + * @param message a string indicating the specific problem + */ + public IllegalNameException(String message) { + super(message); + } + + + /** + *

    + * Creates a new IllegalNameException + * with a detail message and an underlying root cause. + *

    + * + * @param message a string indicating the specific problem + * @param cause the original cause of this exception + */ + public IllegalNameException(String message, Throwable cause) { + super(message, cause); + } + + +} \ No newline at end of file diff --git a/src/nu/xom/IllegalTargetException.java b/src/nu/xom/IllegalTargetException.java new file mode 100644 index 0000000..44b83ea --- /dev/null +++ b/src/nu/xom/IllegalTargetException.java @@ -0,0 +1,71 @@ +/* Copyright 2002-2005 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom; + +/** + *

    + * Indicates an attempt to + * assign a processing instruction target that is not a + * legal XML 1.0 processing instruction target. + * This is either because the proposed target is not a legal + * non-colonized name or because it consists of the three letters + * "XML" in that order in any combination of case. + *

    + + * @author Elliotte Rusty Harold + * @version 1.1b3 + * + */ +public class IllegalTargetException extends IllegalNameException { + + + private static final long serialVersionUID = -368475891285480182L; + + + /** + *

    + * Creates a new IllegalTargetException + * with a detail message. + *

    + * + * @param message a string indicating the specific problem + */ + public IllegalTargetException(String message) { + super(message); + } + + + /** + *

    + * Creates a new IllegalTargetException + * with a detail message and an underlying root cause. + *

    + * + * @param message a string indicating the specific problem + * @param cause the original cause of this exception + */ + public IllegalTargetException(String message, Throwable cause) { + super(message, cause); + } + + +} \ No newline at end of file diff --git a/src/nu/xom/Info.java b/src/nu/xom/Info.java new file mode 100644 index 0000000..717cbb2 --- /dev/null +++ b/src/nu/xom/Info.java @@ -0,0 +1,81 @@ +/* Copyright 2002-2004, 2009 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom; + +import java.io.BufferedReader; +import java.io.InputStream; +import java.io.InputStreamReader; + +/** + *

    + * A simple class used to make the JAR archive do something sensible + * when a user tries java -jar xom.jar. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.2.4 + * + */ +class Info { + + + public static void main(String[] args) { + + String version = "1.2.4 or later"; + try { + InputStream stream = ClassLoader.getSystemResourceAsStream("nu/xom/version.txt"); + BufferedReader in = new BufferedReader(new InputStreamReader(stream, "UTF-8")); + version = in.readLine(); + } catch (Exception ex) { + version = "1.2.4b1 or later"; + } + + System.out.println("This is XOM " + version + ", a new XML Object Model."); + System.out.println("Copyright 2002-2009 Elliotte Rusty Harold"); + System.out.println("http://www.xom.nu/"); + System.out.println(); + System.out.println("XOM is a class library intended to be used with other programs."); + System.out.println("By itself, it doesn't really do anything."); + System.out.println("For more information see http://www.xom.nu/"); + System.out.println(); + System.out.println("This library is free software; you can redistribute it and/or modify it"); + System.out.println("under the terms of version 2.1 of the GNU Lesser General Public License"); + System.out.println("as published by the Free Software Foundation."); + System.out.println(); + System.out.println("This library is distributed in the hope that it will be useful,"); + System.out.println("but WITHOUT ANY WARRANTY; without even the implied warranty of"); + System.out.println("MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. "); + System.out.println("See the GNU Lesser General Public License for more details."); + System.out.println(); + System.out.println("You should have received a copy of the GNU Lesser General"); + System.out.println("Public License along with this library; if not, write to the"); + System.out.println(); + System.out.println("Free Software Foundation, Inc."); + System.out.println("59 Temple Place"); + System.out.println("Suite 330,"); + System.out.println("Boston, MA 02111-1307"); + System.out.println("USA"); + + } + + +} diff --git a/src/nu/xom/JDK15XML1_0Parser.java b/src/nu/xom/JDK15XML1_0Parser.java new file mode 100644 index 0000000..e06c9d4 --- /dev/null +++ b/src/nu/xom/JDK15XML1_0Parser.java @@ -0,0 +1,55 @@ +// Copyright 2004, 2018 Elliotte Rusty Harold +// +// This library is free software; you can redistribute +// it and/or modify it under the terms of version 2.1 of +// the GNU Lesser General Public License as published by +// the Free Software Foundation. +// +// This library 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General +// Public License along with this library; if not, write to the +// Free Software Foundation, Inc., 59 Temple Place, Suite 330, +// Boston, MA 02111-1307 USA +// +// You can contact Elliotte Rusty Harold by sending e-mail to +// elharo@ibiblio.org. Please include the word "XOM" in the +// subject line. The XOM home page is temporarily located at +// http://www.cafeconleche.org/XOM/ but will eventually move +// to http://www.xom.nu/ + +package nu.xom; + +import org.xml.sax.SAXException; + +import com.sun.org.apache.xerces.internal.parsers.SAXParser; +import com.sun.org.apache.xerces.internal.parsers.DTDConfiguration; +import com.sun.org.apache.xerces.internal.impl.Constants +; +/** + *

    + * This class is used by the Builder to prevent the + * repackaged Xerces shipped with Java 1.5 and some of the JAXP + * reference implementations from accepting XML 1.1 documents. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.2.11 + * + */ +class JDK15XML1_0Parser extends SAXParser { + + JDK15XML1_0Parser() throws SAXException { + + super(new DTDConfiguration()); + // workaround for Java 1.5 beta 2 bugs + com.sun.org.apache.xerces.internal.util.SecurityManager manager + = new com.sun.org.apache.xerces.internal.util.SecurityManager(); + setProperty(Constants.XERCES_PROPERTY_PREFIX + Constants.SECURITY_MANAGER_PROPERTY, manager); + + } + +} diff --git a/src/nu/xom/JaxenConnector.java b/src/nu/xom/JaxenConnector.java new file mode 100644 index 0000000..b3716f5 --- /dev/null +++ b/src/nu/xom/JaxenConnector.java @@ -0,0 +1,101 @@ +/* Copyright 2005, 2009 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom; + +import java.util.Iterator; +import java.util.List; +import java.util.ListIterator; + +import org.jaxen.BaseXPath; +import org.jaxen.FunctionContext; +import org.jaxen.JaxenException; +import org.jaxen.XPathFunctionContext; + + +/** + * + * @author Elliotte Rusty Harold + * @version 1.2.4 + * + */ +class JaxenConnector extends BaseXPath { + + + private static final long serialVersionUID = 9025734269448515308L; + + private static FunctionContext functionContext = new XPathFunctionContext(false); + + + JaxenConnector(String expression) throws JaxenException { + super(expression, new JaxenNavigator()); + // possible thread-safety issue???? + this.setFunctionContext(functionContext); + } + + + public List selectNodes(Object expression) throws JaxenException { + + List result = super.selectNodes(expression); + // Text objects are returned wrapped in a List. + // We need to unwrap them here. + ListIterator iterator = result.listIterator(); + while (iterator.hasNext()) { + Object next = iterator.next(); + if (next instanceof List) { + List list = (List) next; + // replace the list with the first item in the list + iterator.set(list.get(0)); + // insert any subsequent Text objects into the list + if (list.size() > 1) { + Iterator texts = list.listIterator(1); + while (texts.hasNext()) { + iterator.add(texts.next()); + } + } + } + else { + try { + Node node = (Node) next; + if (node.isDocumentFragment()) { + iterator.remove(); + // Want to allow // and //* and so forth + // but not / for rootless documents + if (result.isEmpty()) { + throw new XPathException("Tried to get document " + + "node of disconnected subtree"); + } + } + } + catch (ClassCastException ex) { + XPathTypeException qex = new XPathTypeException(result.get(0)); + throw qex; + } + } + } + return result; + + + } + + +} + diff --git a/src/nu/xom/JaxenNavigator.java b/src/nu/xom/JaxenNavigator.java new file mode 100644 index 0000000..bcbae12 --- /dev/null +++ b/src/nu/xom/JaxenNavigator.java @@ -0,0 +1,586 @@ +/* Copyright 2005 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom; + +/** + *

    + * Interface between Jaxen and XOM. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.1.1b1 + * + */ + +import org.jaxen.DefaultNavigator; +import org.jaxen.FunctionCallException; +import org.jaxen.JaxenConstants; +import org.jaxen.JaxenException; +import org.jaxen.NamedAccessNavigator; +import org.jaxen.UnsupportedAxisException; +import org.jaxen.XPath; +import org.jaxen.util.SingleObjectIterator; + +import java.util.Iterator; +import java.util.List; +import java.util.ArrayList; +import java.util.Map; +import java.util.NoSuchElementException; + + +class JaxenNavigator extends DefaultNavigator implements NamedAccessNavigator { + + + private static final long serialVersionUID = 7008740797833836742L; + + + public Iterator getSelfAxisIterator(Object contextNode) { + + if (contextNode instanceof Text) { + // wrap text nodes in a list + Text node = (Text) contextNode; + ArrayList temp = new ArrayList(); + ParentNode parent = node.getParent(); + // parent is never null here due to DocumentFragment + int index = parent.indexOf(node); + int first = index; + int last = index; + while (first > 0 && parent.getChild(first-1).isText()) { + first--; + } + while (last < parent.getChildCount()-1 && parent.getChild(last+1).isText()) { + last++; + } + for (int i = first; i <= last; i++) { + temp.add(parent.getChild(i)); + } + contextNode = temp; + } + return new SingleObjectIterator(contextNode); + + } + + + public Object getElementById(Object node, String id) { + + Node original; + if (node instanceof ArrayList) { + original = (Node) ((List) node).get(0); + } + else { + original = (Node) node; + } + ParentNode parent; + if (original.isElement() || original.isDocument()) { + parent = (ParentNode) original; + } + else { + parent = original.getParent(); + } + + // find highest parent node + ParentNode high = parent; + while (parent != null) { + high = parent; + parent = parent.getParent(); + } + + // Now search down from the highest point for the requested ID + Element root; + if (high.isDocument()) { + root = ((Document) high).getRootElement(); + } + else { // document fragment + Node first = high.getChild(0); + if (first.isElement()) { + root = (Element) high.getChild(0); + } + else { + return null; + } + } + + return findByID(root, id); + + } + + + // ????remove recursion + public static Element findByID(Element top, String id) { + + if (hasID(top, id)) return top; + else { + Elements children = top.getChildElements(); + for (int i = 0; i < children.size(); i++) { + Element result = findByID(children.get(i), id); + if (result != null) return result; + } + } + return null; + + } + + + private static boolean hasID(Element top, String id) { + + int count = top.getAttributeCount(); + for (int i = 0; i < count; i++) { + Attribute a = top.getAttribute(i); + if (Attribute.Type.ID == a.getType()) { + // Do not need to fully normalize here + // because if the value passed to the id() function + // contains any spaces; then it is converted into a + // search for multiple IDs, none of which have spaces + return a.getValue().trim().equals(id); + } + } + return false; + + } + + + public String getNamespacePrefix(Object o) { + Namespace ns = (Namespace) o; + return ns.getPrefix(); + } + + + public String getNamespaceStringValue(Object o) { + Namespace ns = (Namespace) o; + return ns.getValue(); + } + + + public Iterator getNamespaceAxisIterator(Object contextNode) { + + try { + Element element = (Element) contextNode; + // ???? can probably avoid this list copy + Map bindings = element.getNamespacePrefixesInScope(); + Iterator iterator = bindings.entrySet().iterator(); + List result = new ArrayList(bindings.size()+1); + result.add(new Namespace("xml", + "http://www.w3.org/XML/1998/namespace", element)); + + while (iterator.hasNext()) { + Map.Entry binding = (Map.Entry) iterator.next(); + String prefix = (String) binding.getKey(); + String uri = (String) binding.getValue(); + if (! "".equals(prefix) || ! "".equals(uri)) { + Namespace ns = new Namespace(prefix, uri, element); + result.add(ns); + } + } + return result.iterator(); + } + catch (ClassCastException ex) { + return JaxenConstants.EMPTY_ITERATOR; + } + + } + + + public Iterator getParentAxisIterator(Object contextNode) { + + Node parent = (Node) getParentNode(contextNode); + if (parent == null) return JaxenConstants.EMPTY_ITERATOR; + else return new SingleObjectIterator(parent); + + } + + + public Object getDocumentNode(Object o) { + + Node node = (Node) o; + return node.getRoot(); + + } + + + public Object getDocument(String url) throws FunctionCallException { + throw new FunctionCallException("document() function not supported"); + } + + public Iterator getAttributeAxisIterator(Object contextNode) { + + try { + Element element = (Element) contextNode; + return element.attributeIterator(); + } + catch (ClassCastException ex) { + return JaxenConstants.EMPTY_ITERATOR; + } + + } + + + public Iterator getChildAxisIterator(Object o) { + + if (o instanceof ParentNode) { + return new ChildIterator((ParentNode) o); + } + else { + return JaxenConstants.EMPTY_ITERATOR; + } + + } + + + public Iterator getFollowingSiblingAxisIterator(Object o) { + + Node start; + if (o instanceof ArrayList) { + List list = (ArrayList) o; + start = (Node) list.get(list.size()-1); + } + else { + start = (Node) o; + } + ParentNode parent = start.getParent(); + if (parent == null) return JaxenConstants.EMPTY_ITERATOR; + int startPos = parent.indexOf(start) + 1; + return new ChildIterator(parent, startPos); + + } + + + public Object getParentNode(Object o) { + + Node n; + if (o instanceof ArrayList) { + n = (Node) ((List) o).get(0); + } + else { + n = (Node) o; + } + return n.getParent(); + + } + + + public String getTextStringValue(Object o) { + + List texts = (List) o; + if (texts.size() == 1) { + return ((Text) texts.get(0)).getValue(); + } + else { + StringBuffer result = new StringBuffer(); + Iterator iterator = texts.iterator(); + while (iterator.hasNext()) { + Text text = (Text) iterator.next(); + result.append(text.getValue()); + } + return result.toString(); + } + + } + + + private static class ChildIterator implements Iterator { + + private final ParentNode parent; + + private int xomIndex = 0; + private final int xomCount; + + ChildIterator(ParentNode parent) { + this.parent = parent; + this.xomCount = parent.getChildCount(); + } + + + ChildIterator(ParentNode parent, int startNode) { + this.parent = parent; + this.xomIndex = startNode; + this.xomCount = parent.getChildCount(); + } + + + public boolean hasNext() { + + for (int i = xomIndex; i < xomCount; i++) { + Node next = parent.getChild(i); + if (next.isText()) { + if (! ((Text) next).isEmpty()) { + return true; + } + } + else return true; + } + return false; + + } + + + public Object next() { + + Object result; + Node next = parent.getChild(xomIndex++); + if (next.isText()) { + Text t = (Text) next; + // Is this an empty text node? + boolean empty = t.isEmpty(); + List texts = new ArrayList(1); + texts.add(t); + while (xomIndex < xomCount) { + Node nextText = parent.getChild(xomIndex); + if (! nextText.isText()) break; + xomIndex++; + texts.add(nextText); + if (empty) { + if (! ((Text) nextText).isEmpty()) { + empty = false; + } + } + } + // need to make sure at least one of these texts is non-empty + if (empty) return next(); + else result = texts; + } + else if (next.isDocType()) { + return next(); + } + else { + result = next; + } + return result; + + } + + public void remove() { + throw new UnsupportedOperationException(); + } + + } + + + private static class NamedChildIterator implements Iterator { + + private final ParentNode parent; + + private int index = -1; + private final int xomCount; + private Element next; + private final String localName; + private final String URI; + + NamedChildIterator(ParentNode parent, String localName, String prefix, String namespaceURI) { + this.parent = parent; + this.xomCount = parent.getChildCount(); + this.localName = localName; + if (namespaceURI == null) this.URI = ""; + else this.URI = namespaceURI; + + findNext(); + } + + private void findNext() { + + while (++index < xomCount) { + Node next = parent.getChild(index); + if (next.isElement()) { + Element element = (Element) next; + String elementNamespace = element.getNamespaceURI(); + if (elementNamespace.equals(URI)) { + // I don't need to worry about the prefix here because XPath only iterates + // by local name and namespace URI. The prefix doesn't matter. + // This does assume that this class is non-public. + if (element.getLocalName().equals(localName)) { + this.next = element; + return; + } + } + } + } + next = null; + } + + public boolean hasNext() { + return next != null; + } + + + public Object next() { + + if (next == null) throw new NoSuchElementException(); // Correct? Yes. Necessary???? + Object result = next; + findNext(); + return result; + } + + public void remove() { + throw new UnsupportedOperationException(); + } + + } + + + public String getElementNamespaceUri(Object element) { + return ((Element) element).getNamespaceURI(); + } + + + // In Jaxen, name means the local name only + public String getElementName(Object element) { + return ((Element) element).getLocalName(); + } + + public String getElementQName(Object element) { + return ((Element) element).getQualifiedName(); + } + + + public String getAttributeNamespaceUri(Object attr) { + Attribute attribute = (Attribute) attr; + return attribute.getNamespaceURI(); + } + + + // In Jaxen, name means the local name only + public String getAttributeName(Object attr) { + Attribute attribute = (Attribute) attr; + return attribute.getLocalName(); + } + + + public String getAttributeQName(Object attr) { + Attribute attribute = (Attribute) attr; + return attribute.getQualifiedName(); + } + + public String getProcessingInstructionData(Object o) { + ProcessingInstruction pi = (ProcessingInstruction) o; + return pi.getValue(); + } + + + public String getProcessingInstructionTarget(Object o) { + ProcessingInstruction pi = (ProcessingInstruction) o; + return pi.getTarget(); + } + + + public boolean isDocument(Object object) { + return object instanceof Document || object instanceof DocumentFragment; + } + + + public boolean isElement(Object object) { + return object instanceof Element; + } + + + public boolean isAttribute(Object object) { + return object instanceof Attribute; + } + + + public boolean isNamespace(Object object) { + return object instanceof Namespace; + } + + + public boolean isComment(Object object) { + return object instanceof Comment; + } + + + public boolean isText(Object object) { + // ???? hack: need to use a separate special subclass of ArrayList I can identify. + // But may not be necessary since this is not a public API. I don't + // think there's any way to get a different ArrayList into this method. + if (object instanceof ArrayList) { + Iterator iterator = ((List) object).iterator(); + while (iterator.hasNext()) { + if (! (iterator.next() instanceof Text)) return false; + } + return true; + } + return false; + } + + + public boolean isProcessingInstruction(Object object) { + return object instanceof ProcessingInstruction; + } + + + public String getCommentStringValue(Object comment) { + return ((Comment) comment).getValue(); + } + + + public String getElementStringValue(Object element) { + return ((Element) element).getValue(); + } + + + public String getAttributeStringValue(Object attribute) { + return ((Attribute) attribute).getValue(); + } + + + public XPath parseXPath(String expression) throws JaxenException { + return new JaxenConnector(expression); + } + + + public Iterator getChildAxisIterator(Object parent, String localName, String namespacePrefix, String namespaceURI) + throws UnsupportedAxisException { + + if (parent instanceof ParentNode) { + return new NamedChildIterator((ParentNode) parent, localName, namespacePrefix, namespaceURI); + } + return JaxenConstants.EMPTY_ITERATOR; + + } + + + public Iterator getAttributeAxisIterator(Object contextNode, String localName, String namespacePrefix, String namespaceURI) + throws UnsupportedAxisException { + + // I don't need to worry about the prefix here because XPath only iterates + // by local name and namespace URI. The prefix doesn't matter. + // This does assume that this class is non-public. + try { + Element element = (Element) contextNode; + Attribute result = null; + if (namespaceURI == null) { + result = element.getAttribute(localName); + } + else { + result = element.getAttribute(localName, namespaceURI); + } + + if (result == null) return JaxenConstants.EMPTY_ITERATOR; + + return new SingleObjectIterator(result); + } + catch (ClassCastException ex) { + return JaxenConstants.EMPTY_ITERATOR; + } + + } + + +} diff --git a/src/nu/xom/Latin10Writer.java b/src/nu/xom/Latin10Writer.java new file mode 100644 index 0000000..2dc04a9 --- /dev/null +++ b/src/nu/xom/Latin10Writer.java @@ -0,0 +1,283 @@ +/* Copyright 2002-2004 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom; + +import java.io.Writer; + +/** + * + *

    + * ISO 8859-16, Romanian. Not yet supported by Sun's JDK as of 1.5 + * alpha. IBM's 1.4.1 JDK does support it. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.0 + * + */ +class Latin10Writer extends TextWriter { + + + Latin10Writer(Writer out, String encoding) { + super(out, encoding); + } + + + /** + * @see nu.xom.TextWriter#needsEscaping(char) + */ + boolean needsEscaping(char c) { + if (c <= 0xA0) return false; + switch (c) { + case 0x00A7: return false; // SECTION SIGN + case 0x00A8: return true; // place holder to allow optimization of switch statement + case 0x00A9: return false; // COPYRIGHT SIGN + case 0x00AA: return true; // place holder to allow optimization of switch statement + case 0x00AB: return false; // LEFT-POINTING DOUBLE ANGLE QUOTATION MARK + case 0x00AC: return true; // place holder to allow optimization of switch statement + case 0x00AD: return false; // SOFT HYPHEN + case 0x00AE: return true; // place holder to allow optimization of switch statement + case 0x00AF: return true; // place holder to allow optimization of switch statement + case 0x00B0: return false; // DEGREE SIGN + case 0x00B1: return false; // PLUS-MINUS SIGN + case 0x00B2: return true; // place holder to allow optimization of switch statement + case 0x00B3: return true; // place holder to allow optimization of switch statement + case 0x00B4: return true; // place holder to allow optimization of switch statement + case 0x00B5: return true; // place holder to allow optimization of switch statement + case 0x00B6: return false; // PILCROW SIGN + case 0x00B7: return false; // MIDDLE DOT + case 0x00B8: return true; // place holder to allow optimization of switch statement + case 0x00B9: return true; // place holder to allow optimization of switch statement + case 0x00BA: return true; // place holder to allow optimization of switch statement + case 0x00BB: return false; // RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK + case 0x00BC: return true; // place holder to allow optimization of switch statement + case 0x00BD: return true; // place holder to allow optimization of switch statement + case 0x00BE: return true; // place holder to allow optimization of switch statement + case 0x00BF: return true; // place holder to allow optimization of switch statement + case 0x00C0: return false; // LATIN CAPITAL LETTER A WITH GRAVE + case 0x00C1: return false; // LATIN CAPITAL LETTER A WITH ACUTE + case 0x00C2: return false; // LATIN CAPITAL LETTER A WITH CIRCUMFLEX + case 0x00C3: return true; // place holder to allow optimization of switch statement + case 0x00C4: return false; // LATIN CAPITAL LETTER A WITH DIAERESIS + case 0x00C5: return true; // place holder to allow optimization of switch statement + case 0x00C6: return false; // LATIN CAPITAL LETTER AE + case 0x00C7: return false; // LATIN CAPITAL LETTER C WITH CEDILLA + case 0x00C8: return false; // LATIN CAPITAL LETTER E WITH GRAVE + case 0x00C9: return false; // LATIN CAPITAL LETTER E WITH ACUTE + case 0x00CA: return false; // LATIN CAPITAL LETTER E WITH CIRCUMFLEX + case 0x00CB: return false; // LATIN CAPITAL LETTER E WITH DIAERESIS + case 0x00CC: return false; // LATIN CAPITAL LETTER I WITH GRAVE + case 0x00CD: return false; // LATIN CAPITAL LETTER I WITH ACUTE + case 0x00CE: return false; // LATIN CAPITAL LETTER I WITH CIRCUMFLEX + case 0x00CF: return false; // LATIN CAPITAL LETTER I WITH DIAERESIS + case 0x00D0: return true; // place holder to allow optimization of switch statement + case 0x00D1: return true; // place holder to allow optimization of switch statement + case 0x00D2: return false; // LATIN CAPITAL LETTER O WITH GRAVE + case 0x00D3: return false; // LATIN CAPITAL LETTER O WITH ACUTE + case 0x00D4: return false; // LATIN CAPITAL LETTER O WITH CIRCUMFLEX + case 0x00D5: return true; // place holder to allow optimization of switch statement + case 0x00D6: return false; // LATIN CAPITAL LETTER O WITH DIAERESIS + case 0x00D7: return true; // place holder to allow optimization of switch statement + case 0x00D8: return true; // place holder to allow optimization of switch statement + case 0x00D9: return false; // LATIN CAPITAL LETTER U WITH GRAVE + case 0x00DA: return false; // LATIN CAPITAL LETTER U WITH ACUTE + case 0x00DB: return false; // LATIN CAPITAL LETTER U WITH CIRCUMFLEX + case 0x00DC: return false; // LATIN CAPITAL LETTER U WITH DIAERESIS + case 0x00DD: return true; // place holder to allow optimization of switch statement + case 0x00DE: return true; // place holder to allow optimization of switch statement + case 0x00DF: return false; // LATIN SMALL LETTER SHARP S + case 0x00E0: return false; // LATIN SMALL LETTER A WITH GRAVE + case 0x00E1: return false; // LATIN SMALL LETTER A WITH ACUTE + case 0x00E2: return false; // LATIN SMALL LETTER A WITH CIRCUMFLEX + case 0x00E3: return true; // place holder to allow optimization of switch statement + case 0x00E4: return false; // LATIN SMALL LETTER A WITH DIAERESIS + case 0x00E5: return true; // place holder to allow optimization of switch statement + case 0x00E6: return false; // LATIN SMALL LETTER AE + case 0x00E7: return false; // LATIN SMALL LETTER C WITH CEDILLA + case 0x00E8: return false; // LATIN SMALL LETTER E WITH GRAVE + case 0x00E9: return false; // LATIN SMALL LETTER E WITH ACUTE + case 0x00EA: return false; // LATIN SMALL LETTER E WITH CIRCUMFLEX + case 0x00EB: return false; // LATIN SMALL LETTER E WITH DIAERESIS + case 0x00EC: return false; // LATIN SMALL LETTER I WITH GRAVE + case 0x00ED: return false; // LATIN SMALL LETTER I WITH ACUTE + case 0x00EE: return false; // LATIN SMALL LETTER I WITH CIRCUMFLEX + case 0x00EF: return false; // LATIN SMALL LETTER I WITH DIAERESIS + case 0x00F0: return true; // place holder to allow optimization of switch statement + case 0x00F1: return true; // place holder to allow optimization of switch statement + case 0x00F2: return false; // LATIN SMALL LETTER O WITH GRAVE + case 0x00F3: return false; // LATIN SMALL LETTER O WITH ACUTE + case 0x00F4: return false; // LATIN SMALL LETTER O WITH CIRCUMFLEX + case 0x00F5: return true; // place holder to allow optimization of switch statement + case 0x00F6: return false; // LATIN SMALL LETTER O WITH DIAERESIS + case 0x00F7: return true; // place holder to allow optimization of switch statement + case 0x00F8: return true; // place holder to allow optimization of switch statement + case 0x00F9: return false; // LATIN SMALL LETTER U WITH GRAVE + case 0x00FA: return false; // LATIN SMALL LETTER U WITH ACUTE + case 0x00FB: return false; // LATIN SMALL LETTER U WITH CIRCUMFLEX + case 0x00FC: return false; // LATIN SMALL LETTER U WITH DIAERESIS + case 0x00FD: return true; // place holder to allow optimization of switch statement + case 0x00FE: return true; // place holder to allow optimization of switch statement + case 0x00FF: return false; // LATIN SMALL LETTER Y WITH DIAERESIS + case 0x0100: return true; // place holder to allow optimization of switch statement + case 0x0101: return true; // place holder to allow optimization of switch statement + case 0x0102: return false; // LATIN CAPITAL LETTER A WITH BREVE + case 0x0103: return false; // LATIN SMALL LETTER A WITH BREVE + case 0x0104: return false; // LATIN CAPITAL LETTER A WITH OGONEK + case 0x0105: return false; // LATIN SMALL LETTER A WITH OGONEK + case 0x0106: return false; // LATIN CAPITAL LETTER C WITH ACUTE + case 0x0107: return false; // LATIN SMALL LETTER C WITH ACUTE + case 0x0108: return true; // place holder to allow optimization of switch statement + case 0x0109: return true; // place holder to allow optimization of switch statement + case 0x010A: return true; // place holder to allow optimization of switch statement + case 0x010B: return true; // place holder to allow optimization of switch statement + case 0x010C: return false; // LATIN CAPITAL LETTER C WITH CARON + case 0x010D: return false; // LATIN SMALL LETTER C WITH CARON + case 0x010E: return true; // place holder to allow optimization of switch statement + case 0x010F: return true; // place holder to allow optimization of switch statement + case 0x0110: return false; // LATIN CAPITAL LETTER D WITH STROKE + case 0x0111: return false; // LATIN SMALL LETTER D WITH STROKE + case 0x0112: return true; // place holder to allow optimization of switch statement + case 0x0113: return true; // place holder to allow optimization of switch statement + case 0x0114: return true; // place holder to allow optimization of switch statement + case 0x0115: return true; // place holder to allow optimization of switch statement + case 0x0116: return true; // place holder to allow optimization of switch statement + case 0x0117: return true; // place holder to allow optimization of switch statement + case 0x0118: return false; // LATIN CAPITAL LETTER E WITH OGONEK + case 0x0119: return false; // LATIN SMALL LETTER E WITH OGONEK + case 0x011A: return true; // place holder to enable table lookup + case 0x011B: return true; // place holder to enable table lookup + case 0x011C: return true; // place holder to enable table lookup + case 0x011D: return true; // place holder to enable table lookup + case 0x011E: return true; // place holder to enable table lookup + case 0x011F: return true; // place holder to enable table lookup + case 0x0120: return true; // place holder to enable table lookup + case 0x0121: return true; // place holder to enable table lookup + case 0x0122: return true; // place holder to enable table lookup + case 0x0123: return true; // place holder to enable table lookup + case 0x0124: return true; // place holder to enable table lookup + case 0x0125: return true; // place holder to enable table lookup + case 0x0126: return true; // place holder to enable table lookup + case 0x0127: return true; // place holder to enable table lookup + case 0x0128: return true; // place holder to enable table lookup + case 0x0129: return true; // place holder to enable table lookup + case 0x012A: return true; // place holder to enable table lookup + case 0x012B: return true; // place holder to enable table lookup + case 0x012C: return true; // place holder to enable table lookup + case 0x012D: return true; // place holder to enable table lookup + case 0x012E: return true; // place holder to enable table lookup + case 0x012F: return true; // place holder to enable table lookup + case 0x0130: return true; // place holder to enable table lookup + case 0x0131: return true; // place holder to enable table lookup + case 0x0132: return true; // place holder to enable table lookup + case 0x0133: return true; // place holder to enable table lookup + case 0x0134: return true; // place holder to enable table lookup + case 0x0135: return true; // place holder to enable table lookup + case 0x0136: return true; // place holder to enable table lookup + case 0x0137: return true; // place holder to enable table lookup + case 0x0138: return true; // place holder to enable table lookup + case 0x0139: return true; // place holder to enable table lookup + case 0x013A: return true; // place holder to enable table lookup + case 0x013B: return true; // place holder to enable table lookup + case 0x013C: return true; // place holder to enable table lookup + case 0x013D: return true; // place holder to enable table lookup + case 0x013E: return true; // place holder to enable table lookup + case 0x013F: return true; // place holder to enable table lookup + case 0x0140: return true; // place holder to enable table lookup + case 0x0141: return false; // LATIN CAPITAL LETTER L WITH STROKE + case 0x0142: return false; // LATIN SMALL LETTER L WITH STROKE + case 0x0143: return false; // LATIN CAPITAL LETTER N WITH ACUTE + case 0x0144: return false; // LATIN SMALL LETTER N WITH ACUTE + case 0x0145: return true; // place holder to enable table lookup + case 0x0146: return true; // place holder to enable table lookup + case 0x0147: return true; // place holder to enable table lookup + case 0x0148: return true; // place holder to enable table lookup + case 0x0149: return true; // place holder to enable table lookup + case 0x014A: return true; // place holder to enable table lookup + case 0x014B: return true; // place holder to enable table lookup + case 0x014C: return true; // place holder to enable table lookup + case 0x014D: return true; // place holder to enable table lookup + case 0x014E: return true; // place holder to enable table lookup + case 0x014F: return true; // place holder to enable table lookup + case 0x0150: return false; // LATIN CAPITAL LETTER O WITH DOUBLE ACUTE + case 0x0151: return false; // LATIN SMALL LETTER O WITH DOUBLE ACUTE + case 0x0152: return false; // LATIN CAPITAL LIGATURE OE + case 0x0153: return false; // LATIN SMALL LIGATURE OE + case 0x0154: return true; // place holder to enable table lookup + case 0x0155: return true; // place holder to enable table lookup + case 0x0156: return true; // place holder to enable table lookup + case 0x0157: return true; // place holder to enable table lookup + case 0x0158: return true; // place holder to enable table lookup + case 0x0159: return true; // place holder to enable table lookup + case 0x015A: return false; // LATIN CAPITAL LETTER S WITH ACUTE + case 0x015B: return false; // LATIN SMALL LETTER S WITH ACUTE + case 0x015C: return true; // place holder to enable table lookup + case 0x015D: return true; // place holder to enable table lookup + case 0x015E: return true; // place holder to enable table lookup + case 0x015F: return true; // place holder to enable table lookup + case 0x0160: return false; // LATIN CAPITAL LETTER S WITH CARON + case 0x0161: return false; // LATIN SMALL LETTER S WITH CARON + case 0x0162: return true; // place holder to enable table lookup + case 0x0163: return true; // place holder to enable table lookup + case 0x0164: return true; // place holder to enable table lookup + case 0x0165: return true; // place holder to enable table lookup + case 0x0166: return true; // place holder to enable table lookup + case 0x0167: return true; // place holder to enable table lookup + case 0x0168: return true; // place holder to enable table lookup + case 0x0169: return true; // place holder to enable table lookup + case 0x016A: return true; // place holder to enable table lookup + case 0x016B: return true; // place holder to enable table lookup + case 0x016C: return true; // place holder to enable table lookup + case 0x016D: return true; // place holder to enable table lookup + case 0x016E: return true; // place holder to enable table lookup + case 0x016F: return true; // place holder to enable table lookup + case 0x0170: return false; // LATIN CAPITAL LETTER U WITH DOUBLE ACUTE + case 0x0171: return false; // LATIN SMALL LETTER U WITH DOUBLE ACUTE + case 0x0172: return true; // place holder to enable table lookup + case 0x0173: return true; // place holder to enable table lookup + case 0x0174: return true; // place holder to enable table lookup + case 0x0175: return true; // place holder to enable table lookup + case 0x0176: return true; // place holder to enable table lookup + case 0x0177: return true; // place holder to enable table lookup + case 0x0178: return false; // LATIN CAPITAL LETTER Y WITH DIAERESIS + case 0x0179: return false; // LATIN CAPITAL LETTER Z WITH ACUTE + case 0x017A: return false; // LATIN SMALL LETTER Z WITH ACUTE + case 0x017B: return false; // LATIN CAPITAL LETTER Z WITH DOT ABOVE + case 0x017C: return false; // LATIN SMALL LETTER Z WITH DOT ABOVE + case 0x017D: return false; // LATIN CAPITAL LETTER Z WITH CARON + case 0x017E: return false; // LATIN SMALL LETTER Z WITH CARON + } + switch (c) { + case 0x0218: return false; // LATIN CAPITAL LETTER S WITH COMMA BELOW + case 0x0219: return false; // LATIN SMALL LETTER S WITH COMMA BELOW + case 0x021A: return false; // LATIN CAPITAL LETTER T WITH COMMA BELOW + case 0x021B: return false; // LATIN SMALL LETTER T WITH COMMA BELOW + case 0x021C: return true; // place holder to enable table lookup + case 0x201D: return false; // RIGHT DOUBLE QUOTATION MARK + case 0x201E: return false; // DOUBLE LOW-9 QUOTATION MARK + } + if (c == 0x20AC) return false; // EURO SIGN + return true; + + } + + +} \ No newline at end of file diff --git a/src/nu/xom/Latin1Writer.java b/src/nu/xom/Latin1Writer.java new file mode 100644 index 0000000..1b43c3d --- /dev/null +++ b/src/nu/xom/Latin1Writer.java @@ -0,0 +1,55 @@ +/* Copyright 2002, 2003 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom; + +import java.io.Writer; + +/** + *

    + * ISO-8859-1; a.k.a. Latin-1; most Western European letters + * except for Greek. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.0 + * + * + */ +class Latin1Writer extends TextWriter { + + /** + * Constructor for Latin1Writer. + * @param out + * @param encoding + */ + Latin1Writer(Writer out, String encoding) { + super(out, encoding); + } + + /** + * @see nu.xom.TextWriter#needsEscaping(char) + */ + boolean needsEscaping(char c) { + return c > 255; + } + +} \ No newline at end of file diff --git a/src/nu/xom/Latin2Writer.java b/src/nu/xom/Latin2Writer.java new file mode 100644 index 0000000..c7408b9 --- /dev/null +++ b/src/nu/xom/Latin2Writer.java @@ -0,0 +1,293 @@ +/* Copyright 2002, 2003 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom; + +import java.io.Writer; + +/** + *

    + * ISO 8859-2, a.k.a. Latin-2 + *

    + * + * @author Elliotte Rusty Harold + * @version 1.0 + * + */ +class Latin2Writer extends TextWriter { + + Latin2Writer(Writer out, String encoding) { + super(out, encoding); + } + + /** + * @see nu.xom.TextWriter#needsEscaping(char) + */ + boolean needsEscaping(char c) { + if (c <= 0xA0) return false; + switch (c) { + case 0x00A1: return true; // place holder to enable table lookup + case 0x00A2: return true; // place holder to enable table lookup + case 0x00A3: return true; // place holder to enable table lookup + case 0x00A4: return false; // CURRENCY SIGN + case 0x00A5: return true; // place holder to enable table lookup + case 0x00A6: return true; // place holder to enable table lookup + case 0x00A7: return false; // SECTION SIGN + case 0x00A8: return false; // DIAERESIS + case 0x00A9: return true; // place holder to enable table lookup + case 0x00AA: return true; // place holder to enable table lookup + case 0x00AB: return true; // place holder to enable table lookup + case 0x00AC: return true; // place holder to enable table lookup + case 0x00AD: return false; // SOFT HYPHEN + case 0x00AE: return true; // place holder to enable table lookup + case 0x00AF: return true; // place holder to enable table lookup + case 0x00B0: return false; // DEGREE SIGN + case 0x00B1: return true; // place holder to enable table lookup + case 0x00B2: return true; // place holder to enable table lookup + case 0x00B3: return true; // place holder to enable table lookup + case 0x00B4: return false; // ACUTE ACCENT + case 0x00B5: return true; // place holder to enable table lookup + case 0x00B6: return true; // place holder to enable table lookup + case 0x00B7: return true; // place holder to enable table lookup + case 0x00B8: return false; // CEDILLA + case 0x00C0: return true; // place holder to enable table lookup + case 0x00C1: return false; // LATIN CAPITAL LETTER A WITH ACUTE + case 0x00C2: return false; // LATIN CAPITAL LETTER A WITH CIRCUMFLEX + case 0x00C3: return true; // place holder to enable table lookup + case 0x00C4: return false; // LATIN CAPITAL LETTER A WITH DIAERESIS + case 0x00C5: return true; // place holder to enable table lookup + case 0x00C6: return true; // place holder to enable table lookup + case 0x00C7: return false; // LATIN CAPITAL LETTER C WITH CEDILLA + case 0x00C8: return true; // place holder to enable table lookup + case 0x00C9: return false; // LATIN CAPITAL LETTER E WITH ACUTE + case 0x00CA: return true; // place holder to enable table lookup + case 0x00CB: return false; // LATIN CAPITAL LETTER E WITH DIAERESIS + case 0x00CC: return true; // place holder to enable table lookup + case 0x00CD: return false; // LATIN CAPITAL LETTER I WITH ACUTE + case 0x00CE: return false; // LATIN CAPITAL LETTER I WITH CIRCUMFLEX + case 0x00CF: return true; // place holder to enable table lookup + case 0x00D0: return true; // place holder to enable table lookup + case 0x00D1: return true; // place holder to enable table lookup + case 0x00D2: return true; // place holder to enable table lookup + case 0x00D3: return false; // LATIN CAPITAL LETTER O WITH ACUTE + case 0x00D4: return false; // LATIN CAPITAL LETTER O WITH CIRCUMFLEX + case 0x00D5: return true; // place holder to enable table lookup + case 0x00D6: return false; // LATIN CAPITAL LETTER O WITH DIAERESIS + case 0x00D7: return false; // MULTIPLICATION SIGN + case 0x00D8: return true; // place holder to enable table lookup + case 0x00D9: return true; // place holder to enable table lookup + case 0x00DA: return false; // LATIN CAPITAL LETTER U WITH ACUTE + case 0x00DB: return true; // place holder to enable table lookup + case 0x00DC: return false; // LATIN CAPITAL LETTER U WITH DIAERESIS + case 0x00DD: return false; // LATIN CAPITAL LETTER Y WITH ACUTE + case 0x00DE: return true; // place holder to enable table lookup + case 0x00DF: return false; // LATIN SMALL LETTER SHARP S + case 0x00E0: return true; // place holder to enable table lookup + case 0x00E1: return false; // LATIN SMALL LETTER A WITH ACUTE + case 0x00E2: return false; // LATIN SMALL LETTER A WITH CIRCUMFLEX + case 0x00E3: return true; // place holder to enable table lookup + case 0x00E4: return false; // LATIN SMALL LETTER A WITH DIAERESIS + case 0x00E5: return true; // place holder to enable table lookup + case 0x00E6: return true; // place holder to enable table lookup + case 0x00E7: return false; // LATIN SMALL LETTER C WITH CEDILLA + case 0x00E8: return true; // place holder to enable table lookup + case 0x00E9: return false; // LATIN SMALL LETTER E WITH ACUTE + case 0x00EA: return true; // place holder to enable table lookup + case 0x00EB: return false; // LATIN SMALL LETTER E WITH DIAERESIS + case 0x00EC: return true; // place holder to enable table lookup + case 0x00ED: return false; // LATIN SMALL LETTER I WITH ACUTE + case 0x00EE: return false; // LATIN SMALL LETTER I WITH CIRCUMFLEX + case 0x00EF: return true; // place holder to enable table lookup + case 0x00F0: return true; // place holder to enable table lookup + case 0x00F1: return true; // place holder to enable table lookup + case 0x00F2: return true; // place holder to enable table lookup + case 0x00F3: return false; // LATIN SMALL LETTER O WITH ACUTE + case 0x00F4: return false; // LATIN SMALL LETTER O WITH CIRCUMFLEX + case 0x00F5: return true; // place holder to enable table lookup + case 0x00F6: return false; // LATIN SMALL LETTER O WITH DIAERESIS + case 0x00F7: return false; // DIVISION SIGN + case 0x00F8: return true; // place holder to enable table lookup + case 0x00F9: return true; // place holder to enable table lookup + case 0x00FA: return false; // LATIN SMALL LETTER U WITH ACUTE + case 0x00FB: return true; // place holder to enable table lookup + case 0x00FC: return false; // LATIN SMALL LETTER U WITH DIAERESIS + case 0x00FD: return false; // LATIN SMALL LETTER Y WITH ACUTE + case 0x00FE: return true; // place holder to enable table lookup + case 0x00FF: return true; // place holder to enable table lookup + case 0x0100: return true; // place holder to enable table lookup + case 0x0101: return true; // place holder to enable table lookup + case 0x0102: return false; // LATIN CAPITAL LETTER A WITH BREVE + case 0x0103: return false; // LATIN SMALL LETTER A WITH BREVE + case 0x0104: return false; // LATIN CAPITAL LETTER A WITH OGONEK + case 0x0105: return false; // LATIN SMALL LETTER A WITH OGONEK + case 0x0106: return false; // LATIN CAPITAL LETTER C WITH ACUTE + case 0x0107: return false; // LATIN SMALL LETTER C WITH ACUTE + case 0x0108: return true; // place holder to enable table lookup + case 0x0109: return true; // place holder to enable table lookup + case 0x010A: return true; // place holder to enable table lookup + case 0x010B: return true; // place holder to enable table lookup + case 0x010C: return false; // LATIN CAPITAL LETTER C WITH CARON + case 0x010D: return false; // LATIN SMALL LETTER C WITH CARON + case 0x010E: return false; // LATIN CAPITAL LETTER D WITH CARON + case 0x010F: return false; // LATIN SMALL LETTER D WITH CARON + case 0x0110: return false; // LATIN CAPITAL LETTER D WITH STROKE + case 0x0111: return false; // LATIN SMALL LETTER D WITH STROKE + case 0x0112: return true; // place holder to enable table lookup + case 0x0113: return true; // place holder to enable table lookup + case 0x0114: return true; // place holder to enable table lookup + case 0x0115: return true; // place holder to enable table lookup + case 0x0116: return true; // place holder to enable table lookup + case 0x0117: return true; // place holder to enable table lookup + case 0x0118: return false; // LATIN CAPITAL LETTER E WITH OGONEK + case 0x0119: return false; // LATIN SMALL LETTER E WITH OGONEK + case 0x011A: return false; // LATIN CAPITAL LETTER E WITH CARON + case 0x011B: return false; // LATIN SMALL LETTER E WITH CARON + case 0x011C: return true; // place holder to enable table lookup + case 0x011D: return true; // place holder to enable table lookup + case 0x011E: return true; // place holder to enable table lookup + case 0x011F: return true; // place holder to enable table lookup + case 0x0120: return true; // place holder to enable table lookup + case 0x0121: return true; // place holder to enable table lookup + case 0x0122: return true; // place holder to enable table lookup + case 0x0123: return true; // place holder to enable table lookup + case 0x0124: return true; // place holder to enable table lookup + case 0x0125: return true; // place holder to enable table lookup + case 0x0126: return true; // place holder to enable table lookup + case 0x0127: return true; // place holder to enable table lookup + case 0x0128: return true; // place holder to enable table lookup + case 0x0129: return true; // place holder to enable table lookup + case 0x012A: return true; // place holder to enable table lookup + case 0x012B: return true; // place holder to enable table lookup + case 0x012C: return true; // place holder to enable table lookup + case 0x012D: return true; // place holder to enable table lookup + case 0x012E: return true; // place holder to enable table lookup + case 0x012F: return true; // place holder to enable table lookup + case 0x0130: return true; // place holder to enable table lookup + case 0x0131: return true; // place holder to enable table lookup + case 0x0132: return true; // place holder to enable table lookup + case 0x0133: return true; // place holder to enable table lookup + case 0x0134: return true; // place holder to enable table lookup + case 0x0135: return true; // place holder to enable table lookup + case 0x0136: return true; // place holder to enable table lookup + case 0x0137: return true; // place holder to enable table lookup + case 0x0138: return true; // place holder to enable table lookup + case 0x0139: return false; // LATIN CAPITAL LETTER L WITH ACUTE + case 0x013A: return false; // LATIN SMALL LETTER L WITH ACUTE + case 0x013B: return true; // place holder to enable table lookup + case 0x013C: return true; // place holder to enable table lookup + case 0x013D: return false; // LATIN CAPITAL LETTER L WITH CARON + case 0x013E: return false; // LATIN SMALL LETTER L WITH CARON + case 0x013F: return true; // place holder to enable table lookup + case 0x0140: return true; // place holder to enable table lookup + case 0x0141: return false; // LATIN CAPITAL LETTER L WITH STROKE + case 0x0142: return false; // LATIN SMALL LETTER L WITH STROKE + case 0x0143: return false; // LATIN CAPITAL LETTER N WITH ACUTE + case 0x0144: return false; // LATIN SMALL LETTER N WITH ACUTE + case 0x0145: return true; // place holder to enable table lookup + case 0x0146: return true; // place holder to enable table lookup + case 0x0147: return false; // LATIN CAPITAL LETTER N WITH CARON + case 0x0148: return false; // LATIN SMALL LETTER N WITH CARON + case 0x0149: return true; // place holder to enable table lookup + case 0x014A: return true; // place holder to enable table lookup + case 0x014B: return true; // place holder to enable table lookup + case 0x014C: return true; // place holder to enable table lookup + case 0x014D: return true; // place holder to enable table lookup + case 0x014E: return true; // place holder to enable table lookup + case 0x014F: return true; // place holder to enable table lookup + case 0x0150: return false; // LATIN CAPITAL LETTER O WITH DOUBLE ACUTE + case 0x0151: return false; // LATIN SMALL LETTER O WITH DOUBLE ACUTE + case 0x0152: return true; // place holder to enable table lookup + case 0x0153: return true; // place holder to enable table lookup + case 0x0154: return false; // LATIN CAPITAL LETTER R WITH ACUTE + case 0x0155: return false; // LATIN SMALL LETTER R WITH ACUTE + case 0x0156: return true; // place holder to enable table lookup + case 0x0157: return true; // place holder to enable table lookup + case 0x0158: return false; // LATIN CAPITAL LETTER R WITH CARON + case 0x0159: return false; // LATIN SMALL LETTER R WITH CARON + case 0x015A: return false; // LATIN CAPITAL LETTER S WITH ACUTE + case 0x015B: return false; // LATIN SMALL LETTER S WITH ACUTE + case 0x015C: return true; // place holder to enable table lookup + case 0x015D: return true; // place holder to enable table lookup + case 0x015E: return false; // LATIN CAPITAL LETTER S WITH CEDILLA + case 0x015F: return false; // LATIN SMALL LETTER S WITH CEDILLA + case 0x0160: return false; // LATIN CAPITAL LETTER S WITH CARON + case 0x0161: return false; // LATIN SMALL LETTER S WITH CARON + case 0x0162: return false; // LATIN CAPITAL LETTER T WITH CEDILLA + case 0x0163: return false; // LATIN SMALL LETTER T WITH CEDILLA + case 0x0164: return false; // LATIN CAPITAL LETTER T WITH CARON + case 0x0165: return false; // LATIN SMALL LETTER T WITH CARON + case 0x0166: return true; // place holder to enable table lookup + case 0x0167: return true; // place holder to enable table lookup + case 0x0168: return true; // place holder to enable table lookup + case 0x0169: return true; // place holder to enable table lookup + case 0x016A: return true; // place holder to enable table lookup + case 0x016B: return true; // place holder to enable table lookup + case 0x016C: return true; // place holder to enable table lookup + case 0x016D: return true; // place holder to enable table lookup + case 0x016E: return false; // LATIN CAPITAL LETTER U WITH RING ABOVE + case 0x016F: return false; // LATIN SMALL LETTER U WITH RING ABOVE + case 0x0170: return false; // LATIN CAPITAL LETTER U WITH DOUBLE ACUTE + case 0x0171: return false; // LATIN SMALL LETTER U WITH DOUBLE ACUTE + case 0x0172: return true; // place holder to enable table lookup + case 0x0173: return true; // place holder to enable table lookup + case 0x0174: return true; // place holder to enable table lookup + case 0x0175: return true; // place holder to enable table lookup + case 0x0176: return true; // place holder to enable table lookup + case 0x0177: return true; // place holder to enable table lookup + case 0x0178: return true; // place holder to enable table lookup + case 0x0179: return false; // LATIN CAPITAL LETTER Z WITH ACUTE + case 0x017A: return false; // LATIN SMALL LETTER Z WITH ACUTE + case 0x017B: return false; // LATIN CAPITAL LETTER Z WITH DOT ABOVE + case 0x017C: return false; // LATIN SMALL LETTER Z WITH DOT ABOVE + case 0x017D: return false; // LATIN CAPITAL LETTER Z WITH CARON + case 0x017E: return false; // LATIN SMALL LETTER Z WITH CARON + } + switch (c) { + case 0x02C7: return false; // CARON + case 0x02C8: return true; // place holder to enable table lookup + case 0x02C9: return true; // place holder to enable table lookup + case 0x02CA: return true; // place holder to enable table lookup + case 0x02CB: return true; // place holder to enable table lookup + case 0x02CC: return true; // place holder to enable table lookup + case 0x02CD: return true; // place holder to enable table lookup + case 0x02CE: return true; // place holder to enable table lookup + case 0x02CF: return true; // place holder to enable table lookup + case 0x02D0: return true; // place holder to enable table lookup + case 0x02D1: return true; // place holder to enable table lookup + case 0x02D2: return true; // place holder to enable table lookup + case 0x02D3: return true; // place holder to enable table lookup + case 0x02D4: return true; // place holder to enable table lookup + case 0x02D5: return true; // place holder to enable table lookup + case 0x02D6: return true; // place holder to enable table lookup + case 0x02D7: return true; // place holder to enable table lookup + case 0x02D8: return false; // BREVE + case 0x02D9: return false; // DOT ABOVE + case 0x02DA: return true; // place holder to enable table lookup + case 0x02DB: return false; // OGONEK + case 0x02DC: return true; // place holder to enable table lookup + case 0x02DD: return false; // DOUBLE ACUTE ACCENT + } + + return true; + + } + +} \ No newline at end of file diff --git a/src/nu/xom/Latin3Writer.java b/src/nu/xom/Latin3Writer.java new file mode 100644 index 0000000..8fe1e1e --- /dev/null +++ b/src/nu/xom/Latin3Writer.java @@ -0,0 +1,268 @@ +/* Copyright 2002, 2003 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom; + +import java.io.Writer; + +/** + *

    + * ISO 8859-3 + *

    + * + * @author Elliotte Rusty Harold + * @version 1.0 + * + */ +class Latin3Writer extends TextWriter { + + Latin3Writer(Writer out, String encoding) { + super(out, encoding); + } + + /** + * @see nu.xom.TextWriter#needsEscaping(char) + */ + boolean needsEscaping(char c) { + if (c <= 0xA0) return false; + switch (c) { + case 0x00A1: return true; // place holder to enable table lookup + case 0x00A2: return true; // place holder to enable table lookup + case 0x00A3: return true; // place holder to enable table lookup + case 0x00A4: return false; // CURRENCY SIGN + case 0x00A5: return true; // place holder to enable table lookup + case 0x00A6: return true; // place holder to enable table lookup + case 0x00A7: return false; // SECTION SIGN + case 0x00A8: return false; // DIAERESIS + case 0x00A9: return true; // place holder to enable table lookup + case 0x00AA: return true; // place holder to enable table lookup + case 0x00AB: return true; // place holder to enable table lookup + case 0x00AC: return true; // place holder to enable table lookup + case 0x00AD: return false; // SOFT HYPHEN + case 0x00AE: return true; // place holder to enable table lookup + case 0x00AF: return true; // place holder to enable table lookup + case 0x00B0: return false; // DEGREE SIGN + case 0x00B1: return true; // place holder to enable table lookup + case 0x00B2: return false; // SUPERSCRIPT TWO + case 0x00B3: return false; // SUPERSCRIPT THREE + case 0x00B4: return false; // ACUTE ACCENT + case 0x00B5: return false; // MICRO SIGN + case 0x00B7: return false; // MIDDLE DOT + case 0x00B8: return false; // CEDILLA + case 0x00B9: return true; // place holder to enable table lookup + case 0x00BA: return true; // place holder to enable table lookup + case 0x00BB: return true; // place holder to enable table lookup + case 0x00BC: return true; // place holder to enable table lookup + case 0x00BD: return false; // VULGAR FRACTION ONE HALF + case 0x00BE: return true; // place holder to enable table lookup + case 0x00BF: return true; // place holder to enable table lookup + case 0x00C0: return false; // LATIN CAPITAL LETTER A WITH GRAVE + case 0x00C1: return false; // LATIN CAPITAL LETTER A WITH ACUTE + case 0x00C2: return false; // LATIN CAPITAL LETTER A WITH CIRCUMFLEX + case 0x00C3: return true; // place holder to enable table lookup + case 0x00C4: return false; // LATIN CAPITAL LETTER A WITH DIAERESIS + case 0x00C5: return true; // place holder to enable table lookup + case 0x00C6: return true; // place holder to enable table lookup + case 0x00C7: return false; // LATIN CAPITAL LETTER C WITH CEDILLA + case 0x00C8: return false; // LATIN CAPITAL LETTER E WITH GRAVE + case 0x00C9: return false; // LATIN CAPITAL LETTER E WITH ACUTE + case 0x00CA: return false; // LATIN CAPITAL LETTER E WITH CIRCUMFLEX + case 0x00CB: return false; // LATIN CAPITAL LETTER E WITH DIAERESIS + case 0x00CC: return false; // LATIN CAPITAL LETTER I WITH GRAVE + case 0x00CD: return false; // LATIN CAPITAL LETTER I WITH ACUTE + case 0x00CE: return false; // LATIN CAPITAL LETTER I WITH CIRCUMFLEX + case 0x00CF: return false; // LATIN CAPITAL LETTER I WITH DIAERESIS + case 0x00D0: return true; // place holder to enable table lookup + case 0x00D1: return false; // LATIN CAPITAL LETTER N WITH TILDE + case 0x00D2: return false; // LATIN CAPITAL LETTER O WITH GRAVE + case 0x00D3: return false; // LATIN CAPITAL LETTER O WITH ACUTE + case 0x00D4: return false; // LATIN CAPITAL LETTER O WITH CIRCUMFLEX + case 0x00D6: return false; // LATIN CAPITAL LETTER O WITH DIAERESIS + case 0x00D7: return false; // MULTIPLICATION SIGN + case 0x00D9: return false; // LATIN CAPITAL LETTER U WITH GRAVE + case 0x00DA: return false; // LATIN CAPITAL LETTER U WITH ACUTE + case 0x00DB: return false; // LATIN CAPITAL LETTER U WITH CIRCUMFLEX + case 0x00DC: return false; // LATIN CAPITAL LETTER U WITH DIAERESIS + case 0x00DD: return true; // place holder to enable table lookup + case 0x00DE: return true; // place holder to enable table lookup + case 0x00DF: return false; // LATIN SMALL LETTER SHARP S + case 0x00E0: return false; // LATIN SMALL LETTER A WITH GRAVE + case 0x00E1: return false; // LATIN SMALL LETTER A WITH ACUTE + case 0x00E2: return false; // LATIN SMALL LETTER A WITH CIRCUMFLEX + case 0x00E3: return true; // place holder to enable table lookup + case 0x00E4: return false; // LATIN SMALL LETTER A WITH DIAERESIS + case 0x00E5: return true; // place holder to enable table lookup + case 0x00E6: return true; // place holder to enable table lookup + case 0x00E7: return false; // LATIN SMALL LETTER C WITH CEDILLA + case 0x00E8: return false; // LATIN SMALL LETTER E WITH GRAVE + case 0x00E9: return false; // LATIN SMALL LETTER E WITH ACUTE + case 0x00EA: return false; // LATIN SMALL LETTER E WITH CIRCUMFLEX + case 0x00EB: return false; // LATIN SMALL LETTER E WITH DIAERESIS + case 0x00EC: return false; // LATIN SMALL LETTER I WITH GRAVE + case 0x00ED: return false; // LATIN SMALL LETTER I WITH ACUTE + case 0x00EE: return false; // LATIN SMALL LETTER I WITH CIRCUMFLEX + case 0x00EF: return false; // LATIN SMALL LETTER I WITH DIAERESIS + case 0x00F0: return true; // place holder to enable table lookup + case 0x00F1: return false; // LATIN SMALL LETTER N WITH TILDE + case 0x00F2: return false; // LATIN SMALL LETTER O WITH GRAVE + case 0x00F3: return false; // LATIN SMALL LETTER O WITH ACUTE + case 0x00F4: return false; // LATIN SMALL LETTER O WITH CIRCUMFLEX + case 0x00F5: return true; // place holder to enable table lookup + case 0x00F6: return false; // LATIN SMALL LETTER O WITH DIAERESIS + case 0x00F7: return false; // DIVISION SIGN + case 0x00F8: return true; // place holder to enable table lookup + case 0x00F9: return false; // LATIN SMALL LETTER U WITH GRAVE + case 0x00FA: return false; // LATIN SMALL LETTER U WITH ACUTE + case 0x00FB: return false; // LATIN SMALL LETTER U WITH CIRCUMFLEX + case 0x00FC: return false; // LATIN SMALL LETTER U WITH DIAERESIS + case 0x00FD: return true; // place holder to enable table lookup + case 0x00FE: return true; // place holder to enable table lookup + case 0x00FF: return true; // place holder to enable table lookup + case 0x0100: return true; // place holder to enable table lookup + case 0x0101: return true; // place holder to enable table lookup + case 0x0102: return true; // place holder to enable table lookup + case 0x0103: return true; // place holder to enable table lookup + case 0x0104: return true; // place holder to enable table lookup + case 0x0105: return true; // place holder to enable table lookup + case 0x0106: return true; // place holder to enable table lookup + case 0x0107: return true; // place holder to enable table lookup + case 0x0108: return false; // LATIN CAPITAL LETTER C WITH CIRCUMFLEX + case 0x0109: return false; // LATIN SMALL LETTER C WITH CIRCUMFLEX + case 0x010A: return false; // LATIN CAPITAL LETTER C WITH DOT ABOVE + case 0x010B: return false; // LATIN SMALL LETTER C WITH DOT ABOVE + case 0x010C: return true; // place holder to enable table lookup + case 0x010D: return true; // place holder to enable table lookup + case 0x010E: return true; // place holder to enable table lookup + case 0x010F: return true; // place holder to enable table lookup + case 0x0110: return true; // place holder to enable table lookup + case 0x0111: return true; // place holder to enable table lookup + case 0x0112: return true; // place holder to enable table lookup + case 0x0113: return true; // place holder to enable table lookup + case 0x0114: return true; // place holder to enable table lookup + case 0x0115: return true; // place holder to enable table lookup + case 0x0116: return true; // place holder to enable table lookup + case 0x0117: return true; // place holder to enable table lookup + case 0x0118: return true; // place holder to enable table lookup + case 0x0119: return true; // place holder to enable table lookup + case 0x011A: return true; // place holder to enable table lookup + case 0x011B: return true; // place holder to enable table lookup + case 0x011C: return false; // LATIN CAPITAL LETTER G WITH CIRCUMFLEX + case 0x011D: return false; // LATIN SMALL LETTER G WITH CIRCUMFLEX + case 0x011E: return false; // LATIN CAPITAL LETTER G WITH BREVE + case 0x011F: return false; // LATIN SMALL LETTER G WITH BREVE + case 0x0120: return false; // LATIN CAPITAL LETTER G WITH DOT ABOVE + case 0x0121: return false; // LATIN SMALL LETTER G WITH DOT ABOVE + case 0x0122: return true; // place holder to enable table lookup + case 0x0123: return true; // place holder to enable table lookup + case 0x0124: return false; // LATIN CAPITAL LETTER H WITH CIRCUMFLEX + case 0x0125: return false; // LATIN SMALL LETTER H WITH CIRCUMFLEX + case 0x0126: return false; // LATIN CAPITAL LETTER H WITH STROKE + case 0x0127: return false; // LATIN SMALL LETTER H WITH STROKE + case 0x0128: return true; // place holder to enable table lookup + case 0x0129: return true; // place holder to enable table lookup + case 0x0130: return false; // LATIN CAPITAL LETTER I WITH DOT ABOVE + case 0x0131: return false; // LATIN SMALL LETTER DOTLESS I + case 0x0132: return true; // place holder to enable table lookup + case 0x0133: return true; // place holder to enable table lookup + case 0x0134: return false; // LATIN CAPITAL LETTER J WITH CIRCUMFLEX + case 0x0135: return false; // LATIN SMALL LETTER J WITH CIRCUMFLEX + case 0x0136: return true; // place holder to enable table lookup + case 0x0137: return true; // place holder to enable table lookup + case 0x0138: return true; // place holder to enable table lookup + case 0x0139: return true; // place holder to enable table lookup + case 0x013A: return true; // place holder to enable table lookup + case 0x013B: return true; // place holder to enable table lookup + case 0x013C: return true; // place holder to enable table lookup + case 0x013D: return true; // place holder to enable table lookup + case 0x013E: return true; // place holder to enable table lookup + case 0x013F: return true; // place holder to enable table lookup + case 0x0140: return true; // place holder to enable table lookup + case 0x0141: return true; // place holder to enable table lookup + case 0x0142: return true; // place holder to enable table lookup + case 0x0143: return true; // place holder to enable table lookup + case 0x0144: return true; // place holder to enable table lookup + case 0x0145: return true; // place holder to enable table lookup + case 0x0146: return true; // place holder to enable table lookup + case 0x0147: return true; // place holder to enable table lookup + case 0x0148: return true; // place holder to enable table lookup + case 0x0149: return true; // place holder to enable table lookup + case 0x014A: return true; // place holder to enable table lookup + case 0x014B: return true; // place holder to enable table lookup + case 0x014C: return true; // place holder to enable table lookup + case 0x014D: return true; // place holder to enable table lookup + case 0x014E: return true; // place holder to enable table lookup + case 0x014F: return true; // place holder to enable table lookup + case 0x0150: return true; // place holder to enable table lookup + case 0x0151: return true; // place holder to enable table lookup + case 0x0152: return true; // place holder to enable table lookup + case 0x0153: return true; // place holder to enable table lookup + case 0x0154: return true; // place holder to enable table lookup + case 0x0155: return true; // place holder to enable table lookup + case 0x0156: return true; // place holder to enable table lookup + case 0x0157: return true; // place holder to enable table lookup + case 0x0158: return true; // place holder to enable table lookup + case 0x0159: return true; // place holder to enable table lookup + case 0x015A: return true; // place holder to enable table lookup + case 0x015B: return true; // place holder to enable table lookup + case 0x015C: return false; // LATIN CAPITAL LETTER S WITH CIRCUMFLEX + case 0x015D: return false; // LATIN SMALL LETTER S WITH CIRCUMFLEX + case 0x015E: return false; // LATIN CAPITAL LETTER S WITH CEDILLA + case 0x015F: return false; // LATIN SMALL LETTER S WITH CEDILLA + case 0x0160: return true; // place holder to enable table lookup + case 0x0161: return true; // place holder to enable table lookup + case 0x0162: return true; // place holder to enable table lookup + case 0x0163: return true; // place holder to enable table lookup + case 0x0164: return true; // place holder to enable table lookup + case 0x0165: return true; // place holder to enable table lookup + case 0x0166: return true; // place holder to enable table lookup + case 0x0167: return true; // place holder to enable table lookup + case 0x0168: return true; // place holder to enable table lookup + case 0x0169: return true; // place holder to enable table lookup + case 0x016A: return true; // place holder to enable table lookup + case 0x016B: return true; // place holder to enable table lookup + case 0x016C: return false; // LATIN CAPITAL LETTER U WITH BREVE + case 0x016D: return false; // LATIN SMALL LETTER U WITH BREVE + case 0x016E: return true; // place holder to enable table lookup + case 0x016F: return true; // place holder to enable table lookup + case 0x0170: return true; // place holder to enable table lookup + case 0x0171: return true; // place holder to enable table lookup + case 0x0172: return true; // place holder to enable table lookup + case 0x0173: return true; // place holder to enable table lookup + case 0x0174: return true; // place holder to enable table lookup + case 0x0175: return true; // place holder to enable table lookup + case 0x0176: return true; // place holder to enable table lookup + case 0x0177: return true; // place holder to enable table lookup + case 0x0178: return true; // place holder to enable table lookup + case 0x0179: return true; // place holder to enable table lookup + case 0x017A: return true; // place holder to enable table lookup + case 0x017B: return false; // LATIN CAPITAL LETTER Z WITH DOT ABOVE + case 0x017C: return false; // LATIN SMALL LETTER Z WITH DOT ABOVE + } + switch (c) { + case 0x02D8: return false; // BREVE + case 0x02D9: return false; // DOT ABOVE + } + + return true; + + } + +} \ No newline at end of file diff --git a/src/nu/xom/Latin4Writer.java b/src/nu/xom/Latin4Writer.java new file mode 100644 index 0000000..9957d44 --- /dev/null +++ b/src/nu/xom/Latin4Writer.java @@ -0,0 +1,293 @@ +/* Copyright 2002, 2003 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom; + +import java.io.Writer; + +/** + *

    + * ISO 8859-4, Latin plus the characters needed for Greenlandic, + * Icelandic, and Lappish. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.0 + * + */ +class Latin4Writer extends TextWriter { + + Latin4Writer(Writer out, String encoding) { + super(out, encoding); + } + + /** + * @see nu.xom.TextWriter#needsEscaping(char) + */ + boolean needsEscaping(char c) { + if (c <= 0xA0) return false; + switch (c) { + case 0x00A1: return true; // place holder to enable table lookup + case 0x00A2: return true; // place holder to enable table lookup + case 0x00A3: return true; // place holder to enable table lookup + case 0x00A4: return false; // CURRENCY SIGN + case 0x00A5: return true; // place holder to enable table lookup + case 0x00A6: return true; // place holder to enable table lookup + case 0x00A7: return false; // SECTION SIGN + case 0x00A8: return false; // DIAERESIS + case 0x00A9: return true; // place holder to enable table lookup + case 0x00AA: return true; // place holder to enable table lookup + case 0x00AB: return true; // place holder to enable table lookup + case 0x00AC: return true; // place holder to enable table lookup + case 0x00AD: return false; // SOFT HYPHEN + case 0x00AE: return true; // place holder to enable table lookup + case 0x00AF: return false; // MACRON + case 0x00B0: return false; // DEGREE SIGN + case 0x00B1: return true; // place holder to enable table lookup + case 0x00B2: return true; // place holder to enable table lookup + case 0x00B3: return true; // place holder to enable table lookup + case 0x00B4: return false; // ACUTE ACCENT + case 0x00B5: return true; // place holder to enable table lookup + case 0x00B6: return true; // place holder to enable table lookup + case 0x00B7: return true; // place holder to enable table lookup + case 0x00B8: return false; // CEDILLA + case 0x00B9: return true; // place holder to enable table lookup + case 0x00BA: return true; // place holder to enable table lookup + case 0x00BB: return true; // place holder to enable table lookup + case 0x00BC: return true; // place holder to enable table lookup + case 0x00BD: return true; // place holder to enable table lookup + case 0x00BE: return true; // place holder to enable table lookup + case 0x00BF: return true; // place holder to enable table lookup + case 0x00C0: return true; // place holder to enable table lookup + case 0x00C1: return false; // LATIN CAPITAL LETTER A WITH ACUTE + case 0x00C2: return false; // LATIN CAPITAL LETTER A WITH CIRCUMFLEX + case 0x00C3: return false; // LATIN CAPITAL LETTER A WITH TILDE + case 0x00C4: return false; // LATIN CAPITAL LETTER A WITH DIAERESIS + case 0x00C5: return false; // LATIN CAPITAL LETTER A WITH RING ABOVE + case 0x00C6: return false; // LATIN CAPITAL LETTER AE + case 0x00C7: return true; // place holder to enable table lookup + case 0x00C8: return true; // place holder to enable table lookup + case 0x00C9: return false; // LATIN CAPITAL LETTER E WITH ACUTE + case 0x00CA: return true; // place holder to enable table lookup + case 0x00CB: return false; // LATIN CAPITAL LETTER E WITH DIAERESIS + case 0x00CC: return true; // place holder to enable table lookup + case 0x00CD: return false; // LATIN CAPITAL LETTER I WITH ACUTE + case 0x00CE: return false; // LATIN CAPITAL LETTER I WITH CIRCUMFLEX + case 0x00CF: return true; // place holder to enable table lookup + case 0x00D0: return true; // place holder to enable table lookup + case 0x00D1: return true; // place holder to enable table lookup + case 0x00D2: return true; // place holder to enable table lookup + case 0x00D3: return true; // place holder to enable table lookup + case 0x00D4: return false; // LATIN CAPITAL LETTER O WITH CIRCUMFLEX + case 0x00D5: return false; // LATIN CAPITAL LETTER O WITH TILDE + case 0x00D6: return false; // LATIN CAPITAL LETTER O WITH DIAERESIS + case 0x00D7: return false; // MULTIPLICATION SIGN + case 0x00D8: return false; // LATIN CAPITAL LETTER O WITH STROKE + case 0x00D9: return true; // place holder to enable table lookup + case 0x00DA: return false; // LATIN CAPITAL LETTER U WITH ACUTE + case 0x00DB: return false; // LATIN CAPITAL LETTER U WITH CIRCUMFLEX + case 0x00DC: return false; // LATIN CAPITAL LETTER U WITH DIAERESIS + case 0x00DD: return true; // place holder to enable table lookup + case 0x00DE: return true; // place holder to enable table lookup + case 0x00DF: return false; // LATIN SMALL LETTER SHARP S + case 0x00E0: return true; // place holder to enable table lookup + case 0x00E1: return false; // LATIN SMALL LETTER A WITH ACUTE + case 0x00E2: return false; // LATIN SMALL LETTER A WITH CIRCUMFLEX + case 0x00E3: return false; // LATIN SMALL LETTER A WITH TILDE + case 0x00E4: return false; // LATIN SMALL LETTER A WITH DIAERESIS + case 0x00E5: return false; // LATIN SMALL LETTER A WITH RING ABOVE + case 0x00E6: return false; // LATIN SMALL LETTER AE + case 0x00E7: return true; // place holder to enable table lookup + case 0x00E8: return true; // place holder to enable table lookup + case 0x00E9: return false; // LATIN SMALL LETTER E WITH ACUTE + case 0x00EA: return true; // place holder to enable table lookup + case 0x00EB: return false; // LATIN SMALL LETTER E WITH DIAERESIS + case 0x00EC: return true; // place holder to enable table lookup + case 0x00ED: return false; // LATIN SMALL LETTER I WITH ACUTE + case 0x00EE: return false; // LATIN SMALL LETTER I WITH CIRCUMFLEX + case 0x00EF: return true; // place holder to enable table lookup + case 0x00F0: return true; // place holder to enable table lookup + case 0x00F1: return true; // place holder to enable table lookup + case 0x00F2: return true; // place holder to enable table lookup + case 0x00F3: return true; // place holder to enable table lookup + case 0x00F4: return false; // LATIN SMALL LETTER O WITH CIRCUMFLEX + case 0x00F5: return false; // LATIN SMALL LETTER O WITH TILDE + case 0x00F6: return false; // LATIN SMALL LETTER O WITH DIAERESIS + case 0x00F7: return false; // DIVISION SIGN + case 0x00F8: return false; // LATIN SMALL LETTER O WITH STROKE + case 0x00F9: return true; // place holder to enable table lookup + case 0x00FA: return false; // LATIN SMALL LETTER U WITH ACUTE + case 0x00FB: return false; // LATIN SMALL LETTER U WITH CIRCUMFLEX + case 0x00FC: return false; // LATIN SMALL LETTER U WITH DIAERESIS + case 0x00FD: return true; // place holder to enable table lookup + case 0x00FE: return true; // place holder to enable table lookup + case 0x00FF: return true; // place holder to enable table lookup + case 0x0100: return false; // LATIN CAPITAL LETTER A WITH MACRON + case 0x0101: return false; // LATIN SMALL LETTER A WITH MACRON + case 0x0102: return true; // place holder to enable table lookup + case 0x0103: return true; // place holder to enable table lookup + case 0x0104: return false; // LATIN CAPITAL LETTER A WITH OGONEK + case 0x0105: return false; // LATIN SMALL LETTER A WITH OGONEK + case 0x0106: return true; // place holder to enable table lookup + case 0x0107: return true; // place holder to enable table lookup + case 0x0108: return true; // place holder to enable table lookup + case 0x0109: return true; // place holder to enable table lookup + case 0x010A: return true; // place holder to enable table lookup + case 0x010B: return true; // place holder to enable table lookup + case 0x010C: return false; // LATIN CAPITAL LETTER C WITH CARON + case 0x010D: return false; // LATIN SMALL LETTER C WITH CARON + case 0x010E: return true; // place holder to enable table lookup + case 0x010F: return true; // place holder to enable table lookup + case 0x0110: return false; // LATIN CAPITAL LETTER D WITH STROKE + case 0x0111: return false; // LATIN SMALL LETTER D WITH STROKE + case 0x0112: return false; // LATIN CAPITAL LETTER E WITH MACRON + case 0x0113: return false; // LATIN SMALL LETTER E WITH MACRON + case 0x0114: return true; // place holder to enable table lookup + case 0x0115: return true; // place holder to enable table lookup + case 0x0116: return false; // LATIN CAPITAL LETTER E WITH DOT ABOVE + case 0x0117: return false; // LATIN SMALL LETTER E WITH DOT ABOVE + case 0x0118: return false; // LATIN CAPITAL LETTER E WITH OGONEK + case 0x0119: return false; // LATIN SMALL LETTER E WITH OGONEK + case 0x0120: return true; // place holder to enable table lookup + case 0x0121: return true; // place holder to enable table lookup + case 0x0122: return false; // LATIN CAPITAL LETTER G WITH CEDILLA + case 0x0123: return false; // LATIN SMALL LETTER G WITH CEDILLA + case 0x0124: return true; // place holder to enable table lookup + case 0x0125: return true; // place holder to enable table lookup + case 0x0126: return true; // place holder to enable table lookup + case 0x0127: return true; // place holder to enable table lookup + case 0x0128: return false; // LATIN CAPITAL LETTER I WITH TILDE + case 0x0129: return false; // LATIN SMALL LETTER I WITH TILDE + case 0x012A: return false; // LATIN CAPITAL LETTER I WITH MACRON + case 0x012B: return false; // LATIN SMALL LETTER I WITH MACRON + case 0x012C: return true; // place holder to enable table lookup + case 0x012D: return true; // place holder to enable table lookup + case 0x012E: return false; // LATIN CAPITAL LETTER I WITH OGONEK + case 0x012F: return false; // LATIN SMALL LETTER I WITH OGONEK + case 0x0130: return true; // place holder to enable table lookup + case 0x0131: return true; // place holder to enable table lookup + case 0x0132: return true; // place holder to enable table lookup + case 0x0133: return true; // place holder to enable table lookup + case 0x0134: return true; // place holder to enable table lookup + case 0x0135: return true; // place holder to enable table lookup + case 0x0136: return false; // LATIN CAPITAL LETTER K WITH CEDILLA + case 0x0137: return false; // LATIN SMALL LETTER K WITH CEDILLA + case 0x0138: return false; // LATIN SMALL LETTER KRA + case 0x0139: return true; // place holder to enable table lookup + case 0x013A: return true; // place holder to enable table lookup + case 0x013B: return false; // LATIN CAPITAL LETTER L WITH CEDILLA + case 0x013C: return false; // LATIN SMALL LETTER L WITH CEDILLA + case 0x013D: return true; // place holder to enable table lookup + case 0x013E: return true; // place holder to enable table lookup + case 0x013F: return true; // place holder to enable table lookup + case 0x0140: return true; // place holder to enable table lookup + case 0x0141: return true; // place holder to enable table lookup + case 0x0142: return true; // place holder to enable table lookup + case 0x0143: return true; // place holder to enable table lookup + case 0x0144: return true; // place holder to enable table lookup + case 0x0145: return false; // LATIN CAPITAL LETTER N WITH CEDILLA + case 0x0146: return false; // LATIN SMALL LETTER N WITH CEDILLA + case 0x0147: return true; // place holder to enable table lookup + case 0x0148: return true; // place holder to enable table lookup + case 0x0149: return true; // place holder to enable table lookup + case 0x014A: return false; // LATIN CAPITAL LETTER ENG + case 0x014B: return false; // LATIN SMALL LETTER ENG + case 0x014C: return false; // LATIN CAPITAL LETTER O WITH MACRON + case 0x014D: return false; // LATIN SMALL LETTER O WITH MACRON + case 0x014E: return true; // place holder to enable table lookup + case 0x014F: return true; // place holder to enable table lookup + case 0x0150: return true; // place holder to enable table lookup + case 0x0151: return true; // place holder to enable table lookup + case 0x0152: return true; // place holder to enable table lookup + case 0x0153: return true; // place holder to enable table lookup + case 0x0154: return true; // place holder to enable table lookup + case 0x0155: return true; // place holder to enable table lookup + case 0x0156: return false; // LATIN CAPITAL LETTER R WITH CEDILLA + case 0x0157: return false; // LATIN SMALL LETTER R WITH CEDILLA + case 0x0158: return true; // place holder to enable table lookup + case 0x0159: return true; // place holder to enable table lookup + case 0x015A: return true; // place holder to enable table lookup + case 0x015B: return true; // place holder to enable table lookup + case 0x015C: return true; // place holder to enable table lookup + case 0x015D: return true; // place holder to enable table lookup + case 0x015E: return true; // place holder to enable table lookup + case 0x015F: return true; // place holder to enable table lookup + case 0x0160: return false; // LATIN CAPITAL LETTER S WITH CARON + case 0x0161: return false; // LATIN SMALL LETTER S WITH CARON + case 0x0162: return true; // place holder to enable table lookup + case 0x0163: return true; // place holder to enable table lookup + case 0x0164: return true; // place holder to enable table lookup + case 0x0165: return true; // place holder to enable table lookup + case 0x0166: return false; // LATIN CAPITAL LETTER T WITH STROKE + case 0x0167: return false; // LATIN SMALL LETTER T WITH STROKE + case 0x0168: return false; // LATIN CAPITAL LETTER U WITH TILDE + case 0x0169: return false; // LATIN SMALL LETTER U WITH TILDE + case 0x016A: return false; // LATIN CAPITAL LETTER U WITH MACRON + case 0x016B: return false; // LATIN SMALL LETTER U WITH MACRON + case 0x016C: return true; // place holder to enable table lookup + case 0x016D: return true; // place holder to enable table lookup + case 0x016E: return true; // place holder to enable table lookup + case 0x016F: return true; // place holder to enable table lookup + case 0x0170: return true; // place holder to enable table lookup + case 0x0171: return true; // place holder to enable table lookup + case 0x0172: return false; // LATIN CAPITAL LETTER U WITH OGONEK + case 0x0173: return false; // LATIN SMALL LETTER U WITH OGONEK + case 0x0174: return true; // place holder to enable table lookup + case 0x0175: return true; // place holder to enable table lookup + case 0x0176: return true; // place holder to enable table lookup + case 0x0177: return true; // place holder to enable table lookup + case 0x0178: return true; // place holder to enable table lookup + case 0x0179: return true; // place holder to enable table lookup + case 0x017A: return true; // place holder to enable table lookup + case 0x017B: return true; // place holder to enable table lookup + case 0x017C: return true; // place holder to enable table lookup + case 0x017D: return false; // LATIN CAPITAL LETTER Z WITH CARON + case 0x017E: return false; // LATIN SMALL LETTER Z WITH CARON + } + switch (c) { + case 0x02C7: return false; // CARON + case 0x02C8: return true; // place holder to enable table lookup + case 0x02C9: return true; // place holder to enable table lookup + case 0x02CA: return true; // place holder to enable table lookup + case 0x02CB: return true; // place holder to enable table lookup + case 0x02CC: return true; // place holder to enable table lookup + case 0x02CD: return true; // place holder to enable table lookup + case 0x02CE: return true; // place holder to enable table lookup + case 0x02CF: return true; // place holder to enable table lookup + case 0x02D0: return true; // place holder to enable table lookup + case 0x02D1: return true; // place holder to enable table lookup + case 0x02D2: return true; // place holder to enable table lookup + case 0x02D3: return true; // place holder to enable table lookup + case 0x02D4: return true; // place holder to enable table lookup + case 0x02D5: return true; // place holder to enable table lookup + case 0x02D6: return true; // place holder to enable table lookup + case 0x02D7: return true; // place holder to enable table lookup + case 0x02D8: return true; // place holder to enable table lookup + case 0x02D9: return false; // DOT ABOVE + case 0x02DA: return true; // place holder to enable table lookup + case 0x02DB: return false; // OGONEK + } + + return true; + + } + +} \ No newline at end of file diff --git a/src/nu/xom/Latin5Writer.java b/src/nu/xom/Latin5Writer.java new file mode 100644 index 0000000..4ccd355 --- /dev/null +++ b/src/nu/xom/Latin5Writer.java @@ -0,0 +1,199 @@ +/* Copyright 2002-2004 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom; + +import java.io.Writer; + +/** + *

    + * ISO 8859-9 for Turkish. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.0 + * + * + */ +class Latin5Writer extends TextWriter { + + Latin5Writer(Writer out, String encoding) { + super(out, encoding); + } + + /** + * @see nu.xom.TextWriter#needsEscaping(char) + */ + boolean needsEscaping(char c) { + if (c <= 0xCF) return false; + switch (c) { + case 0x00D0: return true; // place holder to enable table lookup + case 0x00D1: return false; // LATIN CAPITAL LETTER N WITH TILDE + case 0x00D2: return false; // LATIN CAPITAL LETTER O WITH GRAVE + case 0x00D3: return false; // LATIN CAPITAL LETTER O WITH ACUTE + case 0x00D4: return false; // LATIN CAPITAL LETTER O WITH CIRCUMFLEX + case 0x00D5: return false; // LATIN CAPITAL LETTER O WITH TILDE + case 0x00D6: return false; // LATIN CAPITAL LETTER O WITH DIAERESIS + case 0x00D7: return false; // MULTIPLICATION SIGN + case 0x00D8: return false; // LATIN CAPITAL LETTER O WITH STROKE + case 0x00D9: return false; // LATIN CAPITAL LETTER U WITH GRAVE + case 0x00DA: return false; // LATIN CAPITAL LETTER U WITH ACUTE + case 0x00DB: return false; // LATIN CAPITAL LETTER U WITH CIRCUMFLEX + case 0x00DC: return false; // LATIN CAPITAL LETTER U WITH DIAERESIS + case 0x00DD: return true; // place holder to enable table lookup + case 0x00DE: return true; // place holder to enable table lookup + case 0x00DF: return false; // LATIN SMALL LETTER SHARP S + case 0x00E0: return false; // LATIN SMALL LETTER A WITH GRAVE + case 0x00E1: return false; // LATIN SMALL LETTER A WITH ACUTE + case 0x00E2: return false; // LATIN SMALL LETTER A WITH CIRCUMFLEX + case 0x00E3: return false; // LATIN SMALL LETTER A WITH TILDE + case 0x00E4: return false; // LATIN SMALL LETTER A WITH DIAERESIS + case 0x00E5: return false; // LATIN SMALL LETTER A WITH RING ABOVE + case 0x00E6: return false; // LATIN SMALL LETTER AE + case 0x00E7: return false; // LATIN SMALL LETTER C WITH CEDILLA + case 0x00E8: return false; // LATIN SMALL LETTER E WITH GRAVE + case 0x00E9: return false; // LATIN SMALL LETTER E WITH ACUTE + case 0x00EA: return false; // LATIN SMALL LETTER E WITH CIRCUMFLEX + case 0x00EB: return false; // LATIN SMALL LETTER E WITH DIAERESIS + case 0x00EC: return false; // LATIN SMALL LETTER I WITH GRAVE + case 0x00ED: return false; // LATIN SMALL LETTER I WITH ACUTE + case 0x00EE: return false; // LATIN SMALL LETTER I WITH CIRCUMFLEX + case 0x00EF: return false; // LATIN SMALL LETTER I WITH DIAERESIS + case 0x00F0: return true; // place holder to enable table lookup + case 0x00F1: return false; // LATIN SMALL LETTER N WITH TILDE + case 0x00F2: return false; // LATIN SMALL LETTER O WITH GRAVE + case 0x00F3: return false; // LATIN SMALL LETTER O WITH ACUTE + case 0x00F4: return false; // LATIN SMALL LETTER O WITH CIRCUMFLEX + case 0x00F5: return false; // LATIN SMALL LETTER O WITH TILDE + case 0x00F6: return false; // LATIN SMALL LETTER O WITH DIAERESIS + case 0x00F7: return false; // DIVISION SIGN + case 0x00F8: return false; // LATIN SMALL LETTER O WITH STROKE + case 0x00F9: return false; // LATIN SMALL LETTER U WITH GRAVE + case 0x00FA: return false; // LATIN SMALL LETTER U WITH ACUTE + case 0x00FB: return false; // LATIN SMALL LETTER U WITH CIRCUMFLEX + case 0x00FC: return false; // LATIN SMALL LETTER U WITH DIAERESIS + case 0x00FD: return true; // place holder to enable table lookup + case 0x00FE: return true; // place holder to enable table lookup + case 0x00FF: return false; // LATIN SMALL LETTER Y WITH DIAERESIS + case 0x0100: return true; // place holder to enable table lookup + case 0x0101: return true; // place holder to enable table lookup + case 0x0102: return true; // place holder to enable table lookup + case 0x0103: return true; // place holder to enable table lookup + case 0x0104: return true; // place holder to enable table lookup + case 0x0105: return true; // place holder to enable table lookup + case 0x0106: return true; // place holder to enable table lookup + case 0x0107: return true; // place holder to enable table lookup + case 0x0108: return true; // place holder to enable table lookup + case 0x0109: return true; // place holder to enable table lookup + case 0x010A: return true; // place holder to enable table lookup + case 0x010B: return true; // place holder to enable table lookup + case 0x010C: return true; // place holder to enable table lookup + case 0x010D: return true; // place holder to enable table lookup + case 0x010E: return true; // place holder to enable table lookup + case 0x010F: return true; // place holder to enable table lookup + case 0x0110: return true; // place holder to enable table lookup + case 0x0111: return true; // place holder to enable table lookup + case 0x0112: return true; // place holder to enable table lookup + case 0x0113: return true; // place holder to enable table lookup + case 0x0114: return true; // place holder to enable table lookup + case 0x0115: return true; // place holder to enable table lookup + case 0x0116: return true; // place holder to enable table lookup + case 0x0117: return true; // place holder to enable table lookup + case 0x0118: return true; // place holder to enable table lookup + case 0x0119: return true; // place holder to enable table lookup + case 0x011A: return true; // place holder to enable table lookup + case 0x011B: return true; // place holder to enable table lookup + case 0x011C: return true; // place holder to enable table lookup + case 0x011D: return true; // place holder to enable table lookup + // Turkish letters + case 0x011E: return false; // LATIN CAPITAL LETTER G WITH BREVE + case 0x011F: return false; // LATIN SMALL LETTER G WITH BREVE + case 0x0120: return true; // place holder to enable table lookup + case 0x0121: return true; // place holder to enable table lookup + case 0x0122: return true; // place holder to enable table lookup + case 0x0123: return true; // place holder to enable table lookup + case 0x0124: return true; // place holder to enable table lookup + case 0x0125: return true; // place holder to enable table lookup + case 0x0126: return true; // place holder to enable table lookup + case 0x0127: return true; // place holder to enable table lookup + case 0x0128: return true; // place holder to enable table lookup + case 0x0129: return true; // place holder to enable table lookup + case 0x012A: return true; // place holder to enable table lookup + case 0x012B: return true; // place holder to enable table lookup + case 0x012C: return true; // place holder to enable table lookup + case 0x012D: return true; // place holder to enable table lookup + case 0x012E: return true; // place holder to enable table lookup + case 0x012F: return true; // place holder to enable table lookup + case 0x0130: return false; // LATIN CAPITAL LETTER I WITH DOT ABOVE + case 0x0131: return false; // LATIN SMALL LETTER DOTLESS I + case 0x0132: return true; // place holder to enable table lookup + case 0x0133: return true; // place holder to enable table lookup + case 0x0134: return true; // place holder to enable table lookup + case 0x0135: return true; // place holder to enable table lookup + case 0x0136: return true; // place holder to enable table lookup + case 0x0137: return true; // place holder to enable table lookup + case 0x0138: return true; // place holder to enable table lookup + case 0x0139: return true; // place holder to enable table lookup + case 0x013A: return true; // place holder to enable table lookup + case 0x013B: return true; // place holder to enable table lookup + case 0x013C: return true; // place holder to enable table lookup + case 0x013D: return true; // place holder to enable table lookup + case 0x013E: return true; // place holder to enable table lookup + case 0x013F: return true; // place holder to enable table lookup + case 0x0140: return true; // place holder to enable table lookup + case 0x0141: return true; // place holder to enable table lookup + case 0x0142: return true; // place holder to enable table lookup + case 0x0143: return true; // place holder to enable table lookup + case 0x0144: return true; // place holder to enable table lookup + case 0x0145: return true; // place holder to enable table lookup + case 0x0146: return true; // place holder to enable table lookup + case 0x0147: return true; // place holder to enable table lookup + case 0x0148: return true; // place holder to enable table lookup + case 0x0149: return true; // place holder to enable table lookup + case 0x014A: return true; // place holder to enable table lookup + case 0x014B: return true; // place holder to enable table lookup + case 0x014C: return true; // place holder to enable table lookup + case 0x014D: return true; // place holder to enable table lookup + case 0x014E: return true; // place holder to enable table lookup + case 0x014F: return true; // place holder to enable table lookup + case 0x0150: return true; // place holder to enable table lookup + case 0x0151: return true; // place holder to enable table lookup + case 0x0152: return true; // place holder to enable table lookup + case 0x0153: return true; // place holder to enable table lookup + case 0x0154: return true; // place holder to enable table lookup + case 0x0155: return true; // place holder to enable table lookup + case 0x0156: return true; // place holder to enable table lookup + case 0x0157: return true; // place holder to enable table lookup + case 0x0158: return true; // place holder to enable table lookup + case 0x0159: return true; // place holder to enable table lookup + case 0x015A: return true; // place holder to enable table lookup + case 0x015B: return true; // place holder to enable table lookup + case 0x015C: return true; // place holder to enable table lookup + case 0x015D: return true; // place holder to enable table lookup + case 0x015E: return false; // LATIN CAPITAL LETTER S WITH CEDILLA + case 0x015F: return false; // LATIN SMALL LETTER S WITH CEDILLA + } + + return true; + + } + +} \ No newline at end of file diff --git a/src/nu/xom/Latin6Writer.java b/src/nu/xom/Latin6Writer.java new file mode 100644 index 0000000..1df8053 --- /dev/null +++ b/src/nu/xom/Latin6Writer.java @@ -0,0 +1,278 @@ +/* Copyright 2002, 2003 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom; + +import java.io.Writer; + +/** + *

    + * ISO-8859-10, for Lithuanian, Estonian, Greenlandic, Icelandic, + * Inuit, Lappish, and other Northern European languages. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.0 + * + */ +class Latin6Writer extends TextWriter { + + Latin6Writer(Writer out, String encoding) { + super(out, encoding); + } + + /** + * @see nu.xom.TextWriter#needsEscaping(char) + */ + boolean needsEscaping(char c) { + if (c <= 0xA0) return false; + switch (c) { + case 0x00A1: return true; // place holder to enable table lookup + case 0x00A2: return true; // place holder to enable table lookup + case 0x00A3: return true; // place holder to enable table lookup + case 0x00A4: return true; // place holder to enable table lookup + case 0x00A5: return true; // place holder to enable table lookup + case 0x00A6: return true; // place holder to enable table lookup + case 0x00A7: return false; // SECTION SIGN + case 0x00A8: return true; // place holder to enable table lookup + case 0x00A9: return true; // place holder to enable table lookup + case 0x00AA: return true; // place holder to enable table lookup + case 0x00AB: return true; // place holder to enable table lookup + case 0x00AC: return true; // place holder to enable table lookup + case 0x00AD: return false; // SOFT HYPHEN + case 0x00AE: return true; // place holder to enable table lookup + case 0x00AF: return true; // place holder to enable table lookup + case 0x00B0: return false; // DEGREE SIGN + case 0x00B1: return true; // place holder to enable table lookup + case 0x00B2: return true; // place holder to enable table lookup + case 0x00B3: return true; // place holder to enable table lookup + case 0x00B4: return true; // place holder to enable table lookup + case 0x00B5: return true; // place holder to enable table lookup + case 0x00B6: return true; // place holder to enable table lookup + case 0x00B7: return false; // MIDDLE DOT + case 0x00B8: return true; // place holder to enable table lookup + case 0x00B9: return true; // place holder to enable table lookup + case 0x00BA: return true; // place holder to enable table lookup + case 0x00BB: return true; // place holder to enable table lookup + case 0x00BC: return true; // place holder to enable table lookup + case 0x00BD: return true; // place holder to enable table lookup + case 0x00BE: return true; // place holder to enable table lookup + case 0x00BF: return true; // place holder to enable table lookup + case 0x00C0: return true; // place holder to enable table lookup + case 0x00C1: return false; // LATIN CAPITAL LETTER A WITH ACUTE + case 0x00C2: return false; // LATIN CAPITAL LETTER A WITH CIRCUMFLEX + case 0x00C3: return false; // LATIN CAPITAL LETTER A WITH TILDE + case 0x00C4: return false; // LATIN CAPITAL LETTER A WITH DIAERESIS + case 0x00C5: return false; // LATIN CAPITAL LETTER A WITH RING ABOVE + case 0x00C6: return false; // LATIN CAPITAL LETTER AE + case 0x00C7: return true; // place holder to enable table lookup + case 0x00C8: return true; // place holder to enable table lookup + case 0x00C9: return false; // LATIN CAPITAL LETTER E WITH ACUTE + case 0x00CA: return true; // place holder to enable table lookup + case 0x00CB: return false; // LATIN CAPITAL LETTER E WITH DIAERESIS + case 0x00CC: return true; // place holder to enable table lookup + case 0x00CD: return false; // LATIN CAPITAL LETTER I WITH ACUTE + case 0x00CE: return false; // LATIN CAPITAL LETTER I WITH CIRCUMFLEX + case 0x00CF: return false; // LATIN CAPITAL LETTER I WITH DIAERESIS + case 0x00D0: return false; // LATIN CAPITAL LETTER ETH (Icelandic) + case 0x00D1: return true; // place holder to enable table lookup + case 0x00D2: return true; // place holder to enable table lookup + case 0x00D3: return false; // LATIN CAPITAL LETTER O WITH ACUTE + case 0x00D4: return false; // LATIN CAPITAL LETTER O WITH CIRCUMFLEX + case 0x00D5: return false; // LATIN CAPITAL LETTER O WITH TILDE + case 0x00D6: return false; // LATIN CAPITAL LETTER O WITH DIAERESIS + case 0x00D7: return true; // place holder to enable table lookup + case 0x00D8: return false; // LATIN CAPITAL LETTER O WITH STROKE + case 0x00D9: return true; // place holder to enable table lookup + case 0x00DA: return false; // LATIN CAPITAL LETTER U WITH ACUTE + case 0x00DB: return false; // LATIN CAPITAL LETTER U WITH CIRCUMFLEX + case 0x00DC: return false; // LATIN CAPITAL LETTER U WITH DIAERESIS + case 0x00DD: return false; // LATIN CAPITAL LETTER Y WITH ACUTE + case 0x00DE: return false; // LATIN CAPITAL LETTER THORN (Icelandic) + case 0x00DF: return false; // LATIN SMALL LETTER SHARP S (German) + case 0x00E0: return true; // place holder to enable table lookup + case 0x00E1: return false; // LATIN SMALL LETTER A WITH ACUTE + case 0x00E2: return false; // LATIN SMALL LETTER A WITH CIRCUMFLEX + case 0x00E3: return false; // LATIN SMALL LETTER A WITH TILDE + case 0x00E4: return false; // LATIN SMALL LETTER A WITH DIAERESIS + case 0x00E5: return false; // LATIN SMALL LETTER A WITH RING ABOVE + case 0x00E6: return false; // LATIN SMALL LETTER AE + case 0x00E7: return true; // place holder to enable table lookup + case 0x00E8: return true; // place holder to enable table lookup + case 0x00E9: return false; // LATIN SMALL LETTER E WITH ACUTE + case 0x00EA: return true; // place holder to enable table lookup + case 0x00EB: return false; // LATIN SMALL LETTER E WITH DIAERESIS + case 0x00EC: return true; // place holder to enable table lookup + case 0x00ED: return false; // LATIN SMALL LETTER I WITH ACUTE + case 0x00EE: return false; // LATIN SMALL LETTER I WITH CIRCUMFLEX + case 0x00EF: return false; // LATIN SMALL LETTER I WITH DIAERESIS + case 0x00F0: return false; // LATIN SMALL LETTER ETH (Icelandic) + case 0x00F1: return true; // place holder to enable table lookup + case 0x00F2: return true; // place holder to enable table lookup + case 0x00F3: return false; // LATIN SMALL LETTER O WITH ACUTE + case 0x00F4: return false; // LATIN SMALL LETTER O WITH CIRCUMFLEX + case 0x00F5: return false; // LATIN SMALL LETTER O WITH TILDE + case 0x00F6: return false; // LATIN SMALL LETTER O WITH DIAERESIS + case 0x00F7: return true; // place holder to enable table lookup + case 0x00F8: return false; // LATIN SMALL LETTER O WITH STROKE + case 0x00F9: return true; // place holder to enable table lookup + case 0x00FA: return false; // LATIN SMALL LETTER U WITH ACUTE + case 0x00FB: return false; // LATIN SMALL LETTER U WITH CIRCUMFLEX + case 0x00FC: return false; // LATIN SMALL LETTER U WITH DIAERESIS + case 0x00FD: return false; // LATIN SMALL LETTER Y WITH ACUTE + case 0x00FE: return false; // LATIN SMALL LETTER THORN (Icelandic) + case 0x00FF: return true; // place holder to enable table lookup + case 0x0100: return false; // LATIN CAPITAL LETTER A WITH MACRON + case 0x0101: return false; // LATIN SMALL LETTER A WITH MACRON + case 0x0102: return true; // place holder to enable table lookup + case 0x0103: return true; // place holder to enable table lookup + case 0x0104: return false; // LATIN CAPITAL LETTER A WITH OGONEK + case 0x0105: return false; // LATIN SMALL LETTER A WITH OGONEK + case 0x0106: return true; // place holder to enable table lookup + case 0x0107: return true; // place holder to enable table lookup + case 0x0108: return true; // place holder to enable table lookup + case 0x0109: return true; // place holder to enable table lookup + case 0x010A: return true; // place holder to enable table lookup + case 0x010B: return true; // place holder to enable table lookup + case 0x010C: return false; // LATIN CAPITAL LETTER C WITH CARON + case 0x010D: return false; // LATIN SMALL LETTER C WITH CARON + case 0x010E: return true; // place holder to enable table lookup + case 0x010F: return true; // place holder to enable table lookup + case 0x0110: return false; // LATIN CAPITAL LETTER D WITH STROKE + case 0x0111: return false; // LATIN SMALL LETTER D WITH STROKE + case 0x0112: return false; // LATIN CAPITAL LETTER E WITH MACRON + case 0x0113: return false; // LATIN SMALL LETTER E WITH MACRON + case 0x0114: return true; // place holder to enable table lookup + case 0x0115: return true; // place holder to enable table lookup + case 0x0116: return false; // LATIN CAPITAL LETTER E WITH DOT ABOVE + case 0x0117: return false; // LATIN SMALL LETTER E WITH DOT ABOVE + case 0x0118: return false; // LATIN CAPITAL LETTER E WITH OGONEK + case 0x0119: return false; // LATIN SMALL LETTER E WITH OGONEK + case 0x011A: return true; // place holder to enable table lookup + case 0x011B: return true; // place holder to enable table lookup + case 0x011C: return true; // place holder to enable table lookup + case 0x011D: return true; // place holder to enable table lookup + case 0x011E: return true; // place holder to enable table lookup + case 0x011F: return true; // place holder to enable table lookup + case 0x0120: return true; // place holder to enable table lookup + case 0x0121: return true; // place holder to enable table lookup + case 0x0122: return false; // LATIN CAPITAL LETTER G WITH CEDILLA + case 0x0123: return false; // LATIN SMALL LETTER G WITH CEDILLA + case 0x0124: return true; // place holder to enable table lookup + case 0x0125: return true; // place holder to enable table lookup + case 0x0126: return true; // place holder to enable table lookup + case 0x0127: return true; // place holder to enable table lookup + case 0x0128: return false; // LATIN CAPITAL LETTER I WITH TILDE + case 0x0129: return false; // LATIN SMALL LETTER I WITH TILDE + case 0x012A: return false; // LATIN CAPITAL LETTER I WITH MACRON + case 0x012B: return false; // LATIN SMALL LETTER I WITH MACRON + case 0x012C: return true; // place holder to enable table lookup + case 0x012D: return true; // place holder to enable table lookup + case 0x012E: return false; // LATIN CAPITAL LETTER I WITH OGONEK + case 0x012F: return false; // LATIN SMALL LETTER I WITH OGONEK + case 0x0130: return true; // place holder to enable table lookup + case 0x0131: return true; // place holder to enable table lookup + case 0x0132: return true; // place holder to enable table lookup + case 0x0133: return true; // place holder to enable table lookup + case 0x0134: return true; // place holder to enable table lookup + case 0x0135: return true; // place holder to enable table lookup + case 0x0136: return false; // LATIN CAPITAL LETTER K WITH CEDILLA + case 0x0137: return false; // LATIN SMALL LETTER K WITH CEDILLA + case 0x0138: return false; // LATIN SMALL LETTER KRA + case 0x0139: return true; // place holder to enable table lookup + case 0x013A: return true; // place holder to enable table lookup + case 0x013B: return false; // LATIN CAPITAL LETTER L WITH CEDILLA + case 0x013C: return false; // LATIN SMALL LETTER L WITH CEDILLA + case 0x013D: return true; // place holder to enable table lookup + case 0x013E: return true; // place holder to enable table lookup + case 0x013F: return true; // place holder to enable table lookup + case 0x0140: return true; // place holder to enable table lookup + case 0x0141: return true; // place holder to enable table lookup + case 0x0142: return true; // place holder to enable table lookup + case 0x0143: return true; // place holder to enable table lookup + case 0x0144: return true; // place holder to enable table lookup + case 0x0145: return false; // LATIN CAPITAL LETTER N WITH CEDILLA + case 0x0146: return false; // LATIN SMALL LETTER N WITH CEDILLA + case 0x0147: return true; // place holder to enable table lookup + case 0x0148: return true; // place holder to enable table lookup + case 0x0149: return true; // place holder to enable table lookup + case 0x014A: return false; // LATIN CAPITAL LETTER ENG + case 0x014B: return false; // LATIN SMALL LETTER ENG + case 0x014C: return false; // LATIN CAPITAL LETTER O WITH MACRON + case 0x014D: return false; // LATIN SMALL LETTER O WITH MACRON + case 0x014E: return true; // place holder to enable table lookup + case 0x014F: return true; // place holder to enable table lookup + case 0x0150: return true; // place holder to enable table lookup + case 0x0151: return true; // place holder to enable table lookup + case 0x0152: return true; // place holder to enable table lookup + case 0x0153: return true; // place holder to enable table lookup + case 0x0154: return true; // place holder to enable table lookup + case 0x0155: return true; // place holder to enable table lookup + case 0x0156: return true; // place holder to enable table lookup + case 0x0157: return true; // place holder to enable table lookup + case 0x0158: return true; // place holder to enable table lookup + case 0x0159: return true; // place holder to enable table lookup + case 0x015A: return true; // place holder to enable table lookup + case 0x015B: return true; // place holder to enable table lookup + case 0x015C: return true; // place holder to enable table lookup + case 0x015D: return true; // place holder to enable table lookup + case 0x015E: return true; // place holder to enable table lookup + case 0x015F: return true; // place holder to enable table lookup + case 0x0160: return false; // LATIN CAPITAL LETTER S WITH CARON + case 0x0161: return false; // LATIN SMALL LETTER S WITH CARON + case 0x0162: return true; // place holder to enable table lookup + case 0x0163: return true; // place holder to enable table lookup + case 0x0164: return true; // place holder to enable table lookup + case 0x0165: return true; // place holder to enable table lookup + case 0x0166: return false; // LATIN CAPITAL LETTER T WITH STROKE + case 0x0167: return false; // LATIN SMALL LETTER T WITH STROKE + case 0x0168: return false; // LATIN CAPITAL LETTER U WITH TILDE + case 0x0169: return false; // LATIN SMALL LETTER U WITH TILDE + case 0x016A: return false; // LATIN CAPITAL LETTER U WITH MACRON + case 0x016B: return false; // LATIN SMALL LETTER U WITH MACRON + case 0x016C: return true; // place holder to enable table lookup + case 0x016D: return true; // place holder to enable table lookup + case 0x016E: return true; // place holder to enable table lookup + case 0x016F: return true; // place holder to enable table lookup + case 0x0170: return true; // place holder to enable table lookup + case 0x0171: return true; // place holder to enable table lookup + case 0x0172: return false; // LATIN CAPITAL LETTER U WITH OGONEK + case 0x0173: return false; // LATIN SMALL LETTER U WITH OGONEK + case 0x0174: return true; // place holder to enable table lookup + case 0x0175: return true; // place holder to enable table lookup + case 0x0176: return true; // place holder to enable table lookup + case 0x0177: return true; // place holder to enable table lookup + case 0x0178: return true; // place holder to enable table lookup + case 0x0179: return true; // place holder to enable table lookup + case 0x017A: return true; // place holder to enable table lookup + case 0x017B: return true; // place holder to enable table lookup + case 0x017C: return true; // place holder to enable table lookup + case 0x017D: return false; // LATIN CAPITAL LETTER Z WITH CARON + case 0x017E: return false; // LATIN SMALL LETTER Z WITH CARON + } + + if (c == 0x2015) return false; // HORIZONTAL BAR + + return true; + + } + +} \ No newline at end of file diff --git a/src/nu/xom/Latin7Writer.java b/src/nu/xom/Latin7Writer.java new file mode 100644 index 0000000..fb55d1a --- /dev/null +++ b/src/nu/xom/Latin7Writer.java @@ -0,0 +1,283 @@ +/* Copyright 2002, 2003 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom; + +import java.io.Writer; + +/** + *

    + * ISO-8859-13, for Latvian and other Baltic languages. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.0 + * + */ +class Latin7Writer extends TextWriter { + + Latin7Writer(Writer out, String encoding) { + super(out, encoding); + } + + /** + * @see nu.xom.TextWriter#needsEscaping(char) + */ + boolean needsEscaping(char c) { + if (c <= 0xA0) return false; + switch (c) { // Latin-1 overlap + case 0x00A1: return true; // place holder to enable table lookup + case 0x00A2: return false; // CENT SIGN + case 0x00A3: return false; // POUND SIGN + case 0x00A4: return false; // CURRENCY SIGN + case 0x00A5: return true; // place holder to enable table lookup + case 0x00A6: return false; // BROKEN BAR + case 0x00A7: return false; // SECTION SIGN + case 0x00A8: return true; // place holder to enable table lookup + case 0x00A9: return false; // COPYRIGHT SIGN + case 0x00AA: return true; // place holder to enable table lookup + case 0x00AB: return false; // LEFT-POINTING DOUBLE ANGLE QUOTATION MARK + case 0x00AC: return false; // NOT SIGN + case 0x00AD: return false; // SOFT HYPHEN + case 0x00AE: return false; // REGISTERED SIGN + case 0x00AF: return true; // place holder to enable table lookup + case 0x00B0: return false; // DEGREE SIGN + case 0x00B1: return false; // PLUS-MINUS SIGN + case 0x00B2: return false; // SUPERSCRIPT TWO + case 0x00B3: return false; // SUPERSCRIPT THREE + case 0x00B4: return true; // place holder to enable table lookup + case 0x00B5: return false; // MICRO SIGN + case 0x00B6: return false; // PILCROW SIGN + case 0x00B7: return false; // MIDDLE DOT + case 0x00B8: return true; // place holder to enable table lookup + case 0x00B9: return false; // SUPERSCRIPT ONE + case 0x00BA: return true; // place holder to enable table lookup + case 0x00BB: return false; // RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK + case 0x00BC: return false; // VULGAR FRACTION ONE QUARTER + case 0x00BD: return false; // VULGAR FRACTION ONE HALF + case 0x00BE: return false; // VULGAR FRACTION THREE QUARTERS + case 0x00BF: return true; // place holder to enable table lookup + case 0x00C0: return true; // place holder to enable table lookup + case 0x00C1: return true; // place holder to enable table lookup + case 0x00C2: return true; // place holder to enable table lookup + case 0x00C3: return true; // place holder to enable table lookup + case 0x00C4: return false; // LATIN CAPITAL LETTER A WITH DIAERESIS + case 0x00C5: return false; // LATIN CAPITAL LETTER A WITH RING ABOVE + case 0x00C6: return false; // LATIN CAPITAL LETTER AE + case 0x00C7: return true; // place holder to enable table lookup + case 0x00C8: return true; // place holder to enable table lookup + case 0x00C9: return false; // LATIN CAPITAL LETTER E WITH ACUTE + case 0x00CA: return true; // place holder to enable table lookup + case 0x00CB: return true; // place holder to enable table lookup + case 0x00CC: return true; // place holder to enable table lookup + case 0x00CD: return true; // place holder to enable table lookup + case 0x00CE: return true; // place holder to enable table lookup + case 0x00CF: return true; // place holder to enable table lookup + case 0x00D0: return true; // place holder to enable table lookup + case 0x00D1: return true; // place holder to enable table lookup + case 0x00D2: return true; // place holder to enable table lookup + case 0x00D3: return false; // LATIN CAPITAL LETTER O WITH ACUTE + case 0x00D4: return true; // place holder to enable table lookup + case 0x00D5: return false; // LATIN CAPITAL LETTER O WITH TILDE + case 0x00D6: return false; // LATIN CAPITAL LETTER O WITH DIAERESIS + case 0x00D7: return false; // MULTIPLICATION SIGN + case 0x00D8: return false; // LATIN CAPITAL LETTER O WITH STROKE + case 0x00D9: return true; // place holder to enable table lookup + case 0x00DA: return true; // place holder to enable table lookup + case 0x00DB: return true; // place holder to enable table lookup + case 0x00DC: return false; // LATIN CAPITAL LETTER U WITH DIAERESIS + case 0x00DD: return true; // place holder to enable table lookup + case 0x00DE: return true; // place holder to enable table lookup + case 0x00DF: return false; // LATIN SMALL LETTER SHARP S (German) + case 0x00E0: return true; // place holder to enable table lookup + case 0x00E1: return true; // place holder to enable table lookup + case 0x00E2: return true; // place holder to enable table lookup + case 0x00E3: return true; // place holder to enable table lookup + case 0x00E4: return false; // LATIN SMALL LETTER A WITH DIAERESIS + case 0x00E5: return false; // LATIN SMALL LETTER A WITH RING ABOVE + case 0x00E6: return false; // LATIN SMALL LETTER AE + case 0x00E7: return true; // place holder to enable table lookup + case 0x00E8: return true; // place holder to enable table lookup + case 0x00E9: return false; // LATIN SMALL LETTER E WITH ACUTE + case 0x00EA: return true; // place holder to enable table lookup + case 0x00EB: return true; // place holder to enable table lookup + case 0x00EC: return true; // place holder to enable table lookup + case 0x00ED: return true; // place holder to enable table lookup + case 0x00EE: return true; // place holder to enable table lookup + case 0x00EF: return true; // place holder to enable table lookup + case 0x00F0: return true; // place holder to enable table lookup + case 0x00F1: return true; // place holder to enable table lookup + case 0x00F2: return true; // place holder to enable table lookup + case 0x00F3: return false; // LATIN SMALL LETTER O WITH ACUTE + case 0x00F4: return true; // place holder to enable table lookup + case 0x00F5: return false; // LATIN SMALL LETTER O WITH TILDE + case 0x00F6: return false; // LATIN SMALL LETTER O WITH DIAERESIS + case 0x00F7: return false; // DIVISION SIGN + case 0x00F8: return false; // LATIN SMALL LETTER O WITH STROKE + case 0x00F9: return true; // place holder to enable table lookup + case 0x00FA: return true; // place holder to enable table lookup + case 0x00FB: return true; // place holder to enable table lookup + case 0x00FC: return false; // LATIN SMALL LETTER U WITH DIAERESIS + case 0x00FD: return true; // place holder to enable table lookup + case 0x00FE: return true; // place holder to enable table lookup + case 0x00FF: return true; // place holder to enable table lookup + case 0x0100: return false; // LATIN CAPITAL LETTER A WITH MACRON + case 0x0101: return false; // LATIN SMALL LETTER A WITH MACRON + case 0x0102: return true; // place holder to enable table lookup + case 0x0103: return true; // place holder to enable table lookup + case 0x0104: return false; // LATIN CAPITAL LETTER A WITH OGONEK + case 0x0105: return false; // LATIN SMALL LETTER A WITH OGONEK + case 0x0106: return false; // LATIN CAPITAL LETTER C WITH ACUTE + case 0x0107: return false; // LATIN SMALL LETTER C WITH ACUTE + case 0x0108: return true; // place holder to enable table lookup + case 0x0109: return true; // place holder to enable table lookup + case 0x010A: return true; // place holder to enable table lookup + case 0x010B: return true; // place holder to enable table lookup + case 0x010C: return false; // LATIN CAPITAL LETTER C WITH CARON + case 0x010D: return false; // LATIN SMALL LETTER C WITH CARON + case 0x010E: return true; // place holder to enable table lookup + case 0x010F: return true; // place holder to enable table lookup + case 0x0110: return true; // place holder to enable table lookup + case 0x0111: return true; // place holder to enable table lookup + case 0x0112: return false; // LATIN CAPITAL LETTER E WITH MACRON + case 0x0113: return false; // LATIN SMALL LETTER E WITH MACRON + case 0x0114: return true; // place holder to enable table lookup + case 0x0115: return true; // place holder to enable table lookup + case 0x0116: return false; // LATIN CAPITAL LETTER E WITH DOT ABOVE + case 0x0117: return false; // LATIN SMALL LETTER E WITH DOT ABOVE + case 0x0118: return false; // LATIN CAPITAL LETTER E WITH OGONEK + case 0x0119: return false; // LATIN SMALL LETTER E WITH OGONEK + case 0x011A: return true; // place holder to enable table lookup + case 0x011B: return true; // place holder to enable table lookup + case 0x011C: return true; // place holder to enable table lookup + case 0x011D: return true; // place holder to enable table lookup + case 0x011E: return true; // place holder to enable table lookup + case 0x011F: return true; // place holder to enable table lookup + case 0x0120: return true; // place holder to enable table lookup + case 0x0121: return true; // place holder to enable table lookup + case 0x0122: return false; // LATIN CAPITAL LETTER G WITH CEDILLA + case 0x0123: return false; // LATIN SMALL LETTER G WITH CEDILLA + case 0x0124: return true; // place holder to enable table lookup + case 0x0125: return true; // place holder to enable table lookup + case 0x0126: return true; // place holder to enable table lookup + case 0x0127: return true; // place holder to enable table lookup + case 0x0128: return true; // place holder to enable table lookup + case 0x0129: return true; // place holder to enable table lookup + case 0x012A: return false; // LATIN CAPITAL LETTER I WITH MACRON + case 0x012B: return false; // LATIN SMALL LETTER I WITH MACRON + case 0x012C: return true; // place holder to enable table lookup + case 0x012D: return true; // place holder to enable table lookup + case 0x012E: return false; // LATIN CAPITAL LETTER I WITH OGONEK + case 0x012F: return false; // LATIN SMALL LETTER I WITH OGONEK + case 0x0130: return true; // place holder to enable table lookup + case 0x0131: return true; // place holder to enable table lookup + case 0x0132: return true; // place holder to enable table lookup + case 0x0133: return true; // place holder to enable table lookup + case 0x0134: return true; // place holder to enable table lookup + case 0x0135: return true; // place holder to enable table lookup + case 0x0136: return false; // LATIN CAPITAL LETTER K WITH CEDILLA + case 0x0137: return false; // LATIN SMALL LETTER K WITH CEDILLA + case 0x0138: return true; // place holder to enable table lookup + case 0x0139: return true; // place holder to enable table lookup + case 0x013A: return true; // place holder to enable table lookup + case 0x013B: return false; // LATIN CAPITAL LETTER L WITH CEDILLA + case 0x013C: return false; // LATIN SMALL LETTER L WITH CEDILLA + case 0x013D: return true; // place holder to enable table lookup + case 0x013E: return true; // place holder to enable table lookup + case 0x013F: return true; // place holder to enable table lookup + case 0x0140: return true; // place holder to enable table lookup + case 0x0141: return false; // LATIN CAPITAL LETTER L WITH STROKE + case 0x0142: return false; // LATIN SMALL LETTER L WITH STROKE + case 0x0143: return false; // LATIN CAPITAL LETTER N WITH ACUTE + case 0x0144: return false; // LATIN SMALL LETTER N WITH ACUTE + case 0x0145: return false; // LATIN CAPITAL LETTER N WITH CEDILLA + case 0x0146: return false; // LATIN SMALL LETTER N WITH CEDILLA + case 0x0147: return true; // place holder to enable table lookup + case 0x0148: return true; // place holder to enable table lookup + case 0x0149: return true; // place holder to enable table lookup + case 0x014A: return true; // place holder to enable table lookup + case 0x014B: return true; // place holder to enable table lookup + case 0x014C: return false; // LATIN CAPITAL LETTER O WITH MACRON + case 0x014D: return false; // LATIN SMALL LETTER O WITH MACRON + case 0x014E: return true; // place holder to enable table lookup + case 0x014F: return true; // place holder to enable table lookup + case 0x0150: return true; // place holder to enable table lookup + case 0x0151: return true; // place holder to enable table lookup + case 0x0152: return true; // place holder to enable table lookup + case 0x0153: return true; // place holder to enable table lookup + case 0x0154: return true; // place holder to enable table lookup + case 0x0155: return true; // place holder to enable table lookup + case 0x0156: return false; // LATIN CAPITAL LETTER R WITH CEDILLA + case 0x0157: return false; // LATIN SMALL LETTER R WITH CEDILLA + case 0x0158: return true; // place holder to enable table lookup + case 0x0159: return true; // place holder to enable table lookup + case 0x015A: return false; // LATIN CAPITAL LETTER S WITH ACUTE + case 0x015B: return false; // LATIN SMALL LETTER S WITH ACUTE + case 0x015C: return true; // place holder to enable table lookup + case 0x015D: return true; // place holder to enable table lookup + case 0x015E: return true; // place holder to enable table lookup + case 0x015F: return true; // place holder to enable table lookup + case 0x0160: return false; // LATIN CAPITAL LETTER S WITH CARON + case 0x0161: return false; // LATIN SMALL LETTER S WITH CARON + case 0x0162: return true; // place holder to enable table lookup + case 0x0163: return true; // place holder to enable table lookup + case 0x0164: return true; // place holder to enable table lookup + case 0x0165: return true; // place holder to enable table lookup + case 0x0166: return true; // place holder to enable table lookup + case 0x0167: return true; // place holder to enable table lookup + case 0x0168: return true; // place holder to enable table lookup + case 0x0169: return true; // place holder to enable table lookup + case 0x016A: return false; // LATIN CAPITAL LETTER U WITH MACRON + case 0x016B: return false; // LATIN SMALL LETTER U WITH MACRON + case 0x016C: return true; // place holder to enable table lookup + case 0x016D: return true; // place holder to enable table lookup + case 0x016E: return true; // place holder to enable table lookup + case 0x016F: return true; // place holder to enable table lookup + case 0x0170: return true; // place holder to enable table lookup + case 0x0171: return true; // place holder to enable table lookup + case 0x0172: return false; // LATIN CAPITAL LETTER U WITH OGONEK + case 0x0173: return false; // LATIN SMALL LETTER U WITH OGONEK + case 0x0174: return true; // place holder to enable table lookup + case 0x0175: return true; // place holder to enable table lookup + case 0x0176: return true; // place holder to enable table lookup + case 0x0177: return true; // place holder to enable table lookup + case 0x0178: return true; // place holder to enable table lookup + case 0x0179: return false; // LATIN CAPITAL LETTER Z WITH ACUTE + case 0x017A: return false; // LATIN SMALL LETTER Z WITH ACUTE + case 0x017B: return false; // LATIN CAPITAL LETTER Z WITH DOT ABOVE + case 0x017C: return false; // LATIN SMALL LETTER Z WITH DOT ABOVE + case 0x017D: return false; // LATIN CAPITAL LETTER Z WITH CARON + case 0x017E: return false; // LATIN SMALL LETTER Z WITH CARON + } + switch (c) { + case 0x2019: return false; // RIGHT SINGLE QUOTATION MARK + case 0x201A: return true; // place holder to enable table lookup + case 0x201B: return true; // place holder to enable table lookup + case 0x201C: return false; // LEFT DOUBLE QUOTATION MARK + case 0x201D: return false; // RIGHT DOUBLE QUOTATION MARK + case 0x201E: return false; // DOUBLE LOW-9 QUOTATION MARK + } + + return true; + + } + +} \ No newline at end of file diff --git a/src/nu/xom/Latin8Writer.java b/src/nu/xom/Latin8Writer.java new file mode 100644 index 0000000..5cb366f --- /dev/null +++ b/src/nu/xom/Latin8Writer.java @@ -0,0 +1,326 @@ +/* Copyright 2002, 2003 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom; + +import java.io.Writer; + +/** + *

    + * ISO-8859-14, for Gaelic, Welsh, and other Celtic languages. + * Not yet supported by Sun's JDK as of 1.5 alpha. IBM's 1.4.1 + * JDK does support it. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.0 + * + */ +class Latin8Writer extends TextWriter { + + Latin8Writer(Writer out, String encoding) { + super(out, encoding); + } + + /** + * @see nu.xom.TextWriter#needsEscaping(char) + */ + boolean needsEscaping(char c) { + if (c <= 0xA0) return false; + switch (c) { // Latin-1 overlap + case 0x00A3: return false; // POUND SIGN + case 0x00A4: return true; // place holder to enable table lookup + case 0x00A5: return true; // place holder to enable table lookup + case 0x00A6: return true; // place holder to enable table lookup + case 0x00A7: return false; // SECTION SIGN + case 0x00A8: return true; // place holder to enable table lookup + case 0x00A9: return false; // COPYRIGHT SIGN + case 0x00AA: return true; // place holder to enable table lookup + case 0x00AB: return true; // place holder to enable table lookup + case 0x00AC: return true; // place holder to enable table lookup + case 0x00AD: return false; // SOFT HYPHEN + case 0x00AE: return false; // REGISTERED SIGN + case 0x00AF: return true; // place holder to enable table lookup + case 0x00B0: return true; // place holder to enable table lookup + case 0x00B1: return true; // place holder to enable table lookup + case 0x00B2: return true; // place holder to enable table lookup + case 0x00B3: return true; // place holder to enable table lookup + case 0x00B4: return true; // place holder to enable table lookup + case 0x00B5: return true; // place holder to enable table lookup + case 0x00B6: return false; // PILCROW SIGN + case 0x00B7: return true; // place holder to enable table lookup + case 0x00B8: return true; // place holder to enable table lookup + case 0x00B9: return true; // place holder to enable table lookup + case 0x00BA: return true; // place holder to enable table lookup + case 0x00BB: return true; // place holder to enable table lookup + case 0x00BC: return true; // place holder to enable table lookup + case 0x00BD: return true; // place holder to enable table lookup + case 0x00BE: return true; // place holder to enable table lookup + case 0x00BF: return true; // place holder to enable table lookup + case 0x00C0: return false; // LATIN CAPITAL LETTER A WITH GRAVE + case 0x00C1: return false; // LATIN CAPITAL LETTER A WITH ACUTE + case 0x00C2: return false; // LATIN CAPITAL LETTER A WITH CIRCUMFLEX + case 0x00C3: return false; // LATIN CAPITAL LETTER A WITH TILDE + case 0x00C4: return false; // LATIN CAPITAL LETTER A WITH DIAERESIS + case 0x00C5: return false; // LATIN CAPITAL LETTER A WITH RING ABOVE + case 0x00C6: return false; // LATIN CAPITAL LETTER AE + case 0x00C7: return false; // LATIN CAPITAL LETTER C WITH CEDILLA + case 0x00C8: return false; // LATIN CAPITAL LETTER E WITH GRAVE + case 0x00C9: return false; // LATIN CAPITAL LETTER E WITH ACUTE + case 0x00CA: return false; // LATIN CAPITAL LETTER E WITH CIRCUMFLEX + case 0x00CB: return false; // LATIN CAPITAL LETTER E WITH DIAERESIS + case 0x00CC: return false; // LATIN CAPITAL LETTER I WITH GRAVE + case 0x00CD: return false; // LATIN CAPITAL LETTER I WITH ACUTE + case 0x00CE: return false; // LATIN CAPITAL LETTER I WITH CIRCUMFLEX + case 0x00CF: return false; // LATIN CAPITAL LETTER I WITH DIAERESIS + case 0x00D0: return true; // place holder to enable table lookup + case 0x00D1: return false; // LATIN CAPITAL LETTER N WITH TILDE + case 0x00D2: return false; // LATIN CAPITAL LETTER O WITH GRAVE + case 0x00D3: return false; // LATIN CAPITAL LETTER O WITH ACUTE + case 0x00D4: return false; // LATIN CAPITAL LETTER O WITH CIRCUMFLEX + case 0x00D5: return false; // LATIN CAPITAL LETTER O WITH TILDE + case 0x00D6: return false; // LATIN CAPITAL LETTER O WITH DIAERESIS + case 0x00D7: return true; // place holder to enable table lookup + case 0x00D8: return false; // LATIN CAPITAL LETTER O WITH STROKE + case 0x00D9: return false; // LATIN CAPITAL LETTER U WITH GRAVE + case 0x00DA: return false; // LATIN CAPITAL LETTER U WITH ACUTE + case 0x00DB: return false; // LATIN CAPITAL LETTER U WITH CIRCUMFLEX + case 0x00DC: return false; // LATIN CAPITAL LETTER U WITH DIAERESIS + case 0x00DD: return false; // LATIN CAPITAL LETTER Y WITH ACUTE + case 0x00DE: return true; // place holder to enable table lookup + case 0x00DF: return false; // LATIN SMALL LETTER SHARP S + case 0x00E0: return false; // LATIN SMALL LETTER A WITH GRAVE + case 0x00E1: return false; // LATIN SMALL LETTER A WITH ACUTE + case 0x00E2: return false; // LATIN SMALL LETTER A WITH CIRCUMFLEX + case 0x00E3: return false; // LATIN SMALL LETTER A WITH TILDE + case 0x00E4: return false; // LATIN SMALL LETTER A WITH DIAERESIS + case 0x00E5: return false; // LATIN SMALL LETTER A WITH RING ABOVE + case 0x00E6: return false; // LATIN SMALL LETTER AE + case 0x00E7: return false; // LATIN SMALL LETTER C WITH CEDILLA + case 0x00E8: return false; // LATIN SMALL LETTER E WITH GRAVE + case 0x00E9: return false; // LATIN SMALL LETTER E WITH ACUTE + case 0x00EA: return false; // LATIN SMALL LETTER E WITH CIRCUMFLEX + case 0x00EB: return false; // LATIN SMALL LETTER E WITH DIAERESIS + case 0x00EC: return false; // LATIN SMALL LETTER I WITH GRAVE + case 0x00ED: return false; // LATIN SMALL LETTER I WITH ACUTE + case 0x00EE: return false; // LATIN SMALL LETTER I WITH CIRCUMFLEX + case 0x00EF: return false; // LATIN SMALL LETTER I WITH DIAERESIS + case 0x00F0: return true; // place holder to enable table lookup + case 0x00F1: return false; // LATIN SMALL LETTER N WITH TILDE + case 0x00F2: return false; // LATIN SMALL LETTER O WITH GRAVE + case 0x00F3: return false; // LATIN SMALL LETTER O WITH ACUTE + case 0x00F4: return false; // LATIN SMALL LETTER O WITH CIRCUMFLEX + case 0x00F5: return false; // LATIN SMALL LETTER O WITH TILDE + case 0x00F6: return false; // LATIN SMALL LETTER O WITH DIAERESIS + case 0x00F8: return false; // LATIN SMALL LETTER O WITH STROKE + case 0x00F9: return false; // LATIN SMALL LETTER U WITH GRAVE + case 0x00FA: return false; // LATIN SMALL LETTER U WITH ACUTE + case 0x00FB: return false; // LATIN SMALL LETTER U WITH CIRCUMFLEX + case 0x00FC: return false; // LATIN SMALL LETTER U WITH DIAERESIS + case 0x00FD: return false; // LATIN SMALL LETTER Y WITH ACUTE + case 0x00FE: return true; // place holder to enable table lookup + case 0x00FF: return false; // LATIN SMALL LETTER Y WITH DIAERESIS + case 0x0100: return true; // place holder to enable table lookup + case 0x0101: return true; // place holder to enable table lookup + case 0x0102: return true; // place holder to enable table lookup + case 0x0103: return true; // place holder to enable table lookup + case 0x0104: return true; // place holder to enable table lookup + case 0x0105: return true; // place holder to enable table lookup + case 0x0106: return true; // place holder to enable table lookup + case 0x0107: return true; // place holder to enable table lookup + case 0x0108: return true; // place holder to enable table lookup + case 0x0109: return true; // place holder to enable table lookup + case 0x010A: return false; // LATIN CAPITAL LETTER C WITH DOT ABOVE + case 0x010B: return false; // LATIN SMALL LETTER C WITH DOT ABOVE + case 0x010C: return true; // place holder to enable table lookup + case 0x010D: return true; // place holder to enable table lookup + case 0x010E: return true; // place holder to enable table lookup + case 0x010F: return true; // place holder to enable table lookup + case 0x0110: return true; // place holder to enable table lookup + case 0x0111: return true; // place holder to enable table lookup + case 0x0112: return true; // place holder to enable table lookup + case 0x0113: return true; // place holder to enable table lookup + case 0x0114: return true; // place holder to enable table lookup + case 0x0115: return true; // place holder to enable table lookup + case 0x0116: return true; // place holder to enable table lookup + case 0x0117: return true; // place holder to enable table lookup + case 0x0118: return true; // place holder to enable table lookup + case 0x0119: return true; // place holder to enable table lookup + case 0x011A: return true; // place holder to enable table lookup + case 0x011B: return true; // place holder to enable table lookup + case 0x011C: return true; // place holder to enable table lookup + case 0x011D: return true; // place holder to enable table lookup + case 0x011E: return true; // place holder to enable table lookup + case 0x011F: return true; // place holder to enable table lookup + case 0x0120: return false; // LATIN CAPITAL LETTER G WITH DOT ABOVE + case 0x0121: return false; // LATIN SMALL LETTER G WITH DOT ABOVE + } + switch (c) { + case 0x0174: return false; // LATIN CAPITAL LETTER W WITH CIRCUMFLEX + case 0x0175: return false; // LATIN SMALL LETTER W WITH CIRCUMFLEX + case 0x0176: return false; // LATIN CAPITAL LETTER Y WITH CIRCUMFLEX + case 0x0177: return false; // LATIN SMALL LETTER Y WITH CIRCUMFLEX + case 0x0178: return false; // LATIN CAPITAL LETTER Y WITH DIAERESIS + } + switch (c) { + case 0x1E02: return false; // LATIN CAPITAL LETTER B WITH DOT ABOVE + case 0x1E03: return false; // LATIN SMALL LETTER B WITH DOT ABOVE + case 0x1E04: return true; // place holder to enable table lookup + case 0x1E05: return true; // place holder to enable table lookup + case 0x1E06: return true; // place holder to enable table lookup + case 0x1E07: return true; // place holder to enable table lookup + case 0x1E08: return true; // place holder to enable table lookup + case 0x1E09: return true; // place holder to enable table lookup + case 0x1E0A: return false; // LATIN CAPITAL LETTER D WITH DOT ABOVE + case 0x1E0B: return false; // LATIN SMALL LETTER D WITH DOT ABOVE + case 0x1E0C: return true; // place holder to enable table lookup + case 0x1E0D: return true; // place holder to enable table lookup + case 0x1E0E: return true; // place holder to enable table lookup + case 0x1E0F: return true; // place holder to enable table lookup + case 0x1E10: return true; // place holder to enable table lookup + case 0x1E11: return true; // place holder to enable table lookup + case 0x1E12: return true; // place holder to enable table lookup + case 0x1E13: return true; // place holder to enable table lookup + case 0x1E14: return true; // place holder to enable table lookup + case 0x1E15: return true; // place holder to enable table lookup + case 0x1E16: return true; // place holder to enable table lookup + case 0x1E17: return true; // place holder to enable table lookup + case 0x1E18: return true; // place holder to enable table lookup + case 0x1E19: return true; // place holder to enable table lookup + case 0x1E1A: return true; // place holder to enable table lookup + case 0x1E1B: return true; // place holder to enable table lookup + case 0x1E1C: return true; // place holder to enable table lookup + case 0x1E1D: return true; // place holder to enable table lookup + case 0x1E1E: return false; // LATIN CAPITAL LETTER F WITH DOT ABOVE + case 0x1E1F: return false; // LATIN SMALL LETTER F WITH DOT ABOVE + case 0x1E20: return true; // place holder to enable table lookup + case 0x1E21: return true; // place holder to enable table lookup + case 0x1E22: return true; // place holder to enable table lookup + case 0x1E23: return true; // place holder to enable table lookup + case 0x1E24: return true; // place holder to enable table lookup + case 0x1E25: return true; // place holder to enable table lookup + case 0x1E26: return true; // place holder to enable table lookup + case 0x1E27: return true; // place holder to enable table lookup + case 0x1E28: return true; // place holder to enable table lookup + case 0x1E29: return true; // place holder to enable table lookup + case 0x1E2A: return true; // place holder to enable table lookup + case 0x1E2B: return true; // place holder to enable table lookup + case 0x1E2C: return true; // place holder to enable table lookup + case 0x1E2D: return true; // place holder to enable table lookup + case 0x1E2E: return true; // place holder to enable table lookup + case 0x1E2F: return true; // place holder to enable table lookup + case 0x1E30: return true; // place holder to enable table lookup + case 0x1E31: return true; // place holder to enable table lookup + case 0x1E32: return true; // place holder to enable table lookup + case 0x1E33: return true; // place holder to enable table lookup + case 0x1E34: return true; // place holder to enable table lookup + case 0x1E35: return true; // place holder to enable table lookup + case 0x1E36: return true; // place holder to enable table lookup + case 0x1E37: return true; // place holder to enable table lookup + case 0x1E38: return true; // place holder to enable table lookup + case 0x1E39: return true; // place holder to enable table lookup + case 0x1E3A: return true; // place holder to enable table lookup + case 0x1E3B: return true; // place holder to enable table lookup + case 0x1E3C: return true; // place holder to enable table lookup + case 0x1E3D: return true; // place holder to enable table lookup + case 0x1E3E: return true; // place holder to enable table lookup + case 0x1E3F: return true; // place holder to enable table lookup + case 0x1E40: return false; // LATIN CAPITAL LETTER M WITH DOT ABOVE + case 0x1E41: return false; // LATIN SMALL LETTER M WITH DOT ABOVE + case 0x1E42: return true; // place holder to enable table lookup + case 0x1E43: return true; // place holder to enable table lookup + case 0x1E44: return true; // place holder to enable table lookup + case 0x1E45: return true; // place holder to enable table lookup + case 0x1E46: return true; // place holder to enable table lookup + case 0x1E47: return true; // place holder to enable table lookup + case 0x1E48: return true; // place holder to enable table lookup + case 0x1E49: return true; // place holder to enable table lookup + case 0x1E4A: return true; // place holder to enable table lookup + case 0x1E4B: return true; // place holder to enable table lookup + case 0x1E4C: return true; // place holder to enable table lookup + case 0x1E4D: return true; // place holder to enable table lookup + case 0x1E4E: return true; // place holder to enable table lookup + case 0x1E4F: return true; // place holder to enable table lookup + case 0x1E50: return true; // place holder to enable table lookup + case 0x1E51: return true; // place holder to enable table lookup + case 0x1E52: return true; // place holder to enable table lookup + case 0x1E53: return true; // place holder to enable table lookup + case 0x1E54: return true; // place holder to enable table lookup + case 0x1E55: return true; // place holder to enable table lookup + case 0x1E56: return false; // LATIN CAPITAL LETTER P WITH DOT ABOVE + case 0x1E57: return false; // LATIN SMALL LETTER P WITH DOT ABOVE + case 0x1E58: return true; // place holder to enable table lookup + case 0x1E59: return true; // place holder to enable table lookup + case 0x1E5A: return true; // place holder to enable table lookup + case 0x1E5B: return true; // place holder to enable table lookup + case 0x1E5C: return true; // place holder to enable table lookup + case 0x1E5D: return true; // place holder to enable table lookup + case 0x1E5E: return true; // place holder to enable table lookup + case 0x1E5F: return true; // place holder to enable table lookup + case 0x1E60: return false; // LATIN CAPITAL LETTER S WITH DOT ABOVE + case 0x1E61: return false; // LATIN SMALL LETTER S WITH DOT ABOVE + case 0x1E62: return true; // place holder to enable table lookup + case 0x1E63: return true; // place holder to enable table lookup + case 0x1E64: return true; // place holder to enable table lookup + case 0x1E65: return true; // place holder to enable table lookup + case 0x1E66: return true; // place holder to enable table lookup + case 0x1E67: return true; // place holder to enable table lookup + case 0x1E68: return true; // place holder to enable table lookup + case 0x1E69: return true; // place holder to enable table lookup + case 0x1E6A: return false; // LATIN CAPITAL LETTER T WITH DOT ABOVE + case 0x1E6B: return false; // LATIN SMALL LETTER T WITH DOT ABOVE + case 0x1E6C: return true; // place holder to enable table lookup + case 0x1E6D: return true; // place holder to enable table lookup + case 0x1E6E: return true; // place holder to enable table lookup + case 0x1E6F: return true; // place holder to enable table lookup + case 0x1E70: return true; // place holder to enable table lookup + case 0x1E71: return true; // place holder to enable table lookup + case 0x1E72: return true; // place holder to enable table lookup + case 0x1E73: return true; // place holder to enable table lookup + case 0x1E74: return true; // place holder to enable table lookup + case 0x1E75: return true; // place holder to enable table lookup + case 0x1E76: return true; // place holder to enable table lookup + case 0x1E77: return true; // place holder to enable table lookup + case 0x1E78: return true; // place holder to enable table lookup + case 0x1E79: return true; // place holder to enable table lookup + case 0x1E7A: return true; // place holder to enable table lookup + case 0x1E7B: return true; // place holder to enable table lookup + case 0x1E7C: return true; // place holder to enable table lookup + case 0x1E7D: return true; // place holder to enable table lookup + case 0x1E7E: return true; // place holder to enable table lookup + case 0x1E7F: return true; // place holder to enable table lookup + case 0x1E80: return false; // LATIN CAPITAL LETTER W WITH GRAVE + case 0x1E81: return false; // LATIN SMALL LETTER W WITH GRAVE + case 0x1E82: return false; // LATIN CAPITAL LETTER W WITH ACUTE + case 0x1E83: return false; // LATIN SMALL LETTER W WITH ACUTE + case 0x1E84: return false; // LATIN CAPITAL LETTER W WITH DIAERESIS + case 0x1E85: return false; // LATIN SMALL LETTER W WITH DIAERESIS + } + switch (c) { + case 0x1EF2: return false; // LATIN CAPITAL LETTER Y WITH GRAVE + case 0x1EF3: return false; // LATIN SMALL LETTER Y WITH GRAVE + } + + return true; + + } + +} \ No newline at end of file diff --git a/src/nu/xom/Latin9Writer.java b/src/nu/xom/Latin9Writer.java new file mode 100644 index 0000000..5cb5a84 --- /dev/null +++ b/src/nu/xom/Latin9Writer.java @@ -0,0 +1,207 @@ +/* Copyright 2002, 2003 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom; + +import java.io.Writer; + +/** + *

    + * ISO 8859-9 for Western Europe. Includes the Euro sign and + * several uncommon French letters. otherwise the same as + * Latin-1. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.0 + * + */ +class Latin9Writer extends TextWriter { + + /** + *

    + * Creates a new TextWriter for the + * ISO-8859-15 (Latin-9) character encoding. + *

    + * + * @param out the Writer on which the text will be written + * @param encoding the character set used by the Writer + */ + Latin9Writer(Writer out, String encoding) { + super(out, encoding); + } + + /** + *

    + * Returns true if and only if this character + * needs to be replaced by a character reference when + * output in this encoding. + *

    + * + * + * @see nu.xom.TextWriter#needsEscaping(char) + */ + boolean needsEscaping(char c) { + if (c <= 0xA3 ) return false; + if (c == 0x20AC) return false; // EURO SIGN + + switch (c) { // Latin-1 overlap + case 0x00A5: return false; // YEN SIGN + case 0x00A6: return true; // place holder to enable table lookup + case 0x00A7: return false; // SECTION SIGN + case 0x00A8: return true; // place holder to enable table lookup + case 0x00A9: return false; // COPYRIGHT SIGN + case 0x00AA: return false; // FEMININE ORDINAL INDICATOR + case 0x00AB: return false; // LEFT-POINTING DOUBLE ANGLE QUOTATION MARK + case 0x00AC: return false; // NOT SIGN + case 0x00AD: return false; // SOFT HYPHEN + case 0x00AE: return false; // REGISTERED SIGN + case 0x00AF: return false; // MACRON + case 0x00B0: return false; // DEGREE SIGN + case 0x00B1: return false; // PLUS-MINUS SIGN + case 0x00B2: return false; // SUPERSCRIPT TWO + case 0x00B3: return false; // SUPERSCRIPT THREE + case 0x00B4: return true; // place holder to enable table lookup + case 0x00B5: return false; // MICRO SIGN + case 0x00B6: return false; // PILCROW SIGN + case 0x00B7: return false; // MIDDLE DOT + case 0x00B8: return true; // place holder to enable table lookup + case 0x00B9: return false; // SUPERSCRIPT ONE + case 0x00BA: return false; // MASCULINE ORDINAL INDICATOR + case 0x00BB: return false; // RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK + case 0x00BC: return true; // place holder to enable table lookup + case 0x00BD: return true; // place holder to enable table lookup + case 0x00BE: return true; // place holder to enable table lookup + case 0x00BF: return false; // INVERTED QUESTION MARK + case 0x00C0: return false; // LATIN CAPITAL LETTER A WITH GRAVE + case 0x00C1: return false; // LATIN CAPITAL LETTER A WITH ACUTE + case 0x00C2: return false; // LATIN CAPITAL LETTER A WITH CIRCUMFLEX + case 0x00C3: return false; // LATIN CAPITAL LETTER A WITH TILDE + case 0x00C4: return false; // LATIN CAPITAL LETTER A WITH DIAERESIS + case 0x00C5: return false; // LATIN CAPITAL LETTER A WITH RING ABOVE + case 0x00C6: return false; // LATIN CAPITAL LETTER AE + case 0x00C7: return false; // LATIN CAPITAL LETTER C WITH CEDILLA + case 0x00C8: return false; // LATIN CAPITAL LETTER E WITH GRAVE + case 0x00C9: return false; // LATIN CAPITAL LETTER E WITH ACUTE + case 0x00CA: return false; // LATIN CAPITAL LETTER E WITH CIRCUMFLEX + case 0x00CB: return false; // LATIN CAPITAL LETTER E WITH DIAERESIS + case 0x00CC: return false; // LATIN CAPITAL LETTER I WITH GRAVE + case 0x00CD: return false; // LATIN CAPITAL LETTER I WITH ACUTE + case 0x00CE: return false; // LATIN CAPITAL LETTER I WITH CIRCUMFLEX + case 0x00CF: return false; // LATIN CAPITAL LETTER I WITH DIAERESIS + case 0x00D0: return false; // LATIN CAPITAL LETTER ETH + case 0x00D1: return false; // LATIN CAPITAL LETTER N WITH TILDE + case 0x00D2: return false; // LATIN CAPITAL LETTER O WITH GRAVE + case 0x00D3: return false; // LATIN CAPITAL LETTER O WITH ACUTE + case 0x00D4: return false; // LATIN CAPITAL LETTER O WITH CIRCUMFLEX + case 0x00D5: return false; // LATIN CAPITAL LETTER O WITH TILDE + case 0x00D6: return false; // LATIN CAPITAL LETTER O WITH DIAERESIS + case 0x00D7: return false; // MULTIPLICATION SIGN + case 0x00D8: return false; // LATIN CAPITAL LETTER O WITH STROKE + case 0x00D9: return false; // LATIN CAPITAL LETTER U WITH GRAVE + case 0x00DA: return false; // LATIN CAPITAL LETTER U WITH ACUTE + case 0x00DB: return false; // LATIN CAPITAL LETTER U WITH CIRCUMFLEX + case 0x00DC: return false; // LATIN CAPITAL LETTER U WITH DIAERESIS + case 0x00DD: return false; // LATIN CAPITAL LETTER Y WITH ACUTE + case 0x00DE: return false; // LATIN CAPITAL LETTER THORN + case 0x00DF: return false; // LATIN SMALL LETTER SHARP S + case 0x00E0: return false; // LATIN SMALL LETTER A WITH GRAVE + case 0x00E1: return false; // LATIN SMALL LETTER A WITH ACUTE + case 0x00E2: return false; // LATIN SMALL LETTER A WITH CIRCUMFLEX + case 0x00E3: return false; // LATIN SMALL LETTER A WITH TILDE + case 0x00E4: return false; // LATIN SMALL LETTER A WITH DIAERESIS + case 0x00E5: return false; // LATIN SMALL LETTER A WITH RING ABOVE + case 0x00E6: return false; // LATIN SMALL LETTER AE + case 0x00E7: return false; // LATIN SMALL LETTER C WITH CEDILLA + case 0x00E8: return false; // LATIN SMALL LETTER E WITH GRAVE + case 0x00E9: return false; // LATIN SMALL LETTER E WITH ACUTE + case 0x00EA: return false; // LATIN SMALL LETTER E WITH CIRCUMFLEX + case 0x00EB: return false; // LATIN SMALL LETTER E WITH DIAERESIS + case 0x00EC: return false; // LATIN SMALL LETTER I WITH GRAVE + case 0x00ED: return false; // LATIN SMALL LETTER I WITH ACUTE + case 0x00EE: return false; // LATIN SMALL LETTER I WITH CIRCUMFLEX + case 0x00EF: return false; // LATIN SMALL LETTER I WITH DIAERESIS + case 0x00F0: return false; // LATIN SMALL LETTER ETH + case 0x00F1: return false; // LATIN SMALL LETTER N WITH TILDE + case 0x00F2: return false; // LATIN SMALL LETTER O WITH GRAVE + case 0x00F3: return false; // LATIN SMALL LETTER O WITH ACUTE + case 0x00F4: return false; // LATIN SMALL LETTER O WITH CIRCUMFLEX + case 0x00F5: return false; // LATIN SMALL LETTER O WITH TILDE + case 0x00F6: return false; // LATIN SMALL LETTER O WITH DIAERESIS + case 0x00F7: return false; // DIVISION SIGN + case 0x00F8: return false; // LATIN SMALL LETTER O WITH STROKE + case 0x00F9: return false; // LATIN SMALL LETTER U WITH GRAVE + case 0x00FA: return false; // LATIN SMALL LETTER U WITH ACUTE + case 0x00FB: return false; // LATIN SMALL LETTER U WITH CIRCUMFLEX + case 0x00FC: return false; // LATIN SMALL LETTER U WITH DIAERESIS + case 0x00FD: return false; // LATIN SMALL LETTER Y WITH ACUTE + case 0x00FE: return false; // LATIN SMALL LETTER THORN + case 0x00FF: return false; // LATIN SMALL LETTER Y WITH DIAERESIS + } + switch (c) { // uncommon French letters + case 0x0152: return false; // LATIN CAPITAL LIGATURE OE + case 0x0153: return false; // LATIN SMALL LIGATURE OE + case 0x0154: return true; // place holder to enable table lookup + case 0x0155: return true; // place holder to enable table lookup + case 0x0156: return true; // place holder to enable table lookup + case 0x0157: return true; // place holder to enable table lookup + case 0x0158: return true; // place holder to enable table lookup + case 0x0159: return true; // place holder to enable table lookup + case 0x015A: return true; // place holder to enable table lookup + case 0x015B: return true; // place holder to enable table lookup + case 0x015C: return true; // place holder to enable table lookup + case 0x015D: return true; // place holder to enable table lookup + case 0x015E: return true; // place holder to enable table lookup + case 0x015F: return true; // place holder to enable table lookup + case 0x0160: return false; // LATIN CAPITAL LETTER S WITH CARON + case 0x0161: return false; // LATIN SMALL LETTER S WITH CARON + case 0x0162: return true; // place holder to enable table lookup + case 0x0163: return true; // place holder to enable table lookup + case 0x0164: return true; // place holder to enable table lookup + case 0x0165: return true; // place holder to enable table lookup + case 0x0166: return true; // place holder to enable table lookup + case 0x0167: return true; // place holder to enable table lookup + case 0x0168: return true; // place holder to enable table lookup + case 0x0169: return true; // place holder to enable table lookup + case 0x016A: return true; // place holder to enable table lookup + case 0x016B: return true; // place holder to enable table lookup + case 0x016C: return true; // place holder to enable table lookup + case 0x016D: return true; // place holder to enable table lookup + case 0x016E: return true; // place holder to enable table lookup + case 0x016F: return true; // place holder to enable table lookup + case 0x0170: return true; // place holder to enable table lookup + case 0x0171: return true; // place holder to enable table lookup + case 0x0172: return true; // place holder to enable table lookup + case 0x0173: return true; // place holder to enable table lookup + case 0x0174: return true; // place holder to enable table lookup + case 0x0175: return true; // place holder to enable table lookup + case 0x0176: return true; // place holder to enable table lookup + case 0x0177: return true; // place holder to enable table lookup + case 0x0178: return false; // LATIN CAPITAL LETTER Y WITH DIAERESIS + case 0x017D: return false; // LATIN CAPITAL LETTER Z WITH CARON + case 0x017E: return false; // LATIN SMALL LETTER Z WITH CARON + } + + return true; + + } + +} \ No newline at end of file diff --git a/src/nu/xom/MalformedURIException.java b/src/nu/xom/MalformedURIException.java new file mode 100644 index 0000000..d0c836b --- /dev/null +++ b/src/nu/xom/MalformedURIException.java @@ -0,0 +1,70 @@ +/* Copyright 2002-2005 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom; + +/** + *
    + + * @author Elliotte Rusty Harold + * @version 1.1b3 + * + */ +public class MalformedURIException extends IllegalDataException { + + + private static final long serialVersionUID = 6510452627371611735L; + + + /** + *

    + * Creates a new MalformedURIException + * with a detail message. + *

    + * + * @param message a string indicating the specific problem + */ + public MalformedURIException(String message) { + super(message); + } + + + /** + *

    + * Creates a new MalformedURIException + * with a detail message and an underlying root cause. + *

    + * + * @param message a string indicating the specific problem + * @param cause the original cause of this exception + */ + public MalformedURIException(String message, Throwable cause) { + super(message, cause); + } + + +} diff --git a/src/nu/xom/MultipleParentException.java b/src/nu/xom/MultipleParentException.java new file mode 100644 index 0000000..b704fb7 --- /dev/null +++ b/src/nu/xom/MultipleParentException.java @@ -0,0 +1,65 @@ +/* Copyright 2002-2005 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom; + +/** + *

    + * Indicates an attempt to add a node that already has a parent to the + * same or a different parent. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.1b3 + * + * + */ +public class MultipleParentException extends IllegalAddException { + + + private static final long serialVersionUID = 2605448365902777548L; + + /** + *

    + * Creates a new MultipleParentException + * with a detail message. + *

    + * + * @param message a string indicating the specific problem + */ + public MultipleParentException(String message) { + super(message); + } + + /** + *

    + * Creates a new MultipleParentException + * with a detail message and an underlying root cause. + *

    + * + * @param message a string indicating the specific problem + * @param cause the original cause of this exception + */ + public MultipleParentException(String message, Throwable cause) { + super(message, cause); + } + +} \ No newline at end of file diff --git a/src/nu/xom/Namespace.java b/src/nu/xom/Namespace.java new file mode 100644 index 0000000..05a1bc9 --- /dev/null +++ b/src/nu/xom/Namespace.java @@ -0,0 +1,237 @@ +/* Copyright 2005 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom; + +/** + *

    + * Represents a namespace in scope. It is used by XOM's + * XPath implementation for the namespace axis. However, it is not + * really part of the XOM data model. Namespace objects are only + * created as needed when evaluating XPath. While a namespace node has + * a parent element (which may be null), that element does not know + * about these namespace nodes and cannot remove them. (This is an + * inconsistency in the XPath data model, and is shared with attributes + * which also have parents but are not children.) + *

    + * + * @author Elliotte Rusty Harold + * @version 1.3.0 + * + */ +public final class Namespace extends Node { + + private final String prefix; + private final String uri; + + /** + * Namespace URI specified for xml prefix + */ + public final static String XML_NAMESPACE + = "http://www.w3.org/XML/1998/namespace"; + + + /** + *

    + * Create a new namespace node. + *

    + * + * @param prefix the prefix for the namespace; may be the empty + * string or a non-colonized name + * @param URI the namespace URI + * @param parent the element that possesses this namespace node + * + * @throws MalformedURIException if URI is + * not an RFC 3986 URI reference + * @throws IllegalNameException if + *
      + *
    • The prefix is xmlns.
    • + *
    • The prefix is not the empty string, and the URI is + * null or the empty string.
    • + *
    + * @throws NamespaceConflictException if + *
      + *
    • The prefix is the empty string, and the URI is + * null or the empty string.
    • + *
    • The prefix is xml, and the URI is not + * http://www.w3.org/XML/1998/namespace.
    • + *
    • The prefix is not xml, and the URI is + * http://www.w3.org/XML/1998/namespace.
    • + *
    + */ + public Namespace(String prefix, String URI, Element parent) { + + if (prefix == null) prefix = ""; + else if ("xmlns".equals(prefix)) { + throw new IllegalNameException( + "The xmlns prefix may not be bound to a URI."); + } + else if ("xml".equals(prefix)) { + if (! XML_NAMESPACE.equals(URI) ) { + throw new NamespaceConflictException( + "The prefix xml can only be bound to the URI " + + "http://www.w3.org/XML/1998/namespace"); + } + } + + if (prefix.length() != 0) Verifier.checkNCName(prefix); + + if (URI == null) URI = ""; + else if (URI.equals(XML_NAMESPACE)) { + if (! "xml".equals(prefix)) { + throw new NamespaceConflictException( + "The URI http://www.w3.org/XML/1998/namespace can " + + "only be bound to the prefix xml"); + } + } + + if (URI.length() == 0) { // faster than "".equals(uri) + if (prefix.length() != 0) { + throw new NamespaceConflictException( + "Prefixed elements must have namespace URIs." + ); + } + } + else Verifier.checkAbsoluteURIReference(URI); + + this.prefix = prefix; + this.uri = URI; + super.setParent(parent); + } + + + /** + *

    + * Returns the namespace prefix, or the empty string if this node + * is the default namespace. + *

    + * + * @return the namespace prefix + */ + public String getPrefix() { + return prefix; + } + + + /** + *

    + * Returns the namespace URI. + *

    + * + * @return the namespace URI + */ + public String getValue() { + return uri; + } + + + /** + *

    + * Throws IndexOutOfBoundsException because + * namespaces do not have children. + *

    + * + * @return never returns because document type declarations do not + * have children. Always throws an exception. + * + * @param position the index of the child node to return + * + * @throws IndexOutOfBoundsException because document type declarations + * do not have children + */ + public Node getChild(int position) { + throw new IndexOutOfBoundsException( + "Namespaces do not have children"); + } + + + /** + *

    + * Returns 0 because namespaces do not have + * children. + *

    + * + * @return zero + */ + public int getChildCount() { + return 0; + } + + + /** + *

    + * Returns a copy of this namespace which has + * the same prefix and URI, but no parent. + *

    + * + * @return a copy of this Namespace + * that is not part of a document + */ + public Namespace copy() { + return new Namespace(prefix, uri, null); + } + + + /** + *

    + * Removes this namespace node from its parent. + *

    + * + * @see nu.xom.Node#detach() + */ + public void detach() { + super.setParent(null); + } + + + /** + *

    + * Returns a string containing the actual XML + * form of the namespace declaration represented + * by this object. For example, + * xmlns:pre="http://www.example.org/". + *

    + * + * @return a String containing + * an XML namespace declaration + */ + public String toXML() { + String colon = prefix.equals("") ? "" : ":"; + return "xmlns" + colon + prefix + "=\"" + uri + "\""; + } + + + /** + *

    + * Returns a string form of the + * Namespace suitable for debugging + * and diagnosis. It deliberately does not return + * an actual XML namespace declaration. + *

    + * + * @return a string representation of this object + */ + public String toString() { + return "[Namespace: " + this.toXML() + "]"; + } + + +} \ No newline at end of file diff --git a/src/nu/xom/NamespaceConflictException.java b/src/nu/xom/NamespaceConflictException.java new file mode 100644 index 0000000..5f003fe --- /dev/null +++ b/src/nu/xom/NamespaceConflictException.java @@ -0,0 +1,71 @@ +/* Copyright 2002-2005 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom; + +/** + *

    + * Signals an attempt to set a namespace in a way that conflicts with + * an existing namespace; for instance, adding an attribute to + * an element that has the same prefix as the element but maps it + * to a different URI. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.1b3 + * + */ +public class NamespaceConflictException + extends WellformednessException { + + + private static final long serialVersionUID = -3527557666747617537L; + + + /** + *

    + * Creates a new NamespaceConflictException + * with a detail message. + *

    + * + * @param message a string indicating the specific problem + */ + public NamespaceConflictException(String message) { + super(message); + } + + + /** + *

    + * Creates a new NamespaceConflictException + * with a detail message and an underlying root cause. + *

    + * + * @param message a string indicating the specific problem + * @param cause the original cause of this exception + */ + public NamespaceConflictException( + String message, Throwable cause) { + super(message, cause); + } + + +} diff --git a/src/nu/xom/Namespaces.java b/src/nu/xom/Namespaces.java new file mode 100644 index 0000000..b1308eb --- /dev/null +++ b/src/nu/xom/Namespaces.java @@ -0,0 +1,105 @@ +/* Copyright 2002-2006, 2019 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom; + +import java.util.ArrayList; +import java.util.HashMap; + +/** + *

    + * The Namespaces container is a read-only list + * used to hold the additional namespace declarations of an + * element. It provides indexed access for convenience, + * but the order is neither predictable nor reproducible, + * and has no meaning. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.3.1 + */ +class Namespaces { + + private HashMap namespaces = new HashMap(1); + private ArrayList prefixes = new ArrayList(1); + + + void put(String prefix, String URI) { + namespaces.put(prefix, URI); + prefixes.remove(prefix); + prefixes.add(prefix); + } + + + void remove(String prefix) { + if (prefix == null) prefix = ""; + namespaces.remove(prefix); + prefixes.remove(prefix); + } + + + /** + *

    + * Return the URI associated with a prefix, as determined + * by the namespaces stored in this list. This method + * returns null if the prefix is not found in the list. + *

    + * + * @param prefix the prefix whose URI is desired + * + * @return the namespace URI for this prefix, or null if this + * prefix is not not mapped to a URI by these namespace + * declarations + */ + String getURI(String prefix) { + return (String) (namespaces.get(prefix)); + } + + + // This violates encapsulation. Don't change the + // array returned. + ArrayList getPrefixes() { + return this.prefixes; + } + + + Namespaces copy() { + + Namespaces result = new Namespaces(); + // shallow copies work here because these collections only + // contain immutable strings + result.namespaces = (HashMap) this.namespaces.clone(); + result.prefixes = (ArrayList) this.prefixes.clone(); + return result; + + } + + + int size() { + return prefixes.size(); + } + + + String getPrefix(int i) { + return (String) prefixes.get(i); + } + +} \ No newline at end of file diff --git a/src/nu/xom/NoSuchAttributeException.java b/src/nu/xom/NoSuchAttributeException.java new file mode 100644 index 0000000..615d6d9 --- /dev/null +++ b/src/nu/xom/NoSuchAttributeException.java @@ -0,0 +1,71 @@ +/* Copyright 2002-2005 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom; + +/** + *

    + * Indicates that an attribute with a certain name and/or namespace + * does not exist. This is normally thrown after an attempt to remove + * such a non-existent attribute. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.0 + * @see Element#removeAttribute(Attribute) + * + */ +public class NoSuchAttributeException extends XMLException { + + + private static final long serialVersionUID = -7472517723464699452L; + + + /** + *

    + * Creates a new NoSuchAttributeException + * with a detail message. + *

    + * + * @param message explains the reason for the exception + */ + public NoSuchAttributeException(String message) { + super(message); + } + + + /** + *

    + * Creates a new NoSuchAttributeException + * with the specified detail message + * and an underlying root cause. + *

    + * + * @param message explains the reason for the exception + * @param cause the nested exception that caused this exception + */ + public NoSuchAttributeException(String message, Throwable cause) { + super(message); + this.initCause(cause); + } + + +} \ No newline at end of file diff --git a/src/nu/xom/NoSuchChildException.java b/src/nu/xom/NoSuchChildException.java new file mode 100644 index 0000000..1fadcd1 --- /dev/null +++ b/src/nu/xom/NoSuchChildException.java @@ -0,0 +1,71 @@ +/* Copyright 2002-2005 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom; + +/** + *

    + * Indicates that a child with a certain name and/or namespace does + * not exist. This is thrown when you attempt to remove a node from + * a parent that does not actually have that node as a child. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.1b3 + * @see ParentNode#removeChild(Node) + * + */ +public class NoSuchChildException extends XMLException { + + + private static final long serialVersionUID = 1944673590646036964L; + + + /** + *

    + * Creates a new NoSuchChildException + * with a detail message. + *

    + * + * @param message explains the reason for the exception + */ + public NoSuchChildException(String message) { + super(message); + } + + + /** + *

    + * Creates a new NoSuchChildException + * with the specified detail message + * and an underlying root cause. + *

    + * + * @param message explains the reason for the exception + * @param cause the nested exception that caused this exception + */ + public NoSuchChildException(String message, Throwable cause) { + super(message); + this.initCause(cause); + } + + +} \ No newline at end of file diff --git a/src/nu/xom/Node.java b/src/nu/xom/Node.java new file mode 100644 index 0000000..dacd571 --- /dev/null +++ b/src/nu/xom/Node.java @@ -0,0 +1,527 @@ +/* Copyright 2002-2005, 2019 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom; + +import java.util.List; + +import org.jaxen.NamespaceContext; + +/** + * + *

    + * The generic superclass for all the contents + * of an XML document. There are exactly eight kinds of + * nodes in XOM: + *

    + * + *
      + *
    • Element
    • + *
    • Document
    • + *
    • Text
    • + *
    • Comment
    • + *
    • Attribute
    • + *
    • ProcessingInstruction
    • + *
    • DocType
    • + *
    • Namespace
    • + *
    + * + *

    + * Every instance of Node is an + * instance of one of these eight classes + * (including, possibly, one of their subclasses). + *

    + * + * + * @author Elliotte Rusty Harold + * @version 1.3.1 + * + */ +public abstract class Node { + + + private ParentNode parent = null; + + /** + *

    + * Creates a new Node object. + * Can only be invoked by other members of + * the nu.xom package. + *

    + */ + Node() {} + + + /** + *

    + * Returns the XPath 1.0 string-value of this node. + *

    + * + * @return the XPath 1.0 string-value of this node + */ + public abstract String getValue(); + + + /** + * + *

    + * Returns the document that contains this node, + * or null if this node is not currently part of a document. + * Each node belongs to no more than one document at a time. + * If this node is a Document, then it returns + * this node. + *

    + * + * @return the document this node is a part of + */ + public final Document getDocument() { + Node parent = this; + while (parent != null && !(parent.isDocument())) { + parent = parent.getParent(); + } + return (Document) parent; + } + + + /** + * + *

    + * Returns the root of the subtree in which this node is found, + * whether that's a document or an element. + *

    + * + * @return the document this node is a part of + */ + final Node getRoot() { + + Node parent = this.getParent(); + if (parent == null) { + return this; + } + while (parent.getParent() != null) { + parent = parent.getParent(); + } + return parent; + + } + + + /** + * + *

    + * Returns the base URI of this node as specified by + * XML + * Base, or the empty string if this is not known. In most + * cases, this is the URL against which relative URLs in this node + * should be resolved. + *

    + * + *

    + * The base URI of a non-parent node is the base URI of the + * element containing the node. The base URI of a document + * node is the URI from which the document was parsed, + * or which was set by calling setBaseURI on + * on the document. + *

    + * + *

    + * The base URI of an element is determined as follows: + *

    + * + *
      + *
    • + * If the element has an xml:base attribute, + * then the value of that attribute is + * converted from an IRI to a URI, absolutized if possible, + * and returned. + *
    • + *
    • + * Otherwise, if any ancestor element of the element loaded + * from the same entity has an xml:base + * attribute, then the value of that attribute from the + * nearest such ancestor is converted from an IRI to a URI, + * absolutized if possible, and returned. + * xml:base attributes from other entities are + * not considered. + *
    • + *
    • + * Otherwise, if setBaseURI() has been invoked on + * this element, then the URI most recently passed to that method + * is absolutized if possible and returned. + *
    • + *
    • + * Otherwise, if the element comes from an externally + * parsed entity or the document entity, and the + * original base URI has not been changed by invoking + * setBaseURI(), then the URI of that entity is + * returned. + *
    • + *
    • + * Otherwise, (the element was created by a constructor + * rather then being parsed from an existing document), the + * base URI of the nearest ancestor that does have a base URI + * is returned. If no ancestors have a base URI, then the + * empty string is returned. + *
    • + *
    + * + *

    + * Absolutization takes place as specified by the + * XML + * Base specification. However, it is not always possible to + * absolutize a relative URI, in which case the empty string will + * be returned. + *

    + * + * @return the base URI of this node + */ + public String getBaseURI() { + if (parent == null) return ""; + return parent.getBaseURI(); + } + + + /** + * + *

    + * Returns the node that contains this node, + * or null if this node does not have a parent. + *

    + * + * @return the element or document that most immediately + * contains this node + */ + public final ParentNode getParent() { + return this.parent; + } + + + final void setParent(ParentNode parent) { + this.parent = parent; + } + + + /** + *

    + * Removes this node from its parent so that it can be added + * to a different parent node or document. This method does nothing + * if the node does not have a parent. + *

    + * + * @throws XMLException if the parent refuses to detach this node + */ + public void detach() { + + if (parent == null) return; + else if (this.isAttribute()) { + Element element = (Element) parent; + element.removeAttribute((Attribute) this); + } + else { + parent.removeChild(this); + } + + } + + + /** + *

    + * Returns the child of this node at the specified position. + *

    + * + * @param position the index of the child node to return + * + * @return the positionth child node of this node + * + * @throws IndexOutOfBoundsException if this node does not have children + */ + public abstract Node getChild(int position); + + + /** + *

    + * Returns the number of children of this node. + * This is always non-negative (greater than or equal to zero). + *

    + * + * @return the number of children of this node + */ + public abstract int getChildCount(); + + + /** + *

    + * Returns a deep copy of this node with no parent, + * that can be added to the current document or a different one. + *

    + * + *

    + * Per Bloch, the Cloneable + * interface is just a mess and should + * be avoided. However, I do not follow his suggestion of a copy + * constructor exclusively because it is useful to be able to + * copy a node without knowing its more specific type. + * Ken Arnold agrees with this. It's more effective for + * subclasses that can return an instance of the subclass. + *

    + * + * @return a copy of this node without a parent + */ + public abstract Node copy(); + + + /** + *

    + * Returns the actual XML form of this node, such as might be + * copied and pasted from the original document. However, this + * does not preserve semantically insignificant details such as + * white space inside tags or the use of empty-element tags vs. + * start-tag end-tag pairs. + *

    + * + * @return an XML representation of this node + */ + public abstract String toXML(); + + + /** + *

    + * Tests for node identity. That is, two + * Node objects are equal + * if and only if they are the same object. + *

    + * + * @param o the object compared for equality to this node + * + * @return true if o is this node; false otherwise + * + * @see java.lang.Object#equals(Object) + */ + public final boolean equals(Object o) { + return this == o; + } + + + /** + *

    + * Returns a unique identifier for this node. + * The value returned is the same as returned by + * super.hashCode() + * because nodes use identity semantics. + *

    + * + * @return a probably unique identifier for this node + * + * @see java.lang.Object#hashCode() + */ + public final int hashCode() { + return super.hashCode(); + } + + + /** + *

    + * Returns the nodes selected by the XPath expression in the + * context of this node in document order as defined in XSLT. + * All namespace prefixes used in the + * expression should be bound to namespace URIs by the + * second argument. + *

    + * + *

    + * Note that XPath expressions operate on the XPath data model, + * not the XOM data model. XPath counts all adjacent + * Text objects as a single text node, and does not + * consider empty Text objects. For instance, an + * element that has exactly three text children in XOM, will + * have exactly one text child in XPath, whose value is the + * concatenation of all three XOM Text objects. + *

    + * + *

    + * You can use XPath expressions that use the namespace axis. + * However, namespace nodes are never returned. If an XPath + * expression only selects namespace nodes, then this method will + * return an empty list. + *

    + * + *

    + * No variables are bound. + *

    + * + *

    + * The context position is the index of this node among its parents + * children, counting adjacent text nodes as one. The context size + * is the number of children this node's parent has, again counting + * adjacent text nodes as one node. If the parent is a + * Document, then the DocType (if any) is + * not counted. If the node has no parent, then the context position + * is 1, and the context size is 1. + *

    + * + *

    + * Queries such as /*, //, and /*//p that refer to the + * root node do work when operating with a context node that is not + * part of a document. However, the query / (return the root node) + * throws an XPathException when applied to a node + * that is not part of the document. Furthermore the top-level + * node in the tree is treated as the first and only child of the + * root node, not as the root node itself. For instance, this + * query stores parent in the result + * variable, not child: + *

    + * + *
      Element parent = new Element("parent");
    +     *   Element child = new Element("child");
    +     *   parent.appendChild(child);
    +     *   Nodes results = child.query("/*");
    +     *   Node result = result.get(0);
    + * + * @param xpath the XPath expression to evaluate + * @param namespaces a collection of namespace prefix bindings + * used in the XPath expression + * + * @return a list of all matched nodes; possibly empty + * + * @throws XPathException if there's a syntax error in the + * expression, the query returns something other than + * a node-set + * + */ + public final Nodes query(String xpath, XPathContext namespaces) { + + if (this.isDocType()) { + throw new XPathException("Can't use XPath on a DocType"); + } + DocumentFragment frag = null; + + Node root = getRoot(); + if (! root.isDocument()) { + frag = new DocumentFragment(); + frag.appendChild(root); + } + + try { + JaxenConnector connector = new JaxenConnector(xpath); + if (namespaces == null) { + connector.setNamespaceContext(emptyContext); + } + else { + connector.setNamespaceContext(namespaces.getJaxenContext()); + } + + List queryResults = connector.selectNodes(this); + return new Nodes(queryResults); + } + catch (XPathException ex) { + ex.setXPath(xpath); + throw ex; + } + catch (Exception ex) { // JaxenException and RuntimeException + // I can't trigger a RuntimeException with the current Jaxen + // code base; but it's been an issue in the past, and I'm + // not convinced it's fully fixed now. + XPathException xpe = new XPathException("XPath error: " + ex.getMessage(), ex); + xpe.setXPath(xpath); + throw xpe; + } + finally { + if (frag != null) frag.removeChild(0); + } + + } + + + private static NamespaceContext emptyContext = new EmptyNamespaceContext(); + + private static class EmptyNamespaceContext implements NamespaceContext { + + public String translateNamespacePrefixToUri(String prefix) { + // XML prefix is recognized automatically in Jaxen without + // calling this method. + // if ("xml".equals(prefix)) return Namespace.XML_NAMESPACE; + return null; + } + + + } + + + /** + *

    + * Returns the nodes selected by the XPath expression in the + * context of this node in document order as defined by XSLT. + * This XPath expression must not contain + * any namespace prefixes. + *

    + * + *

    + * No variables are bound. No namespace prefixes are bound. + *

    + * + * @param xpath the XPath expression to evaluate + * + * @return a list of all matched nodes; possibly empty + * + * @throws XPathException if there's a syntax error in the + * expression; or the query returns something other than + * a node-set + */ + public final Nodes query(String xpath) { + return query(xpath, null); + } + + + // Methods to replace instanceof tests to improve performance + boolean isElement() { + return false; + } + + boolean isText() { + return false; + } + + boolean isComment() { + return false; + } + + boolean isProcessingInstruction() { + return false; + } + + boolean isAttribute() { + return false; + } + + boolean isDocument() { + return false; + } + + boolean isDocType() { + return false; + } + + boolean isDocumentFragment() { + return false; + } + + +} diff --git a/src/nu/xom/NodeFactory.java b/src/nu/xom/NodeFactory.java new file mode 100644 index 0000000..9756c2b --- /dev/null +++ b/src/nu/xom/NodeFactory.java @@ -0,0 +1,409 @@ +/* Copyright 2002-2005, 2019 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom; + +/** + *

    + * Builders use a NodeFactory object + * to construct each Node object (Element, + * Text, Attribute, etc.) they add to the + * tree. The default implementation simply calls the relevant + * constructor, stuffs the resulting Node object in a + * length one Nodes object, and returns it. + *

    + * + *

    + * Subclassing this class allows builders to produce + * instance of subclasses (for example, + * HTMLElement) instead of the + * base classes. + *

    + * + *

    + * Subclasses can also filter content while building. + * For example, namespaces could be added to or changed + * on all elements. Comments could be deleted. Processing + * instructions can be changed into elements. An + * xinclude:include element could be replaced + * with the content it references. All such changes must be + * consistent with the usual rules of well-formedness. For + * example, the makeDocType() method should not + * return a list containing two DocType objects + * because an XML document can have at most one document type + * declaration. Nor should it return a list containing an element, + * because an element cannot appear in a document prolog. However, + * it could return a list containing any number of comments and + * processing instructions, and not more than one DocType + * object. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.3.1 + * + */ +public class NodeFactory { + + /** + *

    + * Constructs a new node factory. + *

    + * + */ + public NodeFactory() {} + + /** + *

    + * Creates a new element in the specified namespace + * with the specified name. The builder calls this + * method to make the root element of the document. + *

    + * + *

    + * Subclasses may change the name, namespace, content, or other + * characteristics of the element returned. + * The default implementation merely calls + * startMakingElement. However, when subclassing, it + * is often useful to be able to easily distinguish between the + * root element and a non-root element because the root element + * cannot be detached. Therefore, subclasses must not return null + * from this method. Doing so will cause a + * NullPointerException. + *

    + * + * @param name the qualified name of the element + * @param namespace the namespace URI of the element + * + * @return the new root element + */ + public Element makeRootElement(String name, String namespace) { + return startMakingElement(name, namespace); + } + + + /** + *

    + * Creates a new Element in the specified namespace + * with the specified name. + *

    + * + *

    + * Subclasses may change the name, namespace, content, or other + * characteristics of the Element returned. + * Subclasses may return null to indicate the + * Element should not be created. + * However, doing so will only remove the element's start-tag and + * end-tag from the result tree. Any content inside the element + * will be attached to the element's parent by default, unless it + * too is filtered. To remove an entire element, return an empty + * Nodes object from the + * finishMakingElement() method. + *

    + * + * @param name the qualified name of the element + * @param namespace the namespace URI of the element + * + * @return the new element + */ + public Element startMakingElement(String name, String namespace) { + return new Element(name, namespace); + } + + + /** + *

    + * Signals the end of an element. This method should return + * the Nodes to be added to the tree. + * They need not contain the Element that + * was passed to this method, though most often they will. + * By default the Nodes returned contain + * only the built element. However, subclasses may return + * a list containing any number of nodes, all of which will be + * added to the tree at the current position in the order given by + * the list (subject to the usual well-formedness constraints, of + * course. For instance, the list should not contain a + * DocType object unless the element is the root + * element, and the document does not already have a + * DocType). All of the nodes returned must be + * parentless. If this method returns an empty list, + * then the element (including all its contents) is not included + * in the finished document. + *

    + * + *

    + * To process an element at a time, override this method in a + * subclass so that it functions as a callback. When you're done + * processing the Element, return an empty list so + * that it will be removed from the tree and garbage collected. + * Be careful not to return an empty list for the root element + * though. That is, when the element passed to this method is the + * root element, the list returned must contain exactly one + * Element object. The simplest way to check this + * is testing if element.getParent() instanceof + * Document. + *

    + * + *

    + * Do not detach element or any of its ancestors + * while inside this method. Doing so can royally muck up the + * build. + *

    + * + * @param element the finished Element + * + * @return the nodes to be added to the tree + * + */ + public Nodes finishMakingElement(Element element) { + return new Nodes(element); + } + + + /** + *

    + * Creates a new Document object. + * The root element of this document is initially set to + * <root xmlns="http://www.xom.nu/fakeRoot"/>. + * This is only temporary. As soon as the real root element's + * start-tag is read, this element is replaced by the real root. + * This fake root should never be exposed. + *

    + * + *

    + * The builder calls this method at the beginning of + * each document, before it calls any other method in this class. + * Thus this is a useful place to perform per-document + * initialization tasks. + *

    + * + *

    + * Subclasses may change the root element, content, + * or other characteristics of the document + * returned. However, this method must not return null + * or the builder will throw a ParsingException. + *

    + * + * @return the newly created Document + */ + public Document startMakingDocument() { + return new Document( + Element.build("root", "http://www.xom.nu/fakeRoot", "root") + ); + } + + + /** + *

    + * Signals the end of a document. The default implementation of + * this method does nothing. The builder does not + * call this method if an exception is thrown while building + * a document. + *

    + * + * @param document the completed Document + */ + public void finishMakingDocument(Document document) {} + + + /** + *

    + * Returns a new Nodes object containing an + * attribute in the specified namespace + * with the specified name and type. + *

    + * + *

    + * Subclasses may change the nodes returned from this method. + * They may return a Nodes object containing any + * number of children and attributes which are appended and + * added to the current parent element. This Nodes + * object may not contain any Document objects. + * All of the nodes returned must be parentless. + * Subclasses may return an empty Nodes to indicate + * the attribute should not be created. + *

    + * + * @param name the prefixed name of the attribute + * @param URI the namespace URI + * @param value the attribute value + * @param type the attribute type + * + * @return the nodes to be added to the tree + */ + public Nodes makeAttribute(String name, String URI, + String value, Attribute.Type type) { + return new Nodes(new Attribute(name, URI, value, type)); + } + + + /** + *

    + * Returns a new Nodes object containing a + * comment with the specified text. + *

    + * + *

    + * Subclasses may change the content or other + * characteristics of the comment returned. + * Subclasses may change the nodes returned from this method. + * They may return a Nodes object containing any + * number of children and attributes which are appended and + * added to the current parent element. This Nodes + * object should not contain any Document objects. + * All of the nodes returned must be parentless. + * Subclasses may return an empty Nodes to indicate + * the comment should not be included in the + * finished document. + *

    + * + * @param data the complete text content of the comment + * + * @return the nodes to be added to the tree + */ + public Nodes makeComment(String data) { + return new Nodes(new Comment(data)); + } + + + /** + *

    + * Returns a new Nodes object containing a + * DocType object with the specified root element + * name, system ID, and public ID. + *

    + * + *

    + * Subclasses may change the root element name, public ID, + * system ID, or other characteristics of the DocType + * returned. Subclasses may change the nodes returned from this + * method. They may return a Nodes object containing + * any number of comments and processing instructions which are + * appended to the current parent node. This Nodes + * object may not contain any Document, + * Element, Attribute, or + * Text objects. All of the nodes returned must be + * parentless. Subclasses may return an empty Nodes to + * indicate the DocType should not be included in the + * finished document. + *

    + * + * @param rootElementName the declared, qualified name + * for the root element + * @param publicID the public ID of the external DTD subset + * @param systemID the URL of the external DTD subset + * + * @return the nodes to be added to the document + */ + public Nodes makeDocType(String rootElementName, + String publicID, String systemID) { + return new Nodes(new DocType(rootElementName, publicID, systemID)); + } + + + /** + *

    + * Returns a new Nodes object containing a + * text node with the specified content. + *

    + * + *

    + * Subclasses may change the content or other characteristics of + * the text returned. Subclasses may also change the nodes + * returned from this method. They may return a Nodes + * object containing any number of nodes which are added or + * appended to the current parent node. This Nodes + * object must not contain any Document nodes. All of + * the nodes returned must be parentless. Subclasses may return an + * empty Nodes to indicate the text should not be + * included in the finished document. + *

    + * + * @param data the complete text content of the node + * + * @return the nodes to be added to the tree + */ + public Nodes makeText(String data) { + return new Nodes(new Text(data)); + } + + + /** + *

    + * Returns a new Nodes object containing a + * CDATASection node with the specified content. + *

    + * + * @param data the complete text content of the node + * + * @return the nodes to be added to the tree + */ + Nodes makeCDATASection(String data) { + return makeText(data); + } + + + /** + *

    + * Returns a new Nodes object containing a + * new ProcessingInstruction object with + * the specified target and data. + *

    + * + *

    + * Subclasses may change the target, data, or other + * characteristics of the ProcessingInstruction + * returned. Subclasses may change the nodes returned from this + * method. They may return a Nodes object containing + * any number of nodes which are added or + * appended to the current parent node. This Nodes + * object must not contain any Document nodes. + * If the processing instruction appears in the prolog or epilog + * of the document, then it must also not contain any + * Element, Attribute, or + * Text objects. + * All of the nodes returned must be parentless. Subclasses + * may return an empty Nodes to indicate the + * processing instruction should not be included in the + * finished document. + *

    + * + * @param target the target of the processing instruction + * @param data the data of the processing instruction + * + * @return the nodes to be added to the tree + */ + public Nodes makeProcessingInstruction( + String target, String data) { + return new Nodes(new ProcessingInstruction(target, data)); + } + + + void addAttribute(Element element, Attribute attribute) { + element.addAttribute(attribute); + } + + + void insertChild(Element element, Node child, int position) { + element.insertChild(child, position); + } + + +} diff --git a/src/nu/xom/Nodes.java b/src/nu/xom/Nodes.java new file mode 100644 index 0000000..397586e --- /dev/null +++ b/src/nu/xom/Nodes.java @@ -0,0 +1,198 @@ +/* Copyright 2002, 2003, 2005, 2018 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +/** + * + *

    + * Implements a list of nodes for traversal purposes. + * Changes to the document from which this list was generated + * are not reflected in this list, nor are changes to the list + * reflected in the document. Changes to the individual + * Node objects in the list and the document + * are reflected in the other one. + *

    + * + *

    + * There is no requirement that the list not contain duplicates, + * or that all the members come from the same document. It is simply + * a list of nodes. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.3.0 + * + */ +public final class Nodes implements Iterable { + + private final List nodes; + + + /** + *

    + * Creates an empty node list. + *

    + */ + public Nodes() { + nodes = new ArrayList(); + } + + + /** + *

    + * Creates a node list containing a single node. + *

    + * + * @param node the node to insert in the list + * + * @throws NullPointerException if node is null + */ + public Nodes(Node node) { + + if (node == null) { + throw new NullPointerException("Nodes content must be non-null"); + } + nodes = new ArrayList(1); + nodes.add(node); + + } + + + Nodes(List results) { + this.nodes = results; + } + + + /** + *

    + * Returns the number of nodes in the list. + * This is guaranteed to be non-negative. + *

    + * + * @return the number of nodes in the list + */ + public int size() { + return nodes.size(); + } + + + /** + *

    + * Returns the indexth node in the list. + * The first node has index 0. The last node + * has index size()-1. + *

    + * + * @param index the node to return + * + * @return the node at the specified position + * + * @throws IndexOutOfBoundsException if index is + * negative or greater than or equal to the size of the list + */ + public Node get(int index) { + return (Node) nodes.get(index); + } + + + /** + *

    + * Removes the indexthnode in the list. + * Subsequent nodes have their indexes reduced by one. + *

    + * + * @param index the node to remove + * + * @return the node at the specified position + * + * @throws IndexOutOfBoundsException if index is + * negative or greater than or equal to the size of the list + */ + public Node remove(int index) { + return (Node) nodes.remove(index); + } + + + /** + *

    + * Inserts a node at the indexth position in the list. + * Subsequent nodes have their indexes increased by one. + *

    + * + * @param node the node to insert + * @param index the position at which to insert the node + * + * @throws IndexOutOfBoundsException if index is + * negative or strictly greater than the size of the list + * @throws NullPointerException if node is null + */ + public void insert(Node node, int index) { + if (node == null) { + throw new NullPointerException("Nodes content must be non-null"); + } + nodes.add(index, node); + } + + + /** + *

    + * Adds a node at the end of this list. + *

    + * + * @param node the node to add to the list + * + * @throws NullPointerException if node is null + */ + public void append(Node node) { + if (node == null) { + throw new NullPointerException("Nodes content must be non-null"); + } + nodes.add(node); + } + + + /** + *

    + * Determines whether a node is contained in this list. + *

    + * + * @param node the node to search for + * + * @return true if this list contains the specified node; + * false otherwise + */ + public boolean contains(Node node) { + return nodes.contains(node); + } + + + @Override + public Iterator iterator() { + return nodes.iterator(); + } + + +} \ No newline at end of file diff --git a/src/nu/xom/NonVerifyingHandler.java b/src/nu/xom/NonVerifyingHandler.java new file mode 100644 index 0000000..082847a --- /dev/null +++ b/src/nu/xom/NonVerifyingHandler.java @@ -0,0 +1,245 @@ +/* Copyright 2002-2006, 2009, 2014 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + + +package nu.xom; + +import org.xml.sax.SAXException; + +/** + * @author Elliotte Rusty Harold + * @version 1.2.11 + * + */ +class NonVerifyingHandler extends XOMHandler { + + NonVerifyingHandler(NodeFactory factory) { + super(factory); + } + + + public void startElement(String namespaceURI, String localName, + String qualifiedName, org.xml.sax.Attributes attributes) throws SAXException { + + flushText(); + Element element = Element.build(qualifiedName, namespaceURI, localName); + if (parent == document) { // root + document.setRootElement(element); + inProlog = false; + } + + current = element; + // Need to push this, even if it's null + parents.add(element); + + if (parent != document) { + // a.k.a. parent not instanceof Document + parent.fastInsertChild(element, parent.getChildCount()); + } + // This is optimized for the very common case where + // everything in the document has the same actual base URI. + // It may add redundant base URIs in cases like XInclude + // where different parts of the document have different + // base URIs. + String baseURI = locator.getSystemId(); + if (baseURI != null && !baseURI.equals(documentBaseURI)) { + element.setActualBaseURI(baseURI); + } + + // Attach the attributes; this must be done before the + // namespaces are attached. + int length = attributes.getLength(); + + // We've got a pretty good guess at how many attributes there + // will be here; we could ensureCapacity up to that length. + // However, that might waste memory because we wouldn't use + // the ones for namespace declarations. We could always + // trimToSize when we're done, but it's probably not worth + // the effort. + for (int i = 0; i < length; i++) { + String qName = attributes.getQName(i); + if (qName.startsWith("xmlns:") || qName.equals("xmlns")) { + continue; + } + else { + String namespace = attributes.getURI(i); + String value = attributes.getValue(i); + Attribute attribute = Attribute.build( + qName, + namespace, + value, + convertStringToType(attributes.getType(i)), + attributes.getLocalName(i) + ); + element.fastAddAttribute(attribute); + } + } + + // Attach the namespaces + for (int i = 0; i < length; i++) { + String qName = attributes.getQName(i); + if (qName.startsWith("xmlns:")) { + String namespaceName = attributes.getValue(i); + String namespacePrefix = qName.substring(6); + String currentValue + = element.getNamespaceURI(namespacePrefix); + if (!namespaceName.equals(currentValue)) { + element.addNamespaceDeclaration( + namespacePrefix, namespaceName); + } + } + else if (qName.equals("xmlns")) { + String namespaceName = attributes.getValue(i); + String namespacePrefix = ""; + String currentValue + = element.getNamespaceURI(namespacePrefix); + if (!namespaceName.equals(currentValue)) { + element.addNamespaceDeclaration(namespacePrefix, + namespaceName); + } + } + } + + // this is the new parent + parent = element; + } + + + public void endElement( + String namespaceURI, String localName, String qualifiedName) { + + // If we're immediately inside a skipped element + // we need to reset current to null, not to the parent + current = (ParentNode) parents.remove(parents.size()-1); + flushText(); + + parent = current.getParent(); + + if (parent.isDocument()) { // root element + Document doc = (Document) parent; + doc.setRootElement((Element) current); + } + + } + + + // accumulate all text that's in the buffer into a text node + private void flushText() { + + if (buffer != null) { + textString = buffer.toString(); + buffer = null; + } + + if (textString != null) { + Text result; + if (!inCDATA) { + result = Text.build(textString); + } + else { + result = CDATASection.build(textString); + } + parent.fastInsertChild(result, parent.getChildCount()); + textString = null; + } + inCDATA = false; + finishedCDATA = false; + + } + + + public void processingInstruction(String target, String data) throws SAXException { + + // simplify logic???? into two cases in and not in DTD + // ditto for comment() method and superclass + if (!inDTD) flushText(); + else if (!inInternalSubset()) return; + + ProcessingInstruction result = ProcessingInstruction.build(target, data); + + if (!inDTD) { + if (inProlog) { + parent.fastInsertChild(result, position); + position++; + } + else { + parent.fastInsertChild(result, parent.getChildCount()); + } + } + else { + internalDTDSubset.append(" "); + internalDTDSubset.append(result.toXML()); + internalDTDSubset.append("\n"); + } + + } + + + // LexicalHandler events + public void startDTD(String rootName, String publicID, + String systemID) { + + inDTD = true; + DocType doctype = DocType.build(rootName, publicID, systemID); + document.fastInsertChild(doctype, position); + position++; + internalDTDSubset = new StringBuffer(); + this.doctype = doctype; + + } + + + public void comment(char[] text, int start, int length) throws SAXException { + + if (!inDTD) flushText(); + else if (!inInternalSubset()) return; + + Comment result = Comment.build(new String(text, start, length)); + + if (!inDTD) { + if (inProlog) { + parent.insertChild(result, position); + position++; + } + else { + parent.fastInsertChild(result, parent.getChildCount()); + } + } + else { + internalDTDSubset.append(" "); + internalDTDSubset.append(result.toXML()); + internalDTDSubset.append("\n"); + } + + } + + + public void endDTD() { + + inDTD = false; + if (doctype != null) { + doctype.fastSetInternalDTDSubset(internalDTDSubset.toString()); + } + + } + + +} \ No newline at end of file diff --git a/src/nu/xom/ParentNode.java b/src/nu/xom/ParentNode.java new file mode 100644 index 0000000..12ec1bc --- /dev/null +++ b/src/nu/xom/ParentNode.java @@ -0,0 +1,462 @@ +/* Copyright 2002-2005, 2018 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom; + + +/** + * + *

    + * The generic superclass for nodes that have children. + * Not counting subclasses, there are exactly two such public + * classes in XOM: + *

    + * + *
      + *
    • Document
    • + *
    • Element
    • + *
    + * + *

    + * This class provides methods to add and remove child nodes. + *

    + * + * + * @author Elliotte Rusty Harold + * @version 1.3.0 + * + */ +public abstract class ParentNode extends Node { + + Node[] children; + int childCount = 0; + String actualBaseURI; + + /** + *

    + * Creates a new ParentNode object. + * Can only be invoked by other members of + * the nu.xom package. + *

    + * + */ + ParentNode() {} + + + /** + *

    + * Returns the number of child nodes this node contains. + * This is always greater than or equal to 0. + *

    + * + * @return the number of children of this node + */ + public int getChildCount() { + return childCount; + } + + + /** + *

    + * Inserts a child node at the specified position. + * The child node previously at that position (if any) + * and all subsequent child nodes are moved up by one. + * That is, when inserting a node at 2, the old node at 2 + * is moved to 3, the old child at 3 is moved to 4, and so + * forth. Inserting at position 0 makes the child the first + * child of this node. Inserting at the position + * getChildCount() makes the child the + * last child of the node. + *

    + * + *

    + * All the other methods that add a node to the tree ultimately + * invoke this method. + *

    + * + * @param position where to insert the child + * @param child the node to insert + * + * @throws IllegalAddException if this node cannot have a child of + * the argument's type + * @throws MultipleParentException if child already + * has a parent + * @throws NullPointerException if child is null + * @throws IndexOutOfBoundsException if the position is negative or + * greater than the number of children of this node + */ + public void insertChild(Node child, int position) { + _insertChild(child, position); + } + + + // because this method is called from Document constructor and + // constructors should not call overridable methods + final void _insertChild(Node child, int position) { + insertionAllowed(child, position); + fastInsertChild(child, position); + } + + + void fastInsertChild(Node child, int position) { + if (position > childCount) { + throw new IndexOutOfBoundsException("Inserted node at position " + position + " after children"); + } + checkCapacity(childCount+1); + if (position < childCount) { + // make space in the middle for the new child; otherwise just add it at the end + System.arraycopy(children, position, children, position+1, childCount-position); + } + children[position] = child; + childCount++; + child.setParent(this); + } + + + private void checkCapacity(int position) { + + if (children == null) { + children = new Node[1]; + } + else if (position >= children.length) { + Node[] data = new Node[children.length * 2]; + System.arraycopy(children, 0, data, 0, children.length); + this.children = data; + } + + } + + + abstract void insertionAllowed(Node child, int position); + + + /** + *

    + * Appends a node to the children of this node. + *

    + * + * @param child node to append to this node + * + * @throws IllegalAddException if this node cannot have children + * of this type + * @throws MultipleParentException if child already has a parent + * @throws NullPointerException if child is null + * + */ + public void appendChild(Node child) { + insertChild(child, childCount); + } + + + /** + *

    + * Returns the child of this node at the specified position. + * Indexes begin at 0 and count up to one less than the number + * of children in this node. + *

    + * + * @param position index of the node to return + * + * @return the node at the requested position + * + * @throws IndexOutOfBoundsException if the index is negative or + * greater than or equal to the number of children of this node + */ + public Node getChild(int position) { + + if (children == null) { + throw new IndexOutOfBoundsException( + "This node has no children" + ); + } + return children[position]; + + } + + + // private int lastPosition = -1; + + /** + *

    + * Returns the position of a node within the children of this + * node. This is a number between 0 and one less than the number of + * children of this node. It returns -1 if child + * does not have this node as a parent. + *

    + * + *

    + * This method does a linear search through the node's children. + * On average, it executes in O(N) where N is the number of + * children of the node. + *

    + * + * @param child the node whose position is desired + * + * @return the position of the argument node among + * the children of this node + */ + public int indexOf(Node child) { + + if (children == null) return -1; + + // Programs tend to iterate through in order so we store the + // last index returned and check the one immediately after it + // first; before searching the list from the beginning. + /* lastPosition++; + if (lastPosition != children.size()) { + if (child == children.get(lastPosition)) { + return lastPosition; + } + else lastPosition = -1; + } + lastPosition = children.indexOf(child); + return lastPosition; */ + for (int i = 0; i < childCount; i++) { + if (child == children[i]) return i; + } + return -1; + + } + + + /** + *

    + * Removes the child of this node at the specified position. + * Indexes begin at 0 and count up to one less than the number + * of children in this node. + *

    + * + * @param position index of the node to remove + * + * @return the node which was removed + * + * @throws IndexOutOfBoundsException if the index is negative or + * greater than or equal to the number of children of this node + */ + public Node removeChild(int position) { + + if (children == null) { + throw new IndexOutOfBoundsException( + "This node has no children" + ); + } + Node removed = children[position]; + // fill in actual base URI + // This way does add base URIs to elements created in memory + // XXX but this is a HotSpot when building; we need a fastRemoveChild + if (removed.isElement()) fillInBaseURI((Element) removed); + + int toCopy = childCount-position-1; + if (toCopy > 0) { + System.arraycopy(children, position+1, children, position, toCopy); + } + childCount--; + children[childCount] = null; + removed.setParent(null); + + return removed; + + } + + + void fillInBaseURI(Element removed) { + + ParentNode parent = removed; + String actualBaseURI = ""; + while (parent != null && actualBaseURI.equals("")) { + actualBaseURI = parent.getActualBaseURI(); + parent = parent.getParent(); + } + removed.setActualBaseURI(actualBaseURI); + + } + + + /** + *

    + * Removes the specified child of this node. + *

    + * + * @param child child node to remove + * + * @return the node which was removed + * + * @throws NoSuchChildException if child is + * not in fact a child of this node + */ + public Node removeChild(Node child) { + + if (children == null) { + throw new NoSuchChildException( + "Child does not belong to this node" + ); + } + // This next line is a hot spot + int position = indexOf(child); + if (position == -1) { + throw new NoSuchChildException( + "Child does not belong to this node" + ); + } + if (child.isElement()) fillInBaseURI((Element) child); + removeChild(position); + + child.setParent(null); + + return child; + + } + + + /** + *

    + * Replaces an existing child with a new child node. + * If oldChild is not a child of this node, + * then a NoSuchChildException is thrown. + *

    + * + * @param oldChild the node removed from the tree + * @param newChild the node inserted into the tree + * + * @throws MultipleParentException if newChild already + * has a parent + * @throws NoSuchChildException if oldChild + * is not a child of this node + * @throws NullPointerException if either argument is null + * @throws IllegalAddException if this node cannot have children + * of the type of newChild + */ + public void replaceChild(Node oldChild, Node newChild) { + + if (oldChild == null) { + throw new NullPointerException( + "Tried to replace null child" + ); + } + if (newChild == null) { + throw new NullPointerException( + "Tried to replace child with null" + ); + } + if (children == null) { + throw new NoSuchChildException( + "Reference node is not a child of this node." + ); + } + int position = indexOf(oldChild); + if (position == -1) { + throw new NoSuchChildException( + "Reference node is not a child of this node." + ); + } + + if (oldChild == newChild) return; + + insertionAllowed(newChild, position); + removeChild(position); + insertChild(newChild, position); + + } + + + /** + * + *

    + * Sets the URI against which relative URIs in this node will be + * resolved. Generally, it's only necessary to set this property if + * it's different from a node's parent's base URI, as it may + * be in a document assembled from multiple entities + * or by XInclude. + *

    + * + *

    + * Relative URIs are not allowed here. Base URIs must be absolute. + * However, the base URI may be set to null or the empty string + * to indicate that the node has no explicit base URI. In this + * case, it inherits the base URI of its parent node, if any. + *

    + * + *

    + * URIs with fragment identifiers are also not allowed. The value + * passed to this method must be a pure URI, not a URI reference. + *

    + * + *

    + * You can also add an xml:base attribute to + * an element in the same way you'd add any other namespaced + * attribute to an element. If an element's base URI + * conflicts with its xml:base attribute, + * then the value found in the xml:base attribute + * is used. + *

    + * + *

    + * If the base URI is null or the empty string and there is + * no xml:base attribute, then the base URI is + * determined by the nearest ancestor node which does have a + * base URI. Moving such a node from one location to another + * can change its base URI. + *

    + * + * @param URI the new base URI for this node + * + * @throws MalformedURIException if URI is + * not a legal RFC 3986 absolute URI + */ + public abstract void setBaseURI(String URI); + + + String getActualBaseURI() { + if (actualBaseURI == null) return ""; + return actualBaseURI; + } + + + void setActualBaseURI(String uri) { + if (uri == null) uri = ""; + if (!"".equals(uri)) Verifier.checkAbsoluteURI(uri); + actualBaseURI = uri; + } + + + final String findActualBaseURI() { + + ParentNode current = this; + while (true) { + String actualBase = current.getActualBaseURI(); + ParentNode parent = current.getParent(); + + if (parent == null) return actualBase; + + if ("".equals(actualBase)) { + current = parent; + continue; + } + + // The parent is loaded from a different entity. + // Therefore just return the actual base. + return actualBase; + } + + } + + +} diff --git a/src/nu/xom/ParsingException.java b/src/nu/xom/ParsingException.java new file mode 100644 index 0000000..4c04edc --- /dev/null +++ b/src/nu/xom/ParsingException.java @@ -0,0 +1,299 @@ +/* Copyright 2002-2004 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom; + +/** + *

    + * The generic superclass for all the + * checked exceptions thrown in XOM. The general + * principle followed is that anything that could + * plausibly be detected by testing such as + * using spaces in an element name is a runtime exception. + * Exceptions that depend on environmental conditions, + * such as might occur when parsing an external file, + * are checked exceptions, because these depend on variable input, + * and thus problems may not all be detected during testing. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.1b3 + * + */ +public class ParsingException extends Exception { + + + private static final long serialVersionUID = -5185450480590174490L; + + private Throwable cause; + private int lineNumber = -1; + private int columnNumber = -1; + private String uri; + + + /** + *

    + * Creates a new ParsingException with a detail message + * and an underlying root cause. + *

    + * + * @param message a string indicating the specific problem + * @param cause the original cause of this exception + */ + public ParsingException(String message, Throwable cause) { + super(message); + this.initCause(cause); + } + + + /** + *

    + * Creates a new ParsingException with a detail message + * and an underlying root cause. + *

    + * + * @param message a string indicating the specific problem + * @param uri the URI of the document that caused this exception + * @param cause the original cause of this exception + */ + public ParsingException(String message, String uri, Throwable cause) { + super(message); + this.uri = uri; + this.initCause(cause); + } + + + /** + *

    + * Creates a new ParsingException with a detail message + * and line and column numbers. + *

    + * + * @param message a string indicating the specific problem + * @param lineNumber the approximate line number + * where the problem occurs + * @param columnNumber the approximate column number + * where the problem occurs + */ + public ParsingException(String message, + int lineNumber, int columnNumber) { + this(message, null, lineNumber, columnNumber, null); + } + + + /** + *

    + * Creates a new ParsingException with a detail message + * and line and column numbers. + *

    + * + * @param message a string indicating the specific problem + * @param uri the URI of the document that caused this exception + * @param lineNumber the approximate line number + * where the problem occurs + * @param columnNumber the approximate column number + * where the problem occurs + */ + public ParsingException(String message, String uri, + int lineNumber, int columnNumber) { + this(message, uri, lineNumber, columnNumber, null); + } + + + /** + *

    + * Creates a new ParsingException with a detail + * message, line and column numbers, and an underlying exception. + *

    + * + * @param message a string indicating the specific problem + * @param uri the URI of the document that caused this exception + * @param lineNumber the approximate line number + * where the problem occurs + * @param columnNumber the approximate column number + * where the problem occurs + * @param cause the original cause of this exception + */ + public ParsingException(String message, String uri, int lineNumber, + int columnNumber, Throwable cause) { + super(message); + this.lineNumber = lineNumber; + this.columnNumber = columnNumber; + this.uri = uri; + this.initCause(cause); + } + + + /** + *

    + * Creates a new ParsingException with a detail + * message, line and column numbers, and an underlying exception. + *

    + * + * @param message a string indicating the specific problem + * @param lineNumber the approximate line number + * where the problem occurs + * @param columnNumber the approximate column number + * where the problem occurs + * @param cause the original cause of this exception + */ + public ParsingException(String message, int lineNumber, + int columnNumber, Throwable cause) { + super(message); + this.lineNumber = lineNumber; + this.columnNumber = columnNumber; + this.initCause(cause); + } + + + /** + *

    + * Creates a new ParsingException with a detail message. + *

    + * + * @param message a string indicating the specific problem + */ + public ParsingException(String message) { + super(message); + } + + + /** + *

    + * Returns the approximate row number of the construct that + * caused this exception. If the row number is not known, + * -1 is returned. + *

    + * + * @return row number where the exception occurred + */ + public int getLineNumber() { + return this.lineNumber; + } + + /** + *

    + * Returns the approximate column number of the construct that + * caused this exception. If the column number is not known, + * -1 is returned. + *

    + * + * @return column number where the exception occurred + */ + public int getColumnNumber() { + return this.columnNumber; + } + + + /** + *

    + * Returns the system ID (generally a URL) of the document that + * caused this exception. If this is not known, for instance + * because the document was parsed from a raw input stream or from + * a string, it returns null. + *

    + * + * @return the URI of the document that caused this exception + */ + public String getURI() { + return this.uri; + } + + + // null is insufficient for determining unset cause. + // The cause may be set to null which may not then be reset. + private boolean causeSet = false; + + /** + *

    + * Sets the root cause of this exception. This may + * only be called once. Subsequent calls throw an + * IllegalStateException. + *

    + * + *

    + * This method is unnecessary in Java 1.4 where it could easily be + * inherited from the superclass. However, including it here + * allows this method to be used in Java 1.3 and earlier. + *

    + * + * @param cause the root cause of this exception + * + * @return this XMLException + * + * @throws IllegalArgumentException if the cause is this exception + * (An exception cannot be its own cause.) + * @throws IllegalStateException if this method is called twice + */ + public final Throwable initCause(Throwable cause) { + + if (causeSet) { + throw new IllegalStateException("Can't overwrite cause"); + } + else if (cause == this) { + throw new IllegalArgumentException("Self-causation not permitted"); + } + else this.cause = cause; + causeSet = true; + return this; + + } + + + /** + *

    + * Returns the underlying exception that caused this exception. + *

    + * + * @return the root exception that caused this exception + * to be thrown + */ + public Throwable getCause() { + return this.cause; + } + + + /** + *

    + * Returns a string suitable for display to the developer + * summarizing what went wrong where. + *

    + * + * @return an exception message suitable for display to a developer + */ + public String toString() { + StringBuffer result = new StringBuffer(super.toString()); + if (lineNumber >= 0) { + result.append(" at line "); + result.append(lineNumber); + result.append(", column "); + result.append(columnNumber); + } + + if (this.uri != null) { + result.append(" in "); + result.append(uri); + } + return result.toString(); + } + + +} \ No newline at end of file diff --git a/src/nu/xom/ProcessingInstruction.java b/src/nu/xom/ProcessingInstruction.java new file mode 100644 index 0000000..b9d2ebc --- /dev/null +++ b/src/nu/xom/ProcessingInstruction.java @@ -0,0 +1,301 @@ +/* Copyright 2002-2004 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + + +package nu.xom; + +/** + *

    + * This class represents an XML processing instruction. + * Each processing instruction has two key properties: + *

    + * + *
      + *
    • The target, a non-colonized name
    • + *
    • The data, a string which does not contain the two character + * sequence ?>. The syntax of the data + * depends completely on the processing instruction. + * Other than forbidding ?>, XML defines + * no rules for processing instruction data. + *
    • + *
    + * + * @author Elliotte Rusty Harold + * @version 1.3.0 + * + */ +public class ProcessingInstruction extends Node { + + private String target; + private String data; + + + /** + *

    + * Create a processing instruction with a certain target and data. + *

    + * + * @param target the target of the processing instruction + * @param data the processing instruction data + * + * @throws IllegalTargetException if the target is not a + * non-colonized name or is the string "xml" in any case + * @throws IllegalDataException if data contains "?>" or any + * other illegal characters + */ + public ProcessingInstruction(String target, String data) { + _setTarget(target); + _setValue(data); + } + + + /** + *

    + * Create a copy of a processing instruction. + *

    + * + * @param instruction the processing instruction to copy + * + */ + public ProcessingInstruction(ProcessingInstruction instruction) { + this.target = instruction.target; + this.data = instruction.data; + } + + + private ProcessingInstruction() {} + + static ProcessingInstruction build(String target, String data) { + ProcessingInstruction result = new ProcessingInstruction(); + result.target = target; + result.data = data; + return result; + } + + + /** + *

    + * Returns the processing instruction target. + *

    + * + * @return the target + */ + public final String getTarget() { + return target; + } + + + /** + *

    + * Sets the target. + *

    + * + * @param target the new target + * + * @throws IllegalTargetException if the proposed target + * is not an XML 1.0 non-colonized name or is the string + * "xml" in any case + */ + public void setTarget(String target) { + _setTarget(target); + } + + + private void _setTarget(String target) { + + try { + Verifier.checkNCName(target); + } + catch (IllegalNameException ex) { + IllegalTargetException tex = new IllegalTargetException(ex.getMessage()); + tex.setData(target); + throw tex; + } + + if (target.equalsIgnoreCase("xml")) { + IllegalTargetException tex = new IllegalTargetException( + target + " is not a legal processing instruction target." + ); + tex.setData(target); + throw tex; + } + + this.target = target; + + } + + + /** + *

    + * Sets the data. + *

    + * + * @param data the data to set + * + * @throws IllegalDataException if data is null + * or otherwise not legal XML processing instruction data + */ + public void setValue(String data) { + _setValue(data); + } + + + private void _setValue(String data) { + + Verifier.checkPCDATA(data); + if (data.length() != 0) { + if (data.indexOf("?>") >= 0) { + IllegalDataException ex = new IllegalDataException( + "Processing instruction data must not contain \"?>\"" + ); + ex.setData(data); + throw ex; + } + if (data.indexOf('\r') >= 0) { + IllegalDataException ex = new IllegalDataException( + "Processing instruction data cannot contain carriage returns" + ); + ex.setData(data); + throw ex; + } + + char first = data.charAt(0); + if (first == ' ' || first == '\n' || first == '\t') { + IllegalDataException ex = new IllegalDataException( + "Processing instruction data cannot contain " + + "leading white space" + ); + ex.setData(data); + throw ex; + } + } + this.data = data; + + } + + + /** + *

    + * Returns the processing instruction data. + *

    + * + * @return the data of the processing instruction + * + */ + public final String getValue() { + return data; + } + + + /** + *

    + * Throws IndexOutOfBoundsException because + * processing instructions do not have children. + *

    + * + * @return never returns because processing instructions do not + * have children; always throws an exception. + * + * @param position the index of the child node to return + * + * @throws IndexOutOfBoundsException because processing + * instructions do not have children + */ + public final Node getChild(int position) { + throw new IndexOutOfBoundsException( + "LeafNodes do not have children"); + } + + + /** + *

    + * Returns 0 because processing instructions do not have children. + *

    + * + * @return zero + */ + public final int getChildCount() { + return 0; + } + + + /** + *

    + * Returns the actual XML form of this processing instruction, + * such as might be copied and pasted from the original document. + *

    + * + * @return an XML representation of this processing instruction + * as a String + */ + public final String toXML() { + + StringBuffer result = new StringBuffer(" 0) { + result.append(' '); + result.append(data); + } + result.append("?>"); + return result.toString(); + + } + + + /** + *

    + * Returns a deep copy of this processing instruction with no + * parent, that can be added to this document or a different + * one. + *

    + * + * @return a copy of this ProcessingInstruction + * with no parent + */ + public ProcessingInstruction copy() { + return new ProcessingInstruction(target, data); + } + + + boolean isProcessingInstruction() { + return true; + } + + + /** + *

    + * Returns a String representation + * of this processing instruction suitable for + * debugging and diagnosis. This is not + * the XML representation of this processing instruction. + *

    + * + * @return a non-XML string representation of this + * ProcessingInstruction + */ + public final String toString() { + return "[" + getClass().getName() + ": target=\"" + + target + "\"; data=\"" + + Text.escapeLineBreaksAndTruncate(data) +"\"]"; + } + + +} diff --git a/src/nu/xom/Serializer.java b/src/nu/xom/Serializer.java new file mode 100644 index 0000000..8778a90 --- /dev/null +++ b/src/nu/xom/Serializer.java @@ -0,0 +1,1307 @@ +/* Copyright 2002-2006 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom; + +import java.io.IOException; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.UnsupportedEncodingException; +import java.io.Writer; +import java.util.Locale; +import org.xml.sax.helpers.NamespaceSupport; + +/** + *

    + * Outputs a Document object in a specific encoding using + * various options for controlling white space, normalization, + * indenting, line breaking, and base URIs. However, in general these + * options do affect the document's infoset. In particular, if you set + * either the maximum line length or the indent size to a positive + * value, then the serializer will not respect input white space. It + * may trim leading and trailing space, condense runs of white + * space to a single space, convert carriage returns and linefeeds + * to spaces, add extra space where none was present before, + * and otherwise muck with the document's white space. + * The defaults, however, preserve all significant white space + * including ignorable white space and boundary white space. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.2d1 + * + */ +public class Serializer { + + private TextWriter escaper; + private boolean preserveBaseURI = false; + // ???? reset when exception is thrown? + private NamespaceSupport namespaces = new NamespaceSupport(); + + + /** + *

    + * Create a new serializer that uses the UTF-8 encoding. + *

    + * + * @param out the output stream to write the document on + * + * @throws NullPointerException if out is null + */ + public Serializer(OutputStream out) { + + try { + this.setOutputStream(out, "UTF-8"); + } + catch (UnsupportedEncodingException ex) { + throw new RuntimeException( + "The VM is broken. It does not understand UTF-8."); + } + + } + + + /** + *

    + * Create a new serializer that uses the specified encoding. + * The encoding must be recognized by the Java virtual machine. If + * you attempt to use an encoding that the local Java virtual + * machine does not support, the constructor will throw an + * UnsupportedEncodingException. + * Currently the following encodings are recognized by XOM: + *

    + * + *
      + *
    • UTF-8
    • + *
    • UTF-16
    • + *
    • UTF-16BE
    • + *
    • UTF-16LE
    • + *
    • ISO-10646-UCS-2
    • + *
    • ISO-8859-1
    • + *
    • ISO-8859-2
    • + *
    • ISO-8859-3
    • + *
    • ISO-8859-4
    • + *
    • ISO-8859-5
    • + *
    • ISO-8859-6
    • + *
    • ISO-8859-7
    • + *
    • ISO-8859-8
    • + *
    • ISO-8859-9
    • + *
    • ISO-8859-10
    • + *
    • ISO-8859-11 (a.k.a. TIS-620)
    • + *
    • ISO-8859-13
    • + *
    • ISO-8859-14
    • + *
    • ISO-8859-15
    • + *
    • ISO-8859-16
    • + *
    • IBM037 (a.k.a. CP037, EBCDIC-CP-US, EBCDIC-CP-CA, + * EBCDIC-CP-WA, EBCDIC-CP-NL, and CSIBM037)
    • + *
    • GB18030
    • + *
    + * + *

    + * You can use encodings not in this list if the virtual + * machine supports them. However, they may be + * significantly slower than the encodings in this list. + *

    + * + *

    + * I've noticed Java has significant bugs in its handling of some + * of these encodings. In some cases such as 0x80 in Big5, XOM + * will escape a character that should not need to be escaped + * because Java can't output that character in the specified + * encoding, even though the output character set does contain it. + * :-( + *

    + * + * @param out the output stream to write the document on + * @param encoding the character encoding for the serialization + + * @throws NullPointerException if out + * or encoding is null + * @throws UnsupportedEncodingException if the VM does not + * support the requested encoding + * + */ + public Serializer(OutputStream out, String encoding) + throws UnsupportedEncodingException { + + if (encoding == null) { + throw new NullPointerException("Null encoding"); + } + this.setOutputStream(out, encoding); + + } + + + /** + *

    + * Flushes the previous output stream and + * redirects further output to the new output stream. + *

    + * + * + * @param out the output stream to write the document on + + * @throws NullPointerException if out is null + * @throws IOException if the previous output stream + * encounters an I/O error when flushed + * + */ + public void setOutputStream(OutputStream out) + throws IOException { + + // flush any data onto the old output stream + this.flush(); + int maxLength = getMaxLength(); + int indent = this.getIndent(); + String lineSeparator = getLineSeparator(); + boolean nfc = getUnicodeNormalizationFormC(); + String encoding = escaper.getEncoding(); + boolean lineSeparatorSet = escaper.lineSeparatorSet; + setOutputStream(out, encoding); + setIndent(indent); + setMaxLength(maxLength); + setUnicodeNormalizationFormC(nfc); + if (lineSeparatorSet) setLineSeparator(lineSeparator); + + } + + + private void setOutputStream(OutputStream out, String encoding) + throws UnsupportedEncodingException { + + if (out == null) { + throw new NullPointerException("Null OutputStream"); + } + Writer writer; + String encodingUpperCase = encoding.toUpperCase(Locale.ENGLISH); + if (encodingUpperCase.equals("UTF-8")) { + writer = new OutputStreamWriter(out, "UTF-8"); + } + else if (encodingUpperCase.equals("UTF-16") + || encodingUpperCase.equals("ISO-10646-UCS-2")) { + // For compatibility with Java 1.2 and earlier + writer = new OutputStreamWriter(out, "UnicodeBig"); + } + // Java's Cp037 encoding is broken, so we have to + // provide our own. + else if (encodingUpperCase.equals("IBM037") + || encodingUpperCase.equals("CP037") + || encodingUpperCase.equals("EBCDIC-CP-US") + || encodingUpperCase.equals("EBCDIC-CP-CA") + || encodingUpperCase.equals("EBCDIC-CP-WA") + || encodingUpperCase.equals("EBCDIC-CP-NL") + || encodingUpperCase.equals("CSIBM037")) { + writer = new EBCDICWriter(out); + } + else if (encodingUpperCase.equals("ISO-8859-11") + || encodingUpperCase.equals("TIS-620")) { + // Java doesn't recognize the name ISO-8859-11 and + // Java 1.3 and earlier don't recognize TIS-620 + writer = new OutputStreamWriter(out, "TIS620"); + } + else writer = new OutputStreamWriter(out, encoding); + + writer = new UnsynchronizedBufferedWriter(writer); + this.escaper = TextWriterFactory.getTextWriter(writer, encoding); + + } + + + /** + *

    + * Serializes a document onto the output + * stream using the current options. + *

    + * + * @param doc the Document to serialize + * + * @throws IOException if the underlying output stream + * encounters an I/O error + * @throws NullPointerException if doc is null + * @throws UnavailableCharacterException if the document contains + * an unescapable character (e.g. in an element name) that is + * not available in the current encoding + */ + public void write(Document doc) throws IOException { + + escaper.reset(); + namespaces.reset(); + namespaces.declarePrefix("", ""); + // The OutputStreamWriter automatically inserts + // the byte order mark if necessary. + writeXMLDeclaration(); + int childCount = doc.getChildCount(); + for (int i = 0; i < childCount; i++) { + writeChild(doc.getChild(i)); + + // Might want to remove this line break in a + // non-XML serializer where it's not guaranteed to be + // OK to add extra line breaks in the prolog + escaper.breakLine(); + } + escaper.flush(); + + } + + + /** + *

    + * Writes the XML declaration onto the output stream, + * followed by a line break. + *

    + * + * @throws IOException if the underlying output stream + * encounters an I/O error + */ + protected void writeXMLDeclaration() throws IOException { + + escaper.writeUncheckedMarkup(""); + escaper.breakLine(); + + } + + + /** + *

    + * Serializes an element onto the output stream using the current + * options. The result is guaranteed to be well-formed. + *

    + * + *

    + * If the element is empty, this method invokes + * writeEmptyElementTag. If the element is not + * empty, then: + *

    + * + *
      + *
    1. It calls writeStartTag.
    2. + *
    3. It passes each of the element's children to + * writeChild in order.
    4. + *
    5. It calls writeEndTag.
    6. + *
    + * + *

    + * It may break lines or add white space if the serializer has + * been configured to indent or use a maximum line length. + *

    + * + * @param element the Element to serialize + * + * @throws IOException if the underlying output stream + * encounters an I/O error + * @throws UnavailableCharacterException if the element name + * contains a character that is not available in the + * current encoding + */ + protected void write(Element element) throws IOException { + + // workaround for case where only children are empty text nodes + boolean hasRealChildren = false; + int childCount = element.getChildCount(); + for (int i = 0; i < childCount; i++) { + Node child = element.getChild(i); + if (child.isText()) { + Text t = (Text) child; + if (t.isEmpty()) continue; + } + hasRealChildren = true; + break; + } + + if (hasRealChildren) { + boolean wasPreservingWhiteSpace = escaper.isPreserveSpace(); + writeStartTag(element); + + // children + for (int i = 0; i < childCount; i++) { + Node child = element.getChild(i); + // need to work around a very tricky case here where + // denormalized characters cross boundaries of + // consecutive text nodes + if (escaper.getNFC() && child.isText()) { + Text t = (Text) child; + while (i < childCount-1) { // not the last node + Node next = element.getChild(i+1); + if (next.isText()) { + t = new Text(t.getValue() + next.getValue()); + i++; + } + else break; + } + writeChild(t); + } + else { + writeChild(child); + } + } + writeEndTag(element); + + // restore parent value + escaper.setPreserveSpace(wasPreservingWhiteSpace); + } + else { + writeEmptyElementTag(element); + } + + } + + + private boolean hasNonTextChildren(Element element) { + + int childCount = element.getChildCount(); + for (int i = 0; i < childCount; i++) { + if (! element.getChild(i).isText()) return true; + } + return false; + + } + + + // writeEndTag should not normally throw UnavailableCharacterException + // because that would already have been thrown for the + // corresponding start-tag. + /** + *

    + * Writes the end-tag for an element in the form + * </name>. + *

    + * + * @param element the element whose end-tag is written + * + * @throws IOException if the underlying output stream + * encounters an I/O error + */ + protected void writeEndTag(Element element) throws IOException { + + escaper.decrementIndent(); + if (escaper.getIndent() > 0 && !escaper.isPreserveSpace()) { + if (hasNonTextChildren(element)) { + escaper.breakLine(); + } + } + escaper.write('<'); + escaper.write('/'); + escaper.writeName(element.getQualifiedName()); + escaper.write('>'); + namespaces.popContext(); + + } + + + /** + * + *

    + * Writes the start-tag for the element including + * all its namespace declarations and attributes. + *

    + * + *

    + * The writeAttributes method is called to write + * all the non-namespace-declaration attributes. + * The writeNamespaceDeclarations method + * is called to write all the namespace declaration attributes. + *

    + * + * @param element the element whose start-tag is written + * + * @throws IOException if the underlying output stream + * encounters an I/O error + * @throws UnavailableCharacterException if the name of the element + * or the name of any of its attributes contains a character + * that is not available in the current encoding + */ + protected void writeStartTag(Element element) throws IOException { + + writeTagBeginning(element); + escaper.write('>'); + escaper.incrementIndent(); + String xmlSpaceValue = element.getAttributeValue( + "space", "http://www.w3.org/XML/1998/namespace"); + if (xmlSpaceValue != null) { + if ("preserve".equals(xmlSpaceValue)){ + escaper.setPreserveSpace(true); + } + else if ("default".equals(xmlSpaceValue)){ + escaper.setPreserveSpace(false); + } + } + + } + + + /** + * + *

    + * Writes an empty-element tag for the element + * including all its namespace declarations and attributes. + *

    + * + *

    + * The writeAttributes method is called to write + * all the non-namespace-declaration attributes. + * The writeNamespaceDeclarations method + * is called to write all the namespace declaration attributes. + *

    + * + *

    + * If subclasses don't wish empty-element tags to be used, + * they can override this method to simply invoke + * writeStartTag followed by + * writeEndTag. + *

    + * + * @param element the element whose empty-element tag is written + * + * @throws IOException if the underlying output stream + * encounters an I/O error + * @throws UnavailableCharacterException if the name of the element or the name of + * any of its attributes contains a character that is not + * available in the current encoding + */ + protected void writeEmptyElementTag(Element element) + throws IOException { + writeTagBeginning(element); + escaper.write('/'); + escaper.write('>'); + namespaces.popContext(); + } + + + // This just extracts the commonality between writeStartTag + // and writeEmptyElementTag + private void writeTagBeginning(Element element) + throws IOException { + + namespaces.pushContext(); + + if (escaper.isIndenting() + && !escaper.isPreserveSpace() + && !escaper.justBroke()) { + escaper.breakLine(); + } + escaper.write('<'); + escaper.writeName(element.getQualifiedName()); + writeAttributes(element); + writeNamespaceDeclarations(element); + + } + + + /** + *

    + * Writes all the attributes of the specified + * element onto the output stream, one at a time, separated + * by white space. If preserveBaseURI is true, and it is + * necessary to add an xml:base attribute + * to the element in order to preserve the base URI, then + * that attribute is also written here. + * Each individual attribute is written by invoking + * write(Attribute). + *

    + * + * @param element the Element whose attributes are + * written + * @throws IOException if the underlying output stream + * encounters an I/O error + * @throws UnavailableCharacterException if the name of any of + * the element's attributes contains a character that is not + * available in the current encoding + */ + protected void writeAttributes(Element element) + throws IOException { + + // check to see if we need an xml:base attribute + if (preserveBaseURI) { + ParentNode parent = element.getParent(); + if (element.getAttribute("base", + "http://www.w3.org/XML/1998/namespace") == null) { + String baseValue = element.getBaseURI(); + if (parent == null + || parent.isDocument() + || !element.getBaseURI() + .equals(parent.getBaseURI())) { + + escaper.write(' '); + Attribute baseAttribute = new Attribute( + "xml:base", + "http://www.w3.org/XML/1998/namespace", + baseValue); + write(baseAttribute); + } + } + } + + int attributeCount = element.getAttributeCount(); + for (int i = 0; i < attributeCount; i++) { + Attribute attribute = element.getAttribute(i); + escaper.write(' '); + write(attribute); + } + } + + + /** + *

    + * Writes all the namespace declaration + * attributes of the specified element onto the output stream, + * one at a time, separated by white space. Each individual + * declaration is written by invoking + * writeNamespaceDeclaration. + *

    + * + * @param element the Element whose namespace + * declarations are written + * @throws IOException if the underlying output stream + * encounters an I/O error + * @throws UnavailableCharacterException if any of the element's + * namespace prefixes contains a character that is not + * available in the current encoding + */ + protected void writeNamespaceDeclarations(Element element) + throws IOException { + + String prefix = element.getNamespacePrefix(); + if (!("xml".equals(prefix))) { + writeNamespaceDeclarationIfNecessary(prefix, element.getNamespaceURI()); + } + + // write attribute namespaces + int attCount = element.getAttributeCount(); + for (int i = 0; i < attCount; i++) { + Attribute att = element.getAttribute(i); + String attPrefix = att.getNamespacePrefix(); + if (attPrefix.length() != 0 && !("xml".equals(attPrefix))) { + writeNamespaceDeclarationIfNecessary(attPrefix, att.getNamespaceURI()); + } + } + + // write additional namespaces + Namespaces namespaces = element.namespaces; + if (namespaces == null) return; + int namespaceCount = namespaces.size(); + for (int i = 0; i < namespaceCount; i++) { + String additionalPrefix = namespaces.getPrefix(i); + String uri = namespaces.getURI(additionalPrefix); + writeNamespaceDeclarationIfNecessary(additionalPrefix, uri); + } + + } + + + private void writeNamespaceDeclarationIfNecessary(String prefix, String uri) + throws IOException { + + String currentValue = namespaces.getURI(prefix); + // NamespaceSupport returns null for no namespace, not the + // empty string like XOM does + if (currentValue == null && "".equals(uri)) { + return; + } + else if (uri.equals(currentValue)) { + return; + } + + escaper.write(' '); + writeNamespaceDeclaration(prefix, uri); + + } + + + /** + *

    + * Writes a namespace declaration in the form + * xmlns:prefix="uri" or + * xmlns="uri". It does not write + * the spaces on either side of the namespace declaration. + * These are written by writeNamespaceDeclarations. + *

    + * + * @param prefix the namespace prefix; the empty string for the + * default namespace + * @param uri the namespace URI + * + * @throws IOException if the underlying output stream + * encounters an I/O error + * @throws UnavailableCharacterException if the namespace prefix contains a + * character that is not available in the current encoding + */ + protected void writeNamespaceDeclaration(String prefix, String uri) + throws IOException { + + namespaces.declarePrefix(prefix, uri); + if ("".equals(prefix)) { + escaper.writeUncheckedMarkup("xmlns"); + } + else { + escaper.writeUncheckedMarkup("xmlns:"); + escaper.writeName(prefix); + } + escaper.write('='); + escaper.write('"'); + escaper.writePCDATA(uri); + escaper.write('"'); + + } + + + /** + *

    + * Writes an attribute in the form + * name="value". + * Characters in the attribute value are escaped as necessary. + *

    + * + * @param attribute the Attribute to write + * + * @throws IOException if the underlying output stream + * encounters an I/O error + * @throws UnavailableCharacterException if the attribute name contains a character + * that is not available in the current encoding + * + */ + protected void write(Attribute attribute) throws IOException { + escaper.writeName(attribute.getQualifiedName()); + escaper.write('='); + escaper.write('"'); + escaper.writeAttributeValue(attribute.getValue()); + escaper.write('"'); + } + + + /** + *

    + * Writes a comment onto the output stream using the current + * options. Since character and entity references are not resolved + * in comments, comments can only be serialized when all + * characters they contain are available in the current + * encoding. + *

    + * + * @param comment the Comment to serialize + * + * @throws IOException if the underlying output stream + * encounters an I/O error + * @throws UnavailableCharacterException if the comment contains a + * character that is not available in the current encoding + */ + protected void write(Comment comment) throws IOException { + if (escaper.isIndenting()) escaper.breakLine(); + escaper.writeUncheckedMarkup(""); + } + + + /** + *

    + * Writes a processing instruction + * onto the output stream using the current options. + * Since character and entity references are not resolved + * in processing instructions, processing instructions + * can only be serialized when all + * characters they contain are available in the current + * encoding. + *

    + * + * @param instruction the ProcessingInstruction + * to serialize + * + * @throws IOException if the underlying output stream + * encounters an I/O error + * @throws UnavailableCharacterException if the comment contains a + * character that is not available in the current encoding + */ + protected void write(ProcessingInstruction instruction) + throws IOException { + + if (escaper.isIndenting()) escaper.breakLine(); + escaper.writeUncheckedMarkup(""); + + } + + /** + *

    + * Writes a Text object + * onto the output stream using the current options. + * Reserved characters such as <, > and " + * are escaped using the standard entity references + * such as &lt;, &gt;, + * and &quot;. + *

    + * + *

    + * Characters which cannot be encoded in the current character set + * (for example, Ω in ISO-8859-1) are encoded using + * character references. + *

    + * + * @param text the Text to serialize + * + * @throws IOException if the underlying output stream + * encounters an I/O error + */ + protected void write(Text text) throws IOException { + + // XXX Is there a shortcut that takes advantage of the + // data being stored in UTF-8 here? perhaps even if only + // when serializing to UTF-8? + String value = text.getValue(); + if (text.isCDATASection() + && value.indexOf("]]>") == -1) { + if (!(escaper instanceof UnicodeWriter)) { + int length = value.length(); + for (int i = 0; i < length; i++) { + if (escaper.needsEscaping(value.charAt(i))) { + // can't use CDATA section + escaper.writePCDATA(value); + return; + } + } + } + escaper.writeUncheckedMarkup(""); + } + // is this boundary whitespace we can ignore? + else if (isBoundaryWhitespace(text, value)) { + return; // without writing node + } + else { + escaper.writePCDATA(value); + } + + } + + + private boolean isBoundaryWhitespace(Text text, String value) { + + if (getIndent() <= 0) return false; + + ParentNode parent = text.getParent(); + if (parent == null) { + return "".equals(value.trim()); + } + + // ???? cutting next line only breaks a few tests; and what it does + // break might be better off if the breakage is accepted as correct behavior + int childCount = parent.getChildCount(); + if (childCount == 1) return false; + if (! "".equals(value.trim())) return false; + + // ???? This is a huge Hotspot. maybe 12% of serialization time + // when indenting. Is there any way to eliminate this? + // We only actually need to test a couple of positions, 0 and + // parent.getChildCount()-1 + // Instead of getting position we could get those two elements and compare + // to the text. But you still need the previous and next + int position = parent.indexOf(text); + + Node previous = null; + Node next = null; + + if (position != 0) previous = parent.getChild(position-1); + if (position != childCount-1) { + next = parent.getChild(position+1); + } + if (previous == null || !previous.isText()) { + if (next == null || !next.isText()) { + return true; + } + } + + return false; + + } + + + /** + *

    + * Writes a DocType object + * onto the output stream using the current options. + *

    + * + * @param doctype the document type declaration to serialize + * + * @throws IOException if the underlying + * output stream encounters an I/O error + * @throws UnavailableCharacterException if the document type + * declaration contains a character that is not available + * in the current encoding + */ + protected void write(DocType doctype) throws IOException { + + escaper.writeUncheckedMarkup("'); + + } + + + /** + *

    + * Writes a child node onto the output stream using the + * current options. It is invoked when walking the tree to + * serialize the entire document. It is not called, and indeed + * should not be called, for either the Document + * node or for attributes. + *

    + * + * @param node the Node to serialize + * + * @throws IOException if the underlying output stream + * encounters an I/O error + * @throws XMLException if an Attribute, a + * Document, or Namespace + * is passed to this method + */ + protected void writeChild(Node node) throws IOException { + + if (node.isElement()) { + write((Element) node); + } + else if (node.isText()) { + write((Text) node); + } + else if (node.isComment()) { + write((Comment) node); + } + else if (node.isProcessingInstruction()) { + write((ProcessingInstruction) node); + } + else if (node.isDocType()) { + write((DocType) node); + } + else { + throw new XMLException("Cannot write a " + + node.getClass().getName() + + " from the writeChild() method"); + } + + } + + + /**

    + * Writes a string onto the underlying output stream. + * Non-ASCII characters that are not available in the + * current character set are encoded with numeric character + * references. The three reserved characters <, >, and & + * are escaped using the standard entity references + * &lt;, &gt;, + * and &amp;. + * Double and single quotes are not escaped. + *

    + * + * @param text the parsed character data to serialize + * + * @throws IOException if the underlying output stream + * encounters an I/O error + */ + protected final void writeEscaped(String text) throws IOException { + escaper.writePCDATA(text); + } + + /**

    + * Writes a string onto the underlying output stream. + * Non-ASCII characters that are not available in the + * current character set are escaped using hexadecimal numeric + * character references. Carriage returns, line feeds, and tabs + * are also escaped using hexadecimal numeric character + * references in order to ensure their preservation on a round + * trip. The four reserved characters <, >, &, + * and " are escaped using the standard entity references + * &lt;, &gt;, + * &amp;, and &quot;. + * The single quote is not escaped. + *

    + * + * @param value the attribute value to serialize + * + * @throws IOException if the underlying output stream + * encounters an I/O error + */ + protected final void writeAttributeValue(String value) + throws IOException { + escaper.writeAttributeValue(value); + } + + + /**

    + * Writes a string onto the underlying output stream. + * without escaping any characters. + * Non-ASCII characters that are not available in the + * current character set cause an IOException. + *

    + * + * @param text the String to serialize + * + * @throws IOException if the underlying output stream + * encounters an I/O error or text contains + * characters not available in the current character set + */ + protected final void writeRaw(String text) throws IOException { + escaper.writeMarkup(text); + } + + + /**

    + * Writes the current line break string + * onto the underlying output stream and indents + * as specified by the current level and the indent property. + *

    + * + * @throws IOException if the underlying output stream + * encounters an I/O error + */ + protected final void breakLine() throws IOException { + escaper.breakLine(); + } + + + /** + *

    + * Flushes the data onto the output stream. + * It is not enough to flush the output stream. + * You must flush the serializer object itself because it + * uses some internal buffering. + * The serializer will flush the underlying output stream. + *

    + * + * @throws IOException if the underlying + * output stream encounters an I/O error + */ + public void flush() throws IOException { + escaper.flush(); + } + + + /** + *

    + * Returns the number of spaces this serializer indents. + *

    + * + * @return the number of spaces this serializer indents + * each successive level beyond the previous one + */ + public int getIndent() { + return escaper.getIndent(); + } + + + /** + *

    + * Sets the number of additional spaces to add to each successive + * level in the hierarchy. Use 0 for no extra indenting. The + * maximum indentation is in limited to approximately half the + * maximum line length. The serializer will not indent further + * than that no matter how many levels deep the hierarchy is. + *

    + * + *

    + * When this variable is set to a value greater than 0, + * the serializer does not preserve white space. Spaces, + * tabs, carriage returns, and line feeds can all be + * interchanged at the serializer's discretion, and additional + * white space may be added before and after tags. + * Carriage returns, line feeds, and tabs will not be + * escaped with numeric character references. + *

    + * + *

    + * Inside elements with an xml:space="preserve" + * attribute, white space is preserved and no indenting + * takes place, regardless of the setting of the indent + * property, unless, of course, an + * xml:space="default" attribute overrides the + * xml:space="preserve" attribute. + *

    + * + *

    + * The default value for indent is 0; that is, the default is + * not to add or subtract any white space from the source + * document. + *

    + * + * @param indent the number of spaces to indent + * each successive level of the hierarchy + * + * @throws IllegalArgumentException if indent is less than zero + * + */ + public void setIndent(int indent) { + if (indent < 0) { + throw new IllegalArgumentException( + "Indent cannot be negative" + ); + } + escaper.setIndent(indent); + } + + + /** + *

    + * Returns the string used as a line separator. + * This is always "\n", "\r", + * or "\r\n". + *

    + * + * @return the line separator + */ + public String getLineSeparator() { + return escaper.getLineSeparator(); + } + + + /** + *

    + * Sets the line separator. This can only be one of the + * three strings "\n", "\r", + * or "\r\n". All other values are forbidden. + * If this method is invoked, then + * line separators in the character data will be changed to this + * string. Line separators in attribute values will be changed + * to the hexadecimal numeric character references corresponding + * to this string. + *

    + * + *

    + * The default line separator is "\r\n". However, + * line separators in character data and attribute values are not + * changed to this string, unless this method is called first. + *

    + * + * @param lineSeparator the line separator to set + * + * @throws IllegalArgumentException if you attempt to use any line + * separator other than "\n", "\r", + * or "\r\n". + * + */ + public void setLineSeparator(String lineSeparator) { + escaper.setLineSeparator(lineSeparator); + } + + + /** + *

    + * Returns the preferred maximum line length. + *

    + * + * @return the preferred maximum line length. + */ + public int getMaxLength() { + return escaper.getMaxLength(); + } + + + /** + *

    + * Sets the suggested maximum line length for this serializer. + * Setting this to 0 indicates that no automatic wrapping is to be + * performed. When a line approaches this length, the serializer + * begins looking for opportunities to break the line. Generally + * it will break on any ASCII white space character (tab, carriage + * return, linefeed, and space). In some circumstances the + * serializer may not be able to break the line before the maximum + * length is reached. For instance, if an element name is longer + * than the maximum line length the only way to correctly + * serialize it is to exceed the maximum line length. In this case, + * the serializer will exceed the maximum line length. + *

    + * + *

    + * The default value for maximum line length is 0, which is + * interpreted as no maximum line length. + * Setting this to a negative value just sets it to 0. + *

    + * + *

    + * When this variable is set to a value greater than 0, + * the serializer does not preserve white space. Spaces, + * tabs, carriage returns, and line feeds can all be + * interchanged at the serializer's discretion. + * Carriage returns, line feeds, and tabs will not be + * escaped with numeric character references. + *

    + * + *

    + * Inside elements with an xml:space="preserve" + * attribute, the maximum line length is not enforced, + * regardless of the setting of the this property, unless, + * of course, an xml:space="default" attribute + * overrides the xml:space="preserve" attribute. + *

    + * + * @param maxLength the preferred maximum line length + */ + public void setMaxLength(int maxLength) { + escaper.setMaxLength(maxLength); + } + + + /** + *

    + * Returns true if this serializer preserves the original + * base URIs by inserting extra xml:base attributes. + *

    + * + * @return true if this Serializer inserts + * extra xml:base attributes to attempt to + * preserve base URI information from the document. + */ + public boolean getPreserveBaseURI() { + return preserveBaseURI; + } + + + /** + *

    + * Determines whether this serializer inserts + * extra xml:base attributes to attempt to + * preserve base URI information from the document. + * The default is false, do not preserve base URI information. + * xml:base attributes that have been explicitly + * added to an element are always output. This property only + * determines whether or not extra xml:base + * attributes are added. + *

    + * + * @param preserve true if xml:base + * attributes should be added as necessary + * to preserve base URI information + */ + public void setPreserveBaseURI(boolean preserve) { + this.preserveBaseURI = preserve; + } + + + /** + *

    + * Returns the name of the character encoding used by + * this serializer. + *

    + * + * @return the encoding used for the output document + */ + public String getEncoding() { + return escaper.getEncoding(); + } + + + /** + *

    + * If true, this property indicates serialization will + * perform Unicode normalization on all data using normalization + * form C (NFC). Performing Unicode normalization may change the + * document's infoset. The default is false; do not normalize. + * This version is based on Unicode 4.0. + *

    + * + *

    + * This feature has not yet been benchmarked or optimized. + * It may result in substantially slower code. + *

    + * + *

    + * If all your data is in the first 256 code points of Unicode + * (i.e. the ISO-8859-1, Latin-1 character set), then it's + * already in normalization form C and normalizing won't change + * anything. + *

    + * + * @param normalize true if normalization is performed; + * false if it isn't + */ + public void setUnicodeNormalizationFormC(boolean normalize) { + escaper.setNFC(normalize); + } + + + /** + *

    + * Indicates whether serialization will + * perform Unicode normalization on all data using normalization + * form C (NFC). The default is false; do not normalize. + *

    + * + * @return true if this serializer performs Unicode + * normalization; false if it doesn't + */ + public boolean getUnicodeNormalizationFormC() { + return escaper.getNFC(); + } + + + /** + *

    + * Returns the current column number of the output stream. This + * method useful for subclasses that implement their own pretty + * printing strategies by inserting white space and line breaks + * at appropriate points. + *

    + * + *

    + * Columns are counted based on Unicode characters, not Java + * chars. A surrogate pair counts as one character in this + * context, not two. However, a character followed by a + * combining character (e.g. e followed by combining accent + * acute) counts as two characters. This latter choice + * (treating combining characters like regular characters) + * is under review, and may change in the future if it's not + * too big a performance hit. + *

    + * + * @return the current column number + */ + protected final int getColumnNumber() { + return escaper.getColumnNumber(); + } + +} \ No newline at end of file diff --git a/src/nu/xom/Text.java b/src/nu/xom/Text.java new file mode 100644 index 0000000..696fe41 --- /dev/null +++ b/src/nu/xom/Text.java @@ -0,0 +1,474 @@ +/* Copyright 2002-2005, 2010 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom; + +import java.io.UnsupportedEncodingException; + +/** + *

    + * This class represents a run of text. + * CDATA sections are not treated differently than + * normal text. Text objects may be adjacent to other + * Text objects. + *

    + * + *

    + * The maximum size of a String or an array in Java limits the maximum + * size of a text node to about 2 gigabytes. If you're stuffing Base-64 + * encoded movies inside one node, you will have problems. Try breaking + * up the text into smaller, contiguous chunks. Even then you will + * probably not be able to use XPath on the result. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.3.0 + * + */ +public class Text extends Node { + + + private byte[] data; + + + /** + *

    + * This constructor creates a new Text object. + * The data is checked for legality according to XML 1.0 rules. + * Characters that can be serialized by escaping them + * such as < and & are allowed. However, characters + * such as the form feed, null, vertical tab, + * unmatched halves of surrogate pairs, + * and 0xFFFE and 0xFFFF are not allowed. + *

    + * + * @param data the initial text of the object + * + * @throws IllegalCharacterDataException if data contains any + * characters which are illegal in well-formed XML 1.0 such as + * null, vertical tab, or unmatched halves of surrogate pairs + */ + public Text(String data) { + _setValue(data); + } + + + /** + *

    + * Creates a copy of the specified Text object. + *

    + * + * @param text the Text object to copy + */ + public Text(Text text) { + // I'm relying here on the data array being immutable. + // If this ever changes, e.g. by adding an append method, + // this method needs to change too. + this.data = text.data; + } + + + private Text() {} + + + static Text build(String data) { + + Text result = new Text(); + try { + result.data = data.getBytes("UTF8"); + } + catch (UnsupportedEncodingException ex) { + throw new RuntimeException( + "Bad VM! Does not support UTF-8" + ); + } + return result; + + } + + + /** + *

    + * Sets the content of the Text object + * to the specified data. The data is checked for + * legality according to XML 1.0 rules. Characters that + * can be serialized such as < and & are allowed. + * However, characters such as the form feed, null, + * vertical tab, unmatched halves of surrogate pairs, + * and 0xFFFE and 0xFFFF are not allowed. Passing null is the same + * as passing the empty string. + *

    + * + * @param data the text to install in the object + * + * @throws IllegalCharacterDataException if data contains any + * characters which are illegal in well-formed XML 1.0 such as + * null, vertical tab, or unmatched halves of surrogate pairs + */ + public void setValue(String data) { + _setValue(data); + } + + + private void _setValue(String data) { + + if (data == null) data = ""; + else Verifier.checkPCDATA(data); + try { + this.data = data.getBytes("UTF8"); + } + catch (UnsupportedEncodingException ex) { + throw new RuntimeException( + "Bad VM! Does not support UTF-8" + ); + } + + } + + /** + *

    + * Returns the XPath 1.0 string-value of this Text + * node. The XPath string-value of a text node is the same as + * the text of the node. + *

    + * + * @return the content of the node + */ + public final String getValue() { + + try { + return new String(data, "UTF8"); + } + catch (UnsupportedEncodingException ex) { + throw new RuntimeException( + "Bad VM! Does not support UTF-8" + ); + } + + } + + + /** + *

    + * Throws IndexOutOfBoundsException because + * texts do not have children. + *

    + * + * @return never returns because texts do not have children; + * always throws an exception. + * + * @param position the index of the child node to return + * + * @throws IndexOutOfBoundsException because texts + * do not have children + */ + public final Node getChild(int position) { + throw new IndexOutOfBoundsException( + "LeafNodes do not have children"); + } + + + /** + *

    + * Returns 0 because texts do not have children. + *

    + * + * @return zero + */ + public final int getChildCount() { + return 0; + } + + + /** + *

    + * Returns a deep copy of this Text with no parent, + * that can be added to this document or a different one. + *

    + * + * @return a deep copy of this text node with no parent + */ + public Text copy() { + + if (isCDATASection()) { + return new CDATASection(this); + } + else { + return new Text(this); + } + + } + + + /** + *

    + * Returns a string containing the XML serialization of this text + * node. Unlike getValue, this method escapes + * characters such as & and < using entity references such + * as &amp; and &lt;. + * It escapes the carriage return (\r) as &#x0D;. + * If this text node is a CDATA section, then it may wrap the value + * in CDATA section delimiters instead of escaping. + *

    + * + * @return the string form of this text node + */ + public final String toXML() { + return escapeText(); + } + + + String escapeText() { + + String s = getValue(); + int length = s.length(); + // Give the string buffer enough room for a couple of escaped characters + StringBuffer result = new StringBuffer(length+12); + for (int i = 0; i < length; i++) { + char c = s.charAt(i); + switch (c) { + case '\r': + result.append(" "); + break; + case 14: + // impossible + break; + case 15: + // impossible + break; + case 16: + // impossible + break; + case 17: + // impossible + break; + case 18: + // impossible + break; + case 19: + // impossible + break; + case 20: + // impossible + break; + case 21: + // impossible + break; + case 22: + // impossible + break; + case 23: + // impossible + break; + case 24: + // impossible + break; + case 25: + // impossible + break; + case 26: + // impossible + break; + case 27: + // impossible + break; + case 28: + // impossible + break; + case 29: + // impossible + break; + case 30: + // impossible + break; + case 31: + // impossible + break; + case ' ': + result.append(' '); + break; + case '!': + result.append('!'); + break; + case '"': + result.append('"'); + break; + case '#': + result.append('#'); + break; + case '$': + result.append('$'); + break; + case '%': + result.append('%'); + break; + case '&': + result.append("&"); + break; + case '\'': + result.append('\''); + break; + case '(': + result.append('('); + break; + case ')': + result.append(')'); + break; + case '*': + result.append('*'); + break; + case '+': + result.append('+'); + break; + case ',': + result.append(','); + break; + case '-': + result.append('-'); + break; + case '.': + result.append('.'); + break; + case '/': + result.append('/'); + break; + case '0': + result.append('0'); + break; + case '1': + result.append('1'); + break; + case '2': + result.append('2'); + break; + case '3': + result.append('3'); + break; + case '4': + result.append('4'); + break; + case '5': + result.append('5'); + break; + case '6': + result.append('6'); + break; + case '7': + result.append('7'); + break; + case '8': + result.append('8'); + break; + case '9': + result.append('9'); + break; + case ':': + result.append(':'); + break; + case ';': + result.append(';'); + break; + case '<': + result.append("<"); + break; + case '=': + result.append('='); + break; + case '>': + result.append(">"); + break; + default: + result.append(c); + } + } + + return result.toString(); + + } + + + boolean isText() { + return true; + } + + + /** + *

    + * Returns a String + * representation of this Text suitable for + * debugging and diagnosis. This is not + * the XML representation of this Text node. + *

    + * + * @return a non-XML string representation of this node + */ + public final String toString() { + + return "[" + getClass().getName() + ": " + + escapeLineBreaksAndTruncate(getValue()) + "]"; + + } + + + static String escapeLineBreaksAndTruncate(String s) { + + int length = s.length(); + boolean tooLong = length > 40; + if (length > 40) { + length = 35; + s = s.substring(0, 35); + } + + StringBuffer result = new StringBuffer(length); + for (int i = 0; i < length; i++) { + char c = s.charAt(i); + switch (c) { + case '\n': + result.append("\\n"); + break; + case '\r': + result.append("\\r"); + break; + case '\t': + result.append("\\t"); + break; + default: + result.append(c); + } + } + if (tooLong) result.append("..."); + + return result.toString(); + + } + + + boolean isCDATASection() { + return false; + } + + + boolean isEmpty() { + return this.data.length == 0; + } + + +} \ No newline at end of file diff --git a/src/nu/xom/TextWriter.java b/src/nu/xom/TextWriter.java new file mode 100644 index 0000000..f23cbc1 --- /dev/null +++ b/src/nu/xom/TextWriter.java @@ -0,0 +1,935 @@ +/* Copyright 2002-2006 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom; + +import java.io.IOException; +import java.io.Writer; + +/** + *

    + * This class is responsible for writing strings with the + * necessary escaping for their context. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.2d1 + * + */ +abstract class TextWriter { + + protected final Writer out; + protected final String encoding; + + private String lineSeparator = "\r\n"; + // true if the user has requested a specific + // line separator + boolean lineSeparatorSet = false; + private boolean inDocType = false; + private int maxLength = 0; + private int indent = 0; + private String indentString = ""; + protected int column = 0; + // Is an xml:space="preserve" attribute in scope? + private boolean preserveSpace = false; + protected boolean normalize = false; + + protected TextWriter(Writer out, String encoding) { + this.out = out; + this.encoding = encoding; + } + + + void reset() { + column = 0; + fakeIndents = 0; + lastCharacterWasSpace = false; + skipFollowingLinefeed = false; + } + + + protected boolean lastCharacterWasSpace = false; + + /** + * Indicates whether a linefeed is just half of a \r\n pair + * used for a line break. + */ + protected boolean skipFollowingLinefeed = false; + + // Needed for memory between calls. + private char highSurrogate; + + + private boolean isHighSurrogate(int c) { + return c >= 0xD800 && c <= 0xDBFF; + } + + + private boolean isLowSurrogate(int c) { + return c >= 0xDC00 && c <= 0xDFFF; + } + + + final void writePCDATA(char c) throws IOException { + + switch(c) { + case '\r': + if (!adjustingWhiteSpace() && !lineSeparatorSet) { + out.write(" "); + column += 6; + justBroke=false; + } + else { + breakLine(); + lastCharacterWasSpace = true; + } + skipFollowingLinefeed = true; + break; + case 14: // unreachable + case 15: // unreachable + case 16: // unreachable + case 17: // unreachable + case 18: // unreachable + case 19: // unreachable + case 20: // unreachable + case 21: // unreachable + case 22: // unreachable + case 23: // unreachable + case 24: // unreachable + case 25: // unreachable + case 26: // unreachable + case 27: // unreachable + case 28: // unreachable + case 29: // unreachable + case 30: // unreachable + case 31: // unreachable + throw new XMLException("Bad character snuck into document"); + case ' ': + write(c); + break; + case '!': + write(c); + break; + case '"': + write(c); + break; + case '#': + write(c); + break; + case '$': + write(c); + break; + case '%': + write(c); + break; + case '&': + out.write("&"); + column += 5; + lastCharacterWasSpace = false; + skipFollowingLinefeed = false; + justBroke = false; + break; + case '\'': + write(c); + break; + case '(': + write(c); + break; + case ')': + write(c); + break; + case '*': + write(c); + break; + case '+': + write(c); + break; + case ',': + write(c); + break; + case '-': + write(c); + break; + case '.': + write(c); + break; + case '/': + write(c); + break; + case '0': + write(c); + break; + case '1': + write(c); + break; + case '2': + write(c); + break; + case '3': + write(c); + break; + case '4': + write(c); + break; + case '5': + write(c); + break; + case '6': + write(c); + break; + case '7': + write(c); + break; + case '8': + write(c); + break; + case '9': + write(c); + break; + case ':': + write(c); + break; + case ';': + write(c); + break; + case '<': + out.write("<"); + column += 4; + lastCharacterWasSpace = false; + skipFollowingLinefeed = false; + justBroke = false; + break; + case '=': + write(c); + break; + case '>': + out.write(">"); + column += 4; + lastCharacterWasSpace = false; + skipFollowingLinefeed = false; + justBroke = false; + break; + default: + if (needsEscaping(c)) writeEscapedChar(c); + else write(c); + } + + } + + + private void writeEscapedChar(char c) throws IOException { + + if (isHighSurrogate(c)) { + //store and wait for low half + highSurrogate = c; + } + else if (isLowSurrogate(c)) { + // decode and write entity reference + // I am assuming here that nothing allows the + // text to be created with a malformed surrogate + // pair such as a low surrogate that is not immediately + // preceded by a high surrogate + int uchar = UnicodeUtil.combineSurrogatePair(highSurrogate, c); + String s = "&#x" + Integer.toHexString(uchar).toUpperCase() + ';'; + out.write(s); + column += s.length(); + lastCharacterWasSpace = false; + skipFollowingLinefeed = false; + justBroke = false; + } + else { + String s = "&#x" + Integer.toHexString(c).toUpperCase() + ';'; + out.write(s); + column += s.length(); + lastCharacterWasSpace = false; + skipFollowingLinefeed = false; + justBroke=false; + } + + } + + + private boolean adjustingWhiteSpace() { + return maxLength > 0 || indent > 0; + } + + + // This is the same as writePCDATA except that it + // also needs to escape " as " and tab as " ". + // I'm not escaping the single quote because Serializer + // always uses double quotes to contain + // values. + final void writeAttributeValue(char c) + throws IOException { + + switch(c) { + // Handle white space that the parser might normalize + // on roundtrip. We only escape them if the serializer + // is not adjusting white space; that is indent is 0 + // and maxLength is 0. + case '\t': + if (!adjustingWhiteSpace()) { + out.write(" "); + column += 6; + lastCharacterWasSpace = true; + skipFollowingLinefeed = false; + justBroke=false; + } + else { + write(' '); + } + break; + case '\n': + if (skipFollowingLinefeed) { + skipFollowingLinefeed = false; + return; + } + else if (adjustingWhiteSpace()) { + out.write(" "); + lastCharacterWasSpace = true; + justBroke=false; + } + else { + if (lineSeparatorSet) { + escapeBreakLine(); + } + else { + out.write(" "); + column += 6; + justBroke=false; + } + lastCharacterWasSpace = true; + } + break; + case 11: + // unreachable + case 12: + // unreachable + throw new XMLException("Bad character snuck into document"); + case '\r': + if (adjustingWhiteSpace()) { + out.write(" "); + lastCharacterWasSpace = true; + skipFollowingLinefeed = true; + justBroke=false; + } + else { + if (lineSeparatorSet) { + escapeBreakLine(); + skipFollowingLinefeed = true; + } + else { + out.write(" "); + column += 6; + justBroke=false; + } + } + break; + case 14: + // unreachable + case 15: + // unreachable + case 16: + // unreachable + case 17: + // unreachable + case 18: + // unreachable + case 19: + // unreachable + case 20: + // unreachable + case 21: + // unreachable + case 22: + // unreachable + case 23: + // unreachable + case 24: + // unreachable + case 25: + // unreachable + case 26: + // unreachable + case 27: + // unreachable + case 28: + // unreachable + case 29: + // unreachable + case 30: + // unreachable + case 31: + // unreachable + throw new XMLException("Bad character snuck into document"); + case ' ': + write(c); + break; + case '!': + write(c); + break; + case '"': + out.write("""); + column += 6; + lastCharacterWasSpace = false; + skipFollowingLinefeed = false; + justBroke=false; + break; + case '#': + write(c); + break; + case '$': + write(c); + break; + case '%': + write(c); + break; + case '&': + out.write("&"); + column += 5; + lastCharacterWasSpace = false; + skipFollowingLinefeed = false; + justBroke = false; + break; + case '\'': + write(c); + break; + case '(': + write(c); + break; + case ')': + write(c); + break; + case '*': + write(c); + break; + case '+': + write(c); + break; + case ',': + write(c); + break; + case '-': + write(c); + break; + case '.': + write(c); + break; + case '/': + write(c); + break; + case '0': + write(c); + break; + case '1': + write(c); + break; + case '2': + write(c); + break; + case '3': + write(c); + break; + case '4': + write(c); + break; + case '5': + write(c); + break; + case '6': + write(c); + break; + case '7': + write(c); + break; + case '8': + write(c); + break; + case '9': + write(c); + break; + case ':': + write(c); + break; + case ';': + write(c); + break; + case '<': + out.write("<"); + column += 4; + lastCharacterWasSpace = false; + skipFollowingLinefeed = false; + justBroke = false; + break; + case '=': + write(c); + break; + case '>': + out.write(">"); + column += 4; + lastCharacterWasSpace = false; + skipFollowingLinefeed = false; + justBroke = false; + break; + default: + if (needsEscaping(c)) writeEscapedChar(c); + else write(c); + } + + } + + + // XXX We might be able to optimize this by using switch statements + // in the methods that call this to separate out the special cases. + // --\n, \t, space, etc.--and passing them to a different method + // thus avoiding the if tests here. See if this method shows up as + // a HotSpot in profiling. + void write(char c) throws IOException { + + // Carriage returns are completely handled by + // writePCDATA and writeAttributeValue. They never + // enter this method. + if ((c == ' ' || c == '\n' || c == '\t')) { + if (needsBreak()) { + breakLine(); + skipFollowingLinefeed = false; + } + else if (preserveSpace || (indent <= 0 && maxLength <= 0)) { + // We're neither indenting nor wrapping + // so we need to preserve white space + if (c == ' ' || c == '\t') { + out.write(c); + skipFollowingLinefeed = false; + column++; + justBroke=false; + } + else { // (c == '\n') + if (!lineSeparatorSet || + !skipFollowingLinefeed) { + writeLineSeparator(c); + } + skipFollowingLinefeed = false; + column = 0; + } + } + else if (!lastCharacterWasSpace) { + out.write(' '); + column++; + skipFollowingLinefeed = false; + justBroke=false; + } + lastCharacterWasSpace = true; + } + else { + out.write(c); + // don't increment column for high surrogate, only low surrogate + if (c < 0xd800 || c > 0xDBFF) column++; + lastCharacterWasSpace = false; + skipFollowingLinefeed = false; + justBroke=false; + } + + } + + + private void writeLineSeparator(char c) + throws IOException { + + if (!inDocType && (!lineSeparatorSet || preserveSpace)) out.write(c); + else if (lineSeparator.equals("\r\n")) { + out.write("\r\n"); + } + else if (lineSeparator.equals("\n")) { + out.write('\n'); + } + else { // lineSeparator.equals("\r")) + out.write('\r'); + } + // Remember, there are only three possible line separators + + } + + + private boolean needsBreak() { + + if (maxLength <= 0 || preserveSpace) return false; + // Better algorithm needed: Should look ahead in the + // stream, see if there's a white space character + // between here and the maxLength, Then again, simple is good. + // Here we just assume there's probably space somewhere + // within the next ten characters + + return column >= maxLength - 10; + + } + + + protected boolean justBroke = false; + + boolean justBroke() { + return justBroke; + } + + + final void breakLine() throws IOException { + + out.write(lineSeparator); + out.write(indentString); + column = indentString.length(); + lastCharacterWasSpace = true; + justBroke = true; + + } + + + private final void escapeBreakLine() throws IOException { + + if ("\n".equals(lineSeparator)) { + out.write(" "); + column += 6; + } + else if ("\r\n".equals(lineSeparator)) { + out.write(" "); + column += 12; + } + else { + out.write(" "); + column += 6; + } + lastCharacterWasSpace = true; + + } + + + // Note that when this method is called directly, then + // normalization is not performed on c. Currently this is + // only called for ASCII characters like <, >, and the space, + // which should be OK + final void writeMarkup(char c) throws IOException { + + if (needsEscaping(c)) { + throw new UnavailableCharacterException(c, encoding); + } + write(c); + + } + + + // XXX should we have a special package protected + // method to be used only for ASCII characters we know don't need escaping or + // normalization such as <, /, A-Z, etc.? + + + void writePCDATA(String s) throws IOException { + + s = normalize(s); + int length = s.length(); + for (int i=0; i < length; i++) { + writePCDATA(s.charAt(i)); + } + + } + + + void writeAttributeValue(String s) + throws IOException { + + s = normalize(s); + int length = s.length(); + for (int i=0; i < length; i++) { + writeAttributeValue(s.charAt(i)); + } + + } + + + void writeMarkup(String s) throws IOException { + + s = normalize(s); + int length = s.length(); + for (int i=0; i < length; i++) { + writeMarkup(s.charAt(i)); + } + + } + + + // This is for ASCII characters like < and = we know are + // available in all encodings and do not need to be normalized + void writeUncheckedMarkup(String s) throws IOException { + + int length = s.length(); + for (int i=0; i < length; i++) { + write(s.charAt(i)); + } + + } + + + protected String normalize(String s) { + + if (normalize) { + return UnicodeUtil.normalize(s); + } + return s; + + } + + + + boolean isIndenting() { + return indentString.length() > 0; + } + + + private int fakeIndents = 0; + + private final static String _128_SPACES=" "; + private final static int _128 = 128; + + void incrementIndent() { + + if (indent == 0) return; + + String newIndent; + int length = indentString.length() + indent; + if (indentString.length() + indent < _128) { + newIndent = _128_SPACES.substring(0, length); + } + else { + StringBuffer sb = new StringBuffer(length); + sb.append(_128_SPACES); + for (int i = _128; i < length; i++) { + sb.append(' '); + } + newIndent = sb.toString(); + } + + // limit maximum indent to half of maximum line length + if (maxLength > 0 && newIndent.length() > maxLength / 2) { + fakeIndents++; + } + else this.indentString = newIndent; + + } + + + void decrementIndent() { + + if (indent == 0) return; + else if (fakeIndents > 0) fakeIndents--; + else { + indentString = indentString.substring( + 0, indentString.length()-indent + ); + } + + } + + + String getEncoding() { + return this.encoding; + } + + + /** + *

    + * Returns the String used as a line separator. + * This is always "\n", "\r", or "\r\n". + *

    + * + * @return the line separator + */ + String getLineSeparator() { + return lineSeparator; + } + + + /** + *

    + * Sets the lineSeparator. This + * can only be one of the three + * strings "\n", "\r", or "\r\n". + * All other values are forbidden. + *

    + * + * @param lineSeparator the lineSeparator to set + * + * @throws IllegalArgumentException if you attempt to use + * any line separator other than "\n", "\r", or "\r\n". + * + */ + void setLineSeparator(String lineSeparator) { + + if (lineSeparator.equals("\n") + || lineSeparator.equals("\r") + || lineSeparator.equals("\r\n")) { + this.lineSeparator = lineSeparator; + this.lineSeparatorSet = true; + } + else { + throw new IllegalArgumentException( + "Illegal Line Separator"); + } + + } + + + void setInDocType(boolean inDocType) { + this.inDocType = inDocType; + } + + + /** + *

    + * Returns the number of spaces this serializer indents. + *

    + * + * @return the number of spaces this serializer indents + */ + int getIndent() { + return indent; + } + + + /** + *

    + * Returns the maximum line length. + *

    + * + * @return the maximum line length. + */ + int getMaxLength() { + return maxLength; + } + + /** + *

    + * Sets the suggested maximum line length for this serializer. + * In some circumstances this may not be respected. + *

    + * + * @param maxLength the maxLength to set + */ + void setMaxLength(int maxLength) { + if (maxLength < 0) maxLength = 0; + this.maxLength = maxLength; + } + + + /** + *

    + * Sets the number of spaces to indent each successive level in the + * hierarchy. Use 0 for no extra indenting. + *

    + * + * @param indent the indent to set + */ + void setIndent(int indent) { + this.indent = indent; + } + + + void flush() throws IOException { + out.flush(); + } + + + abstract boolean needsEscaping(char c); + + + /** + *

    + * Used to track the current status of xml:space. + * This is false by default, unless an xml:space="preserve" + * attribute is in-scope. When such an attribute is in-scope, + * white space is not adjusted even if indenting and/or + * a maximum line length has been requested. + *

    + * + * + * @return true if an xml:space="true" attribute + * is in-scope + */ + boolean isPreserveSpace() { + return preserveSpace; + } + + + /** + * @param preserveSpace whether to preserve all white space + */ + void setPreserveSpace(boolean preserveSpace) { + this.preserveSpace = preserveSpace; + } + + + /** + * @return the current column number + */ + int getColumnNumber() { + return this.column; + } + + + /** + *

    + * If true, this property indicates serialization will + * perform Unicode normalization on all data using normalization + * form C (NFC). Performing Unicode normalization + * does change the document's infoset. + * The default is false; do not normalize. + *

    + * + *

    + * This feature has not yet been benchmarked or optimized. + * It may result in substantially slower code. + *

    + * + * @param normalize true if normalization is performed; + * false if it isn't. + */ + void setNFC(boolean normalize) { + this.normalize = normalize; + } + + + /** + *

    + * If true, this property indicates serialization will + * perform Unicode normalization on all data using normalization + * form C (NFC). The default is false; do not normalize. + *

    + * + * @return true if this serialization performs Unicode + * normalization; false if it doesn't. + */ + boolean getNFC() { + return this.normalize; + } + + + void writeName(String name) throws IOException { + writeMarkup(name); + } + + +} \ No newline at end of file diff --git a/src/nu/xom/TextWriterFactory.java b/src/nu/xom/TextWriterFactory.java new file mode 100644 index 0000000..802ddde --- /dev/null +++ b/src/nu/xom/TextWriterFactory.java @@ -0,0 +1,130 @@ +/* Copyright 2002-2004, 2018 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom; + +import java.io.UnsupportedEncodingException; +import java.io.Writer; +import java.util.Locale; + +/** + * @author Elliotte Rusty Harold + * @version 1.2.11 + */ +class TextWriterFactory { + + public static TextWriter getTextWriter( + Writer out, String encoding) { + + // Not all encoding names are case-insensitive in Java, even + // though they should be. For instance, MacRoman isn't. + String encodingUpperCase = encoding.toUpperCase(Locale.ENGLISH); + if (encodingUpperCase.startsWith("UTF") + || encodingUpperCase.startsWith("UNICODE") + ) { + return new UnicodeWriter(out, encoding); + } + else if (encodingUpperCase.startsWith("ISO-10646-UCS") + || encodingUpperCase.startsWith("UCS") + || encodingUpperCase.equals("GB18030")) { + // GB18030 has a 1-1 mapping to Unicode. However, the Sun + // GB18030 VM is buggy with non-BMP characters. The IBM VM + // gets this right, but for safety we'll escape all non-BMP + // characters. + return new UCSWriter(out, encoding); + } + else if (encodingUpperCase.equals("ISO-8859-1")) { + return new Latin1Writer(out, encoding); + } + else if (encodingUpperCase.equals("ISO-8859-2")) { + return new Latin2Writer(out, encodingUpperCase); + } + else if (encodingUpperCase.equals("ISO-8859-3")) { + return new Latin3Writer(out, encodingUpperCase); + } + else if (encodingUpperCase.equals("ISO-8859-4")) { + return new Latin4Writer(out, encodingUpperCase); + } + else if (encodingUpperCase.equals("ISO-8859-5")) { + return new ISOCyrillicWriter(out, encodingUpperCase); + } + else if (encodingUpperCase.equals("ISO-8859-6")) { + return new ISOArabicWriter(out, encodingUpperCase); + } + else if (encodingUpperCase.equals("ISO-8859-7")) { + return new ISOGreekWriter(out, encodingUpperCase); + } + else if (encodingUpperCase.equals("ISO-8859-8")) { + return new ISOHebrewWriter(out, encodingUpperCase); + } + else if (encodingUpperCase.equals("ISO-8859-9") + || encodingUpperCase.equals("EBCDIC-CP-TR") + || encodingUpperCase.equals("CP1037")) { + return new Latin5Writer(out, encodingUpperCase); + } + else if (encoding.equals("ISO-8859-10")) { + return new Latin6Writer(out, encoding); + } + else if (encodingUpperCase.equals("ISO-8859-11") + || encodingUpperCase.equals("TIS-620") + || encodingUpperCase.equals("TIS620")) { + return new ISOThaiWriter(out, encodingUpperCase); + } + // There's no such thing as ISO-8859-12 + // nor is there likely to be one in the future. + else if (encodingUpperCase.equals("ISO-8859-13")) { + return new Latin7Writer(out, encodingUpperCase); + } + else if (encoding.equals("ISO-8859-14")) { + return new Latin8Writer(out, encoding); + } + else if (encodingUpperCase.equals("ISO-8859-15")) { + return new Latin9Writer(out, encodingUpperCase); + } + else if (encoding.equals("ISO-8859-16")) { + return new Latin10Writer(out, encoding); + } + else if (encodingUpperCase.endsWith("ASCII")) { + return new ASCIIWriter(out, encodingUpperCase); + } + else if (encodingUpperCase.equals("IBM037") + || encodingUpperCase.equals("CP037") + || encodingUpperCase.equals("EBCDIC-CP-US") + || encodingUpperCase.equals("EBCDIC-CP-CA") + || encodingUpperCase.equals("EBCDIC-CP-WA") + || encodingUpperCase.equals("EBCDIC-CP-NL") + || encodingUpperCase.equals("CSIBM037")) { + // EBCDIC-37 has same character set as ISO-8859-1; + // just at different code points. + return new Latin1Writer(out, encodingUpperCase); + } + else { + try { + return new GenericWriter(out, encoding); + } + catch (UnsupportedEncodingException ex) { + return new ASCIIWriter(out, encoding); + } + } + + } + +} diff --git a/src/nu/xom/UCSWriter.java b/src/nu/xom/UCSWriter.java new file mode 100644 index 0000000..96be804 --- /dev/null +++ b/src/nu/xom/UCSWriter.java @@ -0,0 +1,53 @@ +/* Copyright 2004 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom; + +import java.io.Writer; + +/** + *

    + * Theoretically, I should be able to use a UnicodeWriter + * for UCS4. However, there's an apparent bug in handling non-BMP + * characters that I haven't been able to track down, so this class + * works around it buy escaping all non-BMP characters. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.0 + * + */ +class UCSWriter extends TextWriter { + + UCSWriter(Writer out, String encoding) { + super(out, encoding); + } + + /** + * @see nu.xom.TextWriter#needsEscaping(char) + */ + boolean needsEscaping(char c) { + if (c < 0xD800) return false; + if (c <= 0xDFFF) return true; + return false; + } + +} diff --git a/src/nu/xom/URIUtil.java b/src/nu/xom/URIUtil.java new file mode 100644 index 0000000..7604530 --- /dev/null +++ b/src/nu/xom/URIUtil.java @@ -0,0 +1,681 @@ +/* Copyright 2004-2006, 2009 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom; + +import java.io.UnsupportedEncodingException; + + +/** + * These methods are not fully general. + * You would need to uncomment some lines to make this a + * public API. Certain preconditions for these methods to + * operate correctly are true in the context of XOM, + * but may well not be true in a more general context. + * + * @author Elliotte Rusty Harold + * @version 1.2.3 + * + */ +class URIUtil { + + // We assume the URI has already been verified as a potentially + // legal URI. Thus we don't have to check everything here. + static boolean isOpaque(String uri) { + + int colon = uri.indexOf(':'); + // if (colon < 1) return false; + // This next line is the difference between absolute and opaque + if (uri.substring(colon+1).startsWith("/")) return false; + if (!Verifier.isAlpha(uri.charAt(0))) return false; + /* for (int i = 1; i < colon; i++) { + if (!Verifier.isSchemeCharacter(uri.charAt(i))) { + return false; + } + } */ + return true; + + } + + + static boolean isAbsolute(String uri) { + + int colon = uri.indexOf(':'); + if (colon < 1) return false; + // We assume the URI has already been verified as a potentially + // legal URI. Thus we don't have to check everything here. + /*if (!Verifier.isAlpha(uri.charAt(0))) return false; + for (int i = 1; i < colon; i++) { + if (!Verifier.isSchemeCharacter(uri.charAt(i))) return false; + } */ + return true; + + } + + + // This doesn't do enough error checking to be a public API. + static String absolutize(String baseURI, String spec) { + + if ("".equals(baseURI) || baseURI == null) return spec; + + ParsedURI base = new ParsedURI(baseURI); + + // This seems to be necessary to handle base URLs like + // http://www.example.com/test/data/.. + // but I don't think it's part of the 3986 algorithm. + // ???? It may be a bug in that algorithm. Check. + if (base.path.endsWith("/..")) base.path += '/'; + + // The variable names R and T violate Java naming conventions. + // They are taken from the pseudo-code in the RFC 3986 spec. + ParsedURI R = new ParsedURI(spec); + ParsedURI T = new ParsedURI(); + + // We should be able to skip this check. basically it + // asserts that the spec is not an absolute URI already + /* if (R.scheme != null) { + T.scheme = R.scheme; + T.authority = R.authority; + T.query = R.query; + T.path = removeDotSegments(R.path); + } + else { */ + if (R.authority != null) { + T.authority = R.authority; + T.query = R.query; + T.path = removeDotSegments(R.path); + } + else { + if ("".equals(R.path)) { + T.path = base.path; + if (R.query != null) { + T.query = R.query; + } + else { + T.query = base.query; + } + } + else { + if (R.path.startsWith("/")) { + T.path = removeDotSegments(R.path); + } + else { + T.path = merge(base, R.path); + T.path = removeDotSegments(T.path); + } + T.query = R.query; + } + T.authority = base.authority; + } + T.scheme = base.scheme; + // } + // Fragment ID of base URI is never considered + T.fragment = R.fragment; + + return T.toString(); + + } + + + private static String merge(ParsedURI base, String relativePath) { + + if (base.authority != null && "".equals(base.path) + && !"".equals(base.authority)) { + return "/" + relativePath; + } + + int lastSlash = base.path.lastIndexOf('/'); + if (lastSlash == -1) return relativePath; + String topPath = base.path.substring(0, lastSlash+1); + return topPath + relativePath; + + } + + + static String removeDotSegments(String path) { + + StringBuffer output = new StringBuffer(); + + while (path.length() > 0) { + if (path.startsWith("/./")) { + path = '/' + path.substring(3); + } + else if (path.equals("/.")) { + path = "/"; + } + else if (path.startsWith("/../")) { + path = '/' + path.substring(4); + int lastSlash = output.toString().lastIndexOf('/'); + if (lastSlash != -1) output.setLength(lastSlash); + } + else if (path.equals("/..")) { + path = "/"; + int lastSlash = output.toString().lastIndexOf('/'); + if (lastSlash != -1) output.setLength(lastSlash); + } + // These next three cases are unreachable in the context of XOM. + // They may be needed in a more general public URIUtil. + // ???? need to consider whether these are still unreachable now that + // Builder.canonicalizeURL is calling this method. + /* else if (path.equals(".") || path.equals("..")) { + path = ""; + } + else if (path.startsWith("../")) { + path = path.substring(3); + } + else if (path.startsWith("./")) { + path = path.substring(2); + } */ + else { + int nextSlash = path.indexOf('/'); + if (nextSlash == 0) nextSlash = path.indexOf('/', 1); + if (nextSlash == -1) { + output.append(path); + path = ""; + } + else { + output.append(path.substring(0, nextSlash)); + path = path.substring(nextSlash); + } + } + } + + return output.toString(); + + } + + + // really just a struct + static class ParsedURI { + + String scheme; + String schemeSpecificPart; + String query; + String fragment; + String authority; + String path = ""; + + ParsedURI(String spec) { + + int colon = spec.indexOf(':'); + int question; + + // URIs can only contain one sharp sign + int sharp = spec.lastIndexOf('#'); + + // Fragment IDs can contain question marks so we only read + // the question mark before the fragment ID, if any + if (sharp == -1) question = spec.indexOf('?'); + else question = spec.substring(0, sharp).indexOf('?'); + + if (colon != -1) scheme = spec.substring(0, colon); + + if (question == -1 && sharp == -1) { + schemeSpecificPart = spec.substring(colon+1); + } + else if (question != -1) { + if (question < colon) { + MalformedURIException ex + = new MalformedURIException("Unparseable URI"); + ex.setData(spec); + throw ex; + } + schemeSpecificPart = spec.substring(colon+1, question); + } + else { + if (sharp < colon) { + MalformedURIException ex + = new MalformedURIException("Unparseable URI"); + ex.setData(spec); + throw ex; + } + schemeSpecificPart = spec.substring(colon+1, sharp); + } + + if (sharp != -1) { + fragment = spec.substring(sharp+1); + } + + if (question != -1) { + if (sharp == -1) { + query = spec.substring(question+1); + } + else { + query = spec.substring(question+1, sharp); + } + } + + if (schemeSpecificPart.startsWith("//")) { + int authorityBegin = 2; + int authorityEnd = schemeSpecificPart.indexOf('/', authorityBegin); + if (authorityEnd == -1) { + authority = schemeSpecificPart.substring(2); + path = ""; + } + else { + authority = schemeSpecificPart.substring(authorityBegin, authorityEnd); + path = schemeSpecificPart.substring(authorityEnd); + } + } + else { + path = schemeSpecificPart; + } + + } + + ParsedURI() {} + + public String toString() { + + StringBuffer result = new StringBuffer(30); + + if (scheme != null) { + result.append(scheme); + result.append(':'); + } + + if (schemeSpecificPart != null) { + result.append(schemeSpecificPart); + } + else { + result.append("//"); + if (authority != null) result.append(authority); + result.append(path); + } + + if (query != null) { + result.append('?'); + result.append(query); + } + + if (fragment != null) { + result.append('#'); + result.append(fragment); + } + + return result.toString(); + + } + + } + + + static String toURI(String iri) { + + int length = iri.length(); + StringBuffer uri = new StringBuffer(length); + for (int i = 0; i < length; i++) { + char c = iri.charAt(i); + switch(c) { + case ' ': + uri.append("%20"); + break; + case '!': + uri.append(c); + break; + case '"': + uri.append("%22"); + break; + case '#': + uri.append(c); + break; + case '$': + uri.append(c); + break; + case '%': + uri.append(c); + break; + case '&': + uri.append(c); + break; + case '\'': + uri.append(c); + break; + case '(': + uri.append(c); + break; + case ')': + uri.append(c); + break; + case '*': + uri.append(c); + break; + case '+': + uri.append(c); + break; + case ',': + uri.append(c); + break; + case '-': + uri.append(c); + break; + case '.': + uri.append(c); + break; + case '/': + uri.append(c); + break; + case '0': + uri.append(c); + break; + case '1': + uri.append(c); + break; + case '2': + uri.append(c); + break; + case '3': + uri.append(c); + break; + case '4': + uri.append(c); + break; + case '5': + uri.append(c); + break; + case '6': + uri.append(c); + break; + case '7': + uri.append(c); + break; + case '8': + uri.append(c); + break; + case '9': + uri.append(c); + break; + case ':': + uri.append(c); + break; + case ';': + uri.append(c); + break; + case '<': + uri.append("%3C"); + break; + case '=': + uri.append(c); + break; + case '>': + uri.append("%3E"); + break; + case '?': + uri.append(c); + break; + case '@': + uri.append(c); + break; + case 'A': + uri.append(c); + break; + case 'B': + uri.append(c); + break; + case 'C': + uri.append(c); + break; + case 'D': + uri.append(c); + break; + case 'E': + uri.append(c); + break; + case 'F': + uri.append(c); + break; + case 'G': + uri.append(c); + break; + case 'H': + uri.append(c); + break; + case 'I': + uri.append(c); + break; + case 'J': + uri.append(c); + break; + case 'K': + uri.append(c); + break; + case 'L': + uri.append(c); + break; + case 'M': + uri.append(c); + break; + case 'N': + uri.append(c); + break; + case 'O': + uri.append(c); + break; + case 'P': + uri.append(c); + break; + case 'Q': + uri.append(c); + break; + case 'R': + uri.append(c); + break; + case 'S': + uri.append(c); + break; + case 'T': + uri.append(c); + break; + case 'U': + uri.append(c); + break; + case 'V': + uri.append(c); + break; + case 'W': + uri.append(c); + break; + case 'X': + uri.append(c); + break; + case 'Y': + uri.append(c); + break; + case 'Z': + uri.append(c); + break; + case '[': + uri.append(c); + break; + case '\\': + uri.append("%5C"); + break; + case ']': + uri.append(c); + break; + case '^': + uri.append("%5E"); + break; + case '_': + uri.append(c); + break; + case '`': + uri.append("%60"); + break; + case 'a': + uri.append(c); + break; + case 'b': + uri.append(c); + break; + case 'c': + uri.append(c); + break; + case 'd': + uri.append(c); + break; + case 'e': + uri.append(c); + break; + case 'f': + uri.append(c); + break; + case 'g': + uri.append(c); + break; + case 'h': + uri.append(c); + break; + case 'i': + uri.append(c); + break; + case 'j': + uri.append(c); + break; + case 'k': + uri.append(c); + break; + case 'l': + uri.append(c); + break; + case 'm': + uri.append(c); + break; + case 'n': + uri.append(c); + break; + case 'o': + uri.append(c); + break; + case 'p': + uri.append(c); + break; + case 'q': + uri.append(c); + break; + case 'r': + uri.append(c); + break; + case 's': + uri.append(c); + break; + case 't': + uri.append(c); + break; + case 'u': + uri.append(c); + break; + case 'v': + uri.append(c); + break; + case 'w': + uri.append(c); + break; + case 'x': + uri.append(c); + break; + case 'y': + uri.append(c); + break; + case 'z': + uri.append(c); + break; + case '{': + uri.append("%7B"); + break; + case '|': + uri.append("%7C"); + break; + case '}': + uri.append("%7D"); + break; + case '~': + uri.append(c); + break; + default: + uri.append(percentEscape(c)); + } + } + return uri.toString(); + + } + + + static String percentEscape(char c) { + + StringBuffer result = new StringBuffer(3); + String s = String.valueOf(c); + try { + byte[] data = s.getBytes("UTF8"); + for (int i = 0; i < data.length; i++) { + result.append('%'); + String hex = Integer.toHexString(data[i]).toUpperCase(); + if (c < 16) { + result.append('0'); + result.append(hex); + } + else { + // When c is negative as a byte, (e.g. greater + // than 128) the hex strings come out as 8 + // characters rather than 2. + result.append(hex.substring(hex.length()-2)); + } + } + return result.toString(); + } + catch (UnsupportedEncodingException ex) { + throw new RuntimeException( + "Broken VM: does not recognize UTF-8 encoding"); + } + + } + + + static String relativize(String base, String abs) { + + ParsedURI parsedBase = new ParsedURI(base); + ParsedURI parsedAbs = new ParsedURI(abs); + + parsedBase.path = removeDotSegments(parsedBase.path); + + if (parsedBase.scheme.equals(parsedAbs.scheme) + && parsedBase.authority.equals(parsedAbs.authority)) { + + String basePath = parsedBase.path; + String relPath = parsedAbs.path; + + while (basePath.length() > 1) { + basePath = basePath.substring(0, basePath.lastIndexOf('/')); + if (relPath.startsWith(basePath)) { + return relPath.substring(basePath.length()+1); + } + } + + return relPath; + } + else { + return abs; + } + + } + + +} diff --git a/src/nu/xom/UnavailableCharacterException.java b/src/nu/xom/UnavailableCharacterException.java new file mode 100644 index 0000000..4d639de --- /dev/null +++ b/src/nu/xom/UnavailableCharacterException.java @@ -0,0 +1,91 @@ +/* Copyright 2002-2005 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom; + +/** + *

    + * Thrown when serializing documents that contain characters not + * available in the current encoding, and which cannot be escaped + * (for instance, because they're in an element name or processing + * instruction data). This can never happen if the encoding is UTF-8 + * or UTF-16. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.1b3 + * + */ +public class UnavailableCharacterException extends XMLException { + + + private static final long serialVersionUID = -8280912714497572798L; + + private final char unavailableCharacter; + private final String encoding; + + + /** + *

    + * Creates a new UnavailableCharacterException. + *

    + * + * @param character the character which caused the exception + * @param encoding the encoding which does not contain the character + */ + public UnavailableCharacterException(char character, String encoding) { + + super("Cannot use the character " + character + " (&#x" + + Integer.toHexString(character).toUpperCase() + ";) in the " + + encoding + " encoding."); + this.unavailableCharacter = character; + this.encoding = encoding; + + } + + + /** + *

    + * Returns the character which could not be written + * in the current encoding. + *

    + + * @return the character which caused the exception + */ + public char getCharacter() { + return this.unavailableCharacter; + } + + + /** + *

    + * Returns the encoding that does not support the character. + *

    + * + * @return the encoding used by the serializer when the exception + * was thrown + */ + public String getEncoding() { + return this.encoding; + } + + +} \ No newline at end of file diff --git a/src/nu/xom/UnicodeUtil.java b/src/nu/xom/UnicodeUtil.java new file mode 100644 index 0000000..0e25c6d --- /dev/null +++ b/src/nu/xom/UnicodeUtil.java @@ -0,0 +1,4997 @@ +/* Copyright 2005, 2009, 2019 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom; + +import java.io.InputStream; +import java.io.DataInputStream; +import java.io.IOException; +import java.util.Map; +import java.util.HashMap; + +/** + * @author Elliotte Rusty Harold + * @version 1.3.1 + * + */ +final class UnicodeUtil { + + + private static final int CANONICAL_COMBINING_CLASS_NOT_REORDERED = 0; + private static final int CANONICAL_COMBINING_CLASS_OVERLAY = 1; + private static final int CANONICAL_COMBINING_CLASS_NUKTA = 7; + private static final int CANONICAL_COMBINING_CLASS_KANA_VOICING = 8; + private final static int CANONICAL_COMBINING_CLASS_VIRAMA = 9; + private final static int CANONICAL_COMBINING_CLASS_10 = 10; + private final static int CANONICAL_COMBINING_CLASS_11 = 11; + private final static int CANONICAL_COMBINING_CLASS_12 = 12; + private final static int CANONICAL_COMBINING_CLASS_13 = 13; + private final static int CANONICAL_COMBINING_CLASS_14 = 14; + private final static int CANONICAL_COMBINING_CLASS_15 = 15; + private final static int CANONICAL_COMBINING_CLASS_16 = 16; + private final static int CANONICAL_COMBINING_CLASS_17 = 17; + private final static int CANONICAL_COMBINING_CLASS_18 = 18; + private final static int CANONICAL_COMBINING_CLASS_19 = 19; + private final static int CANONICAL_COMBINING_CLASS_20 = 20; + private final static int CANONICAL_COMBINING_CLASS_21 = 21; + private final static int CANONICAL_COMBINING_CLASS_22 = 22; + private final static int CANONICAL_COMBINING_CLASS_23 = 23; + private final static int CANONICAL_COMBINING_CLASS_24 = 24; + private final static int CANONICAL_COMBINING_CLASS_25 = 25; + private final static int CANONICAL_COMBINING_CLASS_26 = 26; + private final static int CANONICAL_COMBINING_CLASS_27 = 27; + private final static int CANONICAL_COMBINING_CLASS_28 = 28; + private final static int CANONICAL_COMBINING_CLASS_29 = 29; + private final static int CANONICAL_COMBINING_CLASS_30 = 30; + private final static int CANONICAL_COMBINING_CLASS_31 = 31; + private final static int CANONICAL_COMBINING_CLASS_32 = 32; + private final static int CANONICAL_COMBINING_CLASS_33 = 33; + private final static int CANONICAL_COMBINING_CLASS_34 = 34; + private final static int CANONICAL_COMBINING_CLASS_35 = 35; + private final static int CANONICAL_COMBINING_CLASS_36 = 36; + private final static int CANONICAL_COMBINING_CLASS_84 = 84; + private final static int CANONICAL_COMBINING_CLASS_91 = 91; + private final static int CANONICAL_COMBINING_CLASS_103 = 103; + private final static int CANONICAL_COMBINING_CLASS_107 = 107; + private final static int CANONICAL_COMBINING_CLASS_118 = 118; + private final static int CANONICAL_COMBINING_CLASS_122 = 122; + private final static int CANONICAL_COMBINING_CLASS_129 = 129; + private final static int CANONICAL_COMBINING_CLASS_130 = 130; + private final static int CANONICAL_COMBINING_CLASS_132 = 132; + private final static int CANONICAL_COMBINING_CLASS_ATTACHED_BELOW = 202; + private static final int CANONICAL_COMBINING_CLASS_214 = 214; + private static final int CANONICAL_COMBINING_CLASS_ATTACHED_ABOVE_RIGHT = 216; + private final static int CANONICAL_COMBINING_CLASS_BELOW_LEFT = 218; + private final static int CANONICAL_COMBINING_CLASS_BELOW = 220; + private final static int CANONICAL_COMBINING_CLASS_BELOW_RIGHT = 222; + private final static int CANONICAL_COMBINING_CLASS_LEFT = 224; + private final static int CANONICAL_COMBINING_CLASS_RIGHT = 226; + private final static int CANONICAL_COMBINING_CLASS_ABOVE_LEFT = 228; + private final static int CANONICAL_COMBINING_CLASS_ABOVE = 230; + private final static int CANONICAL_COMBINING_CLASS_ABOVE_RIGHT = 232; + private final static int CANONICAL_COMBINING_CLASS_DOUBLE_BELOW = 233; + private final static int CANONICAL_COMBINING_CLASS_DOUBLE_ABOVE = 234; + private final static int CANONICAL_COMBINING_CLASS_IOTA_SUBSCRIPT = 240; + + + private static boolean isHighSurrogate(char c) { + return c >= HI_SURROGATE_START && c <= HI_SURROGATE_END; + } + + + private static int HI_SURROGATE_START = 0xD800; + private static int HI_SURROGATE_END = 0xDBFF; + private static int LOW_SURROGATE_START = 0xDC00; + + + private static Map compositions; + + private static void loadCompositions() { + + ClassLoader loader = Verifier.class.getClassLoader(); + if (loader != null) loadCompositions(loader); + // If that didn't work, try a different ClassLoader + if (compositions == null) { + loader = Thread.currentThread().getContextClassLoader(); + loadCompositions(loader); + } + if (compositions == null) { + throw new RuntimeException("Broken XOM installation: " + + "could not load nu/xom/compositions.dat"); + } + + } + + + // XXX use a BiMap and make decompose much simpler? + private static void loadCompositions(ClassLoader loader) { + + DataInputStream in = null; + try { + InputStream source = loader.getResourceAsStream("nu/xom/compositions.dat"); + in = new DataInputStream(source); + // ???? would it make sense to store a serialized HashMap instead???? + compositions = new HashMap(); + try { + while (true) { + String composed = in.readUTF(); + String decomposed = in.readUTF(); + compositions.put(decomposed, composed); + } + } + catch (java.io.EOFException ex) { + // finished + } + } + catch (IOException ex) { + return; + } + finally { + try { + if (in != null) in.close(); + } + catch (IOException ex) { + // no big deal + } + } + + } + + + private static boolean isStarter(int character) { + return getCombiningClass(character) == 0; + } + + + private static int getCombiningClass(int character) { + // ???? optimize with table lookup? + + if (character <= 0x2ff) return CANONICAL_COMBINING_CLASS_NOT_REORDERED; + if (character <= 0x314) return CANONICAL_COMBINING_CLASS_ABOVE; + if (character <= 0x315) return CANONICAL_COMBINING_CLASS_ABOVE_RIGHT; + if (character <= 0x319) return CANONICAL_COMBINING_CLASS_BELOW; + if (character <= 0x31a) return CANONICAL_COMBINING_CLASS_ABOVE_RIGHT; + if (character <= 0x31b) return CANONICAL_COMBINING_CLASS_ATTACHED_ABOVE_RIGHT; + if (character <= 0x320) return CANONICAL_COMBINING_CLASS_BELOW; + if (character <= 0x322) return CANONICAL_COMBINING_CLASS_ATTACHED_BELOW; + if (character <= 0x326) return CANONICAL_COMBINING_CLASS_BELOW; + if (character <= 0x328) return CANONICAL_COMBINING_CLASS_ATTACHED_BELOW; + if (character <= 0x333) return CANONICAL_COMBINING_CLASS_BELOW; + if (character <= 0x338) return CANONICAL_COMBINING_CLASS_OVERLAY; + if (character <= 0x33c) return CANONICAL_COMBINING_CLASS_BELOW; + if (character <= 0x344) return CANONICAL_COMBINING_CLASS_ABOVE; + if (character <= 0x345) return CANONICAL_COMBINING_CLASS_IOTA_SUBSCRIPT; + if (character <= 0x346) return CANONICAL_COMBINING_CLASS_ABOVE; + if (character <= 0x349) return CANONICAL_COMBINING_CLASS_BELOW; + if (character <= 0x34c) return CANONICAL_COMBINING_CLASS_ABOVE; + if (character <= 0x34e) return CANONICAL_COMBINING_CLASS_BELOW; + if (character <= 0x34f) return CANONICAL_COMBINING_CLASS_NOT_REORDERED; + if (character <= 0x352) return CANONICAL_COMBINING_CLASS_ABOVE; + if (character <= 0x356) return CANONICAL_COMBINING_CLASS_BELOW; + if (character <= 0x357) return CANONICAL_COMBINING_CLASS_ABOVE; + if (character <= 0x358) return CANONICAL_COMBINING_CLASS_ABOVE_RIGHT; + if (character <= 0x35a) return CANONICAL_COMBINING_CLASS_BELOW; + if (character <= 0x35b) return CANONICAL_COMBINING_CLASS_ABOVE; + if (character <= 0x35c) return CANONICAL_COMBINING_CLASS_DOUBLE_BELOW; + if (character <= 0x35e) return CANONICAL_COMBINING_CLASS_DOUBLE_ABOVE; + if (character <= 0x35f) return CANONICAL_COMBINING_CLASS_DOUBLE_BELOW; + if (character <= 0x361) return CANONICAL_COMBINING_CLASS_DOUBLE_ABOVE; + if (character <= 0x362) return CANONICAL_COMBINING_CLASS_DOUBLE_BELOW; + if (character <= 0x36f) return CANONICAL_COMBINING_CLASS_ABOVE; + if (character <= 0x482) return CANONICAL_COMBINING_CLASS_NOT_REORDERED; + if (character <= 0x487) return CANONICAL_COMBINING_CLASS_ABOVE; + if (character <= 0x590) return CANONICAL_COMBINING_CLASS_NOT_REORDERED; + if (character <= 0x591) return CANONICAL_COMBINING_CLASS_BELOW; + if (character <= 0x595) return CANONICAL_COMBINING_CLASS_ABOVE; + if (character <= 0x596) return CANONICAL_COMBINING_CLASS_BELOW; + if (character <= 0x599) return CANONICAL_COMBINING_CLASS_ABOVE; + if (character <= 0x59a) return CANONICAL_COMBINING_CLASS_BELOW_RIGHT; + if (character <= 0x59b) return CANONICAL_COMBINING_CLASS_BELOW; + if (character <= 0x5a1) return CANONICAL_COMBINING_CLASS_ABOVE; + if (character <= 0x5a7) return CANONICAL_COMBINING_CLASS_BELOW; + if (character <= 0x5a9) return CANONICAL_COMBINING_CLASS_ABOVE; + if (character <= 0x5aa) return CANONICAL_COMBINING_CLASS_BELOW; + if (character <= 0x5ac) return CANONICAL_COMBINING_CLASS_ABOVE; + if (character <= 0x5ad) return CANONICAL_COMBINING_CLASS_BELOW_RIGHT; + if (character <= 0x5ae) return CANONICAL_COMBINING_CLASS_ABOVE_LEFT; + if (character <= 0x5af) return CANONICAL_COMBINING_CLASS_ABOVE; + if (character <= 0x5b0) return CANONICAL_COMBINING_CLASS_10; + if (character <= 0x5b1) return CANONICAL_COMBINING_CLASS_11; + if (character <= 0x5b2) return CANONICAL_COMBINING_CLASS_12; + if (character <= 0x5b3) return CANONICAL_COMBINING_CLASS_13; + if (character <= 0x5b4) return CANONICAL_COMBINING_CLASS_14; + if (character <= 0x5b5) return CANONICAL_COMBINING_CLASS_15; + if (character <= 0x5b6) return CANONICAL_COMBINING_CLASS_16; + if (character <= 0x5b7) return CANONICAL_COMBINING_CLASS_17; + if (character <= 0x5b8) return CANONICAL_COMBINING_CLASS_18; + if (character <= 0x5ba) return CANONICAL_COMBINING_CLASS_19; + if (character <= 0x5bb) return CANONICAL_COMBINING_CLASS_20; + if (character <= 0x5bc) return CANONICAL_COMBINING_CLASS_21; + if (character <= 0x5bd) return CANONICAL_COMBINING_CLASS_22; + if (character <= 0x5be) return CANONICAL_COMBINING_CLASS_NOT_REORDERED; + if (character <= 0x5bf) return CANONICAL_COMBINING_CLASS_23; + if (character <= 0x5c0) return CANONICAL_COMBINING_CLASS_NOT_REORDERED; + if (character <= 0x5c1) return CANONICAL_COMBINING_CLASS_24; + if (character <= 0x5c2) return CANONICAL_COMBINING_CLASS_25; + if (character <= 0x5c3) return CANONICAL_COMBINING_CLASS_NOT_REORDERED; + if (character <= 0x5c4) return CANONICAL_COMBINING_CLASS_ABOVE; + if (character <= 0x5c5) return CANONICAL_COMBINING_CLASS_BELOW; + if (character <= 0x5c6) return CANONICAL_COMBINING_CLASS_NOT_REORDERED; + if (character <= 0x5c7) return CANONICAL_COMBINING_CLASS_18; + if (character <= 0x60f) return CANONICAL_COMBINING_CLASS_NOT_REORDERED; + if (character <= 0x617) return CANONICAL_COMBINING_CLASS_ABOVE; + if (character <= 0x618) return CANONICAL_COMBINING_CLASS_30; + if (character <= 0x619) return CANONICAL_COMBINING_CLASS_31; + if (character <= 0x61a) return CANONICAL_COMBINING_CLASS_32; + if (character <= 0x64a) return CANONICAL_COMBINING_CLASS_NOT_REORDERED; + if (character <= 0x64b) return CANONICAL_COMBINING_CLASS_27; + if (character <= 0x64c) return CANONICAL_COMBINING_CLASS_28; + if (character <= 0x64d) return CANONICAL_COMBINING_CLASS_29; + if (character <= 0x64e) return CANONICAL_COMBINING_CLASS_30; + if (character <= 0x64f) return CANONICAL_COMBINING_CLASS_31; + if (character <= 0x650) return CANONICAL_COMBINING_CLASS_32; + if (character <= 0x651) return CANONICAL_COMBINING_CLASS_33; + if (character <= 0x652) return CANONICAL_COMBINING_CLASS_34; + if (character <= 0x654) return CANONICAL_COMBINING_CLASS_ABOVE; + if (character <= 0x656) return CANONICAL_COMBINING_CLASS_BELOW; + if (character <= 0x65b) return CANONICAL_COMBINING_CLASS_ABOVE; + if (character <= 0x65c) return CANONICAL_COMBINING_CLASS_BELOW; + if (character <= 0x65e) return CANONICAL_COMBINING_CLASS_ABOVE; + if (character <= 0x66f) return CANONICAL_COMBINING_CLASS_NOT_REORDERED; + if (character <= 0x670) return CANONICAL_COMBINING_CLASS_35; + if (character <= 0x6d5) return CANONICAL_COMBINING_CLASS_NOT_REORDERED; + if (character <= 0x6dc) return CANONICAL_COMBINING_CLASS_ABOVE; + if (character <= 0x6de) return CANONICAL_COMBINING_CLASS_NOT_REORDERED; + if (character <= 0x6e2) return CANONICAL_COMBINING_CLASS_ABOVE; + if (character <= 0x6e3) return CANONICAL_COMBINING_CLASS_BELOW; + if (character <= 0x6e4) return CANONICAL_COMBINING_CLASS_ABOVE; + if (character <= 0x6e6) return CANONICAL_COMBINING_CLASS_NOT_REORDERED; + if (character <= 0x6e8) return CANONICAL_COMBINING_CLASS_ABOVE; + if (character <= 0x6e9) return CANONICAL_COMBINING_CLASS_NOT_REORDERED; + if (character <= 0x6ea) return CANONICAL_COMBINING_CLASS_BELOW; + if (character <= 0x6ec) return CANONICAL_COMBINING_CLASS_ABOVE; + if (character <= 0x6ed) return CANONICAL_COMBINING_CLASS_BELOW; + if (character <= 0x710) return CANONICAL_COMBINING_CLASS_NOT_REORDERED; + if (character <= 0x711) return CANONICAL_COMBINING_CLASS_36; + if (character <= 0x72f) return CANONICAL_COMBINING_CLASS_NOT_REORDERED; + if (character <= 0x730) return CANONICAL_COMBINING_CLASS_ABOVE; + if (character <= 0x731) return CANONICAL_COMBINING_CLASS_BELOW; + if (character <= 0x733) return CANONICAL_COMBINING_CLASS_ABOVE; + if (character <= 0x734) return CANONICAL_COMBINING_CLASS_BELOW; + if (character <= 0x736) return CANONICAL_COMBINING_CLASS_ABOVE; + if (character <= 0x739) return CANONICAL_COMBINING_CLASS_BELOW; + if (character <= 0x73a) return CANONICAL_COMBINING_CLASS_ABOVE; + if (character <= 0x73c) return CANONICAL_COMBINING_CLASS_BELOW; + if (character <= 0x73d) return CANONICAL_COMBINING_CLASS_ABOVE; + if (character <= 0x73e) return CANONICAL_COMBINING_CLASS_BELOW; + if (character <= 0x741) return CANONICAL_COMBINING_CLASS_ABOVE; + if (character <= 0x742) return CANONICAL_COMBINING_CLASS_BELOW; + if (character <= 0x743) return CANONICAL_COMBINING_CLASS_ABOVE; + if (character <= 0x744) return CANONICAL_COMBINING_CLASS_BELOW; + if (character <= 0x745) return CANONICAL_COMBINING_CLASS_ABOVE; + if (character <= 0x746) return CANONICAL_COMBINING_CLASS_BELOW; + if (character <= 0x747) return CANONICAL_COMBINING_CLASS_ABOVE; + if (character <= 0x748) return CANONICAL_COMBINING_CLASS_BELOW; + if (character <= 0x74a) return CANONICAL_COMBINING_CLASS_ABOVE; + if (character <= 0x7ea) return CANONICAL_COMBINING_CLASS_NOT_REORDERED; + if (character <= 0x7f1) return CANONICAL_COMBINING_CLASS_ABOVE; + if (character <= 0x7f2) return CANONICAL_COMBINING_CLASS_BELOW; + if (character <= 0x7f3) return CANONICAL_COMBINING_CLASS_ABOVE; + if (character <= 0x93b) return CANONICAL_COMBINING_CLASS_NOT_REORDERED; + if (character <= 0x93c) return CANONICAL_COMBINING_CLASS_NUKTA; + if (character <= 0x94c) return CANONICAL_COMBINING_CLASS_NOT_REORDERED; + if (character <= 0x94d) return CANONICAL_COMBINING_CLASS_VIRAMA; + if (character <= 0x950) return CANONICAL_COMBINING_CLASS_NOT_REORDERED; + if (character <= 0x951) return CANONICAL_COMBINING_CLASS_ABOVE; + if (character <= 0x952) return CANONICAL_COMBINING_CLASS_BELOW; + if (character <= 0x954) return CANONICAL_COMBINING_CLASS_ABOVE; + if (character <= 0x9bb) return CANONICAL_COMBINING_CLASS_NOT_REORDERED; + if (character <= 0x9bc) return CANONICAL_COMBINING_CLASS_NUKTA; + if (character <= 0x9cc) return CANONICAL_COMBINING_CLASS_NOT_REORDERED; + if (character <= 0x9cd) return CANONICAL_COMBINING_CLASS_VIRAMA; + if (character <= 0xa3b) return CANONICAL_COMBINING_CLASS_NOT_REORDERED; + if (character <= 0xa3c) return CANONICAL_COMBINING_CLASS_NUKTA; + if (character <= 0xa4c) return CANONICAL_COMBINING_CLASS_NOT_REORDERED; + if (character <= 0xa4d) return CANONICAL_COMBINING_CLASS_VIRAMA; + if (character <= 0xabb) return CANONICAL_COMBINING_CLASS_NOT_REORDERED; + if (character <= 0xabc) return CANONICAL_COMBINING_CLASS_NUKTA; + if (character <= 0xacc) return CANONICAL_COMBINING_CLASS_NOT_REORDERED; + if (character <= 0xacd) return CANONICAL_COMBINING_CLASS_VIRAMA; + if (character <= 0xb3b) return CANONICAL_COMBINING_CLASS_NOT_REORDERED; + if (character <= 0xb3c) return CANONICAL_COMBINING_CLASS_NUKTA; + if (character <= 0xb4c) return CANONICAL_COMBINING_CLASS_NOT_REORDERED; + if (character <= 0xb4d) return CANONICAL_COMBINING_CLASS_VIRAMA; + if (character <= 0xbcc) return CANONICAL_COMBINING_CLASS_NOT_REORDERED; + if (character <= 0xbcd) return CANONICAL_COMBINING_CLASS_VIRAMA; + if (character <= 0xc4c) return CANONICAL_COMBINING_CLASS_NOT_REORDERED; + if (character <= 0xc4d) return CANONICAL_COMBINING_CLASS_VIRAMA; + if (character <= 0xc54) return CANONICAL_COMBINING_CLASS_NOT_REORDERED; + if (character <= 0xc55) return CANONICAL_COMBINING_CLASS_84; + if (character <= 0xc56) return CANONICAL_COMBINING_CLASS_91; + if (character <= 0xcbb) return CANONICAL_COMBINING_CLASS_NOT_REORDERED; + if (character <= 0xcbc) return CANONICAL_COMBINING_CLASS_NUKTA; + if (character <= 0xccc) return CANONICAL_COMBINING_CLASS_NOT_REORDERED; + if (character <= 0xccd) return CANONICAL_COMBINING_CLASS_VIRAMA; + if (character <= 0xd4c) return CANONICAL_COMBINING_CLASS_NOT_REORDERED; + if (character <= 0xd4d) return CANONICAL_COMBINING_CLASS_VIRAMA; + if (character <= 0xdc9) return CANONICAL_COMBINING_CLASS_NOT_REORDERED; + if (character <= 0xdca) return CANONICAL_COMBINING_CLASS_VIRAMA; + if (character <= 0xe37) return CANONICAL_COMBINING_CLASS_NOT_REORDERED; + if (character <= 0xe39) return CANONICAL_COMBINING_CLASS_103; + if (character <= 0xe3a) return CANONICAL_COMBINING_CLASS_VIRAMA; + if (character <= 0xe47) return CANONICAL_COMBINING_CLASS_NOT_REORDERED; + if (character <= 0xe4b) return CANONICAL_COMBINING_CLASS_107; + if (character <= 0xeb7) return CANONICAL_COMBINING_CLASS_NOT_REORDERED; + if (character <= 0xeb9) return CANONICAL_COMBINING_CLASS_118; + if (character <= 0xec7) return CANONICAL_COMBINING_CLASS_NOT_REORDERED; + if (character <= 0xecb) return CANONICAL_COMBINING_CLASS_122; + if (character <= 0xf17) return CANONICAL_COMBINING_CLASS_NOT_REORDERED; + if (character <= 0xf19) return CANONICAL_COMBINING_CLASS_BELOW; + if (character <= 0xf34) return CANONICAL_COMBINING_CLASS_NOT_REORDERED; + if (character <= 0xf35) return CANONICAL_COMBINING_CLASS_BELOW; + if (character <= 0xf36) return CANONICAL_COMBINING_CLASS_NOT_REORDERED; + if (character <= 0xf37) return CANONICAL_COMBINING_CLASS_BELOW; + if (character <= 0xf38) return CANONICAL_COMBINING_CLASS_NOT_REORDERED; + if (character <= 0xf39) return CANONICAL_COMBINING_CLASS_ATTACHED_ABOVE_RIGHT; + if (character <= 0xf70) return CANONICAL_COMBINING_CLASS_NOT_REORDERED; + if (character <= 0xf71) return CANONICAL_COMBINING_CLASS_129; + if (character <= 0xf72) return CANONICAL_COMBINING_CLASS_130; + if (character <= 0xf73) return CANONICAL_COMBINING_CLASS_NOT_REORDERED; + if (character <= 0xf74) return CANONICAL_COMBINING_CLASS_132; + if (character <= 0xf79) return CANONICAL_COMBINING_CLASS_NOT_REORDERED; + if (character <= 0xf7d) return CANONICAL_COMBINING_CLASS_130; + if (character <= 0xf7f) return CANONICAL_COMBINING_CLASS_NOT_REORDERED; + if (character <= 0xf80) return CANONICAL_COMBINING_CLASS_130; + if (character <= 0xf81) return CANONICAL_COMBINING_CLASS_NOT_REORDERED; + if (character <= 0xf83) return CANONICAL_COMBINING_CLASS_ABOVE; + if (character <= 0xf84) return CANONICAL_COMBINING_CLASS_VIRAMA; + if (character <= 0xf85) return CANONICAL_COMBINING_CLASS_NOT_REORDERED; + if (character <= 0xf87) return CANONICAL_COMBINING_CLASS_ABOVE; + if (character <= 0xfc5) return CANONICAL_COMBINING_CLASS_NOT_REORDERED; + if (character <= 0xfc6) return CANONICAL_COMBINING_CLASS_BELOW; + if (character <= 0x1036) return CANONICAL_COMBINING_CLASS_NOT_REORDERED; + if (character <= 0x1037) return CANONICAL_COMBINING_CLASS_NUKTA; + if (character <= 0x1038) return CANONICAL_COMBINING_CLASS_NOT_REORDERED; + if (character <= 0x103a) return CANONICAL_COMBINING_CLASS_VIRAMA; + if (character <= 0x108c) return CANONICAL_COMBINING_CLASS_NOT_REORDERED; + if (character <= 0x108d) return CANONICAL_COMBINING_CLASS_BELOW; + if (character <= 0x135e) return CANONICAL_COMBINING_CLASS_NOT_REORDERED; + if (character <= 0x135f) return CANONICAL_COMBINING_CLASS_ABOVE; + if (character <= 0x1713) return CANONICAL_COMBINING_CLASS_NOT_REORDERED; + if (character <= 0x1714) return CANONICAL_COMBINING_CLASS_VIRAMA; + if (character <= 0x1733) return CANONICAL_COMBINING_CLASS_NOT_REORDERED; + if (character <= 0x1734) return CANONICAL_COMBINING_CLASS_VIRAMA; + if (character <= 0x17d1) return CANONICAL_COMBINING_CLASS_NOT_REORDERED; + if (character <= 0x17d2) return CANONICAL_COMBINING_CLASS_VIRAMA; + if (character <= 0x17dc) return CANONICAL_COMBINING_CLASS_NOT_REORDERED; + if (character <= 0x17dd) return CANONICAL_COMBINING_CLASS_ABOVE; + if (character <= 0x18a8) return CANONICAL_COMBINING_CLASS_NOT_REORDERED; + if (character <= 0x18a9) return CANONICAL_COMBINING_CLASS_ABOVE_LEFT; + if (character <= 0x1938) return CANONICAL_COMBINING_CLASS_NOT_REORDERED; + if (character <= 0x1939) return CANONICAL_COMBINING_CLASS_BELOW_RIGHT; + if (character <= 0x193a) return CANONICAL_COMBINING_CLASS_ABOVE; + if (character <= 0x193b) return CANONICAL_COMBINING_CLASS_BELOW; + if (character <= 0x1a16) return CANONICAL_COMBINING_CLASS_NOT_REORDERED; + if (character <= 0x1a17) return CANONICAL_COMBINING_CLASS_ABOVE; + if (character <= 0x1a18) return CANONICAL_COMBINING_CLASS_BELOW; + if (character <= 0x1b33) return CANONICAL_COMBINING_CLASS_NOT_REORDERED; + if (character <= 0x1b34) return CANONICAL_COMBINING_CLASS_NUKTA; + if (character <= 0x1b43) return CANONICAL_COMBINING_CLASS_NOT_REORDERED; + if (character <= 0x1b44) return CANONICAL_COMBINING_CLASS_VIRAMA; + if (character <= 0x1b6a) return CANONICAL_COMBINING_CLASS_NOT_REORDERED; + if (character <= 0x1b6b) return CANONICAL_COMBINING_CLASS_ABOVE; + if (character <= 0x1b6c) return CANONICAL_COMBINING_CLASS_BELOW; + if (character <= 0x1b73) return CANONICAL_COMBINING_CLASS_ABOVE; + if (character <= 0x1ba9) return CANONICAL_COMBINING_CLASS_NOT_REORDERED; + if (character <= 0x1baa) return CANONICAL_COMBINING_CLASS_VIRAMA; + if (character <= 0x1c36) return CANONICAL_COMBINING_CLASS_NOT_REORDERED; + if (character <= 0x1c37) return CANONICAL_COMBINING_CLASS_NUKTA; + if (character <= 0x1dbf) return CANONICAL_COMBINING_CLASS_NOT_REORDERED; + if (character <= 0x1dc1) return CANONICAL_COMBINING_CLASS_ABOVE; + if (character <= 0x1dc2) return CANONICAL_COMBINING_CLASS_BELOW; + if (character <= 0x1dc9) return CANONICAL_COMBINING_CLASS_ABOVE; + if (character <= 0x1dca) return CANONICAL_COMBINING_CLASS_BELOW; + if (character <= 0x1dcc) return CANONICAL_COMBINING_CLASS_ABOVE; + if (character <= 0x1dcd) return CANONICAL_COMBINING_CLASS_DOUBLE_ABOVE; + if (character <= 0x1dce) return CANONICAL_COMBINING_CLASS_214; + if (character <= 0x1dcf) return CANONICAL_COMBINING_CLASS_BELOW; + if (character <= 0x1dd0) return CANONICAL_COMBINING_CLASS_ATTACHED_BELOW; + if (character <= 0x1de6) return CANONICAL_COMBINING_CLASS_ABOVE; + if (character <= 0x1dfd) return CANONICAL_COMBINING_CLASS_NOT_REORDERED; + if (character <= 0x1dfe) return CANONICAL_COMBINING_CLASS_ABOVE; + if (character <= 0x1dff) return CANONICAL_COMBINING_CLASS_BELOW; + if (character <= 0x20cf) return CANONICAL_COMBINING_CLASS_NOT_REORDERED; + if (character <= 0x20d1) return CANONICAL_COMBINING_CLASS_ABOVE; + if (character <= 0x20d3) return CANONICAL_COMBINING_CLASS_OVERLAY; + if (character <= 0x20d7) return CANONICAL_COMBINING_CLASS_ABOVE; + if (character <= 0x20da) return CANONICAL_COMBINING_CLASS_OVERLAY; + if (character <= 0x20dc) return CANONICAL_COMBINING_CLASS_ABOVE; + if (character <= 0x20e0) return CANONICAL_COMBINING_CLASS_NOT_REORDERED; + if (character <= 0x20e1) return CANONICAL_COMBINING_CLASS_ABOVE; + if (character <= 0x20e4) return CANONICAL_COMBINING_CLASS_NOT_REORDERED; + if (character <= 0x20e6) return CANONICAL_COMBINING_CLASS_OVERLAY; + if (character <= 0x20e7) return CANONICAL_COMBINING_CLASS_ABOVE; + if (character <= 0x20e8) return CANONICAL_COMBINING_CLASS_BELOW; + if (character <= 0x20e9) return CANONICAL_COMBINING_CLASS_ABOVE; + if (character <= 0x20eb) return CANONICAL_COMBINING_CLASS_OVERLAY; + if (character <= 0x20ef) return CANONICAL_COMBINING_CLASS_BELOW; + if (character <= 0x20f0) return CANONICAL_COMBINING_CLASS_ABOVE; + if (character <= 0x2ddf) return CANONICAL_COMBINING_CLASS_NOT_REORDERED; + if (character <= 0x2dff) return CANONICAL_COMBINING_CLASS_ABOVE; + if (character <= 0x3029) return CANONICAL_COMBINING_CLASS_NOT_REORDERED; + if (character <= 0x302a) return CANONICAL_COMBINING_CLASS_BELOW_LEFT; + if (character <= 0x302b) return CANONICAL_COMBINING_CLASS_ABOVE_LEFT; + if (character <= 0x302c) return CANONICAL_COMBINING_CLASS_ABOVE_RIGHT; + if (character <= 0x302d) return CANONICAL_COMBINING_CLASS_BELOW_RIGHT; + if (character <= 0x302f) return CANONICAL_COMBINING_CLASS_LEFT; + if (character <= 0x3098) return CANONICAL_COMBINING_CLASS_NOT_REORDERED; + if (character <= 0x309a) return CANONICAL_COMBINING_CLASS_KANA_VOICING; + if (character <= 0xa66e) return CANONICAL_COMBINING_CLASS_NOT_REORDERED; + if (character <= 0xa66f) return CANONICAL_COMBINING_CLASS_ABOVE; + if (character <= 0xa67b) return CANONICAL_COMBINING_CLASS_NOT_REORDERED; + if (character <= 0xa67d) return CANONICAL_COMBINING_CLASS_ABOVE; + if (character <= 0xa805) return CANONICAL_COMBINING_CLASS_NOT_REORDERED; + if (character <= 0xa806) return CANONICAL_COMBINING_CLASS_VIRAMA; + if (character <= 0xa8c3) return CANONICAL_COMBINING_CLASS_NOT_REORDERED; + if (character <= 0xa8c4) return CANONICAL_COMBINING_CLASS_VIRAMA; + if (character <= 0xa92a) return CANONICAL_COMBINING_CLASS_NOT_REORDERED; + if (character <= 0xa92d) return CANONICAL_COMBINING_CLASS_BELOW; + if (character <= 0xa952) return CANONICAL_COMBINING_CLASS_NOT_REORDERED; + if (character <= 0xa953) return CANONICAL_COMBINING_CLASS_VIRAMA; + if (character <= 0xfb1d) return CANONICAL_COMBINING_CLASS_NOT_REORDERED; + if (character <= 0xfb1e) return CANONICAL_COMBINING_CLASS_26; + if (character <= 0xfe1f) return CANONICAL_COMBINING_CLASS_NOT_REORDERED; + if (character <= 0xfe26) return CANONICAL_COMBINING_CLASS_ABOVE; + if (character <= 0x101fc) return CANONICAL_COMBINING_CLASS_NOT_REORDERED; + if (character <= 0x101fd) return CANONICAL_COMBINING_CLASS_BELOW; + if (character <= 0x10a0c) return CANONICAL_COMBINING_CLASS_NOT_REORDERED; + if (character <= 0x10a0d) return CANONICAL_COMBINING_CLASS_BELOW; + if (character <= 0x10a0e) return CANONICAL_COMBINING_CLASS_NOT_REORDERED; + if (character <= 0x10a0f) return CANONICAL_COMBINING_CLASS_ABOVE; + if (character <= 0x10a37) return CANONICAL_COMBINING_CLASS_NOT_REORDERED; + if (character <= 0x10a38) return CANONICAL_COMBINING_CLASS_ABOVE; + if (character <= 0x10a39) return CANONICAL_COMBINING_CLASS_OVERLAY; + if (character <= 0x10a3a) return CANONICAL_COMBINING_CLASS_BELOW; + if (character <= 0x10a3e) return CANONICAL_COMBINING_CLASS_NOT_REORDERED; + if (character <= 0x10a3f) return CANONICAL_COMBINING_CLASS_VIRAMA; + if (character <= 0x1d164) return CANONICAL_COMBINING_CLASS_NOT_REORDERED; + if (character <= 0x1d166) return CANONICAL_COMBINING_CLASS_ATTACHED_ABOVE_RIGHT; + if (character <= 0x1d169) return CANONICAL_COMBINING_CLASS_OVERLAY; + if (character <= 0x1d16c) return CANONICAL_COMBINING_CLASS_NOT_REORDERED; + if (character <= 0x1d16d) return CANONICAL_COMBINING_CLASS_RIGHT; + if (character <= 0x1d172) return CANONICAL_COMBINING_CLASS_ATTACHED_ABOVE_RIGHT; + if (character <= 0x1d17a) return CANONICAL_COMBINING_CLASS_NOT_REORDERED; + if (character <= 0x1d182) return CANONICAL_COMBINING_CLASS_BELOW; + if (character <= 0x1d184) return CANONICAL_COMBINING_CLASS_NOT_REORDERED; + if (character <= 0x1d189) return CANONICAL_COMBINING_CLASS_ABOVE; + if (character <= 0x1d18b) return CANONICAL_COMBINING_CLASS_BELOW; + if (character <= 0x1d1a9) return CANONICAL_COMBINING_CLASS_NOT_REORDERED; + if (character <= 0x1d1ad) return CANONICAL_COMBINING_CLASS_ABOVE; + if (character <= 0x1d241) return CANONICAL_COMBINING_CLASS_NOT_REORDERED; + if (character <= 0x1d244) return CANONICAL_COMBINING_CLASS_ABOVE; + + + return CANONICAL_COMBINING_CLASS_NOT_REORDERED; + } + + + private final static int FIRST_HANGUL_SYLLABLE = 0xAC00; + // even if this is not right; why do tests still pass???? + // private final static int LAST_HANGUL_SYLLABLE = 0xAC00; // FIXME + private final static int LAST_HANGUL_SYLLABLE = 0xD7A3; + + + static int combineSurrogatePair(char highSurrogate, char lowSurrogate) { + + int high = highSurrogate & 0x7FF; + int low = lowSurrogate - 0xDC00; + int highShifted = high << 10; + int combined = highShifted | low; + int codePoint = combined + 0x10000; + return codePoint; + + } + + private static String makeSurrogatePair(int codePoint) { + + StringBuffer s = new StringBuffer(2); + if (codePoint <= 0xFFFF) s.append((char) codePoint); + else { + char high = (char) (0xD800 - (0x10000 >> 10) + (codePoint >> 10)); + char low = (char) (0xDC00 + (codePoint & 0x3FF)); + s.append(high); + s.append(low); + } + return s.toString(); + + } + + private static char getHighSurrogate(int codepoint) { + + char x = (char) codepoint; + int u = (codepoint >> 16) & ((1<<5) - 1); + char w = (char) (u - 1); + return (char) (HI_SURROGATE_START | (w << 6) | x >> 10); + + } + + + private static char getLowSurrogate(int codepoint) { + + char x = (char) codepoint; + return (char) (LOW_SURROGATE_START | x & ((1<<10) - 1)); + + } + + + static String normalize(String s) { + + boolean needsNormalizing = false; + + int length = s.length(); + for (int i = 0; i < length; i++) { + char c = s.charAt(i); + if (c > 255) { + needsNormalizing = true; + break; + } + } + + if (needsNormalizing) { + + // ???? unnecessarily invoking this in many cases + s = decomposeHangul(s); + UnicodeString ustring = new UnicodeString(s); + UnicodeString decomposed = ustring.decompose(); + UnicodeString recomposed = decomposed.compose(); + String result = recomposed.toString(); + // ???? unnecessarily invoking this in many cases + result = composeHangul(result); + return result; + } + + return s; + + } + + + private static String decomposeHangul(String s) { + + int length = s.length(); + StringBuffer sb = new StringBuffer(s.length()); + for (int i = 0; i < length; i++) { + char c = s.charAt(i); + if (c >= FIRST_HANGUL_SYLLABLE && c <= LAST_HANGUL_SYLLABLE) { + sb.append(decomposeHangul(c)); + } + else { + sb.append(c); + } + } + return sb.toString(); + + } + + + // return -1 if the character cannot be combined with the starter; + // otherwise return the composed character + private static int composeCharacter(int starter, int c) { + + StringBuffer decomposed = new StringBuffer(4); + + if (starter > 0xFFFF) { + decomposed.append(getHighSurrogate(starter)); + decomposed.append(getLowSurrogate(starter)); + } + else decomposed.append((char) starter); + + if (c > 0xFFFF) { + decomposed.append(getHighSurrogate(c)); + decomposed.append(getLowSurrogate(c)); + } + else decomposed.append((char) c); + + String recomposed = (String) compositions.get(decomposed.toString()); + if (recomposed == null) return -1; + else if (recomposed.length() == 1) return recomposed.charAt(0); + else return combineSurrogatePair(recomposed.charAt(0), recomposed.charAt(1)); + + } + + + // FIXME must recurse this + ///CLOVER:OFF + private static String decompose(int character) { + + if (character < 0x00C0) { + return String.valueOf((char) character); + } + else if (character >= FIRST_HANGUL_SYLLABLE && character <= LAST_HANGUL_SYLLABLE) { + return decomposeHangul((char) character); + } + + // XXX use lookup table prepopulated with large string at first use + switch (character) { + case 0x00C0: + return "\u0041\u0300"; + case 0x00C1: + return "\u0041\u0301"; + case 0x00C2: + return "\u0041\u0302"; + case 0x00C3: + return "\u0041\u0303"; + case 0x00C4: + return "\u0041\u0308"; + case 0x00C5: + return "\u0041\u030A"; + case 0x00C7: + return "\u0043\u0327"; + case 0x00C8: + return "\u0045\u0300"; + case 0x00C9: + return "\u0045\u0301"; + case 0x00CA: + return "\u0045\u0302"; + case 0x00CB: + return "\u0045\u0308"; + case 0x00CC: + return "\u0049\u0300"; + case 0x00CD: + return "\u0049\u0301"; + case 0x00CE: + return "\u0049\u0302"; + case 0x00CF: + return "\u0049\u0308"; + case 0x00D1: + return "\u004E\u0303"; + case 0x00D2: + return "\u004F\u0300"; + case 0x00D3: + return "\u004F\u0301"; + case 0x00D4: + return "\u004F\u0302"; + case 0x00D5: + return "\u004F\u0303"; + case 0x00D6: + return "\u004F\u0308"; + case 0x00D9: + return "\u0055\u0300"; + case 0x00DA: + return "\u0055\u0301"; + case 0x00DB: + return "\u0055\u0302"; + case 0x00DC: + return "\u0055\u0308"; + case 0x00DD: + return "\u0059\u0301"; + case 0x00E0: + return "\u0061\u0300"; + case 0x00E1: + return "\u0061\u0301"; + case 0x00E2: + return "\u0061\u0302"; + case 0x00E3: + return "\u0061\u0303"; + case 0x00E4: + return "\u0061\u0308"; + case 0x00E5: + return "\u0061\u030A"; + case 0x00E7: + return "\u0063\u0327"; + case 0x00E8: + return "\u0065\u0300"; + case 0x00E9: + return "\u0065\u0301"; + case 0x00EA: + return "\u0065\u0302"; + case 0x00EB: + return "\u0065\u0308"; + case 0x00EC: + return "\u0069\u0300"; + case 0x00ED: + return "\u0069\u0301"; + case 0x00EE: + return "\u0069\u0302"; + case 0x00EF: + return "\u0069\u0308"; + case 0x00F1: + return "\u006E\u0303"; + case 0x00F2: + return "\u006F\u0300"; + case 0x00F3: + return "\u006F\u0301"; + case 0x00F4: + return "\u006F\u0302"; + case 0x00F5: + return "\u006F\u0303"; + case 0x00F6: + return "\u006F\u0308"; + case 0x00F9: + return "\u0075\u0300"; + case 0x00FA: + return "\u0075\u0301"; + case 0x00FB: + return "\u0075\u0302"; + case 0x00FC: + return "\u0075\u0308"; + case 0x00FD: + return "\u0079\u0301"; + case 0x00FF: + return "\u0079\u0308"; + case 0x0100: + return "\u0041\u0304"; + case 0x0101: + return "\u0061\u0304"; + case 0x0102: + return "\u0041\u0306"; + case 0x0103: + return "\u0061\u0306"; + case 0x0104: + return "\u0041\u0328"; + case 0x0105: + return "\u0061\u0328"; + case 0x0106: + return "\u0043\u0301"; + case 0x0107: + return "\u0063\u0301"; + case 0x0108: + return "\u0043\u0302"; + case 0x0109: + return "\u0063\u0302"; + case 0x010A: + return "\u0043\u0307"; + case 0x010B: + return "\u0063\u0307"; + case 0x010C: + return "\u0043\u030C"; + case 0x010D: + return "\u0063\u030C"; + case 0x010E: + return "\u0044\u030C"; + case 0x010F: + return "\u0064\u030C"; + case 0x0112: + return "\u0045\u0304"; + case 0x0113: + return "\u0065\u0304"; + case 0x0114: + return "\u0045\u0306"; + case 0x0115: + return "\u0065\u0306"; + case 0x0116: + return "\u0045\u0307"; + case 0x0117: + return "\u0065\u0307"; + case 0x0118: + return "\u0045\u0328"; + case 0x0119: + return "\u0065\u0328"; + case 0x011A: + return "\u0045\u030C"; + case 0x011B: + return "\u0065\u030C"; + case 0x011C: + return "\u0047\u0302"; + case 0x011D: + return "\u0067\u0302"; + case 0x011E: + return "\u0047\u0306"; + case 0x011F: + return "\u0067\u0306"; + case 0x0120: + return "\u0047\u0307"; + case 0x0121: + return "\u0067\u0307"; + case 0x0122: + return "\u0047\u0327"; + case 0x0123: + return "\u0067\u0327"; + case 0x0124: + return "\u0048\u0302"; + case 0x0125: + return "\u0068\u0302"; + case 0x0128: + return "\u0049\u0303"; + case 0x0129: + return "\u0069\u0303"; + case 0x012A: + return "\u0049\u0304"; + case 0x012B: + return "\u0069\u0304"; + case 0x012C: + return "\u0049\u0306"; + case 0x012D: + return "\u0069\u0306"; + case 0x012E: + return "\u0049\u0328"; + case 0x012F: + return "\u0069\u0328"; + case 0x0130: + return "\u0049\u0307"; + case 0x0134: + return "\u004A\u0302"; + case 0x0135: + return "\u006A\u0302"; + case 0x0136: + return "\u004B\u0327"; + case 0x0137: + return "\u006B\u0327"; + case 0x0139: + return "\u004C\u0301"; + case 0x013A: + return "\u006C\u0301"; + case 0x013B: + return "\u004C\u0327"; + case 0x013C: + return "\u006C\u0327"; + case 0x013D: + return "\u004C\u030C"; + case 0x013E: + return "\u006C\u030C"; + case 0x0143: + return "\u004E\u0301"; + case 0x0144: + return "\u006E\u0301"; + case 0x0145: + return "\u004E\u0327"; + case 0x0146: + return "\u006E\u0327"; + case 0x0147: + return "\u004E\u030C"; + case 0x0148: + return "\u006E\u030C"; + case 0x014C: + return "\u004F\u0304"; + case 0x014D: + return "\u006F\u0304"; + case 0x014E: + return "\u004F\u0306"; + case 0x014F: + return "\u006F\u0306"; + case 0x0150: + return "\u004F\u030B"; + case 0x0151: + return "\u006F\u030B"; + case 0x0154: + return "\u0052\u0301"; + case 0x0155: + return "\u0072\u0301"; + case 0x0156: + return "\u0052\u0327"; + case 0x0157: + return "\u0072\u0327"; + case 0x0158: + return "\u0052\u030C"; + case 0x0159: + return "\u0072\u030C"; + case 0x015A: + return "\u0053\u0301"; + case 0x015B: + return "\u0073\u0301"; + case 0x015C: + return "\u0053\u0302"; + case 0x015D: + return "\u0073\u0302"; + case 0x015E: + return "\u0053\u0327"; + case 0x015F: + return "\u0073\u0327"; + case 0x0160: + return "\u0053\u030C"; + case 0x0161: + return "\u0073\u030C"; + case 0x0162: + return "\u0054\u0327"; + case 0x0163: + return "\u0074\u0327"; + case 0x0164: + return "\u0054\u030C"; + case 0x0165: + return "\u0074\u030C"; + case 0x0168: + return "\u0055\u0303"; + case 0x0169: + return "\u0075\u0303"; + case 0x016A: + return "\u0055\u0304"; + case 0x016B: + return "\u0075\u0304"; + case 0x016C: + return "\u0055\u0306"; + case 0x016D: + return "\u0075\u0306"; + case 0x016E: + return "\u0055\u030A"; + case 0x016F: + return "\u0075\u030A"; + case 0x0170: + return "\u0055\u030B"; + case 0x0171: + return "\u0075\u030B"; + case 0x0172: + return "\u0055\u0328"; + case 0x0173: + return "\u0075\u0328"; + case 0x0174: + return "\u0057\u0302"; + case 0x0175: + return "\u0077\u0302"; + case 0x0176: + return "\u0059\u0302"; + case 0x0177: + return "\u0079\u0302"; + case 0x0178: + return "\u0059\u0308"; + case 0x0179: + return "\u005A\u0301"; + case 0x017A: + return "\u007A\u0301"; + case 0x017B: + return "\u005A\u0307"; + case 0x017C: + return "\u007A\u0307"; + case 0x017D: + return "\u005A\u030C"; + case 0x017E: + return "\u007A\u030C"; + case 0x01A0: + return "\u004F\u031B"; + case 0x01A1: + return "\u006F\u031B"; + case 0x01AF: + return "\u0055\u031B"; + case 0x01B0: + return "\u0075\u031B"; + case 0x01CD: + return "\u0041\u030C"; + case 0x01CE: + return "\u0061\u030C"; + case 0x01CF: + return "\u0049\u030C"; + case 0x01D0: + return "\u0069\u030C"; + case 0x01D1: + return "\u004F\u030C"; + case 0x01D2: + return "\u006F\u030C"; + case 0x01D3: + return "\u0055\u030C"; + case 0x01D4: + return "\u0075\u030C"; + case 0x01D5: + return "\u00DC\u0304"; + case 0x01D6: + return "\u00FC\u0304"; + case 0x01D7: + return "\u00DC\u0301"; + case 0x01D8: + return "\u00FC\u0301"; + case 0x01D9: + return "\u00DC\u030C"; + case 0x01DA: + return "\u00FC\u030C"; + case 0x01DB: + return "\u00DC\u0300"; + case 0x01DC: + return "\u00FC\u0300"; + case 0x01DE: + return "\u00C4\u0304"; + case 0x01DF: + return "\u00E4\u0304"; + case 0x01E0: + return "\u0226\u0304"; + case 0x01E1: + return "\u0227\u0304"; + case 0x01E2: + return "\u00C6\u0304"; + case 0x01E3: + return "\u00E6\u0304"; + case 0x01E6: + return "\u0047\u030C"; + case 0x01E7: + return "\u0067\u030C"; + case 0x01E8: + return "\u004B\u030C"; + case 0x01E9: + return "\u006B\u030C"; + case 0x01EA: + return "\u004F\u0328"; + case 0x01EB: + return "\u006F\u0328"; + case 0x01EC: + return "\u01EA\u0304"; + case 0x01ED: + return "\u01EB\u0304"; + case 0x01EE: + return "\u01B7\u030C"; + case 0x01EF: + return "\u0292\u030C"; + case 0x01F0: + return "\u006A\u030C"; + case 0x01F4: + return "\u0047\u0301"; + case 0x01F5: + return "\u0067\u0301"; + case 0x01F8: + return "\u004E\u0300"; + case 0x01F9: + return "\u006E\u0300"; + case 0x01FA: + return "\u00C5\u0301"; + case 0x01FB: + return "\u00E5\u0301"; + case 0x01FC: + return "\u00C6\u0301"; + case 0x01FD: + return "\u00E6\u0301"; + case 0x01FE: + return "\u00D8\u0301"; + case 0x01FF: + return "\u00F8\u0301"; + case 0x0200: + return "\u0041\u030F"; + case 0x0201: + return "\u0061\u030F"; + case 0x0202: + return "\u0041\u0311"; + case 0x0203: + return "\u0061\u0311"; + case 0x0204: + return "\u0045\u030F"; + case 0x0205: + return "\u0065\u030F"; + case 0x0206: + return "\u0045\u0311"; + case 0x0207: + return "\u0065\u0311"; + case 0x0208: + return "\u0049\u030F"; + case 0x0209: + return "\u0069\u030F"; + case 0x020A: + return "\u0049\u0311"; + case 0x020B: + return "\u0069\u0311"; + case 0x020C: + return "\u004F\u030F"; + case 0x020D: + return "\u006F\u030F"; + case 0x020E: + return "\u004F\u0311"; + case 0x020F: + return "\u006F\u0311"; + case 0x0210: + return "\u0052\u030F"; + case 0x0211: + return "\u0072\u030F"; + case 0x0212: + return "\u0052\u0311"; + case 0x0213: + return "\u0072\u0311"; + case 0x0214: + return "\u0055\u030F"; + case 0x0215: + return "\u0075\u030F"; + case 0x0216: + return "\u0055\u0311"; + case 0x0217: + return "\u0075\u0311"; + case 0x0218: + return "\u0053\u0326"; + case 0x0219: + return "\u0073\u0326"; + case 0x021A: + return "\u0054\u0326"; + case 0x021B: + return "\u0074\u0326"; + case 0x021E: + return "\u0048\u030C"; + case 0x021F: + return "\u0068\u030C"; + case 0x0226: + return "\u0041\u0307"; + case 0x0227: + return "\u0061\u0307"; + case 0x0228: + return "\u0045\u0327"; + case 0x0229: + return "\u0065\u0327"; + case 0x022A: + return "\u00D6\u0304"; + case 0x022B: + return "\u00F6\u0304"; + case 0x022C: + return "\u00D5\u0304"; + case 0x022D: + return "\u00F5\u0304"; + case 0x022E: + return "\u004F\u0307"; + case 0x022F: + return "\u006F\u0307"; + case 0x0230: + return "\u022E\u0304"; + case 0x0231: + return "\u022F\u0304"; + case 0x0232: + return "\u0059\u0304"; + case 0x0233: + return "\u0079\u0304"; + case 0x0340: + return "\u0300"; + case 0x0341: + return "\u0301"; + case 0x0343: + return "\u0313"; + case 0x0344: + return "\u0308\u0301"; + case 0x0374: + return "\u02B9"; + case 0x037E: + return "\u003B"; + case 0x0385: + return "\u00A8\u0301"; + case 0x0386: + return "\u0391\u0301"; + case 0x0387: + return "\u00B7"; + case 0x0388: + return "\u0395\u0301"; + case 0x0389: + return "\u0397\u0301"; + case 0x038A: + return "\u0399\u0301"; + case 0x038C: + return "\u039F\u0301"; + case 0x038E: + return "\u03A5\u0301"; + case 0x038F: + return "\u03A9\u0301"; + case 0x0390: + return "\u03CA\u0301"; + case 0x03AA: + return "\u0399\u0308"; + case 0x03AB: + return "\u03A5\u0308"; + case 0x03AC: + return "\u03B1\u0301"; + case 0x03AD: + return "\u03B5\u0301"; + case 0x03AE: + return "\u03B7\u0301"; + case 0x03AF: + return "\u03B9\u0301"; + case 0x03B0: + return "\u03CB\u0301"; + case 0x03CA: + return "\u03B9\u0308"; + case 0x03CB: + return "\u03C5\u0308"; + case 0x03CC: + return "\u03BF\u0301"; + case 0x03CD: + return "\u03C5\u0301"; + case 0x03CE: + return "\u03C9\u0301"; + case 0x03D3: + return "\u03D2\u0301"; + case 0x03D4: + return "\u03D2\u0308"; + case 0x0400: + return "\u0415\u0300"; + case 0x0401: + return "\u0415\u0308"; + case 0x0403: + return "\u0413\u0301"; + case 0x0407: + return "\u0406\u0308"; + case 0x040C: + return "\u041A\u0301"; + case 0x040D: + return "\u0418\u0300"; + case 0x040E: + return "\u0423\u0306"; + case 0x0419: + return "\u0418\u0306"; + case 0x0439: + return "\u0438\u0306"; + case 0x0450: + return "\u0435\u0300"; + case 0x0451: + return "\u0435\u0308"; + case 0x0453: + return "\u0433\u0301"; + case 0x0457: + return "\u0456\u0308"; + case 0x045C: + return "\u043A\u0301"; + case 0x045D: + return "\u0438\u0300"; + case 0x045E: + return "\u0443\u0306"; + case 0x0476: + return "\u0474\u030F"; + case 0x0477: + return "\u0475\u030F"; + case 0x04C1: + return "\u0416\u0306"; + case 0x04C2: + return "\u0436\u0306"; + case 0x04D0: + return "\u0410\u0306"; + case 0x04D1: + return "\u0430\u0306"; + case 0x04D2: + return "\u0410\u0308"; + case 0x04D3: + return "\u0430\u0308"; + case 0x04D6: + return "\u0415\u0306"; + case 0x04D7: + return "\u0435\u0306"; + case 0x04DA: + return "\u04D8\u0308"; + case 0x04DB: + return "\u04D9\u0308"; + case 0x04DC: + return "\u0416\u0308"; + case 0x04DD: + return "\u0436\u0308"; + case 0x04DE: + return "\u0417\u0308"; + case 0x04DF: + return "\u0437\u0308"; + case 0x04E2: + return "\u0418\u0304"; + case 0x04E3: + return "\u0438\u0304"; + case 0x04E4: + return "\u0418\u0308"; + case 0x04E5: + return "\u0438\u0308"; + case 0x04E6: + return "\u041E\u0308"; + case 0x04E7: + return "\u043E\u0308"; + case 0x04EA: + return "\u04E8\u0308"; + case 0x04EB: + return "\u04E9\u0308"; + case 0x04EC: + return "\u042D\u0308"; + case 0x04ED: + return "\u044D\u0308"; + case 0x04EE: + return "\u0423\u0304"; + case 0x04EF: + return "\u0443\u0304"; + case 0x04F0: + return "\u0423\u0308"; + case 0x04F1: + return "\u0443\u0308"; + case 0x04F2: + return "\u0423\u030B"; + case 0x04F3: + return "\u0443\u030B"; + case 0x04F4: + return "\u0427\u0308"; + case 0x04F5: + return "\u0447\u0308"; + case 0x04F8: + return "\u042B\u0308"; + case 0x04F9: + return "\u044B\u0308"; + case 0x0622: + return "\u0627\u0653"; + case 0x0623: + return "\u0627\u0654"; + case 0x0624: + return "\u0648\u0654"; + case 0x0625: + return "\u0627\u0655"; + case 0x0626: + return "\u064A\u0654"; + case 0x06C0: + return "\u06D5\u0654"; + case 0x06C2: + return "\u06C1\u0654"; + case 0x06D3: + return "\u06D2\u0654"; + case 0x0929: + return "\u0928\u093C"; + case 0x0931: + return "\u0930\u093C"; + case 0x0934: + return "\u0933\u093C"; + case 0x0958: + return "\u0915\u093C"; + case 0x0959: + return "\u0916\u093C"; + case 0x095A: + return "\u0917\u093C"; + case 0x095B: + return "\u091C\u093C"; + case 0x095C: + return "\u0921\u093C"; + case 0x095D: + return "\u0922\u093C"; + case 0x095E: + return "\u092B\u093C"; + case 0x095F: + return "\u092F\u093C"; + case 0x09CB: + return "\u09C7\u09BE"; + case 0x09CC: + return "\u09C7\u09D7"; + case 0x09DC: + return "\u09A1\u09BC"; + case 0x09DD: + return "\u09A2\u09BC"; + case 0x09DF: + return "\u09AF\u09BC"; + case 0x0A33: + return "\u0A32\u0A3C"; + case 0x0A36: + return "\u0A38\u0A3C"; + case 0x0A59: + return "\u0A16\u0A3C"; + case 0x0A5A: + return "\u0A17\u0A3C"; + case 0x0A5B: + return "\u0A1C\u0A3C"; + case 0x0A5E: + return "\u0A2B\u0A3C"; + case 0x0B48: + return "\u0B47\u0B56"; + case 0x0B4B: + return "\u0B47\u0B3E"; + case 0x0B4C: + return "\u0B47\u0B57"; + case 0x0B5C: + return "\u0B21\u0B3C"; + case 0x0B5D: + return "\u0B22\u0B3C"; + case 0x0B94: + return "\u0B92\u0BD7"; + case 0x0BCA: + return "\u0BC6\u0BBE"; + case 0x0BCB: + return "\u0BC7\u0BBE"; + case 0x0BCC: + return "\u0BC6\u0BD7"; + case 0x0C48: + return "\u0C46\u0C56"; + case 0x0CC0: + return "\u0CBF\u0CD5"; + case 0x0CC7: + return "\u0CC6\u0CD5"; + case 0x0CC8: + return "\u0CC6\u0CD6"; + case 0x0CCA: + return "\u0CC6\u0CC2"; + case 0x0CCB: + return "\u0CCA\u0CD5"; + case 0x0D4A: + return "\u0D46\u0D3E"; + case 0x0D4B: + return "\u0D47\u0D3E"; + case 0x0D4C: + return "\u0D46\u0D57"; + case 0x0DDA: + return "\u0DD9\u0DCA"; + case 0x0DDC: + return "\u0DD9\u0DCF"; + case 0x0DDD: + return "\u0DDC\u0DCA"; + case 0x0DDE: + return "\u0DD9\u0DDF"; + case 0x0F43: + return "\u0F42\u0FB7"; + case 0x0F4D: + return "\u0F4C\u0FB7"; + case 0x0F52: + return "\u0F51\u0FB7"; + case 0x0F57: + return "\u0F56\u0FB7"; + case 0x0F5C: + return "\u0F5B\u0FB7"; + case 0x0F69: + return "\u0F40\u0FB5"; + case 0x0F73: + return "\u0F71\u0F72"; + case 0x0F75: + return "\u0F71\u0F74"; + case 0x0F76: + return "\u0FB2\u0F80"; + case 0x0F78: + return "\u0FB3\u0F80"; + case 0x0F81: + return "\u0F71\u0F80"; + case 0x0F93: + return "\u0F92\u0FB7"; + case 0x0F9D: + return "\u0F9C\u0FB7"; + case 0x0FA2: + return "\u0FA1\u0FB7"; + case 0x0FA7: + return "\u0FA6\u0FB7"; + case 0x0FAC: + return "\u0FAB\u0FB7"; + case 0x0FB9: + return "\u0F90\u0FB5"; + case 0x1026: + return "\u1025\u102E"; + case 0x1B06: + return "\u1B05\u1B35"; + case 0x1B08: + return "\u1B07\u1B35"; + case 0x1B0A: + return "\u1B09\u1B35"; + case 0x1B0C: + return "\u1B0B\u1B35"; + case 0x1B0E: + return "\u1B0D\u1B35"; + case 0x1B12: + return "\u1B11\u1B35"; + case 0x1B3B: + return "\u1B3A\u1B35"; + case 0x1B3D: + return "\u1B3C\u1B35"; + case 0x1B40: + return "\u1B3E\u1B35"; + case 0x1B41: + return "\u1B3F\u1B35"; + case 0x1B43: + return "\u1B42\u1B35"; + case 0x1E00: + return "\u0041\u0325"; + case 0x1E01: + return "\u0061\u0325"; + case 0x1E02: + return "\u0042\u0307"; + case 0x1E03: + return "\u0062\u0307"; + case 0x1E04: + return "\u0042\u0323"; + case 0x1E05: + return "\u0062\u0323"; + case 0x1E06: + return "\u0042\u0331"; + case 0x1E07: + return "\u0062\u0331"; + case 0x1E08: + return "\u00C7\u0301"; + case 0x1E09: + return "\u00E7\u0301"; + case 0x1E0A: + return "\u0044\u0307"; + case 0x1E0B: + return "\u0064\u0307"; + case 0x1E0C: + return "\u0044\u0323"; + case 0x1E0D: + return "\u0064\u0323"; + case 0x1E0E: + return "\u0044\u0331"; + case 0x1E0F: + return "\u0064\u0331"; + case 0x1E10: + return "\u0044\u0327"; + case 0x1E11: + return "\u0064\u0327"; + case 0x1E12: + return "\u0044\u032D"; + case 0x1E13: + return "\u0064\u032D"; + case 0x1E14: + return "\u0112\u0300"; + case 0x1E15: + return "\u0113\u0300"; + case 0x1E16: + return "\u0112\u0301"; + case 0x1E17: + return "\u0113\u0301"; + case 0x1E18: + return "\u0045\u032D"; + case 0x1E19: + return "\u0065\u032D"; + case 0x1E1A: + return "\u0045\u0330"; + case 0x1E1B: + return "\u0065\u0330"; + case 0x1E1C: + return "\u0228\u0306"; + case 0x1E1D: + return "\u0229\u0306"; + case 0x1E1E: + return "\u0046\u0307"; + case 0x1E1F: + return "\u0066\u0307"; + case 0x1E20: + return "\u0047\u0304"; + case 0x1E21: + return "\u0067\u0304"; + case 0x1E22: + return "\u0048\u0307"; + case 0x1E23: + return "\u0068\u0307"; + case 0x1E24: + return "\u0048\u0323"; + case 0x1E25: + return "\u0068\u0323"; + case 0x1E26: + return "\u0048\u0308"; + case 0x1E27: + return "\u0068\u0308"; + case 0x1E28: + return "\u0048\u0327"; + case 0x1E29: + return "\u0068\u0327"; + case 0x1E2A: + return "\u0048\u032E"; + case 0x1E2B: + return "\u0068\u032E"; + case 0x1E2C: + return "\u0049\u0330"; + case 0x1E2D: + return "\u0069\u0330"; + case 0x1E2E: + return "\u00CF\u0301"; + case 0x1E2F: + return "\u00EF\u0301"; + case 0x1E30: + return "\u004B\u0301"; + case 0x1E31: + return "\u006B\u0301"; + case 0x1E32: + return "\u004B\u0323"; + case 0x1E33: + return "\u006B\u0323"; + case 0x1E34: + return "\u004B\u0331"; + case 0x1E35: + return "\u006B\u0331"; + case 0x1E36: + return "\u004C\u0323"; + case 0x1E37: + return "\u006C\u0323"; + case 0x1E38: + return "\u1E36\u0304"; + case 0x1E39: + return "\u1E37\u0304"; + case 0x1E3A: + return "\u004C\u0331"; + case 0x1E3B: + return "\u006C\u0331"; + case 0x1E3C: + return "\u004C\u032D"; + case 0x1E3D: + return "\u006C\u032D"; + case 0x1E3E: + return "\u004D\u0301"; + case 0x1E3F: + return "\u006D\u0301"; + case 0x1E40: + return "\u004D\u0307"; + case 0x1E41: + return "\u006D\u0307"; + case 0x1E42: + return "\u004D\u0323"; + case 0x1E43: + return "\u006D\u0323"; + case 0x1E44: + return "\u004E\u0307"; + case 0x1E45: + return "\u006E\u0307"; + case 0x1E46: + return "\u004E\u0323"; + case 0x1E47: + return "\u006E\u0323"; + case 0x1E48: + return "\u004E\u0331"; + case 0x1E49: + return "\u006E\u0331"; + case 0x1E4A: + return "\u004E\u032D"; + case 0x1E4B: + return "\u006E\u032D"; + case 0x1E4C: + return "\u00D5\u0301"; + case 0x1E4D: + return "\u00F5\u0301"; + case 0x1E4E: + return "\u00D5\u0308"; + case 0x1E4F: + return "\u00F5\u0308"; + case 0x1E50: + return "\u014C\u0300"; + case 0x1E51: + return "\u014D\u0300"; + case 0x1E52: + return "\u014C\u0301"; + case 0x1E53: + return "\u014D\u0301"; + case 0x1E54: + return "\u0050\u0301"; + case 0x1E55: + return "\u0070\u0301"; + case 0x1E56: + return "\u0050\u0307"; + case 0x1E57: + return "\u0070\u0307"; + case 0x1E58: + return "\u0052\u0307"; + case 0x1E59: + return "\u0072\u0307"; + case 0x1E5A: + return "\u0052\u0323"; + case 0x1E5B: + return "\u0072\u0323"; + case 0x1E5C: + return "\u1E5A\u0304"; + case 0x1E5D: + return "\u1E5B\u0304"; + case 0x1E5E: + return "\u0052\u0331"; + case 0x1E5F: + return "\u0072\u0331"; + case 0x1E60: + return "\u0053\u0307"; + case 0x1E61: + return "\u0073\u0307"; + case 0x1E62: + return "\u0053\u0323"; + case 0x1E63: + return "\u0073\u0323"; + case 0x1E64: + return "\u015A\u0307"; + case 0x1E65: + return "\u015B\u0307"; + case 0x1E66: + return "\u0160\u0307"; + case 0x1E67: + return "\u0161\u0307"; + case 0x1E68: + return "\u1E62\u0307"; + case 0x1E69: + return "\u1E63\u0307"; + case 0x1E6A: + return "\u0054\u0307"; + case 0x1E6B: + return "\u0074\u0307"; + case 0x1E6C: + return "\u0054\u0323"; + case 0x1E6D: + return "\u0074\u0323"; + case 0x1E6E: + return "\u0054\u0331"; + case 0x1E6F: + return "\u0074\u0331"; + case 0x1E70: + return "\u0054\u032D"; + case 0x1E71: + return "\u0074\u032D"; + case 0x1E72: + return "\u0055\u0324"; + case 0x1E73: + return "\u0075\u0324"; + case 0x1E74: + return "\u0055\u0330"; + case 0x1E75: + return "\u0075\u0330"; + case 0x1E76: + return "\u0055\u032D"; + case 0x1E77: + return "\u0075\u032D"; + case 0x1E78: + return "\u0168\u0301"; + case 0x1E79: + return "\u0169\u0301"; + case 0x1E7A: + return "\u016A\u0308"; + case 0x1E7B: + return "\u016B\u0308"; + case 0x1E7C: + return "\u0056\u0303"; + case 0x1E7D: + return "\u0076\u0303"; + case 0x1E7E: + return "\u0056\u0323"; + case 0x1E7F: + return "\u0076\u0323"; + case 0x1E80: + return "\u0057\u0300"; + case 0x1E81: + return "\u0077\u0300"; + case 0x1E82: + return "\u0057\u0301"; + case 0x1E83: + return "\u0077\u0301"; + case 0x1E84: + return "\u0057\u0308"; + case 0x1E85: + return "\u0077\u0308"; + case 0x1E86: + return "\u0057\u0307"; + case 0x1E87: + return "\u0077\u0307"; + case 0x1E88: + return "\u0057\u0323"; + case 0x1E89: + return "\u0077\u0323"; + case 0x1E8A: + return "\u0058\u0307"; + case 0x1E8B: + return "\u0078\u0307"; + case 0x1E8C: + return "\u0058\u0308"; + case 0x1E8D: + return "\u0078\u0308"; + case 0x1E8E: + return "\u0059\u0307"; + case 0x1E8F: + return "\u0079\u0307"; + case 0x1E90: + return "\u005A\u0302"; + case 0x1E91: + return "\u007A\u0302"; + case 0x1E92: + return "\u005A\u0323"; + case 0x1E93: + return "\u007A\u0323"; + case 0x1E94: + return "\u005A\u0331"; + case 0x1E95: + return "\u007A\u0331"; + case 0x1E96: + return "\u0068\u0331"; + case 0x1E97: + return "\u0074\u0308"; + case 0x1E98: + return "\u0077\u030A"; + case 0x1E99: + return "\u0079\u030A"; + case 0x1E9B: + return "\u017F\u0307"; + case 0x1EA0: + return "\u0041\u0323"; + case 0x1EA1: + return "\u0061\u0323"; + case 0x1EA2: + return "\u0041\u0309"; + case 0x1EA3: + return "\u0061\u0309"; + case 0x1EA4: + return "\u00C2\u0301"; + case 0x1EA5: + return "\u00E2\u0301"; + case 0x1EA6: + return "\u00C2\u0300"; + case 0x1EA7: + return "\u00E2\u0300"; + case 0x1EA8: + return "\u00C2\u0309"; + case 0x1EA9: + return "\u00E2\u0309"; + case 0x1EAA: + return "\u00C2\u0303"; + case 0x1EAB: + return "\u00E2\u0303"; + case 0x1EAC: + return "\u1EA0\u0302"; + case 0x1EAD: + return "\u1EA1\u0302"; + case 0x1EAE: + return "\u0102\u0301"; + case 0x1EAF: + return "\u0103\u0301"; + case 0x1EB0: + return "\u0102\u0300"; + case 0x1EB1: + return "\u0103\u0300"; + case 0x1EB2: + return "\u0102\u0309"; + case 0x1EB3: + return "\u0103\u0309"; + case 0x1EB4: + return "\u0102\u0303"; + case 0x1EB5: + return "\u0103\u0303"; + case 0x1EB6: + return "\u1EA0\u0306"; + case 0x1EB7: + return "\u1EA1\u0306"; + case 0x1EB8: + return "\u0045\u0323"; + case 0x1EB9: + return "\u0065\u0323"; + case 0x1EBA: + return "\u0045\u0309"; + case 0x1EBB: + return "\u0065\u0309"; + case 0x1EBC: + return "\u0045\u0303"; + case 0x1EBD: + return "\u0065\u0303"; + case 0x1EBE: + return "\u00CA\u0301"; + case 0x1EBF: + return "\u00EA\u0301"; + case 0x1EC0: + return "\u00CA\u0300"; + case 0x1EC1: + return "\u00EA\u0300"; + case 0x1EC2: + return "\u00CA\u0309"; + case 0x1EC3: + return "\u00EA\u0309"; + case 0x1EC4: + return "\u00CA\u0303"; + case 0x1EC5: + return "\u00EA\u0303"; + case 0x1EC6: + return "\u1EB8\u0302"; + case 0x1EC7: + return "\u1EB9\u0302"; + case 0x1EC8: + return "\u0049\u0309"; + case 0x1EC9: + return "\u0069\u0309"; + case 0x1ECA: + return "\u0049\u0323"; + case 0x1ECB: + return "\u0069\u0323"; + case 0x1ECC: + return "\u004F\u0323"; + case 0x1ECD: + return "\u006F\u0323"; + case 0x1ECE: + return "\u004F\u0309"; + case 0x1ECF: + return "\u006F\u0309"; + case 0x1ED0: + return "\u00D4\u0301"; + case 0x1ED1: + return "\u00F4\u0301"; + case 0x1ED2: + return "\u00D4\u0300"; + case 0x1ED3: + return "\u00F4\u0300"; + case 0x1ED4: + return "\u00D4\u0309"; + case 0x1ED5: + return "\u00F4\u0309"; + case 0x1ED6: + return "\u00D4\u0303"; + case 0x1ED7: + return "\u00F4\u0303"; + case 0x1ED8: + return "\u1ECC\u0302"; + case 0x1ED9: + return "\u1ECD\u0302"; + case 0x1EDA: + return "\u01A0\u0301"; + case 0x1EDB: + return "\u01A1\u0301"; + case 0x1EDC: + return "\u01A0\u0300"; + case 0x1EDD: + return "\u01A1\u0300"; + case 0x1EDE: + return "\u01A0\u0309"; + case 0x1EDF: + return "\u01A1\u0309"; + case 0x1EE0: + return "\u01A0\u0303"; + case 0x1EE1: + return "\u01A1\u0303"; + case 0x1EE2: + return "\u01A0\u0323"; + case 0x1EE3: + return "\u01A1\u0323"; + case 0x1EE4: + return "\u0055\u0323"; + case 0x1EE5: + return "\u0075\u0323"; + case 0x1EE6: + return "\u0055\u0309"; + case 0x1EE7: + return "\u0075\u0309"; + case 0x1EE8: + return "\u01AF\u0301"; + case 0x1EE9: + return "\u01B0\u0301"; + case 0x1EEA: + return "\u01AF\u0300"; + case 0x1EEB: + return "\u01B0\u0300"; + case 0x1EEC: + return "\u01AF\u0309"; + case 0x1EED: + return "\u01B0\u0309"; + case 0x1EEE: + return "\u01AF\u0303"; + case 0x1EEF: + return "\u01B0\u0303"; + case 0x1EF0: + return "\u01AF\u0323"; + case 0x1EF1: + return "\u01B0\u0323"; + case 0x1EF2: + return "\u0059\u0300"; + case 0x1EF3: + return "\u0079\u0300"; + case 0x1EF4: + return "\u0059\u0323"; + case 0x1EF5: + return "\u0079\u0323"; + case 0x1EF6: + return "\u0059\u0309"; + case 0x1EF7: + return "\u0079\u0309"; + case 0x1EF8: + return "\u0059\u0303"; + case 0x1EF9: + return "\u0079\u0303"; + case 0x1F00: + return "\u03B1\u0313"; + case 0x1F01: + return "\u03B1\u0314"; + case 0x1F02: + return "\u1F00\u0300"; + case 0x1F03: + return "\u1F01\u0300"; + case 0x1F04: + return "\u1F00\u0301"; + case 0x1F05: + return "\u1F01\u0301"; + case 0x1F06: + return "\u1F00\u0342"; + case 0x1F07: + return "\u1F01\u0342"; + case 0x1F08: + return "\u0391\u0313"; + case 0x1F09: + return "\u0391\u0314"; + case 0x1F0A: + return "\u1F08\u0300"; + case 0x1F0B: + return "\u1F09\u0300"; + case 0x1F0C: + return "\u1F08\u0301"; + case 0x1F0D: + return "\u1F09\u0301"; + case 0x1F0E: + return "\u1F08\u0342"; + case 0x1F0F: + return "\u1F09\u0342"; + case 0x1F10: + return "\u03B5\u0313"; + case 0x1F11: + return "\u03B5\u0314"; + case 0x1F12: + return "\u1F10\u0300"; + case 0x1F13: + return "\u1F11\u0300"; + case 0x1F14: + return "\u1F10\u0301"; + case 0x1F15: + return "\u1F11\u0301"; + case 0x1F18: + return "\u0395\u0313"; + case 0x1F19: + return "\u0395\u0314"; + case 0x1F1A: + return "\u1F18\u0300"; + case 0x1F1B: + return "\u1F19\u0300"; + case 0x1F1C: + return "\u1F18\u0301"; + case 0x1F1D: + return "\u1F19\u0301"; + case 0x1F20: + return "\u03B7\u0313"; + case 0x1F21: + return "\u03B7\u0314"; + case 0x1F22: + return "\u1F20\u0300"; + case 0x1F23: + return "\u1F21\u0300"; + case 0x1F24: + return "\u1F20\u0301"; + case 0x1F25: + return "\u1F21\u0301"; + case 0x1F26: + return "\u1F20\u0342"; + case 0x1F27: + return "\u1F21\u0342"; + case 0x1F28: + return "\u0397\u0313"; + case 0x1F29: + return "\u0397\u0314"; + case 0x1F2A: + return "\u1F28\u0300"; + case 0x1F2B: + return "\u1F29\u0300"; + case 0x1F2C: + return "\u1F28\u0301"; + case 0x1F2D: + return "\u1F29\u0301"; + case 0x1F2E: + return "\u1F28\u0342"; + case 0x1F2F: + return "\u1F29\u0342"; + case 0x1F30: + return "\u03B9\u0313"; + case 0x1F31: + return "\u03B9\u0314"; + case 0x1F32: + return "\u1F30\u0300"; + case 0x1F33: + return "\u1F31\u0300"; + case 0x1F34: + return "\u1F30\u0301"; + case 0x1F35: + return "\u1F31\u0301"; + case 0x1F36: + return "\u1F30\u0342"; + case 0x1F37: + return "\u1F31\u0342"; + case 0x1F38: + return "\u0399\u0313"; + case 0x1F39: + return "\u0399\u0314"; + case 0x1F3A: + return "\u1F38\u0300"; + case 0x1F3B: + return "\u1F39\u0300"; + case 0x1F3C: + return "\u1F38\u0301"; + case 0x1F3D: + return "\u1F39\u0301"; + case 0x1F3E: + return "\u1F38\u0342"; + case 0x1F3F: + return "\u1F39\u0342"; + case 0x1F40: + return "\u03BF\u0313"; + case 0x1F41: + return "\u03BF\u0314"; + case 0x1F42: + return "\u1F40\u0300"; + case 0x1F43: + return "\u1F41\u0300"; + case 0x1F44: + return "\u1F40\u0301"; + case 0x1F45: + return "\u1F41\u0301"; + case 0x1F48: + return "\u039F\u0313"; + case 0x1F49: + return "\u039F\u0314"; + case 0x1F4A: + return "\u1F48\u0300"; + case 0x1F4B: + return "\u1F49\u0300"; + case 0x1F4C: + return "\u1F48\u0301"; + case 0x1F4D: + return "\u1F49\u0301"; + case 0x1F50: + return "\u03C5\u0313"; + case 0x1F51: + return "\u03C5\u0314"; + case 0x1F52: + return "\u1F50\u0300"; + case 0x1F53: + return "\u1F51\u0300"; + case 0x1F54: + return "\u1F50\u0301"; + case 0x1F55: + return "\u1F51\u0301"; + case 0x1F56: + return "\u1F50\u0342"; + case 0x1F57: + return "\u1F51\u0342"; + case 0x1F59: + return "\u03A5\u0314"; + case 0x1F5B: + return "\u1F59\u0300"; + case 0x1F5D: + return "\u1F59\u0301"; + case 0x1F5F: + return "\u1F59\u0342"; + case 0x1F60: + return "\u03C9\u0313"; + case 0x1F61: + return "\u03C9\u0314"; + case 0x1F62: + return "\u1F60\u0300"; + case 0x1F63: + return "\u1F61\u0300"; + case 0x1F64: + return "\u1F60\u0301"; + case 0x1F65: + return "\u1F61\u0301"; + case 0x1F66: + return "\u1F60\u0342"; + case 0x1F67: + return "\u1F61\u0342"; + case 0x1F68: + return "\u03A9\u0313"; + case 0x1F69: + return "\u03A9\u0314"; + case 0x1F6A: + return "\u1F68\u0300"; + case 0x1F6B: + return "\u1F69\u0300"; + case 0x1F6C: + return "\u1F68\u0301"; + case 0x1F6D: + return "\u1F69\u0301"; + case 0x1F6E: + return "\u1F68\u0342"; + case 0x1F6F: + return "\u1F69\u0342"; + case 0x1F70: + return "\u03B1\u0300"; + case 0x1F71: + return "\u03AC"; + case 0x1F72: + return "\u03B5\u0300"; + case 0x1F73: + return "\u03AD"; + case 0x1F74: + return "\u03B7\u0300"; + case 0x1F75: + return "\u03AE"; + case 0x1F76: + return "\u03B9\u0300"; + case 0x1F77: + return "\u03AF"; + case 0x1F78: + return "\u03BF\u0300"; + case 0x1F79: + return "\u03CC"; + case 0x1F7A: + return "\u03C5\u0300"; + case 0x1F7B: + return "\u03CD"; + case 0x1F7C: + return "\u03C9\u0300"; + case 0x1F7D: + return "\u03CE"; + case 0x1F80: + return "\u1F00\u0345"; + case 0x1F81: + return "\u1F01\u0345"; + case 0x1F82: + return "\u1F02\u0345"; + case 0x1F83: + return "\u1F03\u0345"; + case 0x1F84: + return "\u1F04\u0345"; + case 0x1F85: + return "\u1F05\u0345"; + case 0x1F86: + return "\u1F06\u0345"; + case 0x1F87: + return "\u1F07\u0345"; + case 0x1F88: + return "\u1F08\u0345"; + case 0x1F89: + return "\u1F09\u0345"; + case 0x1F8A: + return "\u1F0A\u0345"; + case 0x1F8B: + return "\u1F0B\u0345"; + case 0x1F8C: + return "\u1F0C\u0345"; + case 0x1F8D: + return "\u1F0D\u0345"; + case 0x1F8E: + return "\u1F0E\u0345"; + case 0x1F8F: + return "\u1F0F\u0345"; + case 0x1F90: + return "\u1F20\u0345"; + case 0x1F91: + return "\u1F21\u0345"; + case 0x1F92: + return "\u1F22\u0345"; + case 0x1F93: + return "\u1F23\u0345"; + case 0x1F94: + return "\u1F24\u0345"; + case 0x1F95: + return "\u1F25\u0345"; + case 0x1F96: + return "\u1F26\u0345"; + case 0x1F97: + return "\u1F27\u0345"; + case 0x1F98: + return "\u1F28\u0345"; + case 0x1F99: + return "\u1F29\u0345"; + case 0x1F9A: + return "\u1F2A\u0345"; + case 0x1F9B: + return "\u1F2B\u0345"; + case 0x1F9C: + return "\u1F2C\u0345"; + case 0x1F9D: + return "\u1F2D\u0345"; + case 0x1F9E: + return "\u1F2E\u0345"; + case 0x1F9F: + return "\u1F2F\u0345"; + case 0x1FA0: + return "\u1F60\u0345"; + case 0x1FA1: + return "\u1F61\u0345"; + case 0x1FA2: + return "\u1F62\u0345"; + case 0x1FA3: + return "\u1F63\u0345"; + case 0x1FA4: + return "\u1F64\u0345"; + case 0x1FA5: + return "\u1F65\u0345"; + case 0x1FA6: + return "\u1F66\u0345"; + case 0x1FA7: + return "\u1F67\u0345"; + case 0x1FA8: + return "\u1F68\u0345"; + case 0x1FA9: + return "\u1F69\u0345"; + case 0x1FAA: + return "\u1F6A\u0345"; + case 0x1FAB: + return "\u1F6B\u0345"; + case 0x1FAC: + return "\u1F6C\u0345"; + case 0x1FAD: + return "\u1F6D\u0345"; + case 0x1FAE: + return "\u1F6E\u0345"; + case 0x1FAF: + return "\u1F6F\u0345"; + case 0x1FB0: + return "\u03B1\u0306"; + case 0x1FB1: + return "\u03B1\u0304"; + case 0x1FB2: + return "\u1F70\u0345"; + case 0x1FB3: + return "\u03B1\u0345"; + case 0x1FB4: + return "\u03AC\u0345"; + case 0x1FB6: + return "\u03B1\u0342"; + case 0x1FB7: + return "\u1FB6\u0345"; + case 0x1FB8: + return "\u0391\u0306"; + case 0x1FB9: + return "\u0391\u0304"; + case 0x1FBA: + return "\u0391\u0300"; + case 0x1FBB: + return "\u0386"; + case 0x1FBC: + return "\u0391\u0345"; + case 0x1FBE: + return "\u03B9"; + case 0x1FC1: + return "\u00A8\u0342"; + case 0x1FC2: + return "\u1F74\u0345"; + case 0x1FC3: + return "\u03B7\u0345"; + case 0x1FC4: + return "\u03AE\u0345"; + case 0x1FC6: + return "\u03B7\u0342"; + case 0x1FC7: + return "\u1FC6\u0345"; + case 0x1FC8: + return "\u0395\u0300"; + case 0x1FC9: + return "\u0388"; + case 0x1FCA: + return "\u0397\u0300"; + case 0x1FCB: + return "\u0389"; + case 0x1FCC: + return "\u0397\u0345"; + case 0x1FCD: + return "\u1FBF\u0300"; + case 0x1FCE: + return "\u1FBF\u0301"; + case 0x1FCF: + return "\u1FBF\u0342"; + case 0x1FD0: + return "\u03B9\u0306"; + case 0x1FD1: + return "\u03B9\u0304"; + case 0x1FD2: + return "\u03CA\u0300"; + case 0x1FD3: + return "\u0390"; + case 0x1FD6: + return "\u03B9\u0342"; + case 0x1FD7: + return "\u03CA\u0342"; + case 0x1FD8: + return "\u0399\u0306"; + case 0x1FD9: + return "\u0399\u0304"; + case 0x1FDA: + return "\u0399\u0300"; + case 0x1FDB: + return "\u038A"; + case 0x1FDD: + return "\u1FFE\u0300"; + case 0x1FDE: + return "\u1FFE\u0301"; + case 0x1FDF: + return "\u1FFE\u0342"; + case 0x1FE0: + return "\u03C5\u0306"; + case 0x1FE1: + return "\u03C5\u0304"; + case 0x1FE2: + return "\u03CB\u0300"; + case 0x1FE3: + return "\u03B0"; + case 0x1FE4: + return "\u03C1\u0313"; + case 0x1FE5: + return "\u03C1\u0314"; + case 0x1FE6: + return "\u03C5\u0342"; + case 0x1FE7: + return "\u03CB\u0342"; + case 0x1FE8: + return "\u03A5\u0306"; + case 0x1FE9: + return "\u03A5\u0304"; + case 0x1FEA: + return "\u03A5\u0300"; + case 0x1FEB: + return "\u038E"; + case 0x1FEC: + return "\u03A1\u0314"; + case 0x1FED: + return "\u00A8\u0300"; + case 0x1FEE: + return "\u0385"; + case 0x1FEF: + return "\u0060"; + case 0x1FF2: + return "\u1F7C\u0345"; + case 0x1FF3: + return "\u03C9\u0345"; + case 0x1FF4: + return "\u03CE\u0345"; + case 0x1FF6: + return "\u03C9\u0342"; + case 0x1FF7: + return "\u1FF6\u0345"; + case 0x1FF8: + return "\u039F\u0300"; + case 0x1FF9: + return "\u038C"; + case 0x1FFA: + return "\u03A9\u0300"; + case 0x1FFB: + return "\u038F"; + case 0x1FFC: + return "\u03A9\u0345"; + case 0x1FFD: + return "\u00B4"; + case 0x2000: + return "\u2002"; + case 0x2001: + return "\u2003"; + case 0x2126: + return "\u03A9"; + case 0x212A: + return "\u004B"; + case 0x212B: + return "\u00C5"; + case 0x219A: + return "\u2190\u0338"; + case 0x219B: + return "\u2192\u0338"; + case 0x21AE: + return "\u2194\u0338"; + case 0x21CD: + return "\u21D0\u0338"; + case 0x21CE: + return "\u21D4\u0338"; + case 0x21CF: + return "\u21D2\u0338"; + case 0x2204: + return "\u2203\u0338"; + case 0x2209: + return "\u2208\u0338"; + case 0x220C: + return "\u220B\u0338"; + case 0x2224: + return "\u2223\u0338"; + case 0x2226: + return "\u2225\u0338"; + case 0x2241: + return "\u223C\u0338"; + case 0x2244: + return "\u2243\u0338"; + case 0x2247: + return "\u2245\u0338"; + case 0x2249: + return "\u2248\u0338"; + case 0x2260: + return "\u003D\u0338"; + case 0x2262: + return "\u2261\u0338"; + case 0x226D: + return "\u224D\u0338"; + case 0x226E: + return "\u003C\u0338"; + case 0x226F: + return "\u003E\u0338"; + case 0x2270: + return "\u2264\u0338"; + case 0x2271: + return "\u2265\u0338"; + case 0x2274: + return "\u2272\u0338"; + case 0x2275: + return "\u2273\u0338"; + case 0x2278: + return "\u2276\u0338"; + case 0x2279: + return "\u2277\u0338"; + case 0x2280: + return "\u227A\u0338"; + case 0x2281: + return "\u227B\u0338"; + case 0x2284: + return "\u2282\u0338"; + case 0x2285: + return "\u2283\u0338"; + case 0x2288: + return "\u2286\u0338"; + case 0x2289: + return "\u2287\u0338"; + case 0x22AC: + return "\u22A2\u0338"; + case 0x22AD: + return "\u22A8\u0338"; + case 0x22AE: + return "\u22A9\u0338"; + case 0x22AF: + return "\u22AB\u0338"; + case 0x22E0: + return "\u227C\u0338"; + case 0x22E1: + return "\u227D\u0338"; + case 0x22E2: + return "\u2291\u0338"; + case 0x22E3: + return "\u2292\u0338"; + case 0x22EA: + return "\u22B2\u0338"; + case 0x22EB: + return "\u22B3\u0338"; + case 0x22EC: + return "\u22B4\u0338"; + case 0x22ED: + return "\u22B5\u0338"; + case 0x2329: + return "\u3008"; + case 0x232A: + return "\u3009"; + case 0x2ADC: + return "\u2ADD\u0338"; + case 0x304C: + return "\u304B\u3099"; + case 0x304E: + return "\u304D\u3099"; + case 0x3050: + return "\u304F\u3099"; + case 0x3052: + return "\u3051\u3099"; + case 0x3054: + return "\u3053\u3099"; + case 0x3056: + return "\u3055\u3099"; + case 0x3058: + return "\u3057\u3099"; + case 0x305A: + return "\u3059\u3099"; + case 0x305C: + return "\u305B\u3099"; + case 0x305E: + return "\u305D\u3099"; + case 0x3060: + return "\u305F\u3099"; + case 0x3062: + return "\u3061\u3099"; + case 0x3065: + return "\u3064\u3099"; + case 0x3067: + return "\u3066\u3099"; + case 0x3069: + return "\u3068\u3099"; + case 0x3070: + return "\u306F\u3099"; + case 0x3071: + return "\u306F\u309A"; + case 0x3073: + return "\u3072\u3099"; + case 0x3074: + return "\u3072\u309A"; + case 0x3076: + return "\u3075\u3099"; + case 0x3077: + return "\u3075\u309A"; + case 0x3079: + return "\u3078\u3099"; + case 0x307A: + return "\u3078\u309A"; + case 0x307C: + return "\u307B\u3099"; + case 0x307D: + return "\u307B\u309A"; + case 0x3094: + return "\u3046\u3099"; + case 0x309E: + return "\u309D\u3099"; + case 0x30AC: + return "\u30AB\u3099"; + case 0x30AE: + return "\u30AD\u3099"; + case 0x30B0: + return "\u30AF\u3099"; + case 0x30B2: + return "\u30B1\u3099"; + case 0x30B4: + return "\u30B3\u3099"; + case 0x30B6: + return "\u30B5\u3099"; + case 0x30B8: + return "\u30B7\u3099"; + case 0x30BA: + return "\u30B9\u3099"; + case 0x30BC: + return "\u30BB\u3099"; + case 0x30BE: + return "\u30BD\u3099"; + case 0x30C0: + return "\u30BF\u3099"; + case 0x30C2: + return "\u30C1\u3099"; + case 0x30C5: + return "\u30C4\u3099"; + case 0x30C7: + return "\u30C6\u3099"; + case 0x30C9: + return "\u30C8\u3099"; + case 0x30D0: + return "\u30CF\u3099"; + case 0x30D1: + return "\u30CF\u309A"; + case 0x30D3: + return "\u30D2\u3099"; + case 0x30D4: + return "\u30D2\u309A"; + case 0x30D6: + return "\u30D5\u3099"; + case 0x30D7: + return "\u30D5\u309A"; + case 0x30D9: + return "\u30D8\u3099"; + case 0x30DA: + return "\u30D8\u309A"; + case 0x30DC: + return "\u30DB\u3099"; + case 0x30DD: + return "\u30DB\u309A"; + case 0x30F4: + return "\u30A6\u3099"; + case 0x30F7: + return "\u30EF\u3099"; + case 0x30F8: + return "\u30F0\u3099"; + case 0x30F9: + return "\u30F1\u3099"; + case 0x30FA: + return "\u30F2\u3099"; + case 0x30FE: + return "\u30FD\u3099"; + case 0xF900: + return "\u8C48"; + case 0xF901: + return "\u66F4"; + case 0xF902: + return "\u8ECA"; + case 0xF903: + return "\u8CC8"; + case 0xF904: + return "\u6ED1"; + case 0xF905: + return "\u4E32"; + case 0xF906: + return "\u53E5"; + case 0xF907: + return "\u9F9C"; + case 0xF908: + return "\u9F9C"; + case 0xF909: + return "\u5951"; + case 0xF90A: + return "\u91D1"; + case 0xF90B: + return "\u5587"; + case 0xF90C: + return "\u5948"; + case 0xF90D: + return "\u61F6"; + case 0xF90E: + return "\u7669"; + case 0xF90F: + return "\u7F85"; + case 0xF910: + return "\u863F"; + case 0xF911: + return "\u87BA"; + case 0xF912: + return "\u88F8"; + case 0xF913: + return "\u908F"; + case 0xF914: + return "\u6A02"; + case 0xF915: + return "\u6D1B"; + case 0xF916: + return "\u70D9"; + case 0xF917: + return "\u73DE"; + case 0xF918: + return "\u843D"; + case 0xF919: + return "\u916A"; + case 0xF91A: + return "\u99F1"; + case 0xF91B: + return "\u4E82"; + case 0xF91C: + return "\u5375"; + case 0xF91D: + return "\u6B04"; + case 0xF91E: + return "\u721B"; + case 0xF91F: + return "\u862D"; + case 0xF920: + return "\u9E1E"; + case 0xF921: + return "\u5D50"; + case 0xF922: + return "\u6FEB"; + case 0xF923: + return "\u85CD"; + case 0xF924: + return "\u8964"; + case 0xF925: + return "\u62C9"; + case 0xF926: + return "\u81D8"; + case 0xF927: + return "\u881F"; + case 0xF928: + return "\u5ECA"; + case 0xF929: + return "\u6717"; + case 0xF92A: + return "\u6D6A"; + case 0xF92B: + return "\u72FC"; + case 0xF92C: + return "\u90CE"; + case 0xF92D: + return "\u4F86"; + case 0xF92E: + return "\u51B7"; + case 0xF92F: + return "\u52DE"; + case 0xF930: + return "\u64C4"; + case 0xF931: + return "\u6AD3"; + case 0xF932: + return "\u7210"; + case 0xF933: + return "\u76E7"; + case 0xF934: + return "\u8001"; + case 0xF935: + return "\u8606"; + case 0xF936: + return "\u865C"; + case 0xF937: + return "\u8DEF"; + case 0xF938: + return "\u9732"; + case 0xF939: + return "\u9B6F"; + case 0xF93A: + return "\u9DFA"; + case 0xF93B: + return "\u788C"; + case 0xF93C: + return "\u797F"; + case 0xF93D: + return "\u7DA0"; + case 0xF93E: + return "\u83C9"; + case 0xF93F: + return "\u9304"; + case 0xF940: + return "\u9E7F"; + case 0xF941: + return "\u8AD6"; + case 0xF942: + return "\u58DF"; + case 0xF943: + return "\u5F04"; + case 0xF944: + return "\u7C60"; + case 0xF945: + return "\u807E"; + case 0xF946: + return "\u7262"; + case 0xF947: + return "\u78CA"; + case 0xF948: + return "\u8CC2"; + case 0xF949: + return "\u96F7"; + case 0xF94A: + return "\u58D8"; + case 0xF94B: + return "\u5C62"; + case 0xF94C: + return "\u6A13"; + case 0xF94D: + return "\u6DDA"; + case 0xF94E: + return "\u6F0F"; + case 0xF94F: + return "\u7D2F"; + case 0xF950: + return "\u7E37"; + case 0xF951: + return "\u964B"; + case 0xF952: + return "\u52D2"; + case 0xF953: + return "\u808B"; + case 0xF954: + return "\u51DC"; + case 0xF955: + return "\u51CC"; + case 0xF956: + return "\u7A1C"; + case 0xF957: + return "\u7DBE"; + case 0xF958: + return "\u83F1"; + case 0xF959: + return "\u9675"; + case 0xF95A: + return "\u8B80"; + case 0xF95B: + return "\u62CF"; + case 0xF95C: + return "\u6A02"; + case 0xF95D: + return "\u8AFE"; + case 0xF95E: + return "\u4E39"; + case 0xF95F: + return "\u5BE7"; + case 0xF960: + return "\u6012"; + case 0xF961: + return "\u7387"; + case 0xF962: + return "\u7570"; + case 0xF963: + return "\u5317"; + case 0xF964: + return "\u78FB"; + case 0xF965: + return "\u4FBF"; + case 0xF966: + return "\u5FA9"; + case 0xF967: + return "\u4E0D"; + case 0xF968: + return "\u6CCC"; + case 0xF969: + return "\u6578"; + case 0xF96A: + return "\u7D22"; + case 0xF96B: + return "\u53C3"; + case 0xF96C: + return "\u585E"; + case 0xF96D: + return "\u7701"; + case 0xF96E: + return "\u8449"; + case 0xF96F: + return "\u8AAA"; + case 0xF970: + return "\u6BBA"; + case 0xF971: + return "\u8FB0"; + case 0xF972: + return "\u6C88"; + case 0xF973: + return "\u62FE"; + case 0xF974: + return "\u82E5"; + case 0xF975: + return "\u63A0"; + case 0xF976: + return "\u7565"; + case 0xF977: + return "\u4EAE"; + case 0xF978: + return "\u5169"; + case 0xF979: + return "\u51C9"; + case 0xF97A: + return "\u6881"; + case 0xF97B: + return "\u7CE7"; + case 0xF97C: + return "\u826F"; + case 0xF97D: + return "\u8AD2"; + case 0xF97E: + return "\u91CF"; + case 0xF97F: + return "\u52F5"; + case 0xF980: + return "\u5442"; + case 0xF981: + return "\u5973"; + case 0xF982: + return "\u5EEC"; + case 0xF983: + return "\u65C5"; + case 0xF984: + return "\u6FFE"; + case 0xF985: + return "\u792A"; + case 0xF986: + return "\u95AD"; + case 0xF987: + return "\u9A6A"; + case 0xF988: + return "\u9E97"; + case 0xF989: + return "\u9ECE"; + case 0xF98A: + return "\u529B"; + case 0xF98B: + return "\u66C6"; + case 0xF98C: + return "\u6B77"; + case 0xF98D: + return "\u8F62"; + case 0xF98E: + return "\u5E74"; + case 0xF98F: + return "\u6190"; + case 0xF990: + return "\u6200"; + case 0xF991: + return "\u649A"; + case 0xF992: + return "\u6F23"; + case 0xF993: + return "\u7149"; + case 0xF994: + return "\u7489"; + case 0xF995: + return "\u79CA"; + case 0xF996: + return "\u7DF4"; + case 0xF997: + return "\u806F"; + case 0xF998: + return "\u8F26"; + case 0xF999: + return "\u84EE"; + case 0xF99A: + return "\u9023"; + case 0xF99B: + return "\u934A"; + case 0xF99C: + return "\u5217"; + case 0xF99D: + return "\u52A3"; + case 0xF99E: + return "\u54BD"; + case 0xF99F: + return "\u70C8"; + case 0xF9A0: + return "\u88C2"; + case 0xF9A1: + return "\u8AAA"; + case 0xF9A2: + return "\u5EC9"; + case 0xF9A3: + return "\u5FF5"; + case 0xF9A4: + return "\u637B"; + case 0xF9A5: + return "\u6BAE"; + case 0xF9A6: + return "\u7C3E"; + case 0xF9A7: + return "\u7375"; + case 0xF9A8: + return "\u4EE4"; + case 0xF9A9: + return "\u56F9"; + case 0xF9AA: + return "\u5BE7"; + case 0xF9AB: + return "\u5DBA"; + case 0xF9AC: + return "\u601C"; + case 0xF9AD: + return "\u73B2"; + case 0xF9AE: + return "\u7469"; + case 0xF9AF: + return "\u7F9A"; + case 0xF9B0: + return "\u8046"; + case 0xF9B1: + return "\u9234"; + case 0xF9B2: + return "\u96F6"; + case 0xF9B3: + return "\u9748"; + case 0xF9B4: + return "\u9818"; + case 0xF9B5: + return "\u4F8B"; + case 0xF9B6: + return "\u79AE"; + case 0xF9B7: + return "\u91B4"; + case 0xF9B8: + return "\u96B8"; + case 0xF9B9: + return "\u60E1"; + case 0xF9BA: + return "\u4E86"; + case 0xF9BB: + return "\u50DA"; + case 0xF9BC: + return "\u5BEE"; + case 0xF9BD: + return "\u5C3F"; + case 0xF9BE: + return "\u6599"; + case 0xF9BF: + return "\u6A02"; + case 0xF9C0: + return "\u71CE"; + case 0xF9C1: + return "\u7642"; + case 0xF9C2: + return "\u84FC"; + case 0xF9C3: + return "\u907C"; + case 0xF9C4: + return "\u9F8D"; + case 0xF9C5: + return "\u6688"; + case 0xF9C6: + return "\u962E"; + case 0xF9C7: + return "\u5289"; + case 0xF9C8: + return "\u677B"; + case 0xF9C9: + return "\u67F3"; + case 0xF9CA: + return "\u6D41"; + case 0xF9CB: + return "\u6E9C"; + case 0xF9CC: + return "\u7409"; + case 0xF9CD: + return "\u7559"; + case 0xF9CE: + return "\u786B"; + case 0xF9CF: + return "\u7D10"; + case 0xF9D0: + return "\u985E"; + case 0xF9D1: + return "\u516D"; + case 0xF9D2: + return "\u622E"; + case 0xF9D3: + return "\u9678"; + case 0xF9D4: + return "\u502B"; + case 0xF9D5: + return "\u5D19"; + case 0xF9D6: + return "\u6DEA"; + case 0xF9D7: + return "\u8F2A"; + case 0xF9D8: + return "\u5F8B"; + case 0xF9D9: + return "\u6144"; + case 0xF9DA: + return "\u6817"; + case 0xF9DB: + return "\u7387"; + case 0xF9DC: + return "\u9686"; + case 0xF9DD: + return "\u5229"; + case 0xF9DE: + return "\u540F"; + case 0xF9DF: + return "\u5C65"; + case 0xF9E0: + return "\u6613"; + case 0xF9E1: + return "\u674E"; + case 0xF9E2: + return "\u68A8"; + case 0xF9E3: + return "\u6CE5"; + case 0xF9E4: + return "\u7406"; + case 0xF9E5: + return "\u75E2"; + case 0xF9E6: + return "\u7F79"; + case 0xF9E7: + return "\u88CF"; + case 0xF9E8: + return "\u88E1"; + case 0xF9E9: + return "\u91CC"; + case 0xF9EA: + return "\u96E2"; + case 0xF9EB: + return "\u533F"; + case 0xF9EC: + return "\u6EBA"; + case 0xF9ED: + return "\u541D"; + case 0xF9EE: + return "\u71D0"; + case 0xF9EF: + return "\u7498"; + case 0xF9F0: + return "\u85FA"; + case 0xF9F1: + return "\u96A3"; + case 0xF9F2: + return "\u9C57"; + case 0xF9F3: + return "\u9E9F"; + case 0xF9F4: + return "\u6797"; + case 0xF9F5: + return "\u6DCB"; + case 0xF9F6: + return "\u81E8"; + case 0xF9F7: + return "\u7ACB"; + case 0xF9F8: + return "\u7B20"; + case 0xF9F9: + return "\u7C92"; + case 0xF9FA: + return "\u72C0"; + case 0xF9FB: + return "\u7099"; + case 0xF9FC: + return "\u8B58"; + case 0xF9FD: + return "\u4EC0"; + case 0xF9FE: + return "\u8336"; + case 0xF9FF: + return "\u523A"; + case 0xFA00: + return "\u5207"; + case 0xFA01: + return "\u5EA6"; + case 0xFA02: + return "\u62D3"; + case 0xFA03: + return "\u7CD6"; + case 0xFA04: + return "\u5B85"; + case 0xFA05: + return "\u6D1E"; + case 0xFA06: + return "\u66B4"; + case 0xFA07: + return "\u8F3B"; + case 0xFA08: + return "\u884C"; + case 0xFA09: + return "\u964D"; + case 0xFA0A: + return "\u898B"; + case 0xFA0B: + return "\u5ED3"; + case 0xFA0C: + return "\u5140"; + case 0xFA0D: + return "\u55C0"; + case 0xFA10: + return "\u585A"; + case 0xFA12: + return "\u6674"; + case 0xFA15: + return "\u51DE"; + case 0xFA16: + return "\u732A"; + case 0xFA17: + return "\u76CA"; + case 0xFA18: + return "\u793C"; + case 0xFA19: + return "\u795E"; + case 0xFA1A: + return "\u7965"; + case 0xFA1B: + return "\u798F"; + case 0xFA1C: + return "\u9756"; + case 0xFA1D: + return "\u7CBE"; + case 0xFA1E: + return "\u7FBD"; + case 0xFA20: + return "\u8612"; + case 0xFA22: + return "\u8AF8"; + case 0xFA25: + return "\u9038"; + case 0xFA26: + return "\u90FD"; + case 0xFA2A: + return "\u98EF"; + case 0xFA2B: + return "\u98FC"; + case 0xFA2C: + return "\u9928"; + case 0xFA2D: + return "\u9DB4"; + case 0xFA30: + return "\u4FAE"; + case 0xFA31: + return "\u50E7"; + case 0xFA32: + return "\u514D"; + case 0xFA33: + return "\u52C9"; + case 0xFA34: + return "\u52E4"; + case 0xFA35: + return "\u5351"; + case 0xFA36: + return "\u559D"; + case 0xFA37: + return "\u5606"; + case 0xFA38: + return "\u5668"; + case 0xFA39: + return "\u5840"; + case 0xFA3A: + return "\u58A8"; + case 0xFA3B: + return "\u5C64"; + case 0xFA3C: + return "\u5C6E"; + case 0xFA3D: + return "\u6094"; + case 0xFA3E: + return "\u6168"; + case 0xFA3F: + return "\u618E"; + case 0xFA40: + return "\u61F2"; + case 0xFA41: + return "\u654F"; + case 0xFA42: + return "\u65E2"; + case 0xFA43: + return "\u6691"; + case 0xFA44: + return "\u6885"; + case 0xFA45: + return "\u6D77"; + case 0xFA46: + return "\u6E1A"; + case 0xFA47: + return "\u6F22"; + case 0xFA48: + return "\u716E"; + case 0xFA49: + return "\u722B"; + case 0xFA4A: + return "\u7422"; + case 0xFA4B: + return "\u7891"; + case 0xFA4C: + return "\u793E"; + case 0xFA4D: + return "\u7949"; + case 0xFA4E: + return "\u7948"; + case 0xFA4F: + return "\u7950"; + case 0xFA50: + return "\u7956"; + case 0xFA51: + return "\u795D"; + case 0xFA52: + return "\u798D"; + case 0xFA53: + return "\u798E"; + case 0xFA54: + return "\u7A40"; + case 0xFA55: + return "\u7A81"; + case 0xFA56: + return "\u7BC0"; + case 0xFA57: + return "\u7DF4"; + case 0xFA58: + return "\u7E09"; + case 0xFA59: + return "\u7E41"; + case 0xFA5A: + return "\u7F72"; + case 0xFA5B: + return "\u8005"; + case 0xFA5C: + return "\u81ED"; + case 0xFA5D: + return "\u8279"; + case 0xFA5E: + return "\u8279"; + case 0xFA5F: + return "\u8457"; + case 0xFA60: + return "\u8910"; + case 0xFA61: + return "\u8996"; + case 0xFA62: + return "\u8B01"; + case 0xFA63: + return "\u8B39"; + case 0xFA64: + return "\u8CD3"; + case 0xFA65: + return "\u8D08"; + case 0xFA66: + return "\u8FB6"; + case 0xFA67: + return "\u9038"; + case 0xFA68: + return "\u96E3"; + case 0xFA69: + return "\u97FF"; + case 0xFA6A: + return "\u983B"; + case 0xFA70: + return "\u4E26"; + case 0xFA71: + return "\u51B5"; + case 0xFA72: + return "\u5168"; + case 0xFA73: + return "\u4F80"; + case 0xFA74: + return "\u5145"; + case 0xFA75: + return "\u5180"; + case 0xFA76: + return "\u52C7"; + case 0xFA77: + return "\u52FA"; + case 0xFA78: + return "\u559D"; + case 0xFA79: + return "\u5555"; + case 0xFA7A: + return "\u5599"; + case 0xFA7B: + return "\u55E2"; + case 0xFA7C: + return "\u585A"; + case 0xFA7D: + return "\u58B3"; + case 0xFA7E: + return "\u5944"; + case 0xFA7F: + return "\u5954"; + case 0xFA80: + return "\u5A62"; + case 0xFA81: + return "\u5B28"; + case 0xFA82: + return "\u5ED2"; + case 0xFA83: + return "\u5ED9"; + case 0xFA84: + return "\u5F69"; + case 0xFA85: + return "\u5FAD"; + case 0xFA86: + return "\u60D8"; + case 0xFA87: + return "\u614E"; + case 0xFA88: + return "\u6108"; + case 0xFA89: + return "\u618E"; + case 0xFA8A: + return "\u6160"; + case 0xFA8B: + return "\u61F2"; + case 0xFA8C: + return "\u6234"; + case 0xFA8D: + return "\u63C4"; + case 0xFA8E: + return "\u641C"; + case 0xFA8F: + return "\u6452"; + case 0xFA90: + return "\u6556"; + case 0xFA91: + return "\u6674"; + case 0xFA92: + return "\u6717"; + case 0xFA93: + return "\u671B"; + case 0xFA94: + return "\u6756"; + case 0xFA95: + return "\u6B79"; + case 0xFA96: + return "\u6BBA"; + case 0xFA97: + return "\u6D41"; + case 0xFA98: + return "\u6EDB"; + case 0xFA99: + return "\u6ECB"; + case 0xFA9A: + return "\u6F22"; + case 0xFA9B: + return "\u701E"; + case 0xFA9C: + return "\u716E"; + case 0xFA9D: + return "\u77A7"; + case 0xFA9E: + return "\u7235"; + case 0xFA9F: + return "\u72AF"; + case 0xFAA0: + return "\u732A"; + case 0xFAA1: + return "\u7471"; + case 0xFAA2: + return "\u7506"; + case 0xFAA3: + return "\u753B"; + case 0xFAA4: + return "\u761D"; + case 0xFAA5: + return "\u761F"; + case 0xFAA6: + return "\u76CA"; + case 0xFAA7: + return "\u76DB"; + case 0xFAA8: + return "\u76F4"; + case 0xFAA9: + return "\u774A"; + case 0xFAAA: + return "\u7740"; + case 0xFAAB: + return "\u78CC"; + case 0xFAAC: + return "\u7AB1"; + case 0xFAAD: + return "\u7BC0"; + case 0xFAAE: + return "\u7C7B"; + case 0xFAAF: + return "\u7D5B"; + case 0xFAB0: + return "\u7DF4"; + case 0xFAB1: + return "\u7F3E"; + case 0xFAB2: + return "\u8005"; + case 0xFAB3: + return "\u8352"; + case 0xFAB4: + return "\u83EF"; + case 0xFAB5: + return "\u8779"; + case 0xFAB6: + return "\u8941"; + case 0xFAB7: + return "\u8986"; + case 0xFAB8: + return "\u8996"; + case 0xFAB9: + return "\u8ABF"; + case 0xFABA: + return "\u8AF8"; + case 0xFABB: + return "\u8ACB"; + case 0xFABC: + return "\u8B01"; + case 0xFABD: + return "\u8AFE"; + case 0xFABE: + return "\u8AED"; + case 0xFABF: + return "\u8B39"; + case 0xFAC0: + return "\u8B8A"; + case 0xFAC1: + return "\u8D08"; + case 0xFAC2: + return "\u8F38"; + case 0xFAC3: + return "\u9072"; + case 0xFAC4: + return "\u9199"; + case 0xFAC5: + return "\u9276"; + case 0xFAC6: + return "\u967C"; + case 0xFAC7: + return "\u96E3"; + case 0xFAC8: + return "\u9756"; + case 0xFAC9: + return "\u97DB"; + case 0xFACA: + return "\u97FF"; + case 0xFACB: + return "\u980B"; + case 0xFACC: + return "\u983B"; + case 0xFACD: + return "\u9B12"; + case 0xFACE: + return "\u9F9C"; + case 0xFACF: + return "\ud84a\udc4a"; + case 0xFAD0: + return "\ud84a\udc44"; + case 0xFAD1: + return "\ud84c\udfd5"; + case 0xFAD2: + return "\u3B9D"; + case 0xFAD3: + return "\u4018"; + case 0xFAD4: + return "\u4039"; + case 0xFAD5: + return "\ud854\ude49"; + case 0xFAD6: + return "\ud857\udcd0"; + case 0xFAD7: + return "\ud85f\uded3"; + case 0xFAD8: + return "\u9F43"; + case 0xFAD9: + return "\u9F8E"; + case 0xFB1D: + return "\u05D9\u05B4"; + case 0xFB1F: + return "\u05F2\u05B7"; + case 0xFB2A: + return "\u05E9\u05C1"; + case 0xFB2B: + return "\u05E9\u05C2"; + case 0xFB2C: + return "\uFB49\u05C1"; + case 0xFB2D: + return "\uFB49\u05C2"; + case 0xFB2E: + return "\u05D0\u05B7"; + case 0xFB2F: + return "\u05D0\u05B8"; + case 0xFB30: + return "\u05D0\u05BC"; + case 0xFB31: + return "\u05D1\u05BC"; + case 0xFB32: + return "\u05D2\u05BC"; + case 0xFB33: + return "\u05D3\u05BC"; + case 0xFB34: + return "\u05D4\u05BC"; + case 0xFB35: + return "\u05D5\u05BC"; + case 0xFB36: + return "\u05D6\u05BC"; + case 0xFB38: + return "\u05D8\u05BC"; + case 0xFB39: + return "\u05D9\u05BC"; + case 0xFB3A: + return "\u05DA\u05BC"; + case 0xFB3B: + return "\u05DB\u05BC"; + case 0xFB3C: + return "\u05DC\u05BC"; + case 0xFB3E: + return "\u05DE\u05BC"; + case 0xFB40: + return "\u05E0\u05BC"; + case 0xFB41: + return "\u05E1\u05BC"; + case 0xFB43: + return "\u05E3\u05BC"; + case 0xFB44: + return "\u05E4\u05BC"; + case 0xFB46: + return "\u05E6\u05BC"; + case 0xFB47: + return "\u05E7\u05BC"; + case 0xFB48: + return "\u05E8\u05BC"; + case 0xFB49: + return "\u05E9\u05BC"; + case 0xFB4A: + return "\u05EA\u05BC"; + case 0xFB4B: + return "\u05D5\u05B9"; + case 0xFB4C: + return "\u05D1\u05BF"; + case 0xFB4D: + return "\u05DB\u05BF"; + case 0xFB4E: + return "\u05E4\u05BF"; + case 0x1D15E: + return "\ud834\udd57\ud834\udd65"; + case 0x1D15F: + return "\ud834\udd58\ud834\udd65"; + case 0x1D160: + return "\ud834\udd5f\ud834\udd6e"; + case 0x1D161: + return "\ud834\udd5f\ud834\udd6f"; + case 0x1D162: + return "\ud834\udd5f\ud834\udd70"; + case 0x1D163: + return "\ud834\udd5f\ud834\udd71"; + case 0x1D164: + return "\ud834\udd5f\ud834\udd72"; + case 0x1D1BB: + return "\ud834\uddb9\ud834\udd65"; + case 0x1D1BC: + return "\ud834\uddba\ud834\udd65"; + case 0x1D1BD: + return "\ud834\uddbb\ud834\udd6e"; + case 0x1D1BE: + return "\ud834\uddbc\ud834\udd6e"; + case 0x1D1BF: + return "\ud834\uddbb\ud834\udd6f"; + case 0x1D1C0: + return "\ud834\uddbc\ud834\udd6f"; + case 0x2F800: + return "\u4E3D"; + case 0x2F801: + return "\u4E38"; + case 0x2F802: + return "\u4E41"; + case 0x2F803: + return "\ud840\udd22"; + case 0x2F804: + return "\u4F60"; + case 0x2F805: + return "\u4FAE"; + case 0x2F806: + return "\u4FBB"; + case 0x2F807: + return "\u5002"; + case 0x2F808: + return "\u507A"; + case 0x2F809: + return "\u5099"; + case 0x2F80A: + return "\u50E7"; + case 0x2F80B: + return "\u50CF"; + case 0x2F80C: + return "\u349E"; + case 0x2F80D: + return "\ud841\ude3a"; + case 0x2F80E: + return "\u514D"; + case 0x2F80F: + return "\u5154"; + case 0x2F810: + return "\u5164"; + case 0x2F811: + return "\u5177"; + case 0x2F812: + return "\ud841\udd1c"; + case 0x2F813: + return "\u34B9"; + case 0x2F814: + return "\u5167"; + case 0x2F815: + return "\u518D"; + case 0x2F816: + return "\ud841\udd4b"; + case 0x2F817: + return "\u5197"; + case 0x2F818: + return "\u51A4"; + case 0x2F819: + return "\u4ECC"; + case 0x2F81A: + return "\u51AC"; + case 0x2F81B: + return "\u51B5"; + case 0x2F81C: + return "\ud864\udddf"; + case 0x2F81D: + return "\u51F5"; + case 0x2F81E: + return "\u5203"; + case 0x2F81F: + return "\u34DF"; + case 0x2F820: + return "\u523B"; + case 0x2F821: + return "\u5246"; + case 0x2F822: + return "\u5272"; + case 0x2F823: + return "\u5277"; + case 0x2F824: + return "\u3515"; + case 0x2F825: + return "\u52C7"; + case 0x2F826: + return "\u52C9"; + case 0x2F827: + return "\u52E4"; + case 0x2F828: + return "\u52FA"; + case 0x2F829: + return "\u5305"; + case 0x2F82A: + return "\u5306"; + case 0x2F82B: + return "\u5317"; + case 0x2F82C: + return "\u5349"; + case 0x2F82D: + return "\u5351"; + case 0x2F82E: + return "\u535A"; + case 0x2F82F: + return "\u5373"; + case 0x2F830: + return "\u537D"; + case 0x2F831: + return "\u537F"; + case 0x2F832: + return "\u537F"; + case 0x2F833: + return "\u537F"; + case 0x2F834: + return "\ud842\ude2c"; + case 0x2F835: + return "\u7070"; + case 0x2F836: + return "\u53CA"; + case 0x2F837: + return "\u53DF"; + case 0x2F838: + return "\ud842\udf63"; + case 0x2F839: + return "\u53EB"; + case 0x2F83A: + return "\u53F1"; + case 0x2F83B: + return "\u5406"; + case 0x2F83C: + return "\u549E"; + case 0x2F83D: + return "\u5438"; + case 0x2F83E: + return "\u5448"; + case 0x2F83F: + return "\u5468"; + case 0x2F840: + return "\u54A2"; + case 0x2F841: + return "\u54F6"; + case 0x2F842: + return "\u5510"; + case 0x2F843: + return "\u5553"; + case 0x2F844: + return "\u5563"; + case 0x2F845: + return "\u5584"; + case 0x2F846: + return "\u5584"; + case 0x2F847: + return "\u5599"; + case 0x2F848: + return "\u55AB"; + case 0x2F849: + return "\u55B3"; + case 0x2F84A: + return "\u55C2"; + case 0x2F84B: + return "\u5716"; + case 0x2F84C: + return "\u5606"; + case 0x2F84D: + return "\u5717"; + case 0x2F84E: + return "\u5651"; + case 0x2F84F: + return "\u5674"; + case 0x2F850: + return "\u5207"; + case 0x2F851: + return "\u58EE"; + case 0x2F852: + return "\u57CE"; + case 0x2F853: + return "\u57F4"; + case 0x2F854: + return "\u580D"; + case 0x2F855: + return "\u578B"; + case 0x2F856: + return "\u5832"; + case 0x2F857: + return "\u5831"; + case 0x2F858: + return "\u58AC"; + case 0x2F859: + return "\ud845\udce4"; + case 0x2F85A: + return "\u58F2"; + case 0x2F85B: + return "\u58F7"; + case 0x2F85C: + return "\u5906"; + case 0x2F85D: + return "\u591A"; + case 0x2F85E: + return "\u5922"; + case 0x2F85F: + return "\u5962"; + case 0x2F860: + return "\ud845\udea8"; + case 0x2F861: + return "\ud845\udeea"; + case 0x2F862: + return "\u59EC"; + case 0x2F863: + return "\u5A1B"; + case 0x2F864: + return "\u5A27"; + case 0x2F865: + return "\u59D8"; + case 0x2F866: + return "\u5A66"; + case 0x2F867: + return "\u36EE"; + case 0x2F868: + return "\u36FC"; + case 0x2F869: + return "\u5B08"; + case 0x2F86A: + return "\u5B3E"; + case 0x2F86B: + return "\u5B3E"; + case 0x2F86C: + return "\ud846\uddc8"; + case 0x2F86D: + return "\u5BC3"; + case 0x2F86E: + return "\u5BD8"; + case 0x2F86F: + return "\u5BE7"; + case 0x2F870: + return "\u5BF3"; + case 0x2F871: + return "\ud846\udf18"; + case 0x2F872: + return "\u5BFF"; + case 0x2F873: + return "\u5C06"; + case 0x2F874: + return "\u5F53"; + case 0x2F875: + return "\u5C22"; + case 0x2F876: + return "\u3781"; + case 0x2F877: + return "\u5C60"; + case 0x2F878: + return "\u5C6E"; + case 0x2F879: + return "\u5CC0"; + case 0x2F87A: + return "\u5C8D"; + case 0x2F87B: + return "\ud847\udde4"; + case 0x2F87C: + return "\u5D43"; + case 0x2F87D: + return "\ud847\udde6"; + case 0x2F87E: + return "\u5D6E"; + case 0x2F87F: + return "\u5D6B"; + case 0x2F880: + return "\u5D7C"; + case 0x2F881: + return "\u5DE1"; + case 0x2F882: + return "\u5DE2"; + case 0x2F883: + return "\u382F"; + case 0x2F884: + return "\u5DFD"; + case 0x2F885: + return "\u5E28"; + case 0x2F886: + return "\u5E3D"; + case 0x2F887: + return "\u5E69"; + case 0x2F888: + return "\u3862"; + case 0x2F889: + return "\ud848\udd83"; + case 0x2F88A: + return "\u387C"; + case 0x2F88B: + return "\u5EB0"; + case 0x2F88C: + return "\u5EB3"; + case 0x2F88D: + return "\u5EB6"; + case 0x2F88E: + return "\u5ECA"; + case 0x2F88F: + return "\ud868\udf92"; + case 0x2F890: + return "\u5EFE"; + case 0x2F891: + return "\ud848\udf31"; + case 0x2F892: + return "\ud848\udf31"; + case 0x2F893: + return "\u8201"; + case 0x2F894: + return "\u5F22"; + case 0x2F895: + return "\u5F22"; + case 0x2F896: + return "\u38C7"; + case 0x2F897: + return "\ud84c\udeb8"; + case 0x2F898: + return "\ud858\uddda"; + case 0x2F899: + return "\u5F62"; + case 0x2F89A: + return "\u5F6B"; + case 0x2F89B: + return "\u38E3"; + case 0x2F89C: + return "\u5F9A"; + case 0x2F89D: + return "\u5FCD"; + case 0x2F89E: + return "\u5FD7"; + case 0x2F89F: + return "\u5FF9"; + case 0x2F8A0: + return "\u6081"; + case 0x2F8A1: + return "\u393A"; + case 0x2F8A2: + return "\u391C"; + case 0x2F8A3: + return "\u6094"; + case 0x2F8A4: + return "\ud849\uded4"; + case 0x2F8A5: + return "\u60C7"; + case 0x2F8A6: + return "\u6148"; + case 0x2F8A7: + return "\u614C"; + case 0x2F8A8: + return "\u614E"; + case 0x2F8A9: + return "\u614C"; + case 0x2F8AA: + return "\u617A"; + case 0x2F8AB: + return "\u618E"; + case 0x2F8AC: + return "\u61B2"; + case 0x2F8AD: + return "\u61A4"; + case 0x2F8AE: + return "\u61AF"; + case 0x2F8AF: + return "\u61DE"; + case 0x2F8B0: + return "\u61F2"; + case 0x2F8B1: + return "\u61F6"; + case 0x2F8B2: + return "\u6210"; + case 0x2F8B3: + return "\u621B"; + case 0x2F8B4: + return "\u625D"; + case 0x2F8B5: + return "\u62B1"; + case 0x2F8B6: + return "\u62D4"; + case 0x2F8B7: + return "\u6350"; + case 0x2F8B8: + return "\ud84a\udf0c"; + case 0x2F8B9: + return "\u633D"; + case 0x2F8BA: + return "\u62FC"; + case 0x2F8BB: + return "\u6368"; + case 0x2F8BC: + return "\u6383"; + case 0x2F8BD: + return "\u63E4"; + case 0x2F8BE: + return "\ud84a\udff1"; + case 0x2F8BF: + return "\u6422"; + case 0x2F8C0: + return "\u63C5"; + case 0x2F8C1: + return "\u63A9"; + case 0x2F8C2: + return "\u3A2E"; + case 0x2F8C3: + return "\u6469"; + case 0x2F8C4: + return "\u647E"; + case 0x2F8C5: + return "\u649D"; + case 0x2F8C6: + return "\u6477"; + case 0x2F8C7: + return "\u3A6C"; + case 0x2F8C8: + return "\u654F"; + case 0x2F8C9: + return "\u656C"; + case 0x2F8CA: + return "\ud84c\udc0a"; + case 0x2F8CB: + return "\u65E3"; + case 0x2F8CC: + return "\u66F8"; + case 0x2F8CD: + return "\u6649"; + case 0x2F8CE: + return "\u3B19"; + case 0x2F8CF: + return "\u6691"; + case 0x2F8D0: + return "\u3B08"; + case 0x2F8D1: + return "\u3AE4"; + case 0x2F8D2: + return "\u5192"; + case 0x2F8D3: + return "\u5195"; + case 0x2F8D4: + return "\u6700"; + case 0x2F8D5: + return "\u669C"; + case 0x2F8D6: + return "\u80AD"; + case 0x2F8D7: + return "\u43D9"; + case 0x2F8D8: + return "\u6717"; + case 0x2F8D9: + return "\u671B"; + case 0x2F8DA: + return "\u6721"; + case 0x2F8DB: + return "\u675E"; + case 0x2F8DC: + return "\u6753"; + case 0x2F8DD: + return "\ud84c\udfc3"; + case 0x2F8DE: + return "\u3B49"; + case 0x2F8DF: + return "\u67FA"; + case 0x2F8E0: + return "\u6785"; + case 0x2F8E1: + return "\u6852"; + case 0x2F8E2: + return "\u6885"; + case 0x2F8E3: + return "\ud84d\udc6d"; + case 0x2F8E4: + return "\u688E"; + case 0x2F8E5: + return "\u681F"; + case 0x2F8E6: + return "\u6914"; + case 0x2F8E7: + return "\u3B9D"; + case 0x2F8E8: + return "\u6942"; + case 0x2F8E9: + return "\u69A3"; + case 0x2F8EA: + return "\u69EA"; + case 0x2F8EB: + return "\u6AA8"; + case 0x2F8EC: + return "\ud84d\udea3"; + case 0x2F8ED: + return "\u6ADB"; + case 0x2F8EE: + return "\u3C18"; + case 0x2F8EF: + return "\u6B21"; + case 0x2F8F0: + return "\ud84e\udca7"; + case 0x2F8F1: + return "\u6B54"; + case 0x2F8F2: + return "\u3C4E"; + case 0x2F8F3: + return "\u6B72"; + case 0x2F8F4: + return "\u6B9F"; + case 0x2F8F5: + return "\u6BBA"; + case 0x2F8F6: + return "\u6BBB"; + case 0x2F8F7: + return "\ud84e\ude8d"; + case 0x2F8F8: + return "\ud847\udd0b"; + case 0x2F8F9: + return "\ud84e\udefa"; + case 0x2F8FA: + return "\u6C4E"; + case 0x2F8FB: + return "\ud84f\udcbc"; + case 0x2F8FC: + return "\u6CBF"; + case 0x2F8FD: + return "\u6CCD"; + case 0x2F8FE: + return "\u6C67"; + case 0x2F8FF: + return "\u6D16"; + case 0x2F900: + return "\u6D3E"; + case 0x2F901: + return "\u6D77"; + case 0x2F902: + return "\u6D41"; + case 0x2F903: + return "\u6D69"; + case 0x2F904: + return "\u6D78"; + case 0x2F905: + return "\u6D85"; + case 0x2F906: + return "\ud84f\udd1e"; + case 0x2F907: + return "\u6D34"; + case 0x2F908: + return "\u6E2F"; + case 0x2F909: + return "\u6E6E"; + case 0x2F90A: + return "\u3D33"; + case 0x2F90B: + return "\u6ECB"; + case 0x2F90C: + return "\u6EC7"; + case 0x2F90D: + return "\ud84f\uded1"; + case 0x2F90E: + return "\u6DF9"; + case 0x2F90F: + return "\u6F6E"; + case 0x2F910: + return "\ud84f\udf5e"; + case 0x2F911: + return "\ud84f\udf8e"; + case 0x2F912: + return "\u6FC6"; + case 0x2F913: + return "\u7039"; + case 0x2F914: + return "\u701E"; + case 0x2F915: + return "\u701B"; + case 0x2F916: + return "\u3D96"; + case 0x2F917: + return "\u704A"; + case 0x2F918: + return "\u707D"; + case 0x2F919: + return "\u7077"; + case 0x2F91A: + return "\u70AD"; + case 0x2F91B: + return "\ud841\udd25"; + case 0x2F91C: + return "\u7145"; + case 0x2F91D: + return "\ud850\ude63"; + case 0x2F91E: + return "\u719C"; + case 0x2F91F: + return "\ud850\udfab"; + case 0x2F920: + return "\u7228"; + case 0x2F921: + return "\u7235"; + case 0x2F922: + return "\u7250"; + case 0x2F923: + return "\ud851\ude08"; + case 0x2F924: + return "\u7280"; + case 0x2F925: + return "\u7295"; + case 0x2F926: + return "\ud851\udf35"; + case 0x2F927: + return "\ud852\udc14"; + case 0x2F928: + return "\u737A"; + case 0x2F929: + return "\u738B"; + case 0x2F92A: + return "\u3EAC"; + case 0x2F92B: + return "\u73A5"; + case 0x2F92C: + return "\u3EB8"; + case 0x2F92D: + return "\u3EB8"; + case 0x2F92E: + return "\u7447"; + case 0x2F92F: + return "\u745C"; + case 0x2F930: + return "\u7471"; + case 0x2F931: + return "\u7485"; + case 0x2F932: + return "\u74CA"; + case 0x2F933: + return "\u3F1B"; + case 0x2F934: + return "\u7524"; + case 0x2F935: + return "\ud853\udc36"; + case 0x2F936: + return "\u753E"; + case 0x2F937: + return "\ud853\udc92"; + case 0x2F938: + return "\u7570"; + case 0x2F939: + return "\ud848\udd9f"; + case 0x2F93A: + return "\u7610"; + case 0x2F93B: + return "\ud853\udfa1"; + case 0x2F93C: + return "\ud853\udfb8"; + case 0x2F93D: + return "\ud854\udc44"; + case 0x2F93E: + return "\u3FFC"; + case 0x2F93F: + return "\u4008"; + case 0x2F940: + return "\u76F4"; + case 0x2F941: + return "\ud854\udcf3"; + case 0x2F942: + return "\ud854\udcf2"; + case 0x2F943: + return "\ud854\udd19"; + case 0x2F944: + return "\ud854\udd33"; + case 0x2F945: + return "\u771E"; + case 0x2F946: + return "\u771F"; + case 0x2F947: + return "\u771F"; + case 0x2F948: + return "\u774A"; + case 0x2F949: + return "\u4039"; + case 0x2F94A: + return "\u778B"; + case 0x2F94B: + return "\u4046"; + case 0x2F94C: + return "\u4096"; + case 0x2F94D: + return "\ud855\udc1d"; + case 0x2F94E: + return "\u784E"; + case 0x2F94F: + return "\u788C"; + case 0x2F950: + return "\u78CC"; + case 0x2F951: + return "\u40E3"; + case 0x2F952: + return "\ud855\ude26"; + case 0x2F953: + return "\u7956"; + case 0x2F954: + return "\ud855\ude9a"; + case 0x2F955: + return "\ud855\udec5"; + case 0x2F956: + return "\u798F"; + case 0x2F957: + return "\u79EB"; + case 0x2F958: + return "\u412F"; + case 0x2F959: + return "\u7A40"; + case 0x2F95A: + return "\u7A4A"; + case 0x2F95B: + return "\u7A4F"; + case 0x2F95C: + return "\ud856\udd7c"; + case 0x2F95D: + return "\ud856\udea7"; + case 0x2F95E: + return "\ud856\udea7"; + case 0x2F95F: + return "\u7AEE"; + case 0x2F960: + return "\u4202"; + case 0x2F961: + return "\ud856\udfab"; + case 0x2F962: + return "\u7BC6"; + case 0x2F963: + return "\u7BC9"; + case 0x2F964: + return "\u4227"; + case 0x2F965: + return "\ud857\udc80"; + case 0x2F966: + return "\u7CD2"; + case 0x2F967: + return "\u42A0"; + case 0x2F968: + return "\u7CE8"; + case 0x2F969: + return "\u7CE3"; + case 0x2F96A: + return "\u7D00"; + case 0x2F96B: + return "\ud857\udf86"; + case 0x2F96C: + return "\u7D63"; + case 0x2F96D: + return "\u4301"; + case 0x2F96E: + return "\u7DC7"; + case 0x2F96F: + return "\u7E02"; + case 0x2F970: + return "\u7E45"; + case 0x2F971: + return "\u4334"; + case 0x2F972: + return "\ud858\ude28"; + case 0x2F973: + return "\ud858\ude47"; + case 0x2F974: + return "\u4359"; + case 0x2F975: + return "\ud858\uded9"; + case 0x2F976: + return "\u7F7A"; + case 0x2F977: + return "\ud858\udf3e"; + case 0x2F978: + return "\u7F95"; + case 0x2F979: + return "\u7FFA"; + case 0x2F97A: + return "\u8005"; + case 0x2F97B: + return "\ud859\udcda"; + case 0x2F97C: + return "\ud859\udd23"; + case 0x2F97D: + return "\u8060"; + case 0x2F97E: + return "\ud859\udda8"; + case 0x2F97F: + return "\u8070"; + case 0x2F980: + return "\ud84c\udf5f"; + case 0x2F981: + return "\u43D5"; + case 0x2F982: + return "\u80B2"; + case 0x2F983: + return "\u8103"; + case 0x2F984: + return "\u440B"; + case 0x2F985: + return "\u813E"; + case 0x2F986: + return "\u5AB5"; + case 0x2F987: + return "\ud859\udfa7"; + case 0x2F988: + return "\ud859\udfb5"; + case 0x2F989: + return "\ud84c\udf93"; + case 0x2F98A: + return "\ud84c\udf9c"; + case 0x2F98B: + return "\u8201"; + case 0x2F98C: + return "\u8204"; + case 0x2F98D: + return "\u8F9E"; + case 0x2F98E: + return "\u446B"; + case 0x2F98F: + return "\u8291"; + case 0x2F990: + return "\u828B"; + case 0x2F991: + return "\u829D"; + case 0x2F992: + return "\u52B3"; + case 0x2F993: + return "\u82B1"; + case 0x2F994: + return "\u82B3"; + case 0x2F995: + return "\u82BD"; + case 0x2F996: + return "\u82E6"; + case 0x2F997: + return "\ud85a\udf3c"; + case 0x2F998: + return "\u82E5"; + case 0x2F999: + return "\u831D"; + case 0x2F99A: + return "\u8363"; + case 0x2F99B: + return "\u83AD"; + case 0x2F99C: + return "\u8323"; + case 0x2F99D: + return "\u83BD"; + case 0x2F99E: + return "\u83E7"; + case 0x2F99F: + return "\u8457"; + case 0x2F9A0: + return "\u8353"; + case 0x2F9A1: + return "\u83CA"; + case 0x2F9A2: + return "\u83CC"; + case 0x2F9A3: + return "\u83DC"; + case 0x2F9A4: + return "\ud85b\udc36"; + case 0x2F9A5: + return "\ud85b\udd6b"; + case 0x2F9A6: + return "\ud85b\udcd5"; + case 0x2F9A7: + return "\u452B"; + case 0x2F9A8: + return "\u84F1"; + case 0x2F9A9: + return "\u84F3"; + case 0x2F9AA: + return "\u8516"; + case 0x2F9AB: + return "\ud85c\udfca"; + case 0x2F9AC: + return "\u8564"; + case 0x2F9AD: + return "\ud85b\udf2c"; + case 0x2F9AE: + return "\u455D"; + case 0x2F9AF: + return "\u4561"; + case 0x2F9B0: + return "\ud85b\udfb1"; + case 0x2F9B1: + return "\ud85c\udcd2"; + case 0x2F9B2: + return "\u456B"; + case 0x2F9B3: + return "\u8650"; + case 0x2F9B4: + return "\u865C"; + case 0x2F9B5: + return "\u8667"; + case 0x2F9B6: + return "\u8669"; + case 0x2F9B7: + return "\u86A9"; + case 0x2F9B8: + return "\u8688"; + case 0x2F9B9: + return "\u870E"; + case 0x2F9BA: + return "\u86E2"; + case 0x2F9BB: + return "\u8779"; + case 0x2F9BC: + return "\u8728"; + case 0x2F9BD: + return "\u876B"; + case 0x2F9BE: + return "\u8786"; + case 0x2F9BF: + return "\u45D7"; + case 0x2F9C0: + return "\u87E1"; + case 0x2F9C1: + return "\u8801"; + case 0x2F9C2: + return "\u45F9"; + case 0x2F9C3: + return "\u8860"; + case 0x2F9C4: + return "\u8863"; + case 0x2F9C5: + return "\ud85d\ude67"; + case 0x2F9C6: + return "\u88D7"; + case 0x2F9C7: + return "\u88DE"; + case 0x2F9C8: + return "\u4635"; + case 0x2F9C9: + return "\u88FA"; + case 0x2F9CA: + return "\u34BB"; + case 0x2F9CB: + return "\ud85e\udcae"; + case 0x2F9CC: + return "\ud85e\udd66"; + case 0x2F9CD: + return "\u46BE"; + case 0x2F9CE: + return "\u46C7"; + case 0x2F9CF: + return "\u8AA0"; + case 0x2F9D0: + return "\u8AED"; + case 0x2F9D1: + return "\u8B8A"; + case 0x2F9D2: + return "\u8C55"; + case 0x2F9D3: + return "\ud85f\udca8"; + case 0x2F9D4: + return "\u8CAB"; + case 0x2F9D5: + return "\u8CC1"; + case 0x2F9D6: + return "\u8D1B"; + case 0x2F9D7: + return "\u8D77"; + case 0x2F9D8: + return "\ud85f\udf2f"; + case 0x2F9D9: + return "\ud842\udc04"; + case 0x2F9DA: + return "\u8DCB"; + case 0x2F9DB: + return "\u8DBC"; + case 0x2F9DC: + return "\u8DF0"; + case 0x2F9DD: + return "\ud842\udcde"; + case 0x2F9DE: + return "\u8ED4"; + case 0x2F9DF: + return "\u8F38"; + case 0x2F9E0: + return "\ud861\uddd2"; + case 0x2F9E1: + return "\ud861\udded"; + case 0x2F9E2: + return "\u9094"; + case 0x2F9E3: + return "\u90F1"; + case 0x2F9E4: + return "\u9111"; + case 0x2F9E5: + return "\ud861\udf2e"; + case 0x2F9E6: + return "\u911B"; + case 0x2F9E7: + return "\u9238"; + case 0x2F9E8: + return "\u92D7"; + case 0x2F9E9: + return "\u92D8"; + case 0x2F9EA: + return "\u927C"; + case 0x2F9EB: + return "\u93F9"; + case 0x2F9EC: + return "\u9415"; + case 0x2F9ED: + return "\ud862\udffa"; + case 0x2F9EE: + return "\u958B"; + case 0x2F9EF: + return "\u4995"; + case 0x2F9F0: + return "\u95B7"; + case 0x2F9F1: + return "\ud863\udd77"; + case 0x2F9F2: + return "\u49E6"; + case 0x2F9F3: + return "\u96C3"; + case 0x2F9F4: + return "\u5DB2"; + case 0x2F9F5: + return "\u9723"; + case 0x2F9F6: + return "\ud864\udd45"; + case 0x2F9F7: + return "\ud864\ude1a"; + case 0x2F9F8: + return "\u4A6E"; + case 0x2F9F9: + return "\u4A76"; + case 0x2F9FA: + return "\u97E0"; + case 0x2F9FB: + return "\ud865\udc0a"; + case 0x2F9FC: + return "\u4AB2"; + case 0x2F9FD: + return "\ud865\udc96"; + case 0x2F9FE: + return "\u980B"; + case 0x2F9FF: + return "\u980B"; + case 0x2FA00: + return "\u9829"; + case 0x2FA01: + return "\ud865\uddb6"; + case 0x2FA02: + return "\u98E2"; + case 0x2FA03: + return "\u4B33"; + case 0x2FA04: + return "\u9929"; + case 0x2FA05: + return "\u99A7"; + case 0x2FA06: + return "\u99C2"; + case 0x2FA07: + return "\u99FE"; + case 0x2FA08: + return "\u4BCE"; + case 0x2FA09: + return "\ud866\udf30"; + case 0x2FA0A: + return "\u9B12"; + case 0x2FA0B: + return "\u9C40"; + case 0x2FA0C: + return "\u9CFD"; + case 0x2FA0D: + return "\u4CCE"; + case 0x2FA0E: + return "\u4CED"; + case 0x2FA0F: + return "\u9D67"; + case 0x2FA10: + return "\ud868\udcce"; + case 0x2FA11: + return "\u4CF8"; + case 0x2FA12: + return "\ud868\udd05"; + case 0x2FA13: + return "\ud868\ude0e"; + case 0x2FA14: + return "\ud868\ude91"; + case 0x2FA15: + return "\u9EBB"; + case 0x2FA16: + return "\u4D56"; + case 0x2FA17: + return "\u9EF9"; + case 0x2FA18: + return "\u9EFE"; + case 0x2FA19: + return "\u9F05"; + case 0x2FA1A: + return "\u9F0F"; + case 0x2FA1B: + return "\u9F16"; + case 0x2FA1C: + return "\u9F3B"; + case 0x2FA1D: + return "\ud869\ude00"; + default: // not decomposable + if (character <= 0xFFFF) { + return String.valueOf((char) character); + } + else { + StringBuffer sb = new StringBuffer(2); + sb.append(getHighSurrogate(character)); + sb.append(getLowSurrogate(character)); + return sb.toString(); + } + } + + } + ///CLOVER:ON + + + private static String decomposeHangul(char c) { + + final int firstLeadingConsonant = 0x1100; + final int firstMedialVowel = 0x1161; + final int firstTrailingConsonant = 0x11A7; + + final int numberOfLeadingConsonants = 19; + final int numberOfMedialVowels = 21; + final int numberOfTrailingConsonants = 28; + + final int numberOfFinalPairs + = numberOfMedialVowels * numberOfTrailingConsonants; + final int numberOfSyllables + = numberOfLeadingConsonants * numberOfFinalPairs; + + final int syllable = c - FIRST_HANGUL_SYLLABLE; + + if (syllable < 0 || syllable >= numberOfSyllables) { + return String.valueOf(c); + } + + int leadingConsonant = firstLeadingConsonant + + syllable / numberOfFinalPairs; + int medialVowel = firstMedialVowel + + (syllable % numberOfFinalPairs) / numberOfTrailingConsonants; + int trailingConsonant = firstTrailingConsonant + + syllable % numberOfTrailingConsonants; + + StringBuffer result = new StringBuffer(3); + result.append((char) leadingConsonant); + result.append((char) medialVowel); + if (trailingConsonant != firstTrailingConsonant) { + result.append((char) trailingConsonant); + } + + return result.toString(); + + } + + + private static String composeHangul(String source) { + + final int firstLeadingConsonant = 0x1100; + final int firstMedialVowel = 0x1161; + final int firstTrailingConsonant = 0x11A7; + + final int numberOfLeadingConsonants = 19; + final int numberOfMedialVowels = 21; + final int numberOfTrailingConsonants = 28; + + final int numberOfFinalPairs + = numberOfMedialVowels * numberOfTrailingConsonants; + final int numberOfSyllables + = numberOfLeadingConsonants * numberOfFinalPairs; + + final int length = source.length(); + if (length == 0) return ""; + StringBuffer result = new StringBuffer(length); + char previous = source.charAt(0); + result.append(previous); + + for (int i = 1; i < length; ++i) { + char c = source.charAt(i); + + int leadingConsonant = previous - firstLeadingConsonant; + if (0 <= leadingConsonant && leadingConsonant < numberOfLeadingConsonants) { + int medialVowel = c - firstMedialVowel; + if (medialVowel >= 0 && medialVowel < numberOfMedialVowels) { + previous = (char) (FIRST_HANGUL_SYLLABLE + + (leadingConsonant * numberOfMedialVowels + medialVowel) + * numberOfTrailingConsonants); + result.setCharAt(result.length()-1, previous); + continue; + } + } + + int syllable = previous - FIRST_HANGUL_SYLLABLE; + if (syllable >= 0 && syllable < numberOfSyllables + && (syllable % numberOfTrailingConsonants) == 0) { + int trailingConsonant = c - firstTrailingConsonant; + if (trailingConsonant >= 0 && trailingConsonant <= numberOfTrailingConsonants) { + previous += trailingConsonant; + result.setCharAt(result.length()-1, previous); + continue; + } + } + + previous = c; + result.append(c); + } + + return result.toString(); + + } + + + private static class UnicodeString { + + private int[] data; + private int size = 0; + + UnicodeString(String s) { + + int length = s.length(); + data = new int[length]; + int index = 0; + for (int i = 0; i < length; i++) { + char c = s.charAt(i); + int codePoint = c; + if (isHighSurrogate(c)) { + i++; + codePoint = combineSurrogatePair(c, s.charAt(i)); + } + data[index] = codePoint; + index++; + } + this.size = index; + + } + + + UnicodeString(int length) { + this.size = 0; + data = new int[length]; + } + + + UnicodeString decompose() { + + UnicodeString result = new UnicodeString(size); + for (int i = 0; i < size; i++) { + int c = data[i]; + String d = UnicodeUtil.decompose(c); + result.append(d); + } + + /* now put into canonical order */ + + for (int i = 0; i < result.size-1; i++) { + int first = result.data[i]; + int second = result.data[i+1]; + int secondClass = getCombiningClass(second); + if (secondClass == 0) continue; + int firstClass = getCombiningClass(first); + if (firstClass > secondClass ) { + result.data[i] = second; + result.data[i+1] = first; + i -= 2; + if (i == -2) i = -1; + } + } + + return result; + + } + + + UnicodeString compose() { + + if (compositions == null) loadCompositions(); + + UnicodeString composed = new UnicodeString(size); + + int lastStarter = -1; + int lastStarterIndex = -1; + int composedLastStarterIndex = -1; + + for (int i = 0; i < size; i++) { + int c = data[i]; + if (lastStarter == -1 || isBlocked(lastStarterIndex, i)) { + composed.append(c); + if (isStarter(c) ) { + lastStarter = c; + lastStarterIndex = i; + composedLastStarterIndex = composed.size-1; + } + } + else { + int composedChar = composeCharacter(lastStarter, c); + if (composedChar == -1) { + composed.append(c); + if (isStarter(c) ) { + lastStarter = c; + lastStarterIndex = i; + composedLastStarterIndex = composed.size-1; + } + } + else { + lastStarter = composedChar; + // XXX dangerous side effects + data[lastStarterIndex] = composedChar; + data[i] = 0; + composed.data[composedLastStarterIndex] = composedChar; + } + } + } + + return composed; + + } + + + void append(String s) { + + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + if (isHighSurrogate(c)) { + append(UnicodeUtil.combineSurrogatePair(c, s.charAt(i+1))); + i++; + } + else { + append(c); + } + } + + } + + + void append(int c) { + + if (size < data.length-1) { + data[size] = c; + size++; + } + else { + int[] array = new int[data.length+10]; + System.arraycopy(data, 0, array, 0, size); + data = array; + append(c); + } + + } + + public String toString() { + + StringBuffer sb = new StringBuffer(); + for (int i = 0; i < size; i++) { + int c = data[i]; + if (c <= 0xFFFF) sb.append((char) c); + else { + sb.append(makeSurrogatePair(c)); + } + } + return sb.toString(); + + } + + private boolean isBlocked(int lastStarterIndex, int index) { + + int combiningClass = getCombiningClass(data[index]); + for (int i = lastStarterIndex+1; i < index; i++) { + if (data[i] !=0 && combiningClass == getCombiningClass(data[i])) { + return true; + } + } + return false; + + } + + } + + +} diff --git a/src/nu/xom/UnicodeWriter.java b/src/nu/xom/UnicodeWriter.java new file mode 100644 index 0000000..063545c --- /dev/null +++ b/src/nu/xom/UnicodeWriter.java @@ -0,0 +1,445 @@ +/* Copyright 2002, 2003, 2005, 2006 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom; + +import java.io.IOException; +import java.io.Writer; + +/** + * @author Elliotte Rusty Harold + * @version 1.2d1 + * + */ +final class UnicodeWriter extends TextWriter { + + UnicodeWriter(Writer out, String encoding) { + super(out, encoding); + } + + /** + * @see nu.xom.TextWriter#needsEscaping(char) + */ + boolean needsEscaping(char c) { + return false; + } + + + void writeMarkup(String s) throws IOException { + + if (normalize) { + s = normalize(s); + } + + int unicodeStringLength = getUnicodeLengthForMarkup(s); + if (unicodeStringLength >= 0) { + out.write(s); + if (unicodeStringLength > 0) { + column += unicodeStringLength; + lastCharacterWasSpace = false; + skipFollowingLinefeed = false; + justBroke=false; + } + } + else { // write character by character + int length = s.length(); + for (int i=0; i < length; i++) { + writeMarkup(s.charAt(i)); + } + } + + } + + + // Names don't contain white space + void writeName(String name) throws IOException { + + if (normalize) { + name = normalize(name); + } + + int unicodeStringLength = getUnicodeLengthForName(name); + out.write(name); + column += unicodeStringLength; + lastCharacterWasSpace = false; + skipFollowingLinefeed = false; + justBroke=false; + + } + + + /* + * This is tricky. This method is doing two things: + * + * 1. It's counting the number of Unicode characters in s. + * 2. It's checking to see if this text contains anything + * that might need to be escaped. + * + * If the latter it returns -1; otherwise it returns the number of characters. + */ + private static int getUnicodeLengthForMarkup(String s) { + + int unicodeLength = 0; + int javaLength = s.length(); + for (int i = 0; i < javaLength; i++) { + // Benchmarking shows using toCharArray to be a little slower than using charAt + char c = s.charAt(i); + if (c <= ' ') { + // Really we're testing only for \t, \n, and space here. + // However all other characters less than or equal to 32 + // can't appear in markup sections. + // These characters cause an adjustment of + // lastCharacterWasSpace, skipFollowingLinefeed, and justBroke + // They may need to be escaped but only in doctype declarations. + // Should these have their own writeDoctypeDeclaration method???? + // Also an issue with spaces and such in PIs, XML declaration, comments + return -1; + } + // Count the low surrogates but skip the high surrogates + // so surrogate pairs aren't counted twice. + else if (c < 0xD800 || c > 0xDBFF) unicodeLength++; + } + return unicodeLength; + + } + + + private static int getUnicodeLengthForName(String name) { + + int unicodeLength = 0; + int javaLength = name.length(); + for (int i = 0; i < javaLength; i++) { + char c = name.charAt(i); + if (c < 0xD800 || c > 0xDBFF) unicodeLength++; + } + return unicodeLength; + + } + + + void writeAttributeValue(String s) throws IOException { + + if (normalize) { + s = normalize(s); + } + int unicodeStringLength = getUnicodeLengthForAttributeValue(s); + if (unicodeStringLength >= 0) { + out.write(s); + if (unicodeStringLength > 0) { + column += unicodeStringLength; + lastCharacterWasSpace = false; + skipFollowingLinefeed = false; + justBroke=false; + } + } + else { + int length = s.length(); + for (int i=0; i < length; i++) { + writeAttributeValue(s.charAt(i)); + } + } + + } + + + // All three getUnicodeLengthForFOO methods are very similar. + // Could the code duplciation be eliminated efficiently somehow? + private static int getUnicodeLengthForAttributeValue(String s) { + + int unicodeLength = 0; + int javaLength = s.length(); + for (int i = 0; i < javaLength; i++) { + char c = s.charAt(i); + switch (c) { + case '\t': return -1; + case '\n': return -1; + case 11: // unreachable + case 12: throw new XMLException("Bad character snuck into document"); + case '\r': return -1; + case 14: // unreachable + case 15: // unreachable + case 16: // unreachable + case 17: // unreachable + case 18: // unreachable + case 19: // unreachable + case 20: // unreachable + case 21: // unreachable + case 22: // unreachable + case 23: // unreachable + case 24: // unreachable + case 25: // unreachable + case 26: // unreachable + case 27: // unreachable + case 28: // unreachable + case 29: // unreachable + case 30: // unreachable + case 31: // unreachable + throw new XMLException("Bad character snuck into document"); + case ' ': return -1; + case '!': + unicodeLength++; + break; + case '"': + return -1; + case '#': + unicodeLength++; + break; + case '$': + unicodeLength++; + break; + case '%': + unicodeLength++; + break; + case '&': + return -1; + case '\'': + unicodeLength++; + break; + case '(': + unicodeLength++; + break; + case ')': + unicodeLength++; + break; + case '*': + unicodeLength++; + break; + case '+': + unicodeLength++; + break; + case ',': + unicodeLength++; + break; + case '-': + unicodeLength++; + break; + case '.': + unicodeLength++; + break; + case '/': + unicodeLength++; + break; + case '0': + unicodeLength++; + break; + case '1': + unicodeLength++; + break; + case '2': + unicodeLength++; + break; + case '3': + unicodeLength++; + break; + case '4': + unicodeLength++; + break; + case '5': + unicodeLength++; + break; + case '6': + unicodeLength++; + break; + case '7': + unicodeLength++; + break; + case '8': + unicodeLength++; + break; + case '9': + unicodeLength++; + break; + case ':': + unicodeLength++; + break; + case ';': + unicodeLength++; + break; + case '<': + return -1; + case '=': + unicodeLength++; + break; + case '>': + return -1; + default: + if (c < 0xd800 || c > 0xDBFF) unicodeLength++; + } + } + return unicodeLength; + + } + + + void writePCDATA(String s) throws IOException { + + if (normalize) { + s = normalize(s); + } + + int unicodeStringLength = getUnicodeLengthForPCDATA(s); + if (unicodeStringLength >= 0) { + out.write(s); + if (unicodeStringLength > 0) { + column += unicodeStringLength; + lastCharacterWasSpace = false; + skipFollowingLinefeed = false; + justBroke=false; + } + } + else { + int length = s.length(); + for (int i=0; i < length; i++) { + writePCDATA(s.charAt(i)); + } + } + + } + + + private static int getUnicodeLengthForPCDATA(String s) { + + int unicodeLength = 0; + int javaLength = s.length(); + for (int i = 0; i < javaLength; i++) { + char c = s.charAt(i); + switch (c) { + case '\t': return -1; + case '\n': return -1; + case 11: // unreachable + case 12: throw new XMLException("Bad character snuck into document"); + case '\r': return -1; + case 14: // unreachable + case 15: // unreachable + case 16: // unreachable + case 17: // unreachable + case 18: // unreachable + case 19: // unreachable + case 20: // unreachable + case 21: // unreachable + case 22: // unreachable + case 23: // unreachable + case 24: // unreachable + case 25: // unreachable + case 26: // unreachable + case 27: // unreachable + case 28: // unreachable + case 29: // unreachable + case 30: // unreachable + case 31: // unreachable + throw new XMLException("Bad character snuck into document"); + case ' ': return -1; + case '!': + unicodeLength++; + break; + case '"': + unicodeLength++; + break; + case '#': + unicodeLength++; + break; + case '$': + unicodeLength++; + break; + case '%': + unicodeLength++; + break; + case '&': + return -1; + case '\'': + unicodeLength++; + break; + case '(': + unicodeLength++; + break; + case ')': + unicodeLength++; + break; + case '*': + unicodeLength++; + break; + case '+': + unicodeLength++; + break; + case ',': + unicodeLength++; + break; + case '-': + unicodeLength++; + break; + case '.': + unicodeLength++; + break; + case '/': + unicodeLength++; + break; + case '0': + unicodeLength++; + break; + case '1': + unicodeLength++; + break; + case '2': + unicodeLength++; + break; + case '3': + unicodeLength++; + break; + case '4': + unicodeLength++; + break; + case '5': + unicodeLength++; + break; + case '6': + unicodeLength++; + break; + case '7': + unicodeLength++; + break; + case '8': + unicodeLength++; + break; + case '9': + unicodeLength++; + break; + case ':': + unicodeLength++; + break; + case ';': + unicodeLength++; + break; + case '<': + return -1; + case '=': + unicodeLength++; + break; + case '>': + return -1; + default: + if (c < 0xd800 || c > 0xDBFF) unicodeLength++; + } + } + return unicodeLength; + + } + +} diff --git a/src/nu/xom/UnsynchronizedBufferedWriter.java b/src/nu/xom/UnsynchronizedBufferedWriter.java new file mode 100644 index 0000000..caebc6c --- /dev/null +++ b/src/nu/xom/UnsynchronizedBufferedWriter.java @@ -0,0 +1,89 @@ +/* Copyright 2002-2006 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ +package nu.xom; + +import java.io.IOException; +import java.io.Writer; + +final class UnsynchronizedBufferedWriter extends Writer { + + private final static int CAPACITY = 8192; + private char[] buffer = new char[CAPACITY]; + private int position = 0; + private Writer out; + + + public UnsynchronizedBufferedWriter(Writer out) { + this.out = out; + } + + + public void write(char[] buffer, int offset, int length) throws IOException { + throw new UnsupportedOperationException("XOM bug: this statement shouldn't be reachable."); + } + + + public void write(String s) throws IOException { + write(s, 0, s.length()); + } + + + public void write(String s, int offset, int length) throws IOException { + + while (length > 0) { + int n = CAPACITY - position; + if (length < n) n = length; + s.getChars(offset, offset + n, buffer, position); + position += n; + offset += n; + length -= n; + if (position >= CAPACITY) flushInternal(); + } + + } + + + public void write(int c) throws IOException { + if (position >= CAPACITY) flushInternal(); + buffer[position] = (char) c; + position++; + } + + + public void flush() throws IOException { + flushInternal(); + out.flush(); + } + + + private void flushInternal() throws IOException { + if (position != 0) { + out.write(buffer, 0, position); + position = 0; + } + } + + + public void close() throws IOException { + throw new UnsupportedOperationException("How'd we get here?"); + } + +} diff --git a/src/nu/xom/ValidityException.java b/src/nu/xom/ValidityException.java new file mode 100644 index 0000000..5cff9f1 --- /dev/null +++ b/src/nu/xom/ValidityException.java @@ -0,0 +1,277 @@ +/* Copyright 2002-2005, 2019 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom; + +import java.util.ArrayList; +import java.util.List; + +import org.xml.sax.SAXParseException; + +/** + *

    + * Signals a validity error in a document being parsed. + * These are not thrown by default, unless you specifically + * request that the builder validate. + *

    + + * @author Elliotte Rusty Harold + * @version 1.3.1 + * + */ +public class ValidityException extends ParsingException { + + + private static final long serialVersionUID = 8950434465665278751L; + + private List saxExceptions = new ArrayList(); + private transient Document document; + + + /** + *

    + * Creates a new ValidityException + * with a detail message and an underlying root cause. + *

    + * + * @param message a string indicating the specific problem + * @param cause the original cause of this exception + */ + public ValidityException(String message, Throwable cause) { + super(message, cause); + } + + + /** + *

    + * Creates a new ValidityException + * with a detail message and line and column numbers. + *

    + * + * @param message a string indicating the specific problem + * @param lineNumber the approximate line number + * where the problem occurs + * @param columnNumber the approximate column number + * where the problem occurs + */ + public ValidityException( + String message, + int lineNumber, + int columnNumber) { + super(message, lineNumber, columnNumber); + } + + + /** + *

    + * Creates a new ValidityException + * with a detail message, line and column numbers, + * and an underlying exception. + *

    + * + * @param message a string indicating the specific problem + * @param lineNumber the approximate line number + * where the problem occurs + * @param columnNumber the approximate column number + * where the problem occurs + * @param cause the original cause of this exception + */ + public ValidityException( + String message, + int lineNumber, + int columnNumber, + Throwable cause) { + super(message, lineNumber, columnNumber, cause); + } + + + /** + *

    + * Creates a new ValidityException + * with a detail message, the URI of the document that contained + * the error, and approximate line and column numbers of the + * first validity error. + *

    + * + * @param message a string indicating the specific problem + * @param uri URL of the document with a validity error + * @param lineNumber the approximate line number + * where the problem occurs + * @param columnNumber the approximate column number + * where the problem occurs + */ + public ValidityException(String message, String uri, + int lineNumber, int columnNumber) { + super(message, uri, lineNumber, columnNumber); + } + + + /** + *

    + * Creates a new ValidityException + * with a detail message, URI of the document containing the + * validity error, line and column numbers of the error, + * and an underlying exception. + *

    + * + * @param message a string indicating the specific problem + * @param uri URL of the document with a validity error + * @param lineNumber the approximate line number + * where the problem occurs + * @param columnNumber the approximate column number + * where the problem occurs + * @param cause the original cause of this exception + */ + public ValidityException( + String message, + String uri, + int lineNumber, + int columnNumber, + Throwable cause) { + super(message, uri, lineNumber, columnNumber, cause); + } + + + /** + *

    + * Creates a new ValidityException + * with a detail message. + *

    + * + * @param message a string indicating the specific problem + */ + public ValidityException(String message) { + super(message); + } + + + /** + *

    + * Returns a Document object for the document that + * caused this exception. This is useful if you want notification + * of validity errors, but nonetheless wish to further process + * the invalid document. + *

    + * + * @return the invalid document + */ + public Document getDocument() { + return document; + } + + + void setDocument(Document doc) { + this.document = doc; + } + + + void addError(SAXParseException ex) { + saxExceptions.add(ex); + } + + + /** + *

    + * Returns the number of validity errors the parser detected + * in the document. This is likely to not be consistent from one + * parser to another. + *

    + * + * @return the number of validity errors the parser detected + */ + public int getErrorCount() { + return saxExceptions.size(); + } + + + /** + *

    + * Returns a message indicating a specific validity problem + * in the input document as detected by the parser. Normally, + * these will be in the order they appear in the document. + * For instance, an error in the root element is likely + * to appear before an error in a child element. However, this + * depends on the underlying parser and is not guaranteed. + *

    + * + * @param n the index of the validity error to report + * + * @return a string describing the nth validity error + * + * @throws IndexOutOfBoundsException if n is greater + * than or equal to the number of errors detected + */ + public String getValidityError(int n) { + Exception ex = (Exception) saxExceptions.get(n); + return ex.getMessage(); + } + + + /** + *

    + * Returns the line number of the nth validity + * error. It returns -1 if this is not known. This number + * may be helpful for debugging, but should not be relied on. + * Different parsers may set it differently. For instance + * a problem with an element might be reported using the + * line number of the start-tag or the line number of the + * end-tag. + *

    + * + * @param n the index of the validity error to report + * @return the approximate line number where the nth + * validity error was detected + * + * @throws IndexOutOfBoundsException if n is greater + * than or equal to the number of errors detected + */ + public int getLineNumber(int n) { + SAXParseException ex = (SAXParseException) saxExceptions.get(n); + return ex.getLineNumber(); + } + + + /** + *

    + * Returns the column number of the nth validity + * error. It returns -1 if this is not known. This number + * may be helpful for debugging, but should not be relied on. + * Different parsers may set it differently. For instance + * a problem with an element might be reported using the + * column of the < or the > + * of the start-tag + *

    + * + * @param n the index of the validity error to report + * + * @return the approximate column where the nth + * validity error was detected + * + * @throws IndexOutOfBoundsException if n is greater + * than or equal to the number of errors detected + */ + public int getColumnNumber(int n) { + SAXParseException ex = (SAXParseException) saxExceptions.get(n); + return ex.getColumnNumber(); + } + + +} diff --git a/src/nu/xom/Verifier.java b/src/nu/xom/Verifier.java new file mode 100644 index 0000000..7e5e3d2 --- /dev/null +++ b/src/nu/xom/Verifier.java @@ -0,0 +1,1386 @@ +/* Copyright 2002-2006, 2018 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom; + +import java.io.DataInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; +import java.io.StringReader; +import java.util.StringTokenizer; + +import org.xml.sax.EntityResolver; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; +import org.xml.sax.XMLReader; + +/** + *

    + * Verifier checks names and data for + * compliance with XML 1.0 and Namespaces in XML rules. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.2.11 + * + */ +final class Verifier { + + private Verifier() {} + + // constants for the bit flags in the characters lookup table + private final static byte XML_CHARACTER = 1; + private final static byte NAME_CHARACTER = 2; + private final static byte NAME_START_CHARACTER = 4; + private final static byte NCNAME_CHARACTER = 8; + + private static byte[] flags = null; + + static { + + ClassLoader loader = Verifier.class.getClassLoader(); + if (loader != null) loadFlags(loader); + // If that didn't work, try a different ClassLoader + if (flags == null) { + loader = Thread.currentThread().getContextClassLoader(); + loadFlags(loader); + } + + } + + + private static void loadFlags(ClassLoader loader) { + + DataInputStream in = null; + try { + InputStream raw = loader.getResourceAsStream("nu/xom/characters.dat"); + if (raw == null) { + throw new RuntimeException("Broken XOM installation: " + + "could not load nu/xom/characters.dat"); + } + // buffer this???? + in = new DataInputStream(raw); + flags = new byte[65536]; + in.readFully(flags); + } + catch (IOException ex) { + throw new RuntimeException("Broken XOM installation: " + + "could not load nu/xom/characters.dat"); + } + finally { + try { + if (in != null) in.close(); + } + catch (IOException ex) { + // no big deal + } + } + + } + + + /** + *

    + * Check whether name is + * a non-colonized name as defined in + * Namespaces in XML. + *

    + * + * @param name String name to check + * + * @throws IllegalNameException if name is not a + * non-colonized name + */ + static void checkNCName(String name) { + + if (name == null) { + throwIllegalNameException(name, "NCNames cannot be null"); + } + + int length = name.length(); + if (length == 0) { + throwIllegalNameException(name, "NCNames cannot be empty"); + } + + char first = name.charAt(0); + if ((flags[first] & NAME_START_CHARACTER) == 0) { + throwIllegalNameException(name, "NCNames cannot start " + + "with the character " + Integer.toHexString(first)); + } + + for (int i = 1; i < length; i++) { + char c = name.charAt(i); + if ((flags[c] & NCNAME_CHARACTER) == 0) { + if (c == ':') { + throwIllegalNameException(name, "NCNames cannot contain colons"); + } + else { + throwIllegalNameException(name, "0x" + + Integer.toHexString(c) + " is not a legal NCName character"); + } + } + } + + } + + + private static void throwIllegalNameException(String name, String message) { + IllegalNameException ex = new IllegalNameException(message); + ex.setData(name); + throw ex; + } + + + private static void throwIllegalCharacterDataException(String data, String message) { + IllegalDataException ex = new IllegalCharacterDataException(message); + ex.setData(data); + throw ex; + } + + + private static void throwMalformedURIException(String uri, String message) { + MalformedURIException ex = new MalformedURIException(message); + ex.setData(uri); + throw ex; + } + + + /** + *

    + * This methods checks whether a string contains only + * characters allowed by the XML 1.0 specification. + *

    + * + * @param text String value to verify + * + * @throws IllegalCharacterDataException if text is + * not legal PCDATA + */ + static void checkPCDATA(String text) { + + if (text == null) throw new IllegalCharacterDataException("Null text"); + + char[] data = text.toCharArray(); + for (int i = 0, len = data.length; i < len; i++) { + int result = data[i]; + if (result >= 0xD800 && result <= 0xDBFF) { + try { + int low = data[i+1]; + if (low < 0xDC00 || low > 0xDFFF) { + IllegalCharacterDataException ex + = new IllegalCharacterDataException("Bad surrogate pair"); + ex.setData(text); + throw ex; + } + i++; // increment past low surrogate + } + catch (ArrayIndexOutOfBoundsException ex) { + IllegalCharacterDataException ide + = new IllegalCharacterDataException("Bad Surrogate Pair", ex); + ide.setData(text); + throw ide; + } + // all properly matched surrogate pairs are legal in PCDATA + } // end if + else if ((flags[result] & XML_CHARACTER) == 0) { + throwIllegalCharacterDataException(text, "0x" + + Integer.toHexString(result) + + " is not allowed in XML content"); + } + + } + + } + + + /** + *

    + * Checks a string to see if it is a syntactically correct + * RFC 3986 URI reference. Both absolute and relative + * URIs are supported, as are URIs with fragment identifiers. + *

    + * + * @param uri String containing the potential URI + * + * @throws MalformedURIException if this is not a + * legal URI reference + */ + static void checkURIReference(String uri) { + + if ((uri == null) || uri.length() == 0) return; + + URIUtil.ParsedURI parsed = new URIUtil.ParsedURI(uri); + try { + if (parsed.scheme != null) checkScheme(parsed.scheme); + if (parsed.authority != null) checkAuthority(parsed.authority); + checkPath(parsed.path); + if (parsed.fragment != null) checkFragment(parsed.fragment); + if (parsed.query != null) checkQuery(parsed.query); + } + catch (MalformedURIException ex) { + ex.setData(uri); + throw ex; + } + + } + + + private static void checkQuery(final String query) { + + int length = query.length(); + for (int i = 0; i < length; i++) { + char c = query.charAt(i); + if (c == '%') { + try { + if (!isHexDigit(query.charAt(i+1)) || !isHexDigit(query.charAt(i+2))) { + throwMalformedURIException(query, + "Bad percent escape sequence"); + } + } + catch (StringIndexOutOfBoundsException ex) { + throwMalformedURIException(query, + "Bad percent escape sequence"); + } + i += 2; + } + else if (!isQueryCharacter(c)) { + throw new MalformedURIException( + "Illegal query character " + c + ); + } + } + + } + + + // same for fragment ID + private static boolean isQueryCharacter(char c) { + + switch(c) { + case '!': return true; + case '"': return false; + case '#': return false; + case '$': return true; + case '%': return false; // tested in checkQuery + case '&': return true; + case '\'': return true; + case '(': return true; + case ')': return true; + case '*': return true; + case '+': return true; + case ',': return true; + case '-': return true; + case '.': return true; + case '/': return true; + case '0': return true; + case '1': return true; + case '2': return true; + case '3': return true; + case '4': return true; + case '5': return true; + case '6': return true; + case '7': return true; + case '8': return true; + case '9': return true; + case ':': return true; + case ';': return true; + case '<': return false; + case '=': return true; + case '>': return false; + case '?': return true; + case '@': return true; + case 'A': return true; + case 'B': return true; + case 'C': return true; + case 'D': return true; + case 'E': return true; + case 'F': return true; + case 'G': return true; + case 'H': return true; + case 'I': return true; + case 'J': return true; + case 'K': return true; + case 'L': return true; + case 'M': return true; + case 'N': return true; + case 'O': return true; + case 'P': return true; + case 'Q': return true; + case 'R': return true; + case 'S': return true; + case 'T': return true; + case 'U': return true; + case 'V': return true; + case 'W': return true; + case 'X': return true; + case 'Y': return true; + case 'Z': return true; + case '[': return false; + case '\\': return false; + case ']': return false; + case '^': return false; + case '_': return true; + case '`': return false; + case 'a': return true; + case 'b': return true; + case 'c': return true; + case 'd': return true; + case 'e': return true; + case 'f': return true; + case 'g': return true; + case 'h': return true; + case 'i': return true; + case 'j': return true; + case 'k': return true; + case 'l': return true; + case 'm': return true; + case 'n': return true; + case 'o': return true; + case 'p': return true; + case 'q': return true; + case 'r': return true; + case 's': return true; + case 't': return true; + case 'u': return true; + case 'v': return true; + case 'w': return true; + case 'x': return true; + case 'y': return true; + case 'z': return true; + case '{': return false; + case '|': return false; + case '}': return false; + case '~': return true; + } + return false; + + } + + + private static void checkFragment(String fragment) { + // The BNF for fragments is the same as for query strings + checkQuery(fragment); + } + + + // Besides the legal characters issues, a path must + // not contain two consecutive forward slashes + private static void checkPath(final String path) { + + int length = path.length(); + char[] text = path.toCharArray(); + for (int i = 0; i < length; i++) { + char c = text[i]; + if (c == '/') { + if (i < length-1) { + if (text[i+1] == '/') { + throwMalformedURIException(path, + "Double slash (//) in path"); + } + } + } + else if (c == '%') { + try { + if (!isHexDigit(text[i+1]) + || !isHexDigit(text[i+2])) { + throwMalformedURIException(path, + "Bad percent escape sequence"); + } + } + catch (ArrayIndexOutOfBoundsException ex) { + throwMalformedURIException(path, + "Bad percent escape sequence"); + } + i += 2; + } + else if (!isPathCharacter(c)) { + throwMalformedURIException(path, + "Illegal path character " + c + ); + } + } + + } + + + private static void checkAuthority(String authority) { + + String userInfo = null; + String host = null; + String port = null; + + int atSign = authority.indexOf('@'); + if (atSign != -1) { + userInfo = authority.substring(0, atSign); + authority = authority.substring(atSign+1); + } + + int colon; + if (authority.startsWith("[")) { + colon = authority.indexOf("]:"); + if (colon != -1) colon = colon+1; + } + else colon = authority.indexOf(':'); + + if (colon != -1) { + host = authority.substring(0, colon); + port = authority.substring(colon+1); + } + else { + host = authority; + } + + if (userInfo != null) checkUserInfo(userInfo); + if (port != null) checkPort(port); + checkHost(host); + + } + + + private static void checkHost(final String host) { + + int length = host.length(); + if (length == 0) return; // file URI + + char[] text = host.toCharArray(); + if (text[0] == '[') { + if (text[length-1] != ']') { + throw new MalformedURIException("Missing closing ]"); + } + // trim [ and ] from ends of host + checkIPv6Address(host.substring(1, length-1)); + } + else { + if (length > 255) { + throw new MalformedURIException("Host name too long: " + host); + } + + for (int i = 0; i < length; i++) { + char c = text[i]; + if (c == '%') { + try { + if (!isHexDigit(text[i+1]) || !isHexDigit(text[i+2])) { + throwMalformedURIException(host, + "Bad percent escape sequence"); + } + } + catch (ArrayIndexOutOfBoundsException ex) { + throwMalformedURIException(host, + "Bad percent escape sequence"); + } + i += 2; + } + else if (!isRegNameCharacter(c)) { + throwMalformedURIException(host, + "Illegal host character " + c + ); + } + } + } + } + + + private static boolean isRegNameCharacter(char c) { + + switch(c) { + case '!': return true; + case '"': return false; + case '#': return false; + case '$': return true; + case '%': return false; // checked separately + case '&': return true; + case '\'': return true; + case '(': return true; + case ')': return true; + case '*': return true; + case '+': return true; + case ',': return true; + case '-': return true; + case '.': return true; + case '/': return false; + case '0': return true; + case '1': return true; + case '2': return true; + case '3': return true; + case '4': return true; + case '5': return true; + case '6': return true; + case '7': return true; + case '8': return true; + case '9': return true; + case ':': return false; + case ';': return true; + case '<': return false; + case '=': return true; + case '>': return false; + case '?': return false; + case '@': return false; + case 'A': return true; + case 'B': return true; + case 'C': return true; + case 'D': return true; + case 'E': return true; + case 'F': return true; + case 'G': return true; + case 'H': return true; + case 'I': return true; + case 'J': return true; + case 'K': return true; + case 'L': return true; + case 'M': return true; + case 'N': return true; + case 'O': return true; + case 'P': return true; + case 'Q': return true; + case 'R': return true; + case 'S': return true; + case 'T': return true; + case 'U': return true; + case 'V': return true; + case 'W': return true; + case 'X': return true; + case 'Y': return true; + case 'Z': return true; + case '[': return false; + case '\\': return false; + case ']': return false; + case '^': return false; + case '_': return true; + case '`': return false; + case 'a': return true; + case 'b': return true; + case 'c': return true; + case 'd': return true; + case 'e': return true; + case 'f': return true; + case 'g': return true; + case 'h': return true; + case 'i': return true; + case 'j': return true; + case 'k': return true; + case 'l': return true; + case 'm': return true; + case 'n': return true; + case 'o': return true; + case 'p': return true; + case 'q': return true; + case 'r': return true; + case 's': return true; + case 't': return true; + case 'u': return true; + case 'v': return true; + case 'w': return true; + case 'x': return true; + case 'y': return true; + case 'z': return true; + case '{': return false; + case '|': return false; + case '}': return false; + case '~': return true; + } + return false; + + } + + + private static void checkPort(String port) { + + for (int i = port.length()-1; i >= 0; i--) { + char c = port.charAt(i); + if (c < '0' || c > '9') { + throw new MalformedURIException("Bad port: " + port); + } + } + + } + + + private static void checkUserInfo(String userInfo) { + + int length = userInfo.length(); + for (int i = 0; i < length; i++) { + char c = userInfo.charAt(i); + if (c == '%') { + try { + if (!isHexDigit(userInfo.charAt(i+1)) + || !isHexDigit(userInfo.charAt(i+2))) { + throwMalformedURIException(userInfo, + "Bad percent escape sequence"); + } + } + catch (StringIndexOutOfBoundsException ex) { + throwMalformedURIException(userInfo, + "Bad percent escape sequence"); + } + i += 2; + } + else if (!isUserInfoCharacter(c)) { + throw new MalformedURIException("Bad user info: " + userInfo); + } + } + + } + + + private static void checkScheme(String scheme) { + + // http is probably 99% of cases so check it first + if ("http".equals(scheme)) return; + + if (scheme.length() == 0) { + throw new MalformedURIException("URIs cannot begin with a colon"); + } + char c = scheme.charAt(0); + if (!isAlpha(c)) { + throw new MalformedURIException( + "Illegal initial scheme character " + c); + } + + for (int i = scheme.length()-1; i >= 1; i--) { + c = scheme.charAt(i); + if (!isSchemeCharacter(c)) { + throw new MalformedURIException( + "Illegal scheme character " + c + ); + } + } + + } + + + // http://www.faqs.org/rfcs/rfc2373.html + // http://www.faqs.org/rfcs/rfc2732.html + private static void checkIPv6Address(String ip6Address) { + + StringTokenizer st = new StringTokenizer(ip6Address, ":", true); + int numTokens = st.countTokens(); + if (numTokens > 15 || numTokens < 2) { + throw new MalformedURIException( + "Illegal IPv6 host address: " + ip6Address + ); + } + for (int i = 0; i < numTokens; i++) { + String hexPart = st.nextToken(); + if (":".equals(hexPart)) continue; + try { + int part = Integer.parseInt(hexPart, 16); + if (part < 0) { + throw new MalformedURIException( + "Illegal IPv6 host address: " + ip6Address + ); + } + } + catch (NumberFormatException ex) { + if (i == numTokens-1) { + checkIP4Address(hexPart, ip6Address); + } + else { + throwMalformedURIException(ip6Address, + "Illegal IPv6 host address: " + ip6Address + ); + } + } + } + + if (ip6Address.indexOf("::") != ip6Address.lastIndexOf("::")) { + throw new MalformedURIException( + "Illegal IPv6 host address: " + ip6Address + ); + } + + } + + + private static void checkIP4Address(String address, String ip6Address) { + + StringTokenizer st = new StringTokenizer(address, "."); + int numTokens = st.countTokens(); + if (numTokens != 4) { + throw new MalformedURIException( + "Illegal IPv6 host address: " + ip6Address + ); + } + for (int i = 0; i < 4; i++) { + String decPart = st.nextToken(); + // https://github.com/elharo/xom/issues/12 + if (decPart.startsWith("+")) { + throw new MalformedURIException( + "Illegal IPv6 host address: " + ip6Address + ); + } + try { + int dec = Integer.parseInt(decPart); + if (dec > 255 || dec < 0) { + throw new MalformedURIException( + "Illegal IPv6 host address: " + ip6Address + ); + } + } + catch (NumberFormatException ex) { + throw new MalformedURIException( + "Illegal IPv6 host address: " + ip6Address + ); + } + } + + } + + + static void checkXMLName(String name) { + + if (name == null) { + throwIllegalNameException(name, "XML names cannot be null"); + } + + int length = name.length(); + if (length == 0) { + throwIllegalNameException(name, "XML names cannot be empty"); + } + + char first = name.charAt(0); + if ((flags[first] & NAME_START_CHARACTER) == 0) { + throwIllegalNameException(name, "XML names cannot start " + + "with the character " + Integer.toHexString(first)); + } + + for (int i = 1; i < length; i++) { + char c = name.charAt(i); + if ((flags[c] & NAME_CHARACTER) == 0) { + throwIllegalNameException(name, "0x" + + Integer.toHexString(c) + + " is not a legal name character"); + } + } + + } + + + private static boolean[] C0Table = new boolean[0x21]; + static { + C0Table['\n'] = true; + C0Table['\r'] = true; + C0Table['\t'] = true; + C0Table[' '] = true; + } + + + static boolean isXMLSpaceCharacter(char c) { + if (c > ' ') return false; + return C0Table[c]; + } + + + private static boolean isHexDigit(char c) { + + switch(c) { + case '0': return true; + case '1': return true; + case '2': return true; + case '3': return true; + case '4': return true; + case '5': return true; + case '6': return true; + case '7': return true; + case '8': return true; + case '9': return true; + case ':': return false; + case ';': return false; + case '<': return false; + case '=': return false; + case '>': return false; + case '?': return false; + case '@': return false; + case 'A': return true; + case 'B': return true; + case 'C': return true; + case 'D': return true; + case 'E': return true; + case 'F': return true; + case 'G': return false; + case 'H': return false; + case 'I': return false; + case 'J': return false; + case 'K': return false; + case 'L': return false; + case 'M': return false; + case 'N': return false; + case 'O': return false; + case 'P': return false; + case 'Q': return false; + case 'R': return false; + case 'S': return false; + case 'T': return false; + case 'U': return false; + case 'V': return false; + case 'W': return false; + case 'X': return false; + case 'Y': return false; + case 'Z': return false; + case '[': return false; + case '\\': return false; + case ']': return false; + case '^': return false; + case '_': return false; + case '`': return false; + case 'a': return true; + case 'b': return true; + case 'c': return true; + case 'd': return true; + case 'e': return true; + case 'f': return true; + } + return false; + } + + + // Since namespace URIs are commonly repeated, we can save a lot + // of redundant code by storing the ones we've seen before. + private static URICache cache = new URICache(); + + private final static class URICache { + + private final static int LOAD = 6; + private String[] cache = new String[LOAD]; + private int position = 0; + + synchronized boolean contains(String s) { + + for (int i = 0; i < LOAD; i++) { + // Here I'm assuming the namespace URIs are interned. + // This is commonly but not always true. This won't + // break if they haven't been. Using equals() instead + // of == is faster when the namespace URIs haven't been + // interned but slower if they have. + if (s == cache[i]) { + return true; + } + } + return false; + + } + + synchronized void put(String s) { + cache[position] = s; + position++; + if (position == LOAD) position = 0; + } + + } + + + /** + *

    + * Checks a string to see if it is an RFC 3986 absolute + * URI reference. URI references can contain fragment identifiers. + * Absolute URI references must have a scheme. + *

    + * + * @param uri String to check + * + * @throws MalformedURIException if this is not a legal + * URI reference + */ + static void checkAbsoluteURIReference(String uri) { + + if (cache.contains(uri)) { + return; + } + URIUtil.ParsedURI parsed = new URIUtil.ParsedURI(uri); + try { + if (parsed.scheme == null) { + throwMalformedURIException( + uri, "Missing scheme in absolute URI reference"); + } + checkScheme(parsed.scheme); + if (parsed.authority != null) checkAuthority(parsed.authority); + checkPath(parsed.path); + if (parsed.fragment != null) checkFragment(parsed.fragment); + if (parsed.query != null) checkQuery(parsed.query); + cache.put(uri); + } + catch (MalformedURIException ex) { + ex.setData(uri); + throw ex; + } + + } + + + static boolean isAlpha(char c) { + + switch(c) { + case 'A': return true; + case 'B': return true; + case 'C': return true; + case 'D': return true; + case 'E': return true; + case 'F': return true; + case 'G': return true; + case 'H': return true; + case 'I': return true; + case 'J': return true; + case 'K': return true; + case 'L': return true; + case 'M': return true; + case 'N': return true; + case 'O': return true; + case 'P': return true; + case 'Q': return true; + case 'R': return true; + case 'S': return true; + case 'T': return true; + case 'U': return true; + case 'V': return true; + case 'W': return true; + case 'X': return true; + case 'Y': return true; + case 'Z': return true; + case '[': return false; + case '\\': return false; + case ']': return false; + case '^': return false; + case '_': return false; + case '`': return false; + case 'a': return true; + case 'b': return true; + case 'c': return true; + case 'd': return true; + case 'e': return true; + case 'f': return true; + case 'g': return true; + case 'h': return true; + case 'i': return true; + case 'j': return true; + case 'k': return true; + case 'l': return true; + case 'm': return true; + case 'n': return true; + case 'o': return true; + case 'p': return true; + case 'q': return true; + case 'r': return true; + case 's': return true; + case 't': return true; + case 'u': return true; + case 'v': return true; + case 'w': return true; + case 'x': return true; + case 'y': return true; + case 'z': return true; + } + + return false; + + } + + + static boolean isSchemeCharacter(char c) { + + /* The : and the ? cannot be reached here because they'll + * have been parsed out separately before this method is + * called. They're included here strictly for alignment + * so the compiler will generate a table lookup. + */ + switch(c) { + case '+': return true; + case ',': return false; + case '-': return true; + case '.': return true; + case '/': return false; + case '0': return true; + case '1': return true; + case '2': return true; + case '3': return true; + case '4': return true; + case '5': return true; + case '6': return true; + case '7': return true; + case '8': return true; + case '9': return true; + case ':': return false; // unreachable + case ';': return false; + case '<': return false; + case '=': return false; + case '>': return false; + case '?': return false; // unreachable + case '@': return false; + case 'A': return true; + case 'B': return true; + case 'C': return true; + case 'D': return true; + case 'E': return true; + case 'F': return true; + case 'G': return true; + case 'H': return true; + case 'I': return true; + case 'J': return true; + case 'K': return true; + case 'L': return true; + case 'M': return true; + case 'N': return true; + case 'O': return true; + case 'P': return true; + case 'Q': return true; + case 'R': return true; + case 'S': return true; + case 'T': return true; + case 'U': return true; + case 'V': return true; + case 'W': return true; + case 'X': return true; + case 'Y': return true; + case 'Z': return true; + case '[': return false; + case '\\': return false; + case ']': return false; + case '^': return false; + case '_': return false; + case '`': return false; + case 'a': return true; + case 'b': return true; + case 'c': return true; + case 'd': return true; + case 'e': return true; + case 'f': return true; + case 'g': return true; + case 'h': return true; + case 'i': return true; + case 'j': return true; + case 'k': return true; + case 'l': return true; + case 'm': return true; + case 'n': return true; + case 'o': return true; + case 'p': return true; + case 'q': return true; + case 'r': return true; + case 's': return true; + case 't': return true; + case 'u': return true; + case 'v': return true; + case 'w': return true; + case 'x': return true; + case 'y': return true; + case 'z': return true; + } + + return false; + + } + + + private static boolean isPathCharacter(char c) { + + switch(c) { + case '!': return true; + case '"': return false; + case '#': return false; + case '$': return true; + case '%': return false; // checked separately + case '&': return true; + case '\'': return true; + case '(': return true; + case ')': return true; + case '*': return true; + case '+': return true; + case ',': return true; + case '-': return true; + case '.': return true; + case '/': return false; // handled separately + case '0': return true; + case '1': return true; + case '2': return true; + case '3': return true; + case '4': return true; + case '5': return true; + case '6': return true; + case '7': return true; + case '8': return true; + case '9': return true; + case ':': return true; + case ';': return true; + case '<': return false; + case '=': return true; + case '>': return false; + case '?': return false; + case '@': return true; + case 'A': return true; + case 'B': return true; + case 'C': return true; + case 'D': return true; + case 'E': return true; + case 'F': return true; + case 'G': return true; + case 'H': return true; + case 'I': return true; + case 'J': return true; + case 'K': return true; + case 'L': return true; + case 'M': return true; + case 'N': return true; + case 'O': return true; + case 'P': return true; + case 'Q': return true; + case 'R': return true; + case 'S': return true; + case 'T': return true; + case 'U': return true; + case 'V': return true; + case 'W': return true; + case 'X': return true; + case 'Y': return true; + case 'Z': return true; + case '[': return false; + case '\\': return false; + case ']': return false; + case '^': return false; + case '_': return true; + case '`': return false; + case 'a': return true; + case 'b': return true; + case 'c': return true; + case 'd': return true; + case 'e': return true; + case 'f': return true; + case 'g': return true; + case 'h': return true; + case 'i': return true; + case 'j': return true; + case 'k': return true; + case 'l': return true; + case 'm': return true; + case 'n': return true; + case 'o': return true; + case 'p': return true; + case 'q': return true; + case 'r': return true; + case 's': return true; + case 't': return true; + case 'u': return true; + case 'v': return true; + case 'w': return true; + case 'x': return true; + case 'y': return true; + case 'z': return true; + case '{': return false; + case '|': return false; + case '}': return false; + case '~': return true; + } + + return false; + + } + + + private static boolean isUserInfoCharacter(char c) { + + switch(c) { + case '!': return true; + case '"': return false; + case '#': return false; + case '$': return true; + case '%': return false; // checked separately + case '&': return true; + case '\'': return true; + case '(': return true; + case ')': return true; + case '*': return true; + case '+': return true; + case ',': return true; + case '-': return true; + case '.': return true; + case '/': return true; + case '0': return true; + case '1': return true; + case '2': return true; + case '3': return true; + case '4': return true; + case '5': return true; + case '6': return true; + case '7': return true; + case '8': return true; + case '9': return true; + case ':': return true; + case ';': return true; + case '<': return false; + case '=': return true; + case '>': return false; + case '?': return false; + case '@': return false; + case 'A': return true; + case 'B': return true; + case 'C': return true; + case 'D': return true; + case 'E': return true; + case 'F': return true; + case 'G': return true; + case 'H': return true; + case 'I': return true; + case 'J': return true; + case 'K': return true; + case 'L': return true; + case 'M': return true; + case 'N': return true; + case 'O': return true; + case 'P': return true; + case 'Q': return true; + case 'R': return true; + case 'S': return true; + case 'T': return true; + case 'U': return true; + case 'V': return true; + case 'W': return true; + case 'X': return true; + case 'Y': return true; + case 'Z': return true; + case '[': return false; + case '\\': return false; + case ']': return false; + case '^': return false; + case '_': return true; + case '`': return false; + case 'a': return true; + case 'b': return true; + case 'c': return true; + case 'd': return true; + case 'e': return true; + case 'f': return true; + case 'g': return true; + case 'h': return true; + case 'i': return true; + case 'j': return true; + case 'k': return true; + case 'l': return true; + case 'm': return true; + case 'n': return true; + case 'o': return true; + case 'p': return true; + case 'q': return true; + case 'r': return true; + case 's': return true; + case 't': return true; + case 'u': return true; + case 'v': return true; + case 'w': return true; + case 'x': return true; + case 'y': return true; + case 'z': return true; + case '{': return false; + case '|': return false; + case '}': return false; + case '~': return true; + } + + return false; + + } + + + /** + * Check to see that this string is an absolute URI, + * neither a relative URI nor a URI reference. + * + */ + static void checkAbsoluteURI(String uri) { + + URIUtil.ParsedURI parsed = new URIUtil.ParsedURI(uri); + try { + if (parsed.scheme == null) { + throwMalformedURIException(uri, "Missing scheme in absolute URI"); + } + checkScheme(parsed.scheme); + if (parsed.authority != null) checkAuthority(parsed.authority); + checkPath(parsed.path); + if (parsed.fragment != null) { + throwMalformedURIException(uri, "URIs cannot have fragment identifiers"); + } + if (parsed.query != null) checkQuery(parsed.query); + } + catch (MalformedURIException ex) { + ex.setData(uri); + throw ex; + } + + } + + + // For use in checking internal DTD subsets + private static XMLReader parser; + + static synchronized void checkInternalDTDSubset(String subset) { + + if (parser == null) { + final InputSource empty = new InputSource(new EmptyReader()); + parser = Builder.findParser(false); + // parser = new org.apache.crimson.parser.XMLReaderImpl(); + // Now let's stop this parser from loading any external + // entities the subset references + parser.setEntityResolver(new EntityResolver() { + + public InputSource resolveEntity(String publicID, String systemID) { + return empty; + } + + }); + } + + String doc = ""; + try { + InputSource source = new InputSource(new StringReader(doc)); + // just to make sure relative URLs can be resolved; don't + // actually need to connect to this; the EntityResolver + // prevents that + source.setSystemId("http://www.example.org/"); + parser.parse(source); + } + catch (SAXException ex) { + IllegalDataException idex = new IllegalDataException( + "Malformed internal DTD subset: " + ex.getMessage(), ex); + idex.setData(subset); + throw idex; + } + catch (IOException ex) { + throw new RuntimeException("BUG: I don't think this can happen"); + } + + } + + + // A reader that immediately returns end of stream. This is a great + // big hack to avoid reading anything when setting the internal + // DTD subset. I could use the + // http://xml.org/sax/features/external-parameter-entities SAX + // feature, but many parsers don't reliably implement that so + // instead we simply pretend that all URLs point to empty files. + private static class EmptyReader extends Reader { + + public int read(char[] text, int start, int length) throws IOException { + return -1; + } + + public void close() {} + + } + + +} \ No newline at end of file diff --git a/src/nu/xom/WellformednessException.java b/src/nu/xom/WellformednessException.java new file mode 100644 index 0000000..1f2ae8c --- /dev/null +++ b/src/nu/xom/WellformednessException.java @@ -0,0 +1,67 @@ +/* Copyright 2002-2005 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom; + +/** + *

    + * Indicates an attempt to do or create something that could not + * possibly be serialized in a namespace well-formed XML 1.0 document. + *

    + + * @author Elliotte Rusty Harold + * @version 1.1b3 + * + */ +public class WellformednessException extends XMLException { + + + private static final long serialVersionUID = -4268754263017704202L; + + + /** + *

    + * Creates a new WellformednessException + * with a detail message. + *

    + * + * @param message a string indicating the specific problem + */ + public WellformednessException(String message) { + super(message); + } + + + /** + *

    + * Creates a new WellformednessException + * with a detail message and an underlying root cause. + *

    + * + * @param message a string indicating the specific problem + * @param cause the original cause of this exception + */ + public WellformednessException(String message, Throwable cause) { + super(message, cause); + } + + +} diff --git a/src/nu/xom/XML1_0Parser.java b/src/nu/xom/XML1_0Parser.java new file mode 100644 index 0000000..fff1d38 --- /dev/null +++ b/src/nu/xom/XML1_0Parser.java @@ -0,0 +1,52 @@ +/* Copyright 2004, 2009 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ +package nu.xom; + +import org.apache.xerces.parsers.SAXParser; +import org.apache.xerces.parsers.DTDConfiguration; + +/** + *

    + * This class is used by the Builder to prevent Xerces + * from accepting XML 1.1 documents. When using regular Xerces + * (org.apache.xerces.parsers.SAXParser) XOM verifies + * everything. When using this subclass, XOM will rely on Xerces + * to verify the rules, and skip its own verification checks. + *

    + * + *

    + * This class does not support schema validation. If you want to use + * the W3C XML Schema Language, you'll need to download and install + * the full version of Xerces from http://xml.apache.org. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.2.2b2 + * + */ +class XML1_0Parser extends SAXParser { + + XML1_0Parser() { + super(new DTDConfiguration()); + } + +} diff --git a/src/nu/xom/XMLException.java b/src/nu/xom/XMLException.java new file mode 100644 index 0000000..b318a16 --- /dev/null +++ b/src/nu/xom/XMLException.java @@ -0,0 +1,132 @@ +/* Copyright 2002-2005 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + + +package nu.xom; + +/** + *

    + * The generic superclass for most runtime exceptions thrown in + * nu.xom. The general principle followed is that + * anything that can normally be detected by testing such as + * using spaces in an element name is a runtime exception. + * Exceptions that depend on environmental conditions, + * such as might occur when parsing an external file, + * are checked exceptions, because these depend on variable input, + * and thus all problems may not be detected during testing. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.1b3 + * + */ +public class XMLException extends RuntimeException { + + + private static final long serialVersionUID = -4497254051626978523L; + + private Throwable cause; + + + /** + *

    + * Creates a new XMLException + * with the specified detail message + * and an underlying root cause. + *

    + * + * @param message information about the cause of the exception + * @param cause the nested exception that caused this exception + */ + public XMLException(String message, Throwable cause) { + super(message); + this.initCause(cause); + } + + + /** + *

    + * Creates a new XMLException with + * the specified detail message. + *

    + * + * @param message information about the cause of the exception + */ + public XMLException(String message) { + super(message); + } + + + /** + *

    + * Return the original cause that led to this exception, + * or null if there was no original exception. + *

    + * + * @return the root cause of this exception + */ + public Throwable getCause() { + return this.cause; + } + + + // null is insufficient for detecting an uninitialized cause. + // The cause may be set to null which may not then be reset. + private boolean causeSet = false; + + + /** + *

    + * Sets the root cause of this exception. This may + * only be called once. Subsequent calls throw an + * IllegalStateException. + *

    + * + *

    + * This method is unnecessary in Java 1.4 where it could easily be + * inherited from the superclass. However, including it here + * allows this method to be used in Java 1.3 and earlier. + *

    + * + * @param cause the root cause of this exception + * + * @return this XMLException + * + * @throws IllegalArgumentException if the cause is this exception + * (An exception cannot be its own cause.) + * @throws IllegalStateException if this method is called twice + */ + public final Throwable initCause(Throwable cause) { + + if (causeSet) { + throw new IllegalStateException("Can't overwrite cause"); + } + else if (cause == this) { + throw new IllegalArgumentException("Self-causation not permitted"); + } + else this.cause = cause; + causeSet = true; + return this; + + } + + +} diff --git a/src/nu/xom/XOMHandler.java b/src/nu/xom/XOMHandler.java new file mode 100644 index 0000000..49fc571 --- /dev/null +++ b/src/nu/xom/XOMHandler.java @@ -0,0 +1,984 @@ +/* Copyright 2002-2006, 2009, 2014, 2018 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + + +package nu.xom; + +import java.util.ArrayList; + +import org.xml.sax.ContentHandler; +import org.xml.sax.DTDHandler; +import org.xml.sax.Locator; +import org.xml.sax.SAXException; +import org.xml.sax.ext.DeclHandler; +import org.xml.sax.ext.LexicalHandler; + +/** + * @author Elliotte Rusty Harold + * @version 1.2.11 + * + */ +class XOMHandler + implements ContentHandler, LexicalHandler, DeclHandler, DTDHandler { + + protected Document document; + protected String documentBaseURI; + + // parent is never null. It is the node we're adding children + // to. current corresponds to the most recent startElement() + // method and may be null if we've skipped it (makeElement + // returned null.) If we didn't skip it, then parent and + // current should be the same node. + protected ParentNode parent; + protected ParentNode current; + protected ArrayList parents; + protected boolean inProlog; + protected boolean inDTD; + protected int position; // current number of items in prolog + protected Locator locator; + protected DocType doctype; + protected StringBuffer internalDTDSubset; + protected NodeFactory factory; + boolean usingCrimson = false; + + + XOMHandler(NodeFactory factory) { + this.factory = factory; + } + + + public void setDocumentLocator(Locator locator) { + this.locator = locator; + } + + + Document getDocument() { + return document; + } + + + // See http://www.servlets.com/archive/servlet/ReadMsg?msgId=554071&listName=jdom-interest + // This method is called to avoid leaking document sized memory + // when a Builder is not immediately reused + void freeMemory() { + document = null; + parent = null; + current = null; + parents = null; + locator = null; + doctype = null; + internalDTDSubset = null; + System.gc(); + } + + + public void startDocument() { + + inDTD = false; + document = factory.startMakingDocument(); + parent = document; + current = document; + parents = new ArrayList(); + parents.add(document); + inProlog = true; + position = 0; + textString = null; + doctype = null; + if (locator != null) { + documentBaseURI = locator.getSystemId(); + // According to the XML spec, + // "It is an error for a fragment identifier + // (beginning with a # character) to be part of a system identifier" + // but some parsers including Xerces seem to get this wrong, so we'll + document.setBaseURI(documentBaseURI); + } + buffer = null; + + } + + + public void endDocument() { + factory.finishMakingDocument(document); + parents.remove(parents.size()-1); + } + + + public void startElement(String namespaceURI, String localName, + String qualifiedName, org.xml.sax.Attributes attributes) throws SAXException { + + flushText(); + Element element; + if (parent != document) { + element = factory.startMakingElement(qualifiedName, namespaceURI); + } + else { // root + element = factory.makeRootElement(qualifiedName, namespaceURI); + if (element == null) { // null root; that's a no-no + throw new NullPointerException( + "Factory failed to create root element." + ); + } + document.setRootElement(element); + inProlog = false; + } + + current = element; + // Need to push this, even if it's null + parents.add(element); + + if (element != null) { // wasn't filtered out + if (parent != document) { + // a.k.a. parent not instanceof Document + parent.appendChild(element); + } + // This is optimized for the very common case where + // everything in the document has the same actual base URI. + // It may add redundant base URIs in cases like XInclude + // where different parts of the document have different + // base URIs. + if (locator != null) { + String baseURI = locator.getSystemId(); + if (baseURI != null && !baseURI.equals(documentBaseURI)) { + element.setActualBaseURI(baseURI); + } + } + + // Attach the attributes; this must be done before the + // namespaces are attached. + // XXX pull out length + + // XXX we've got a pretty good guess at how many attributes there + // will be here; we should ensureCapacity up to that length + for (int i = 0; i < attributes.getLength(); i++) { + String qName = attributes.getQName(i); + if (qName.startsWith("xmlns:") || qName.equals("xmlns")) { + continue; + } + else { + String namespace = attributes.getURI(i); + String value = attributes.getValue(i); + Nodes nodes = factory.makeAttribute( + qName, + namespace, + value, + convertStringToType(attributes.getType(i)) + ); + int numberChildren = 0; + for (int j=0; j < nodes.size(); j++) { + Node node = nodes.get(j); + if (node.isAttribute()) { + factory.addAttribute(element, (Attribute) node); + } + else { + factory.insertChild(element, node, numberChildren++); + } + } + } + } + + // Attach the namespaces + for (int i = 0; i < attributes.getLength(); i++) { + String qName = attributes.getQName(i); + if (qName.startsWith("xmlns:")) { + String namespaceName = attributes.getValue(i); + String namespacePrefix = qName.substring(6); + String currentValue + = element.getNamespaceURI(namespacePrefix); + if (!namespaceName.equals(currentValue) && ! namespacePrefix.equals(element.getNamespacePrefix())) { + element.addNamespaceDeclaration( + namespacePrefix, namespaceName); + } + } + else if (qName.equals("xmlns")) { + String namespaceName = attributes.getValue(i); + String namespacePrefix = ""; + String currentValue + = element.getNamespaceURI(namespacePrefix); + if (!namespaceName.equals(currentValue) && ! "".equals(element.getNamespacePrefix())) { + element.addNamespaceDeclaration(namespacePrefix, + namespaceName); + } + } + } + + // this is the new parent + parent = element; + } + + } + + + public void endElement( + String namespaceURI, String localName, String qualifiedName) { + + // If we're immediately inside a skipped element + // we need to reset current to null, not to the parent + current = (ParentNode) parents.remove(parents.size()-1); + flushText(); + + if (current != null) { + parent = current.getParent(); + Nodes result = factory.finishMakingElement((Element) current); + + // Optimization for default case where result only contains current + if (result.size() != 1 || result.get(0) != current) { + if (!parent.isDocument()) { + // allow factories to detach the element itself in + // finishMakingElement + int childCount = parent.getChildCount(); + try { + parent.removeChild(childCount - 1); + } + catch (IndexOutOfBoundsException ex) { + throw new XMLException( + "Factory detached element in finishMakingElement()", + ex); + } + for (int i=0; i < result.size(); i++) { + Node node = result.get(i); + if (node.isAttribute()) { + ((Element) parent).addAttribute((Attribute) node); + } + else { + parent.appendChild(node); + } + } + } + else { // root element + Document doc = (Document) parent; + Element currentRoot = doc.getRootElement(); + boolean beforeRoot = true; + for (int i=0; i < result.size(); i++) { + Node node = result.get(i); + if (node.isElement()) { + if (node != currentRoot) { + if (!beforeRoot) { + // already set root, oops + throw new IllegalAddException("Factory returned multiple roots"); + } + doc.setRootElement((Element) node); + } + beforeRoot = false; + } + else if (beforeRoot) { + doc.insertChild(node, doc.indexOf(doc.getRootElement())); + } + else { + doc.appendChild(node); + } + } + if (beforeRoot) { + // somebody tried to replace the root element with + // no element at all. That's a no-no + throw new WellformednessException( + "Factory attempted to remove the root element"); + } + } + } + } + + } + + + static Attribute.Type convertStringToType(String saxType) { + + if (saxType.equals("CDATA")) return Attribute.Type.CDATA; + if (saxType.equals("ID")) return Attribute.Type.ID; + if (saxType.equals("IDREF")) return Attribute.Type.IDREF; + if (saxType.equals("IDREFS")) return Attribute.Type.IDREFS; + if (saxType.equals("NMTOKEN")) return Attribute.Type.NMTOKEN; + if (saxType.equals("NMTOKENS")) return Attribute.Type.NMTOKENS; + if (saxType.equals("ENTITY")) return Attribute.Type.ENTITY; + if (saxType.equals("ENTITIES")) return Attribute.Type.ENTITIES; + if (saxType.equals("NOTATION")) return Attribute.Type.NOTATION; + + // non-standard but some parsers use this + if (saxType.equals("ENUMERATION")) { + return Attribute.Type.ENUMERATION; + } + if (saxType.startsWith("(")) return Attribute.Type.ENUMERATION; + + return Attribute.Type.UNDECLARED; + + } + + + protected String textString = null; + protected StringBuffer buffer = null; + + public void characters(char[] text, int start, int length) throws SAXException { + if (length <= 0) return; + if (textString == null) textString = new String(text, start, length); + else { + if (buffer == null) buffer = new StringBuffer(textString); + buffer.append(text, start, length); + } + if (finishedCDATA) inCDATA = false; + + } + + + // accumulate all text that's in the buffer into a text node + private void flushText() { + + if (buffer != null) { + textString = buffer.toString(); + buffer = null; + } + + if (textString != null) { + Nodes result; + if (!inCDATA) { + result = factory.makeText(textString); + } + else { + result = factory.makeCDATASection(textString); + } + for (int i=0; i < result.size(); i++) { + Node node = result.get(i); + if (node.isAttribute()) { + ((Element) parent).addAttribute((Attribute) node); + } + else { + parent.appendChild(node); + } + } + textString = null; + } + inCDATA = false; + finishedCDATA = false; + + } + + + public void ignorableWhitespace( + char[] text, int start, int length) throws SAXException { + characters(text, start, length); + } + + + public void processingInstruction(String target, String data) throws SAXException { + + if (!inDTD) flushText(); + if (inDTD && !inInternalSubset()) return; + Nodes result = factory.makeProcessingInstruction(target, data); + + for (int i = 0; i < result.size(); i++) { + Node node = result.get(i); + if (!inDTD) { + if (inProlog) { + parent.insertChild(node, position); + position++; + } + else { + if (node.isAttribute()) { + ((Element) parent).addAttribute((Attribute) node); + } + else parent.appendChild(node); + } + } + else { + if (node.isProcessingInstruction() || node.isComment()) { + internalDTDSubset.append(" "); + internalDTDSubset.append(node.toXML()); + internalDTDSubset.append("\n"); + } + else { + throw new XMLException("Factory tried to put a " + + node.getClass().getName() + + " in the internal DTD subset"); + } + } + } + + } + + + // XOM handles this with attribute values; not prefix mappings + public void startPrefixMapping(String prefix, String uri) {} + public void endPrefixMapping(String prefix) {} + + public void skippedEntity(String name) { + + // Xerces 2.7 now calls this method in the DTD + // for parameter entities it doesn't resolve. We can ignore these. + if (name.startsWith("%")) return; + flushText(); + throw new XMLException("Could not resolve entity " + name); + + } + + + // LexicalHandler events + public void startDTD(String rootName, String publicID, + String systemID) throws SAXException { + + inDTD = true; + Nodes result = factory.makeDocType(rootName, publicID, systemID); + for (int i = 0; i < result.size(); i++) { + Node node = result.get(i); + document.insertChild(node, position); + position++; + if (node.isDocType()) { + DocType doctype = (DocType) node; + internalDTDSubset = new StringBuffer(); + this.doctype = doctype; + } + } + + } + + + public void endDTD() { + + inDTD = false; + if (doctype != null) { + doctype.setInternalDTDSubset(internalDTDSubset.toString()); + } + + } + + + protected boolean inExternalSubset = false; + + // We have a problem here. Xerces gets this right, + // but Crimson and possibly other parsers don't properly + // report these entities, or perhaps just not tag them + // with [dtd] like they're supposed to. + public void startEntity(String name) { + if (name.equals("[dtd]")) inExternalSubset = true; + } + + + public void endEntity(String name) { + if (name.equals("[dtd]")) inExternalSubset = false; + } + + + protected boolean inCDATA = false; + protected boolean finishedCDATA = false; + + + public void startCDATA() { + if (textString == null) inCDATA = true; + finishedCDATA = false; + } + + + public void endCDATA() { + finishedCDATA = true; + } + + + public void comment(char[] text, int start, int length) throws SAXException { + + if (!inDTD) flushText(); + if (inDTD && !inInternalSubset()) return; + + Nodes result = factory.makeComment(new String(text, start, length)); + + for (int i = 0; i < result.size(); i++) { + Node node = result.get(i); + if (!inDTD) { + if (inProlog) { + parent.insertChild(node, position); + position++; + } + else { + if (node instanceof Attribute) { + ((Element) parent).addAttribute((Attribute) node); + } + else parent.appendChild(node); + } + } + else { + if (node.isComment() || node.isProcessingInstruction()) { + internalDTDSubset.append(" "); + internalDTDSubset.append(node.toXML()); + internalDTDSubset.append("\n"); + } + else { + throw new XMLException("Factory tried to put a " + + node.getClass().getName() + + " in the internal DTD subset"); + } + } + } + + } + + + // TODO(elharo) an untrusted parser could push in bad names and + // values in declaration in the internal DTD subset. + // Possibly declarations should be created in the factory too. + public void elementDecl(String name, String model) { + + if (inInternalSubset() && doctype != null) { + internalDTDSubset.append(" 0 && model.indexOf('|') > 0) { + if (model.endsWith(")")) { + internalDTDSubset.append('*'); + } + } + internalDTDSubset.append(">\n"); + } + + } + + + // This method only behaves properly when called from the DeclHandler + // and DTDHandler callbacks; i.e. from inside the DTD; + // It is not intended for use anywhere in the document. + protected boolean inInternalSubset() { + + if (!usingCrimson) { + return !inExternalSubset; + } + String currentURI = locator.getSystemId(); + if (currentURI == this.documentBaseURI) return true; + if (currentURI == null) return false; + if (currentURI.equals(this.documentBaseURI)) return true; + return false; + + } + + + public void attributeDecl(String elementName, + String attributeName, String type, String mode, + String defaultValue) { + + // workaround for Crimson bug + if (type.startsWith("NOTATION ")) { + if (type.indexOf('(') == -1 && ! type.endsWith(")")) { + type = "NOTATION (" + type.substring("NOTATION ".length()) + ")"; + } + } + + if (inInternalSubset() && doctype != null) { + internalDTDSubset.append(" \n"); + } + + } + + + public void internalEntityDecl(String name, + String value) { + + if (inInternalSubset() && doctype != null) { + internalDTDSubset.append(" \n"); + } + + } + + + public void externalEntityDecl(String name, + String publicID, String systemID) { + + if (inInternalSubset() && doctype != null) { + internalDTDSubset.append(" \n"); + + } + + } + + + public void notationDecl(String name, String publicID, + String systemID) { + + if (systemID != null) { + systemID = escapeReservedCharactersInDeclarations(systemID); + } + + if (inInternalSubset() && doctype != null) { + internalDTDSubset.append(" \n"); + } + + } + + + public void unparsedEntityDecl(String name, String publicID, + String systemID, String notationName) { + + // escapable characters???? + if (inInternalSubset() && doctype != null) { + internalDTDSubset.append(" \n"); + } + + } + + + private static String escapeReservedCharactersInDeclarations(String s) { + + int length = s.length(); + StringBuffer result = new StringBuffer(length); + for (int i = 0; i < length; i++) { + char c = s.charAt(i); + switch (c) { + case '\r': + result.append(" "); + break; + case 14: + // placeholder for table lookup + break; + case 15: + // placeholder for table lookup + break; + case 16 : + // placeholder for table lookup + break; + case 17: + // placeholder for table lookup + break; + case 18: + // placeholder for table lookup + break; + case 19: + // placeholder for table lookup + break; + case 20: + // placeholder for table lookup + break; + case 21: + // placeholder for table lookup + break; + case 22: + // placeholder for table lookup + break; + case 23: + // placeholder for table lookup + break; + case 24: + // placeholder for table lookup + break; + case 25: + // placeholder for table lookup + break; + case 26: + // placeholder for table lookup + break; + case 27: + // placeholder for table lookup + break; + case 28: + // placeholder for table lookup + break; + case 29: + // placeholder for table lookup + break; + case 30: + // placeholder for table lookup + break; + case 31: + // placeholder for table lookup + break; + case ' ': + result.append(' '); + break; + case '!': + result.append('!'); + break; + case '\"': + result.append("""); + break; + case '#': + result.append('#'); + break; + case '$': + result.append('$'); + break; + case '%': + result.append("%"); + break; + case '&': + result.append("&"); + break; + default: + result.append(c); + } + } + + return result.toString(); + + } + + + private static String escapeReservedCharactersInDefaultAttributeValues(String s) { + + int length = s.length(); + StringBuffer result = new StringBuffer(length); + for (int i = 0; i < length; i++) { + char c = s.charAt(i); + switch (c) { + case '\r': + result.append(" "); + break; + case 14: + // placeholder for table lookup + break; + case 15: + // placeholder for table lookup + break; + case 16 : + // placeholder for table lookup + break; + case 17: + // placeholder for table lookup + break; + case 18: + // placeholder for table lookup + break; + case 19: + // placeholder for table lookup + break; + case 20: + // placeholder for table lookup + break; + case 21: + // placeholder for table lookup + break; + case 22: + // placeholder for table lookup + break; + case 23: + // placeholder for table lookup + break; + case 24: + // placeholder for table lookup + break; + case 25: + // placeholder for table lookup + break; + case 26: + // placeholder for table lookup + break; + case 27: + // placeholder for table lookup + break; + case 28: + // placeholder for table lookup + break; + case 29: + // placeholder for table lookup + break; + case 30: + // placeholder for table lookup + break; + case 31: + // placeholder for table lookup + break; + case ' ': + result.append(' '); + break; + case '!': + result.append('!'); + break; + case '\"': + result.append("""); + break; + case '#': + result.append('#'); + break; + case '$': + result.append('$'); + break; + case '%': + result.append("%"); + break; + case '&': + result.append("&"); + break; + case '\'': + result.append('\''); + break; + case '(': + result.append('('); + break; + case ')': + result.append(')'); + break; + case '*': + result.append('*'); + break; + case '+': + result.append('+'); + break; + case ',': + result.append(','); + break; + case '-': + result.append('-'); + break; + case '.': + result.append('.'); + break; + case '/': + result.append('/'); + break; + case '0': + result.append('0'); + break; + case '1': + result.append('1'); + break; + case '2': + result.append('2'); + break; + case '3': + result.append('3'); + break; + case '4': + result.append('4'); + break; + case '5': + result.append('5'); + break; + case '6': + result.append('6'); + break; + case '7': + result.append('7'); + break; + case '8': + result.append('8'); + break; + case '9': + result.append('9'); + break; + case ':': + result.append(':'); + break; + case ';': + result.append(';'); + break; + case '<': + result.append("<"); + break; + default: + result.append(c); + } + } + + return result.toString(); + + } + + +} \ No newline at end of file diff --git a/src/nu/xom/XPathContext.java b/src/nu/xom/XPathContext.java new file mode 100644 index 0000000..b2f545c --- /dev/null +++ b/src/nu/xom/XPathContext.java @@ -0,0 +1,175 @@ +/* Copyright 2005, 2006, 2012, 2019 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom; + +import java.util.HashMap; +import java.util.Map; + +import org.jaxen.NamespaceContext; + +/** + * + *

    + * Provides namespace prefix bindings for use in an XPath expression. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.3.1 + * + */ +public final class XPathContext { + + + private Map namespaces = new HashMap(); + + + /** + *

    + * Creates a new XPath context that binds the specified prefix to + * the specified URI. The xml + * prefix is also bound to the URI + * http://www.w3.org/XML/1998/namespace. + *

    + * + * @param prefix the prefix to bind + * @param uri the namespace URI the prefix is bound to + */ + public XPathContext(String prefix, String uri) { + this(); + addNamespace(prefix, uri); + } + + + /** + *

    + * Creates a new XPath context that binds the xml + * prefix to the URI + * http://www.w3.org/XML/1998/namespace. + *

    + */ + public XPathContext() { + addNamespace("xml", Namespace.XML_NAMESPACE); + } + + + /** + *

    + * Binds the specified prefix to the specified namespace URI. + * If the prefix is already bound in this context, the new URI + * replaces the old URI. Binding a prefix to null removes the + * declaration. The binding of the xml prefix + * may not be changed. + *

    + * + * @param prefix the prefix to bind + * @param uri the namespace URI the prefix is bound to + * + * @throws NamespaceConflictException if the prefix is + * xml and the URI is not + * http://www.w3.org/XML/1998/namespace or the + * prefix is the empty string + * @throws NullPointerException if the prefix is null + * + */ + public void addNamespace(String prefix, String uri) { + + if ("xml".equals(prefix) + && !Namespace.XML_NAMESPACE.equals(uri)) { + throw new NamespaceConflictException( + "Wrong namespace URI for xml prefix: " + uri); + } + if ("".equals(uri)) uri = null; + if (prefix == null) { + throw new NullPointerException("Prefixes used in XPath expressions cannot be null"); + } + else if ("".equals(prefix)){ + throw new NamespaceConflictException( + "XPath expressions do not use the default namespace"); + } + Verifier.checkNCName(prefix); + + // should there be a separate remove method???? + if (uri == null) { + namespaces.remove(prefix); + } + else { + namespaces.put(prefix, uri); + } + + } + + + // should this be a Node rather than an Element???? + /** + *

    + * Creates a new XPath context that contains all the namespace + * bindings in scope on the element. Changing + * the prefixes in scope on the element after the context + * is returned does not change the context. + *

    + * + * @param element the element whose namespace bindings are copied + * + * @return all the namespace prefix mappings + * in scope on the element + */ + public static XPathContext makeNamespaceContext(Element element) { + + XPathContext context = new XPathContext(); + context.namespaces = element.getNamespacePrefixesInScope(); + return context; + + } + + + NamespaceContext getJaxenContext() { + return new JaxenNamespaceContext(); + } + + + private class JaxenNamespaceContext implements NamespaceContext { + + public String translateNamespacePrefixToUri(String prefix) { + return lookup(prefix); + } + + } + + /** + * Returns the namespace URI associated with a specified prefix in + * this context. It returns null if this prefix is not bound + * to a namespace in this context. The prefix is not checked + * for validity. If you pass in a prefix that is not an NCName, + * this method simply returns null. + * + * @param prefix the prefix to look up + * + * @return the namespace URI associated with the specified prefix in + * this context or null + */ + public String lookup(String prefix) { + if ("".equals(prefix)) return null; + return namespaces.get(prefix); + } + + +} diff --git a/src/nu/xom/XPathException.java b/src/nu/xom/XPathException.java new file mode 100644 index 0000000..ade0c68 --- /dev/null +++ b/src/nu/xom/XPathException.java @@ -0,0 +1,148 @@ +/* Copyright 2005 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ +package nu.xom; + +/** + *

    + * Indicates problems with XPath syntax or evaluation. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.1b3 + * + */ +public class XPathException extends RuntimeException { + + + private static final long serialVersionUID = 6362087755031657439L; + + private String expression; + private Throwable cause; + + + /** + *

    + * Creates a new XPathException + * with a detail message. + *

    + * + * @param message a string indicating the specific problem + */ + public XPathException(String message) { + super(message); + } + + + /** + *

    + * Creates a new IllegalNameException + * with a detail message and an underlying root cause. + *

    + * + * @param message a string indicating the specific problem + * @param cause the original cause of this exception + */ + public XPathException(String message, Throwable cause) { + super(message); + this.initCause(cause); + } + + + /** + *

    + * Return the original cause that led to this exception, + * or null if there was no original exception. + *

    + * + * @return the root cause of this exception + */ + public Throwable getCause() { + return this.cause; + } + + + // null is insufficient for detecting an uninitialized cause. + // The cause may be set to null which may not then be reset. + private boolean causeSet = false; + + + /** + *

    + * Sets the root cause of this exception. This may + * only be called once. Subsequent calls throw an + * IllegalStateException. + *

    + * + *

    + * This method is unnecessary in Java 1.4 where it could easily be + * inherited from the superclass. However, including it here + * allows this method to be used in Java 1.3 and earlier. + *

    + * + * @param cause the root cause of this exception + * + * @return this XMLException + * + * @throws IllegalArgumentException if the cause is this exception + * (An exception cannot be its own cause.) + * @throws IllegalStateException if this method is called twice + */ + public Throwable initCause(Throwable cause) { + + if (causeSet) { + throw new IllegalStateException("Can't overwrite cause"); + } + else if (cause == this) { + throw new IllegalArgumentException("Self-causation not permitted"); + } + else this.cause = cause; + causeSet = true; + return this; + + } + + + /** + *

    + * Sets the specific XPath expression that caused this exception. + *

    + * + * @param expression the XPath expression that caused the exception + */ + void setXPath(String expression) { + this.expression = expression; + } + + + /** + *

    + * Returns the specific XPath expression being evaluated when this + * excepiton was thrown. + *

    + * + * @return the XPath expression that caused the exception + */ + public String getXPath() { + return this.expression; + } + + +} diff --git a/src/nu/xom/XPathTypeException.java b/src/nu/xom/XPathTypeException.java new file mode 100644 index 0000000..8d71d58 --- /dev/null +++ b/src/nu/xom/XPathTypeException.java @@ -0,0 +1,70 @@ +/* Copyright 2005 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ +package nu.xom; + +/** + *

    + * Indicates that an XPath query returned a non-node-set. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.2.4 + * + */ +public class XPathTypeException extends XPathException { + + private static final long serialVersionUID = 1247817719683497718L; + + private final Object returnValue; + + + XPathTypeException(Object returnValue) { + // Irrelevant empty message because we override getMessage() below + super(""); + this.returnValue = returnValue; + } + + + /** + *

    + * This method usually returns the actual object returned by the + * query. However, XOM makes no guarantees about the type of this + * object. + *

    + * + * @return the query result + */ + public Object getReturnValue() { + return this.returnValue; + } + + public String getMessage() { + String xpath = getXPath(); + String type = returnValue.getClass().getName(); + if (xpath == null) { + return "XPath expression returned a " + type + " instead of a node-set."; + } else { + return "XPath expression " + xpath + " returned a " + type + " instead of a node-set."; + } + } + + +} diff --git a/src/nu/xom/benchmarks/BigText.java b/src/nu/xom/benchmarks/BigText.java new file mode 100644 index 0000000..7f3b0fc --- /dev/null +++ b/src/nu/xom/benchmarks/BigText.java @@ -0,0 +1,116 @@ +/* Copyright 2004 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.benchmarks; + +import java.io.IOException; +import java.text.DecimalFormat; + +import nu.xom.Builder; +import nu.xom.Document; +import nu.xom.Element; +import nu.xom.ParsingException; + +/** + *

    + * Simple memory benchmark focusing on a big document + * with lots of text covering all of Unicode. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.1.1b1 + * + */ +class BigText { + + static Document makeFullUnicode() { + + Element root = new Element("root"); + Document doc = new Document(root); + + StringBuffer sb = new StringBuffer(65536); + for (int i = 0x20; i <= 0xD7FF; i++) { + sb.append((char) i); + } + + // skip surrogates between 0xD800 and 0xDFFF + + for (int i = 0xE000; i <= 0xFFFD; i++) { + sb.append((char) i); + } + + System.gc(); + + // Plane-1 characters are tricky because Java + // strings encode them as surrogate pairs. First, fill + // a byte array with the characters from 1D100 to 1D1FF + // (the musical symbols) + for (int i = 0; i < 256; i++) { + // algorithm from RFC 2781 + int u = 0x1D100 + i; + int uprime = u - 0x10000; + int W1 = 0xD800; + int W2 = 0xDC00; + W2 = W2 | (uprime & 0x7FF ); + W1 = W1 | (uprime & 0xFF800); + sb.append( ((char) W1) ); + sb.append( ((char) W2) ); + } + + root.appendChild(sb.toString()); + + return doc; + + } + + public static void main(String[] args) + throws IOException, ParsingException { + + DecimalFormat format = new DecimalFormat(); + format.setMaximumFractionDigits(2); + Runtime r = Runtime.getRuntime(); + System.gc(); System.gc(); System.gc(); + long before = r.totalMemory() - r.freeMemory(); + Document doc; + if (args.length > 0) { + Builder builder = new Builder(); + doc = builder.build(args[0]); + builder = null; + } + else { + doc = makeFullUnicode(); + } + long after = r.totalMemory() - r.freeMemory(); + double usage = (after - before)/(1024.0*1024.0); + System.out.println("Memory used: " + + format.format(usage) + "M"); + System.gc(); System.gc(); System.gc(); + long postGC = r.totalMemory() - r.freeMemory(); + usage = (postGC - before)/(1024.0*1024.0); + System.out.println("Memory used after garbage collection: " + + format.format(usage) + "M"); + + // Make sure the document isn't prematurely garbage collected + System.out.println("Meaningless number: " + + doc.toXML().length()); + } + +} diff --git a/src/nu/xom/benchmarks/DOMConverterBench.java b/src/nu/xom/benchmarks/DOMConverterBench.java new file mode 100644 index 0000000..58db5b7 --- /dev/null +++ b/src/nu/xom/benchmarks/DOMConverterBench.java @@ -0,0 +1,122 @@ +/* Copyright 2004 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.benchmarks; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; + +import org.w3c.dom.DOMImplementation; + +import nu.xom.Builder; +import nu.xom.Document; +import nu.xom.Element; +import nu.xom.converters.DOMConverter; + +/** + *

    + * Benchmark the performance of DOMConverter on a large, record-like + * document. For convenience the document is formed by copying one + * record 30,000 times. Running this benchmark normally requires + * increasing Java's default heap size. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.0 + * + */ +class DOMConverterBench { + + private static String elementData = "\n" + + "476w4l73\n" + + "6729\n" + + "N-Vgj / Yvv Zjysbeu Wwvmk @Yhwc 3=\n" + + "gmaj/zhffyi\n" + + " RUWU: 7124 GR0Z: 20\n" + + "Njm Qmskwo\n" + + "Tgnnja Fvwlc\n" + + "Iii Pbou Ia Yniugiq\n" + + "Vzbpqhud Fm Ptwv\n" + + "Fu Ffe Uxvkf\n" + + "Kw Biv Bh Ngu Rpaw\n" + + "Pnzm Vj Qav Tapnie\n" + + "Pawqj Dkqxaslb\n" + + "Hsb Aotlml Faa Vpbotugw\n" + + "Wvex Wwlrr'o Rnbklfz\n" + + "Sdu Edrbo\n" + + "Urvr Ot Udbq\n" + + "Prgovq Mrm\n" + + "Nxr'l Zgoh Zvtl\n" + + "Vkcixopj Ccdo\n" + + "Dlx'h Rclq Mtxiba Gim\n" + + "Bepj Hbrvmgp\n" + + "Khxh Anpl\n" + + "\n"; + + + public static void main(String[] args) throws Exception { + + Runtime r = Runtime.getRuntime(); + long memory = r.totalMemory() - r.freeMemory(); + + System.out.println("Initial: " + memory); + + Builder builder = new Builder(); + Document dataDoc = builder.build(elementData, "http://www.example.com"); + Element root = new Element("root"); + Document doc = new Document(root); + Element dataElement = dataDoc.getRootElement(); + + + for (int i = 0; i < 30000; i++) { + root.appendChild(dataElement.copy()); + } + + System.out.println("Built document: " + + (r.totalMemory() - r.freeMemory())/(1024.0*1024) + "MB"); + System.gc(); System.gc(); System.gc(); + System.out.println("After garbage collection: " + + (r.totalMemory() - r.freeMemory())/(1024.0*1024) + "MB"); + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + factory.setNamespaceAware(true); + DocumentBuilder jaxp = factory.newDocumentBuilder(); + DOMImplementation impl = jaxp.getDOMImplementation(); + + System.gc(); System.gc(); System.gc(); + // Warm up HotSpot + DOMConverter.convert(doc, impl); + DOMConverter.convert(doc, impl); + DOMConverter.convert(doc, impl); + System.out.println("Warmed up: " + + ((r.totalMemory() - r.freeMemory())/(1024.0*1024)) + "MB"); + System.gc(); System.gc(); System.gc(); + // System.out.println("Turn on profiling and hit return"); + // System.in.read(); + + // time + long pre = System.currentTimeMillis(); + DOMConverter.convert(doc, impl); + long post = System.currentTimeMillis(); + System.out.println((post - pre)/1000.0 + "s to convert document"); + + } + +} diff --git a/src/nu/xom/benchmarks/DocumentModifier.java b/src/nu/xom/benchmarks/DocumentModifier.java new file mode 100644 index 0000000..a9b2623 --- /dev/null +++ b/src/nu/xom/benchmarks/DocumentModifier.java @@ -0,0 +1,179 @@ +/* Copyright 2002-2004 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.benchmarks; + +import java.io.BufferedInputStream; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; + +import nu.xom.Attribute; +import nu.xom.Builder; +import nu.xom.DocType; +import nu.xom.Document; +import nu.xom.Element; +import nu.xom.Node; +import nu.xom.ParentNode; +import nu.xom.Serializer; +import nu.xom.Text; +import nu.xom.ParsingException; + + +/** + * + *

    + * Based on Dennis Sosnoski's benchmarks: + *

    + * + *
    + * This test looks at the time required to systematically + * modify the constructed document representation. It walks + * the representation, deleting all isolated whitespace content + * and wrapping each non-whitespace content string with a new, + * added, element. It also adds an attribute to each element of + * the original document that contained non-whitespace content. + * This test is intended to represent the performance of the + * document models across a range of modifications to the + * documents. As with the walk times, the modify times are + * considerably faster than the parse times. As a result, + * the parse times are going to be more important for applications + * that make only a single pass through each parsed document. + *
    + * + * @author Elliotte Rusty Harold + * @version 1.0 + * + */ +class DocumentModifier { + + public static void main(String[] args) { + + if (args.length <= 0) { + System.out.println( + "Usage: java nu.xom.benchmarks.DocumentModifier URL" + ); + return; + } + + DocumentModifier iterator = new DocumentModifier(); + Builder parser = new Builder(); + try { + // Separate out the basic I/O by parsing document, + // and then serializing into a byte array. + // This caches the and removes any dependence on the DTD. + Document doc = parser.build(args[0]); + DocType type = doc.getDocType(); + if (type != null) { + doc.removeChild(type); + } + ByteArrayOutputStream out = new ByteArrayOutputStream(); + Serializer serializer = new Serializer(out); + serializer.write(doc); + serializer.flush(); + out.close(); + byte[] data = out.toByteArray(); + + warmup(parser, iterator, data, 5); + InputStream raw = new BufferedInputStream( + new ByteArrayInputStream(data) + ); + + // Try to avoid garbage collection pauses + System.gc(); System.gc(); System.gc(); + + long prebuild = System.currentTimeMillis(); + + // Read the entire document into memory + Document document = parser.build(raw); + long postbuild = System.currentTimeMillis(); + + System.out.println((postbuild - prebuild) + + "ms to build the document"); + + long prewalk = System.currentTimeMillis(); + performTask(iterator, document); + long postwalk = System.currentTimeMillis(); + + System.out.println((postwalk - prewalk) + + "ms to modify the document"); + + } + catch (IOException ex) { + System.out.println(ex); + } + catch (ParsingException ex) { + System.out.println(ex); + } + + } // end main + + private static void warmup(Builder builder, + DocumentModifier iterator, byte[] data, int numPasses) + throws IOException, ParsingException { + + InputStream in = new BufferedInputStream( + new ByteArrayInputStream(data)); + Document doc = builder.build(in); + for (int i = 0; i < numPasses; i++) { + performTask(iterator, doc); + } + } + + private static void performTask(DocumentModifier iterator, Document document) + throws IOException { + iterator.followNode(document); + } + + // note use of recursion + public void followNode(Node node) throws IOException { + + // Chances are most of the time is spent in the instanceof test + if (node instanceof Text) { + if (node.getValue().trim().length() == 0) { + node.detach(); + } + else { + Element dummy = new Element("dummy"); + ParentNode parent = node.getParent(); + parent.insertChild(dummy, parent.indexOf(node)); + node.detach(); + dummy.appendChild(node); + } + return; + } + else if (node instanceof Element){ + Element element = (Element) node; + element.addAttribute(new Attribute("class", "original")); + for (int i = 0; i < node.getChildCount(); i++) { + followNode(node.getChild(i)); + } + } + else { + for (int i = 0; i < node.getChildCount(); i++) { + followNode(node.getChild(i)); + } + } + + } + +} \ No newline at end of file diff --git a/src/nu/xom/benchmarks/ElementCopyBench.java b/src/nu/xom/benchmarks/ElementCopyBench.java new file mode 100644 index 0000000..0fa1b8e --- /dev/null +++ b/src/nu/xom/benchmarks/ElementCopyBench.java @@ -0,0 +1,106 @@ +/* Copyright 2004, 2005 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.benchmarks; + +import nu.xom.Builder; +import nu.xom.Document; +import nu.xom.Element; + +/** + *

    + * Benchmark the performance of copying elements, documents, + * toXML, and getValue on a large, record-like + * document. The document is formed by copying one + * record 30,000 times. Running this benchmark normally requires + * increasing Java's default heap size. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.1d5 + * + */ +class ElementCopyBench { + + private static String elementData = "\n" + + "476w4l73\n" + + "6729\n" + + "N-Vgj / Yvv Zjysbeu Wwvmk @Yhwc 3=\n" + + "gmaj/zhffyi\n" + + " RUWU: 7124 GR0Z: 20\n" + + "Njm Qmskwo\n" + + "Tgnnja Fvwlc\n" + + "Iii Pbou Ia Yniugiq\n" + + "Vzbpqhud Fm Ptwv\n" + + "Fu Ffe Uxvkf\n" + + "Kw Biv Bh Ngu Rpaw\n" + + "Pnzm Vj Qav Tapnie\n" + + "Pawqj Dkqxaslb\n" + + "Hsb Aotlml Faa Vpbotugw\n" + + "Wvex Wwlrr'o Rnbklfz\n" + + "Sdu Edrbo\n" + + "Urvr Ot Udbq\n" + + "Prgovq Mrm\n" + + "Nxr'l Zgoh Zvtl\n" + + "Vkcixopj Ccdo\n" + + "Dlx'h Rclq Mtxiba Gim\n" + + "Bepj Hbrvmgp\n" + + "Khxh Anpl\n" + + "\n"; + + + public static void main(String[] args) throws Exception { + + Builder builder = new Builder(); + Document dataDoc = builder.build(elementData, "http://www.example.com"); + Element root = new Element("root"); + Document doc = new Document(root); + Element dataElement = dataDoc.getRootElement(); + + long pre = System.currentTimeMillis(); + for (int i = 0; i < 20000; i++) { + root.appendChild(dataElement.copy()); + } + long post = System.currentTimeMillis(); + System.out.println((post - pre)/1000.0 + "s to build document by copying"); + + pre = System.currentTimeMillis(); + doc.copy(); + post = System.currentTimeMillis(); + System.out.println((post - pre)/1000.0 + "s to copy entire Document"); + + System.gc(); System.gc(); System.gc(); + + pre = System.currentTimeMillis(); + doc.getValue(); + post = System.currentTimeMillis(); + System.out.println((post - pre)/1000.0 + "s to get value of entire Document"); + + System.gc(); System.gc(); System.gc(); + + pre = System.currentTimeMillis(); + doc.toXML(); + post = System.currentTimeMillis(); + System.out.println((post - pre)/1000.0 + "s to get XML for entire Document"); + + } + +} diff --git a/src/nu/xom/benchmarks/FastReproducer.java b/src/nu/xom/benchmarks/FastReproducer.java new file mode 100644 index 0000000..e31ef77 --- /dev/null +++ b/src/nu/xom/benchmarks/FastReproducer.java @@ -0,0 +1,185 @@ +/* Copyright 2002-2005 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.benchmarks; + +import java.io.IOException; + +import nu.xom.Attribute; +import nu.xom.Builder; +import nu.xom.Comment; +import nu.xom.DocType; +import nu.xom.Document; +import nu.xom.Element; +import nu.xom.Node; +import nu.xom.ParsingException; +import nu.xom.ProcessingInstruction; +import nu.xom.Text; + +/** + * + *

    + * Benchmarks building a tree in memory by copying an existing document + * without using copy. Thus everything is reverified so that + * constructors and Verifier are hit heavily. + * Doesn't appear to help to add children after child is appended. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.1d5 + * + */ +class FastReproducer { + + public static void main(String[] args) { + + if (args.length <= 0) { + System.out.println( + "Usage: java nu.xom.benchmarks.Reproducer URL" + ); + return; + } + + FastReproducer iterator = new FastReproducer(); + Builder parser = new Builder(); + try { + // Separate out the basic I/O by parsing document, + // and then serializing into a byte array. This caches + // the document and removes any dependence on the DTD. + Document document = parser.build(args[0]); + + long prewalk = System.currentTimeMillis(); + // Process it starting at the root + iterator.copy(document); + long postwalk = System.currentTimeMillis(); + System.out.println((postwalk - prewalk) + + "ms to walk tree"); + + } + catch (IOException ex) { + System.out.println(ex); + } + catch (ParsingException ex) { + System.out.println(ex); + } + + } + + private Document copy(Document doc) + throws IOException { + + Element originalRoot = doc.getRootElement(); + Element root = copy(originalRoot, null); + Document copy = new Document(root); + copy.setBaseURI(doc.getBaseURI()); + for (int i = 0; i < doc.getChildCount(); i++) { + Node child = doc.getChild(i); + if (child == originalRoot) continue; + Node node = copy(child); + copy.insertChild(node, i); + } + return copy; + + } + + + private Element copy(Element original, Element parent) { + + Element copy = new Element(original.getQualifiedName(), + new String(original.getNamespaceURI())); + if (parent != null) parent.appendChild(copy); + for (int i = original.getAttributeCount()-1; i >= 0; i--) { + Attribute att = original.getAttribute(i); + copy.addAttribute(copy(att)); + } + // Weird; need to find just the additional namespaces???? + /* for (int i = original.getNamespaceDeclarationCount()-1; i >= 0; i--) { + copy.addNamespaceDeclaration(original.); + } */ + for (int i = 0; i < original.getChildCount(); i++) { + Node child = original.getChild(i); + if (child instanceof Element) { + copy((Element) child, copy); + } + else { + Node node = copy(child); + copy.appendChild(node); + } + } + return copy; + + } + + + private Node copy(Node node) { + + if (node instanceof Text) { + return copy((Text) node); + } + else if (node instanceof Comment) { + return copy((Comment) node); + } + else if (node instanceof ProcessingInstruction) { + return copy((ProcessingInstruction) node); + } + else if (node instanceof DocType) { + return copy((DocType) node); + } + return null; + + } + + + private Node copy(Text text) { + return new Text(text.getValue()); + } + + + private Node copy(Comment comment) { + return new Comment(comment.getValue()); + } + + + private Node copy(ProcessingInstruction pi) { + return new ProcessingInstruction(pi.getTarget(), pi.getValue()); + } + + + private Node copy(DocType doctype) { + return new DocType( + doctype.getRootElementName(), + doctype.getPublicID(), + doctype.getSystemID()); + } + + + private Attribute copy(Attribute original) { + + Attribute copy = new Attribute(original.getQualifiedName(), + original.getNamespaceURI(), + original.getValue(), + original.getType()); + return copy; + + } + + +} \ No newline at end of file diff --git a/src/nu/xom/benchmarks/ManyNamespaces.java b/src/nu/xom/benchmarks/ManyNamespaces.java new file mode 100644 index 0000000..4edd887 --- /dev/null +++ b/src/nu/xom/benchmarks/ManyNamespaces.java @@ -0,0 +1,30 @@ + + +package nu.xom.benchmarks; + +import java.io.*; +import nu.xom.Builder; +import nu.xom.Document; +import nu.xom.canonical.*; + +class ManyNamespaces { + + public static void main(String[] args) throws Exception { + + Builder builder = new Builder(); + Document dataDoc = builder.build("data/manynamespaces.xml"); + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + Canonicalizer c = new Canonicalizer(out); + long pre = System.currentTimeMillis(); + for (int i = 0; i < 1; i++) { + c.write(dataDoc); + } + out.close(); + long post = System.currentTimeMillis(); + System.out.println(out.toByteArray().length); + System.out.println((post - pre)/1000.0 + "s to canonicalize document"); + + } + +} diff --git a/src/nu/xom/benchmarks/MemoryTest.java b/src/nu/xom/benchmarks/MemoryTest.java new file mode 100644 index 0000000..d5a90a8 --- /dev/null +++ b/src/nu/xom/benchmarks/MemoryTest.java @@ -0,0 +1,123 @@ +/* Copyright 2002, 2003 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.benchmarks; + +import java.io.IOException; +import java.text.DecimalFormat; + +import nu.xom.Attribute; +import nu.xom.Builder; +import nu.xom.Document; +import nu.xom.Element; +import nu.xom.ParsingException; + +/** + *

    + * Simple memory benchmark focusing on a big document + * full of small elements or a user supplied document. + *

    + * + * + * @author Elliotte Rusty Harold + * @version 1.0 + * + */ +class MemoryTest { + + public static Document makeFullUnicode() { + + Element root = new Element("root"); + Document doc = new Document(root); + + for (int i = 0x20; i <= 0xD7FF; i++) { + Element data = new Element("d"); + data.appendChild(((char) i) + ""); + data.addAttribute(new Attribute("c", String.valueOf(i))); + root.appendChild(data); + } + + // skip surrogates between 0xD800 and 0xDFFF + + for (int i = 0xE000; i <= 0xFFFD; i++) { + Element data = new Element("d"); + data.appendChild(((char) i) + ""); + data.addAttribute(new Attribute("c", String.valueOf(i))); + root.appendChild(data); + } + + System.gc(); + + // Plane-1 characters are tricky because Java + // strings encode them as surrogate pairs. First, fill + // a byte array with the characters from 1D100 to 1D1FF + // (the musical symbols) + for (int i = 0; i < 256; i++) { + // algorithm from RFC 2781 + int u = 0x1D100 + i; + int uprime = u - 0x10000; + int W1 = 0xD800; + int W2 = 0xDC00; + W2 = W2 | (uprime & 0x7FF ); + W1 = W1 | (uprime & 0xFF800); + Element data = new Element("d"); + data.appendChild( ((char) W1) + "" + ((char) W2) ); + data.addAttribute(new Attribute("c", String.valueOf(u))); + root.appendChild(data); + } + + return doc; + + } + + public static void main(String[] args) + throws IOException, ParsingException { + + DecimalFormat format = new DecimalFormat(); + format.setMaximumFractionDigits(2); + Runtime r = Runtime.getRuntime(); + System.gc(); System.gc(); System.gc(); + long before = r.totalMemory() - r.freeMemory(); + Document doc; + if (args.length > 0) { + Builder builder = new Builder(); + doc = builder.build(args[0]); + builder = null; + } + else { + doc = makeFullUnicode(); + } + long after = r.totalMemory() - r.freeMemory(); + double usage = (after - before)/(1024.0*1024.0); + System.out.println("Memory used: " + + format.format(usage) + "M"); + System.gc(); System.gc(); System.gc(); + long postGC = r.totalMemory() - r.freeMemory(); + usage = (postGC - before)/(1024.0*1024.0); + System.out.println("Memory used after garbage collection: " + + format.format(usage) + "M"); + + // Make sure the document isn't prematurely garbage collected + System.out.println("Meaningless number: " + + doc.toXML().length()); + } + +} diff --git a/src/nu/xom/benchmarks/NamespaceLister.java b/src/nu/xom/benchmarks/NamespaceLister.java new file mode 100644 index 0000000..388a52a --- /dev/null +++ b/src/nu/xom/benchmarks/NamespaceLister.java @@ -0,0 +1,118 @@ +/* Copyright 2006 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.benchmarks; + +import java.io.IOException; + +import nu.xom.Builder; +import nu.xom.Document; +import nu.xom.Element; +import nu.xom.Elements; +import nu.xom.Namespace; +import nu.xom.Nodes; +import nu.xom.ParsingException; + +/** + * + *

    + * Benchmarks listing the namespace prefixes and URIs in scope + * and declared for every element in a document + *

    + * + * @author Elliotte Rusty Harold + * @version 1.2d1 + * + */ +class NamespaceLister { + + public static void main(String[] args) { + + if (args.length <= 0) { + System.out.println( + "Usage: java nu.xom.benchmarks.NamespaceLister URL" + ); + return; + } + + NamespaceLister lister = new NamespaceLister(); + Builder parser = new Builder(); + try { + Document document = parser.build(args[0]); + + // warmup Hotspot + bench(lister, document); + + long ms = 0; + int repeat = 100; + for (int i = 1; i < repeat; i++) { + ms += bench(lister, document); + } + System.out.println(ms/(double) repeat + "ms to list all URIs on average"); + + } + catch (IOException ex) { + System.out.println(ex); + } + catch (ParsingException ex) { + System.out.println(ex); + } + + } + + private static long bench(NamespaceLister iterator, Document document) { + + long prewalk = System.currentTimeMillis(); + // Process it starting at the root + iterator.list(document.getRootElement()); + long postwalk = System.currentTimeMillis(); + return postwalk - prewalk; + } + + + private void list(Element original) { + + getDeclaredNamepaces(original); + getInscopeNamespaces(original); + Elements children = original.getChildElements(); + for (int i = 0; i < children.size(); i++) { + list(children.get(i)); + } + + } + + private void getDeclaredNamepaces(Element original) { + + for (int i = original.getNamespaceDeclarationCount()-1; i >= 0; i--) { + String prefix = original.getNamespacePrefix(i); + System.out.println(original.getNamespaceURI(prefix)); + } + } + + private void getInscopeNamespaces(Element original) { + Nodes spaces = original.query("namespace::node()"); + for (int i = spaces.size() -1; i >= 0; i--) { + Namespace space = (Namespace) spaces.get(i); + System.out.println(space.getValue()); + } + } + +} \ No newline at end of file diff --git a/src/nu/xom/benchmarks/Reproducer.java b/src/nu/xom/benchmarks/Reproducer.java new file mode 100644 index 0000000..0e1921d --- /dev/null +++ b/src/nu/xom/benchmarks/Reproducer.java @@ -0,0 +1,189 @@ +/* Copyright 2002-2006 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.benchmarks; + +import java.io.IOException; + +import nu.xom.Attribute; +import nu.xom.Builder; +import nu.xom.Comment; +import nu.xom.DocType; +import nu.xom.Document; +import nu.xom.Element; +import nu.xom.Node; +import nu.xom.ParsingException; +import nu.xom.ProcessingInstruction; +import nu.xom.Text; + +/** + * + *

    + * Benchmarks building a tree in memory by copying an existing document + * without using copy. Thus everything is reverified so that + * constructors and Verifier are hit heavily. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.2d1 + * + */ +class Reproducer { + + public static void main(String[] args) { + + if (args.length <= 0) { + System.out.println( + "Usage: java nu.xom.benchmarks.Reproducer URL" + ); + return; + } + + Reproducer iterator = new Reproducer(); + Builder parser = new Builder(); + try { + Document document = parser.build(args[0]); + + // warmup Hotspot + bench(iterator, document); + + long ms = 0; + int repeat = 100; + for (int i = 1; i < repeat; i++) { + ms += bench(iterator, document); + } + System.out.println(ms/(double) repeat + "ms to build tree on average"); + + } + catch (IOException ex) { + System.out.println(ex); + } + catch (ParsingException ex) { + System.out.println(ex); + } + + } + + private static long bench(Reproducer iterator, Document document) throws IOException { + + long prewalk = System.currentTimeMillis(); + // Process it starting at the root + iterator.copy(document); + long postwalk = System.currentTimeMillis(); + return postwalk - prewalk; + } + + private Document copy(Document doc) + throws IOException { + + Element originalRoot = doc.getRootElement(); + Element root = copy(originalRoot); + Document copy = new Document(root); + copy.setBaseURI(doc.getBaseURI()); + for (int i = 0; i < doc.getChildCount(); i++) { + Node child = doc.getChild(i); + if (child == originalRoot) continue; + Node node = copy(child); + copy.insertChild(node, i); + } + return copy; + + } + + + private Element copy(Element original) { + + Element copy = new Element(original.getQualifiedName(), + original.getNamespaceURI()); + for (int i = original.getAttributeCount()-1; i >= 0; i--) { + Attribute att = original.getAttribute(i); + copy.addAttribute(copy(att)); + } + // Weird; need to find just the additional namespaces???? + /* for (int i = original.getNamespaceDeclarationCount()-1; i >= 0; i--) { + copy.addNamespaceDeclaration(original.); + } */ + for (int i = 0; i < original.getChildCount(); i++) { + Node node = copy(original.getChild(i)); + copy.appendChild(node); + } + return copy; + + } + + + private Node copy(Node node) { + + if (node instanceof Text) { + return copy((Text) node); + } + else if (node instanceof Element) { + return copy((Element) node); + } + else if (node instanceof Comment) { + return copy((Comment) node); + } + else if (node instanceof ProcessingInstruction) { + return copy((ProcessingInstruction) node); + } + else if (node instanceof DocType) { + return copy((DocType) node); + } + return null; + + } + + + private Node copy(Text text) { + return new Text(text.getValue()); + } + + + private Node copy(Comment comment) { + return new Comment(comment.getValue()); + } + + + private Node copy(ProcessingInstruction pi) { + return new ProcessingInstruction(pi.getTarget(), pi.getValue()); + } + + + private Node copy(DocType doctype) { + return new DocType( + doctype.getRootElementName(), + doctype.getPublicID(), + doctype.getSystemID()); + } + + + private Attribute copy(Attribute original) { + + Attribute copy = new Attribute(original.getQualifiedName(), + original.getNamespaceURI(), + original.getValue(), + original.getType()); + return copy; + + } + + +} \ No newline at end of file diff --git a/src/nu/xom/benchmarks/ToXMLBench.java b/src/nu/xom/benchmarks/ToXMLBench.java new file mode 100644 index 0000000..3af74a5 --- /dev/null +++ b/src/nu/xom/benchmarks/ToXMLBench.java @@ -0,0 +1,94 @@ +/* Copyright 2004, 2005 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.benchmarks; + +import nu.xom.Builder; +import nu.xom.Document; +import nu.xom.Element; + + +/** + *

    + * Benchmark the performance of toXML on a large, record-like + * document. For convenience, the document is formed by copying one + * record 30,000 times. Running this benchmark normally requires + * increasing Java's default heap size. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.1d5 + * + */ +class ToXMLBench { + + private static String elementData = "\n" + + "476w4l73\n" + + "6729\n" + + "N-Vgj / Yvv Zjysbeu Wwvmk @Yhwc 3=\n" + + "gmaj/zhffyi\n" + + " RUWU: 7124 GR0Z: 20\n" + + "Njm Qmskwo\n" + + "Tgnnja Fvwlc\n" + + "Iii Pbou Ia Yniugiq\n" + + "Vzbpqhud Fm Ptwv\n" + + "Fu Ffe Uxvkf\n" + + "Kw Biv Bh Ngu Rpaw\n" + + "Pnzm Vj Qav Tapnie\n" + + "Pawqj Dkqxaslb\n" + + "Hsb Aotlml Faa Vpbotugw\n" + + "Wvex Wwlrr'o Rnbklfz\n" + + "Sdu Edrbo\n" + + "Urvr Ot Udbq\n" + + "Prgovq Mrm\n" + + "Nxr'l Zgoh Zvtl\n" + + "Vkcixopj Ccdo\n" + + "Dlx'h Rclq Mtxiba Gim\n" + + "Bepj Hbrvmgp\n" + + "Khxh Anpl\n" + + "\n"; + + + public static void main(String[] args) throws Exception { + + Builder builder = new Builder(); + Document dataDoc = builder.build( + elementData, "http://www.example.com"); + Element root = new Element("root"); + Document doc = new Document(root); + Element dataElement = dataDoc.getRootElement(); + + for (int i = 0; i < 20000; i++) { + root.appendChild(dataElement.copy()); + } + + System.out.println("Ready to bench"); + Thread.sleep(5000); + + long pre = System.currentTimeMillis(); + doc.toXML(); + long post = System.currentTimeMillis(); + System.out.println((post - pre)/1000.0 + + "s to get XML for entire Document"); + + } + +} diff --git a/src/nu/xom/benchmarks/TreeWalker.java b/src/nu/xom/benchmarks/TreeWalker.java new file mode 100644 index 0000000..7b7dc83 --- /dev/null +++ b/src/nu/xom/benchmarks/TreeWalker.java @@ -0,0 +1,193 @@ +/* Copyright 2002-2004 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.benchmarks; + +import java.io.BufferedInputStream; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; + +import nu.xom.Builder; +import nu.xom.DocType; +import nu.xom.Document; +import nu.xom.Node; +import nu.xom.ParsingException; +import nu.xom.Serializer; +import nu.xom.ValidityException; + +/** + * + *

    + * Benchmark performance against a specified URL for + * building, tree-walking, and serializing. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.2.3 + * + */ +class TreeWalker { + + public static void main(String[] args) { + + if (args.length <= 0) { + System.out.println( + "Usage: java nu.xom.benchmarks.TreeWalker URL" + ); + return; + } + + TreeWalker iterator = new TreeWalker(); + Builder parser = new Builder(); + try { + // Separate out the basic I/O by parsing document, + // and then serializing into a byte array. This caches + // the document and removes any dependence on the DTD. + Document doc = parser.build(args[0]); + DocType type = doc.getDocType(); + if (type != null) { + doc.removeChild(type); + } + ByteArrayOutputStream out = new ByteArrayOutputStream(); + Serializer serializer = new Serializer(out); + serializer.write(doc); + serializer.flush(); + out.close(); + byte[] data = out.toByteArray(); + + warmup(data, parser, iterator, 5, args[0]); + + // try to avoid garbage collection pauses + System.gc(); System.gc(); System.gc(); + + long prebuild = System.currentTimeMillis(); + // Read the entire document into memory + Document document = build(data, args[0], parser); + long postbuild = System.currentTimeMillis(); + System.out.println((postbuild - prebuild) + + "ms to build the tree"); + + long prewalk = System.currentTimeMillis(); + // Process it starting at the root + walkTree(iterator, document); + long postwalk = System.currentTimeMillis(); + System.out.println((postwalk - prewalk) + + "ms to walk tree"); + + OutputStream result = new ByteArrayOutputStream(3000000); + + long preserialize = System.currentTimeMillis(); + serialize(document, result); + long postserialize = System.currentTimeMillis(); + System.out.println((postserialize - preserialize) + + "ms to serialize the tree in UTF-8"); + + long preprettyserialize = System.currentTimeMillis(); + prettyPrint(document, result); + long postprettyserialize = System.currentTimeMillis(); + System.out.println( + (postprettyserialize - preprettyserialize) + + "ms to pretty print the tree in UTF-8"); + + long preUTF16serialize = System.currentTimeMillis(); + serializeUTF16(document, result); + long postUTF16serialize = System.currentTimeMillis(); + System.out.println((postUTF16serialize - preUTF16serialize) + + "ms to serialize the tree in UTF-16"); + } + catch (IOException ex) { + System.out.println(ex); + } + catch (ParsingException ex) { + System.out.println(ex); + } + + } + + private static void serializeUTF16( + Document document, OutputStream out) + throws UnsupportedEncodingException, IOException { + Serializer serializer = new Serializer(out, "UTF-16"); + serializer.write(document); + serializer.flush(); + } + + private static void prettyPrint( + Document document, OutputStream out) + throws UnsupportedEncodingException, IOException { + Serializer serializer = new Serializer(out, "UTF-8"); + serializer.setIndent(2); + serializer.setMaxLength(72); + serializer.write(document); + serializer.flush(); + } + + private static void serialize(Document document, OutputStream out) + throws UnsupportedEncodingException, IOException { + Serializer serializer = new Serializer(out, "UTF-8"); + serializer.write(document); + serializer.flush(); + } + + private static void walkTree(TreeWalker iterator, Document doc) + throws IOException { + iterator.followNode(doc); + } + + private static Document build( + byte[] data, String base, Builder parser) + throws ParsingException, ValidityException, IOException { + InputStream raw = new BufferedInputStream( + new ByteArrayInputStream(data) + ); + Document document = parser.build(raw, base); + return document; + } + + private static void warmup(byte[] data, Builder parser, + TreeWalker iterator, int numPasses, String base) + throws IOException, ParsingException { + for (int i = 0; i < numPasses; i++) { + InputStream raw = new BufferedInputStream( + new ByteArrayInputStream(data) + ); + Document doc = parser.build(raw, base); + walkTree(iterator, doc); + serialize(doc, new ByteArrayOutputStream(3000000)); + prettyPrint(doc, new ByteArrayOutputStream(4000000)); + serializeUTF16(doc, new ByteArrayOutputStream(4000000)); + } + } + + // note use of recursion + public void followNode(Node node) throws IOException { + + for (int i = 0; i < node.getChildCount(); i++) { + followNode(node.getChild(i)); + } + + } + +} \ No newline at end of file diff --git a/src/nu/xom/benchmarks/package.html b/src/nu/xom/benchmarks/package.html new file mode 100644 index 0000000..f8bc88a --- /dev/null +++ b/src/nu/xom/benchmarks/package.html @@ -0,0 +1,44 @@ + + + + + + + +

    +nu.xom.benchmarks +includes some basic benchmarks to test the speed and +memory usage of XOM programs. +

    + +@since 1.0 + + + diff --git a/src/nu/xom/canonical/CanonicalizationException.java b/src/nu/xom/canonical/CanonicalizationException.java new file mode 100644 index 0000000..ed4b5d2 --- /dev/null +++ b/src/nu/xom/canonical/CanonicalizationException.java @@ -0,0 +1,106 @@ +/* Copyright 2005 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ +package nu.xom.canonical; + +/** + *

    + * Indicates problems with canonicalization. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.1b3 + * + */ +public class CanonicalizationException extends RuntimeException { + + + private static final long serialVersionUID = 6935623053373600014L; + + private Throwable cause; + + + /** + *

    + * Creates a new CanonicalizationException + * with a detail message. + *

    + * + * @param message a string indicating the specific problem + */ + public CanonicalizationException(String message) { + super(message); + } + + + /** + *

    + * Return the original cause that led to this exception, + * or null if there was no original exception. + *

    + * + * @return the root cause of this exception + */ + public final Throwable getCause() { + return this.cause; + } + + + // null is insufficient for detecting an uninitialized cause. + // The cause may be set to null which may not then be reset. + private boolean causeSet = false; + + + /** + *

    + * Sets the root cause of this exception. This may + * only be called once. Subsequent calls throw an + * IllegalStateException. + *

    + * + *

    + * This method is unnecessary in Java 1.4 where it could easily be + * inherited from the superclass. However, including it here + * allows this method to be used in Java 1.3 and earlier. + *

    + * + * @param cause the root cause of this exception + * + * @return this XMLException + * + * @throws IllegalArgumentException if the cause is this exception + * (An exception cannot be its own cause.) + * @throws IllegalStateException if this method is called twice + */ + public final Throwable initCause(Throwable cause) { + + if (causeSet) { + throw new IllegalStateException("Can't overwrite cause"); + } + else if (cause == this) { + throw new IllegalArgumentException("Self-causation not permitted"); + } + else this.cause = cause; + causeSet = true; + return this; + + } + +} diff --git a/src/nu/xom/canonical/Canonicalizer.java b/src/nu/xom/canonical/Canonicalizer.java new file mode 100644 index 0000000..68c96f8 --- /dev/null +++ b/src/nu/xom/canonical/Canonicalizer.java @@ -0,0 +1,1172 @@ +/* Copyright 2002-2006, 2009, 2011, 2019 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.canonical; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.SortedMap; +import java.util.StringTokenizer; +import java.util.TreeMap; +import java.util.Map.Entry; + +import org.xml.sax.helpers.NamespaceSupport; + +import nu.xom.Attribute; +import nu.xom.Comment; +import nu.xom.DocType; +import nu.xom.Document; +import nu.xom.Element; +import nu.xom.Namespace; +import nu.xom.Node; +import nu.xom.Nodes; +import nu.xom.ParentNode; +import nu.xom.ProcessingInstruction; +import nu.xom.Serializer; +import nu.xom.Text; +import nu.xom.XPathContext; + +/** + *

    + * Writes XML in the format specified by Canonical + * XML Version 1.0 or Exclusive + * XML Canonicalization Version 1.0. Canonical XML 1.1 support is in progress. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.3.1 + * + */ +public class Canonicalizer { + + private boolean withComments; + private boolean exclusive = false; + private boolean v11 = false; + private CanonicalXMLSerializer serializer; + private List inclusiveNamespacePrefixes = new ArrayList(); + + private static Comparator comparator = new AttributeComparator(); + + + public final static String CANONICAL_XML = + "http://www.w3.org/TR/2001/REC-xml-c14n-20010315"; + public final static String CANONICAL_XML_WITH_COMMENTS = + "http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments"; + public final static String EXCLUSIVE_XML_CANONICALIZATION = + "http://www.w3.org/2001/10/xml-exc-c14n#"; + public final static String EXCLUSIVE_XML_CANONICALIZATION_WITH_COMMENTS = + "http://www.w3.org/2001/10/xml-exc-c14n#WithComments"; + public final static String CANONICAL_XML_11 = + "http://www.w3.org/2006/12/xml-c14n11"; + public final static String CANONICAL_XML_11_WITH_COMMENTS = + "http://www.w3.org/2006/12/xml-c14n11#WithComments"; + + + private static class AttributeComparator implements Comparator { + + @Override + public int compare(Attribute a1, Attribute a2) { + + String namespace1 = a1.getNamespaceURI(); + String namespace2 = a2.getNamespaceURI(); + if (namespace1.equals(namespace2)) { + return a1.getLocalName().compareTo(a2.getLocalName()); + } + else if (namespace1.equals("")) { + return -1; + } + else if (namespace2.equals("")) { + return 1; + } + else { // compare namespace URIs + return namespace1.compareTo(namespace2); + } + + } + + } + + + /** + *

    + * Creates a Canonicalizer that outputs a + * canonical XML document with comments. + *

    + * + * @param out the output stream the document + * is written onto + */ + public Canonicalizer(OutputStream out) { + this(out, true, false); + } + + + /** + *

    + * Creates a Canonicalizer that outputs a + * canonical XML document with or without comments. + *

    + * + * @param out the output stream the document + * is written onto + * @param withComments true if comments should be included + * in the output, false otherwise + */ + public Canonicalizer( + OutputStream out, boolean withComments) { + this(out, withComments, false); + } + + + /** + *

    + * Creates a Canonicalizer that outputs a + * canonical XML document with or without comments, + * using either the original or the exclusive canonicalization + * algorithm. + *

    + * + * @param out the output stream the document + * is written onto + * @param withComments true if comments should be included + * in the output, false otherwise + * @param exclusive true if exclusive XML canonicalization + * should be performed, false if regular XML canonicalization + * should be performed + */ + private Canonicalizer( + OutputStream out, boolean withComments, boolean exclusive) { + + this.serializer = new CanonicalXMLSerializer(out); + serializer.setLineSeparator("\n"); + this.withComments = withComments; + this.exclusive = exclusive; + + } + + + /** + *

    + * Creates a Canonicalizer that outputs a + * canonical XML document using the specified algorithm. + * Currently, four algorithms are defined and supported: + *

    + * + *
      + *
    • Canonical XML without comments: + * http://www.w3.org/TR/2001/REC-xml-c14n-20010315
    • + *
    • Canonical XML with comments: + * http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments
    • + *
    • Exclusive XML canonicalization without comments: + * http://www.w3.org/2001/10/xml-exc-c14n#
    • + *
    • Exclusive XML canonicalization with comments: + * http://www.w3.org/2001/10/xml-exc-c14n#WithComments
    • + *
    + * + * @param out the output stream the document + * is written onto + * @param algorithm the URI for the canonicalization algorithm + * + * @throws CanonicalizationException if the algorithm is + * not recognized + * @throws NullPointerException if the algorithm is null + * + */ + public Canonicalizer( + OutputStream out, String algorithm) { + + if (algorithm == null) { + throw new NullPointerException("Null algorithm"); + } + this.serializer = new CanonicalXMLSerializer(out); + serializer.setLineSeparator("\n"); + if (algorithm.equals(CANONICAL_XML)) { + this.withComments = false; + this.exclusive = false; + } + else if (algorithm.equals(CANONICAL_XML_WITH_COMMENTS)) { + this.withComments = true; + this.exclusive = false; + } + else if (algorithm.equals(EXCLUSIVE_XML_CANONICALIZATION)) { + this.withComments = false; + this.exclusive = true; + } + else if (algorithm.equals(EXCLUSIVE_XML_CANONICALIZATION_WITH_COMMENTS)) { + this.withComments = true; + this.exclusive = true; + } + else if (algorithm.equals(CANONICAL_XML_11)) { + this.withComments = false; + this.exclusive = false; + this.v11 = true; + } + else if (algorithm.equals(CANONICAL_XML_11_WITH_COMMENTS)) { + this.withComments = true; + this.exclusive = false; + this.v11 = true; + } + else { + throw new CanonicalizationException( + "Unsupported canonicalization algorithm: " + algorithm); + } + + } + + + private class CanonicalXMLSerializer extends Serializer { + + // If nodes is null we're canonicalizing all nodes; + // the entire document; this is somewhat easier than when + // canonicalizing only a document subset embedded in nodes + private Nodes nodes; + private NamespaceSupport inScope; + + /** + *

    + * Creates a Serializer that outputs a + * canonical XML document with or without comments. + *

    + * + * @param out the OutputStream the document + * is written onto + * @param withComments true if comments should be included + * in the output, false otherwise + */ + CanonicalXMLSerializer(OutputStream out) { + super(out); + setLineSeparator("\n"); + } + + + /** + *

    + * Serializes a document onto the output + * stream using the canonical XML algorithm. + *

    + * + * @param doc the Document to serialize + * + * @throws IOException if the underlying OutputStream + * encounters an I/O error + */ + public final void write(Document doc) throws IOException { + + inScope = new NamespaceSupport(); + int position = 0; + while (true) { + Node child = doc.getChild(position); + if (nodes == null || child instanceof Element || nodes.contains(child)) { + writeChild(child); + if (child instanceof ProcessingInstruction) breakLine(); + else if (child instanceof Comment && withComments) { + breakLine(); + } + } + position++; + if (child instanceof Element) break; + } + + for (int i = position; i < doc.getChildCount(); i++) { + Node child = doc.getChild(i); + if (nodes == null || child instanceof Element || nodes.contains(child)) { + if (child instanceof ProcessingInstruction) breakLine(); + else if (child instanceof Comment && withComments) { + breakLine(); + } + writeChild(child); + } + } + + flush(); + + } + + + /** + *

    + * Serializes an element onto the output stream using the canonical + * XML algorithm. The result is guaranteed to be well-formed. + * If element does not have a parent element, it will + * also be namespace well-formed. + *

    + * + * @param element the Element to serialize + * + * @throws IOException if the underlying OutputStream + * encounters an I/O error + */ + protected final void write(Element element) + throws IOException { + + // treat empty elements differently to avoid an + // instanceof test + if (element.getChildCount() == 0) { + writeStartTag(element, false); + writeEndTag(element); + } + else { + Node current = element; + boolean end = false; + int index = -1; + int[] indexes = new int[10]; + int top = 0; + indexes[0] = -1; + while (true) { + if (!end && current.getChildCount() > 0) { + writeStartTag((Element) current, false); + current = current.getChild(0); + index = 0; + top++; + indexes = grow(indexes, top); + indexes[top] = 0; + } + else { + if (end) { + writeEndTag((Element) current); + if (current == element) break; + } + else { + writeChild(current); + } + end = false; + ParentNode parent = current.getParent(); + if (parent.getChildCount() - 1 == index) { + current = parent; + top--; + if (current != element) { + index = indexes[top]; + } + end = true; + } + else { + index++; + indexes[top] = index; + current = parent.getChild(index); + } + } + } + } + + } + + + private int[] grow(int[] indexes, int top) { + + if (top < indexes.length) return indexes; + int[] result = new int[indexes.length*2]; + System.arraycopy(indexes, 0, result, 0, indexes.length); + return result; + + } + + + protected void writeStartTag(Element element, boolean isEmpty) + throws IOException { + + boolean writeElement = nodes == null || nodes.contains(element); + if (writeElement) { + inScope.pushContext(); + writeRaw("<"); + writeRaw(element.getQualifiedName()); + } + + SortedMap map = new TreeMap(); + if (nodes == null) { + ParentNode parent = element.getParent(); + Element parentElement = null; + if (parent instanceof Element) { + parentElement = (Element) parent; + } + for (int i = 0; + i < element.getNamespaceDeclarationCount(); + i++) { + String prefix = element.getNamespacePrefix(i); + String uri = element.getNamespaceURI(prefix); + + if (uri.equals(inScope.getURI(prefix))) { + continue; + } + else if (exclusive) { + if (needToDeclareNamespace(element, prefix, uri)) { + map.put(prefix, uri); + } + } + else if (uri.equals("")) { + // no need to say xmlns="" + if (parentElement == null) continue; + if ("".equals(parentElement.getNamespaceURI(""))) { + continue; + } + map.put(prefix, uri); + } + else { + map.put(prefix, uri); + } + + } + + writeNamespaceDeclarations(map); + + } + else { + int position = indexOf(element); + // do we need to undeclare a default namespace? + // You know, should I instead create an output tree and then just + // canonicalize that? probably not + if (position != -1 && "".equals(element.getNamespaceURI())) { + ParentNode parent = element.getParent(); + // Here we have to check for the nearest default on parents in the + // output tree, not the input tree + while (parent instanceof Element + && !(nodes.contains(parent))) { + parent = parent.getParent(); + } + if (parent instanceof Element) { + String uri = ((Element) parent).getNamespaceURI(""); + if (! "".equals(uri)) { + map.put("", ""); + } + } + } + + for (int i = position+1; i < nodes.size(); i++) { + Node next = nodes.get(i); + if ( !(next instanceof Namespace) ) break; + Namespace namespace = (Namespace) next; + String prefix = namespace.getPrefix(); + String uri = namespace.getValue(); + + if (uri.equals(inScope.getURI(prefix))) { + continue; + } + else if (exclusive) { + if (needToDeclareNamespace(element, prefix, uri)) { + map.put(prefix, uri); + } + } + else { + map.put(prefix, uri); + } + + } + + writeNamespaceDeclarations(map); + + } + + Attribute[] sorted = sortAttributes(element); + for (int i = 0; i < sorted.length; i++) { + if (nodes == null || nodes.contains(sorted[i]) + || (sorted[i].getNamespaceURI().equals(Namespace.XML_NAMESPACE) && sorted[i].getParent() != element) + ) { + write(sorted[i]); + } + } + + if (writeElement) { + writeRaw(">"); + } + + } + + + private void writeNamespaceDeclarations(SortedMap map) throws IOException { + + Iterator> prefixes = map.entrySet().iterator(); + while (prefixes.hasNext()) { + Entry entry = (Entry) prefixes.next(); + String prefix = (String) entry.getKey(); + String uri = (String) entry.getValue(); + writeRaw(" "); + writeNamespaceDeclaration(prefix, uri); + inScope.declarePrefix(prefix, uri); + } + + } + + + private boolean needToDeclareNamespace( + Element parent, String prefix, String uri) { + + boolean match = visiblyUtilized(parent, prefix, uri); + + if (match || inclusiveNamespacePrefixes.contains(prefix)) { + return noOutputAncestorUsesPrefix(parent, prefix, uri); + } + + return false; + + } + + + private boolean visiblyUtilized(Element element, String prefix, String uri) { + + boolean match = false; + String pfx = element.getNamespacePrefix(); + String local = element.getNamespaceURI(); + if (prefix.equals(pfx) && local.equals(uri)) { + match = true; + } + else { + for (int i = 0; i < element.getAttributeCount(); i++) { + Attribute attribute = element.getAttribute(i); + if (nodes == null || nodes.contains(attribute)) { + pfx = attribute.getNamespacePrefix(); + if (prefix.equals(pfx)) { + match = true; + break; + } + } + } + } + return match; + } + + + private boolean noOutputAncestorUsesPrefix(Element original, String prefix, String uri) { + + ParentNode parent = original.getParent(); + if (parent instanceof Document && "".equals(uri)) { + return false; + } + + while (parent != null && !(parent instanceof Document)) { + if (nodes == null || nodes.contains(parent)) { + Element element = (Element) parent; + String pfx = element.getNamespacePrefix(); + if (pfx.equals(prefix)) { + String newURI = element.getNamespaceURI(prefix); + return ! newURI.equals(uri); + } + + for (int i = 0; i < element.getAttributeCount(); i++) { + Attribute attribute = element.getAttribute(i); + String current = attribute.getNamespacePrefix(); + if (current.equals(prefix)) { + String newURI = element.getNamespaceURI(prefix); + return ! newURI.equals(uri); + } + } + } + parent = parent.getParent(); + } + return true; + + } + + + // ???? move into Nodes? + private int indexOf(Element element) { + for (int i = 0; i < nodes.size(); i++) { + if (nodes.get(i) == element) return i; + } + return -1; + } + + + protected void write(Attribute attribute) throws IOException { + + writeRaw(" "); + writeRaw(attribute.getQualifiedName()); + writeRaw("=\""); + writeRaw(prepareAttributeValue(attribute)); + writeRaw("\""); + + } + + + protected void writeEndTag(Element element) throws IOException { + + if (nodes == null || nodes.contains(element)) { + writeRaw(""); + inScope.popContext(); + } + + } + + private final XPathContext xmlcontext = new XPathContext("xml", Namespace.XML_NAMESPACE); + + private Attribute[] sortAttributes(Element element) { + + Map nearest = new TreeMap(); + // add in any inherited xml: attributes + if (!exclusive && nodes != null && nodes.contains(element) + && !nodes.contains(element.getParent())) { + // grab all xml: attributes + Nodes attributes = element.query("ancestor::*/@xml:*", xmlcontext); + if (attributes.size() != 0) { + // It's important to count backwards here because + // XPath returns all nodes in document order, which + // is top-down. To get the nearest we need to go + // bottom up instead. + for (int i = attributes.size()-1; i >= 0; i--) { + Attribute a = (Attribute) attributes.get(i); + String name = a.getLocalName(); + if (element.getAttribute(name, Namespace.XML_NAMESPACE) != null) { + // this element already has that attribute + continue; + } else if (v11 && "id".equals(name)) { // Don't inherit xml:id in C14N 1.1 + continue; + } + if (! nearest.containsKey(name)) { + if (!v11 || "lang".equals(name) || "space".equals(name)) { + Element parent = (Element) a.getParent(); + if (! nodes.contains(parent)) { + nearest.put(name, a); + } + else { + nearest.put(name, null); + } + } + } + } + } + + // remove null values + Iterator iterator = nearest.values().iterator(); + while (iterator.hasNext()) { + Attribute a = iterator.next(); + if (a == null) iterator.remove(); + } + + if (v11) { // fixup xml:base attributes + List bases = getOmittedBases(element); + Attribute baseAttribute = element.getAttribute("base", Namespace.XML_NAMESPACE); + String baseValue = ""; + if (baseAttribute != null) { + baseValue = baseAttribute.getValue(); + element.removeAttribute(baseAttribute); + } + if (!bases.isEmpty()) { + for (int i = 0; i < bases.size(); i++) { + baseValue = joinURIReferences(bases.get(i), baseValue); + } + } + if (baseValue != null && baseValue.length() > 0) { + nearest.put("base", new Attribute("xml:base", Namespace.XML_NAMESPACE, baseValue)); + } + } + } + + + int localCount = element.getAttributeCount(); + Attribute[] result + = new Attribute[localCount + nearest.size()]; + for (int i = 0; i < localCount; i++) { + result[i] = element.getAttribute(i); + } + + Iterator iterator = nearest.values().iterator(); + for (int j = localCount; j < result.length; j++) { + result[j] = iterator.next(); + } + + Arrays.sort(result, comparator); + + return result; + + } + + + private List getOmittedBases(Element element) { + ArrayList bases = new ArrayList(); + // TODO(elharo): rework this to not need this next variable + ParentNode parent = element.getParent(); + while (parent != null && parent instanceof Element && !nodes.contains(parent)) { + Element parentElement = (Element) parent; + String base = parentElement.getAttributeValue("base", Namespace.XML_NAMESPACE); + if (base != null) bases.add(base); + parent = parentElement.getParent(); + } + + return bases; + } + + + private String prepareAttributeValue(Attribute attribute) { + + String value = attribute.getValue(); + StringBuffer result = new StringBuffer(value.length()); + + if (attribute.getType().equals(Attribute.Type.CDATA) + || attribute.getType().equals(Attribute.Type.UNDECLARED)) { + char[] data = value.toCharArray(); + for (int i = 0; i < data.length; i++) { + char c = data[i]; + if (c == '\t') { + result.append(" "); + } + else if (c == '\n') { + result.append(" "); + } + else if (c == '\r') { + result.append(" "); + } + else if (c == '\"') { + result.append("""); + } + else if (c == '&') { + result.append("&"); + } + else if (c == '<') { + result.append("<"); + } + else { + result.append(c); + } + } + } + else { + // According to the spec, "Whitespace character references + // other than are not affected by attribute value + // normalization. For parsed documents, the parser will + // still replace these with the actual character. I am + // going to assume that if one is found here, that the + // user meant to put it there; and so we will escape it + // with a character reference + char[] data = value.toCharArray(); + boolean seenFirstNonSpace = false; + for (int i = 0; i < data.length; i++) { + if (data[i] == ' ') { + if (i != data.length-1 && data[i+1] != ' ' && seenFirstNonSpace) { + result.append(data[i]); + } + continue; + } + seenFirstNonSpace = true; + if (data[i] == '\t') { + result.append(" "); + } + else if (data[i] == '\n') { + result.append(" "); + } + else if (data[i] == '\r') { + result.append(" "); + } + else if (data[i] == '\"') { + result.append("""); + } + else if (data[i] == '&') { + result.append("&"); + } + else if (data[i] == '<') { + result.append("<"); + } + else { + result.append(data[i]); + } + } + } + + return result.toString(); + + } + + + /** + *

    + * Serializes a Text object + * onto the output stream using the UTF-8 encoding. + * The reserved characters <, >, and & + * are escaped using the standard entity references such as + * &lt;, &gt;, + * and &amp;. + *

    + * + * @param text the Text to serialize + * + * @throws IOException if the underlying OutputStream + * encounters an I/O error + */ + protected final void write(Text text) throws IOException { + + if (nodes == null || nodes.contains(text)) { + String input = text.getValue(); + StringBuffer result = new StringBuffer(input.length()); + for (int i = 0; i < input.length(); i++) { + char c = input.charAt(i); + if (c == '\r') { + result.append(" "); + } + else if (c == '&') { + result.append("&"); + } + else if (c == '<') { + result.append("<"); + } + else if (c == '>') { + result.append(">"); + } + else { + result.append(c); + } + } + writeRaw(result.toString()); + } + + } + + + /** + *

    + * Serializes a Comment object + * onto the output stream if and only if this + * serializer is configured to produce canonical XML + * with comments. + *

    + * + * @param comment the Comment to serialize + * + * @throws IOException if the underlying OutputStream + * encounters an I/O error + */ + protected final void write(Comment comment) + throws IOException { + if (withComments && (nodes == null || nodes.contains(comment))) { + super.write(comment); + } + } + + + protected final void write(ProcessingInstruction pi) + throws IOException { + if (nodes == null || nodes.contains(pi)) { + super.write(pi); + } + } + + + /** + *

    + * Does nothing because canonical XML does not include + * document type declarations. + *

    + * + * @param doctype the document type declaration to serialize + */ + protected final void write(DocType doctype) { + // DocType is not serialized in canonical XML + } + + + public void write(Node node) throws IOException { + + if (node instanceof Document) { + write((Document) node); + } + else if (node instanceof Attribute) { + write((Attribute) node); + } + else if (node instanceof Namespace) { + write((Namespace) node); + } + else { + writeChild(node); + } + + } + + + private void write(Namespace namespace) throws IOException { + + String prefix = namespace.getPrefix(); + String uri = namespace.getValue(); + writeRaw(" xmlns" ); + if (!"".equals(prefix)) { + writeRaw(":"); + writeRaw(prefix); + } + writeRaw("=\""); + writeAttributeValue(uri); + writeRaw("\""); + + } + + } + + + /** + *

    + * Serializes a node onto the output stream using the specified + * canonicalization algorithm. If the node is a document or an + * element, then the node's entire subtree is written out. + *

    + * + * @param node the node to canonicalize + * + * @throws IOException if the underlying OutputStream + * encounters an I/O error + */ + public final void write(Node node) throws IOException { + + // See this thread: + // http://lists.ibiblio.org/pipermail/xom-interest/2005-October/002656.html + if (node instanceof Element) { + Document doc = node.getDocument(); + Element pseudoRoot = null; + if (doc == null) { + pseudoRoot = new Element("pseudo"); + new Document(pseudoRoot); + ParentNode root = (ParentNode) node; + while (root.getParent() != null) root = root.getParent(); + pseudoRoot.appendChild(root); + } + try { + // XXX Consider if it's faster to do this without XPath + Nodes nodes = node.query(".//. | .//@* | .//namespace::*"); + if (exclusive) { // only include namespace nodes in scope + Set prefixes = new HashSet(nodes.size()); + for (int i = 0; i < nodes.size(); i++) { + Node n = nodes.get(i); + if (n instanceof Element) { + String prefix = ((Element) n).getNamespacePrefix(); + prefixes.add(prefix); + } + else if (n instanceof Attribute) { + String prefix = ((Attribute) n).getNamespacePrefix(); + if (! "".equals(prefix)) prefixes.add(prefix); + } + } + // run through the namespaces and remove any that aren't visibly utilized + for (int i = 0; i < nodes.size(); i++) { + Node n = nodes.get(i); + if (n instanceof Namespace) { + String prefix = ((Namespace) n).getPrefix(); + if (! prefixes.contains((prefix))) { + nodes.remove(i); + i--; + } + } + } + } + write(nodes); + } + finally { + if (pseudoRoot != null) pseudoRoot.removeChild(0); + } + } + else { + serializer.nodes = null; + serializer.write(node); + } + serializer.flush(); + + } + + + /** + *

    + * Serializes a document subset onto the output stream using the + * canonical XML algorithm. All nodes in the list must come from + * same document. Furthermore, they must come from a document. + * They cannot be detached. The nodes need not be sorted. This + * method will sort them into the appropriate order for + * canonicalization. + *

    + * + *

    + * In most common use cases, these nodes will be the result of + * evaluating an XPath expression. For example, + *

    + * + *
     Canonicalizer canonicalizer 
    +     *   = new Canonicalizer(System.out, Canonicalizer.CANONICAL_XML);
    +     * Nodes result = doc.query("//. | //@* | //namespace::*");
    +     * canonicalizer.write(result);  
    +     * 
    + * + *

    + * Children are not output unless the subset also includes them. + * Including an element in the subset does not automatically + * select all the element's children, attributes, and namespaces. + * Furthermore, not selecting an element does not imply that its + * children, namespaces, attributes will not be output. + *

    + * + * @param documentSubset the nodes to serialize + * + * @throws IOException if the underlying OutputStream + * encounters an I/O error + * @throws CanonicalizationException if the nodes come from more + * than one document; or if a detached node is in the list + */ + public final void write(Nodes documentSubset) throws IOException { + + if (documentSubset.size() > 0) { + Document doc = documentSubset.get(0).getDocument(); + if (doc == null) { + throw new CanonicalizationException( + "Canonicalization is not defined for detached nodes"); + } + Nodes result = sort(documentSubset); + serializer.nodes = result; + serializer.write(doc); + serializer.flush(); + } + + } + + + /** + *

    + * Specifies the prefixes that will be output as specified in + * regular canonical XML, even when doing exclusive + * XML canonicalization. + *

    + * + * @param inclusiveNamespacePrefixes a whitespace separated list + * of namespace prefixes that will always be included in the + * output, even in exclusive canonicalization + */ + public final void setInclusiveNamespacePrefixList(String inclusiveNamespacePrefixes) { + + this.inclusiveNamespacePrefixes.clear(); + if (this.exclusive && inclusiveNamespacePrefixes != null) { + StringTokenizer tokenizer = new StringTokenizer( + inclusiveNamespacePrefixes, " \t\r\n", false); + while (tokenizer.hasMoreTokens()) { + this.inclusiveNamespacePrefixes.add(tokenizer.nextToken()); + } + } + + } + + + // XXX remove recursion + // recursively descend through document; in document + // order, and add results as they are found + private Nodes sort(Nodes in) { + + Node root = in.get(0).getDocument(); + if (in.size() > 1) { + Nodes out = new Nodes(); + List list = new ArrayList(in.size()); + List namespaces = new ArrayList(); + for (int i = 0; i < in.size(); i++) { + Node node = in.get(i); + list.add(node); + if (node instanceof Namespace) namespaces.add(node); + } + sort(list, namespaces, out, (ParentNode) root); + if (! list.isEmpty() ) { + // Are these just duplicates; or is there really a node + // from a different document? + Iterator iterator = list.iterator(); + while (iterator.hasNext()) { + Node next = iterator.next(); + if (root != next.getDocument()) { + throw new CanonicalizationException( + "Cannot canonicalize subsets that contain nodes from more than one document"); + } + } + } + return out; + } + else { + return new Nodes(in.get(0)); + } + + } + + private static String joinURIReferences(String parent, String child) { + if ("..".equals(parent)) parent = "../"; + if ("..".equals(child)) child = "../"; + + if (child == null) { + return parent; + } + //return parent + child; + return URIJoiner.absolutize(parent, child); + } + + + private static void sort(List in, List namespaces, Nodes out, ParentNode parent) { + + if (in.isEmpty()) return; + if (in.contains(parent)) { + out.append(parent); + in.remove(parent); + // I'm fairly sure this next line is unreachable, but just + // in case it isn't I'll leave this comment here. + // if (in.isEmpty()) return; + } + + int childCount = parent.getChildCount(); + for (int i = 0; i < childCount; i++) { + Node child = parent.getChild(i); + if (child instanceof Element) { + Element element = (Element) child; + if (in.contains(element)) { + out.append(element); + in.remove(element); + } + // attach namespaces + if (!namespaces.isEmpty()) { + Iterator iterator = in.iterator(); + while (iterator.hasNext()) { + Object o = iterator.next(); + if (o instanceof Namespace) { + Namespace n = (Namespace) o; + if (element == n.getParent()) { + out.append(n); + iterator.remove(); + } + } + } + } + + // attach attributes + for (int a = 0; a < element.getAttributeCount(); a++) { + Attribute att = element.getAttribute(a); + if (in.contains(att)) { + out.append(att); + in.remove(att); + if (in.isEmpty()) return; + } + } + sort(in, namespaces, out, element); + } + else { + if (in.contains(child)) { + out.append(child); + in.remove(child); + if (in.isEmpty()) return; + } + } + } + + } + + +} \ No newline at end of file diff --git a/src/nu/xom/canonical/URIJoiner.java b/src/nu/xom/canonical/URIJoiner.java new file mode 100644 index 0000000..435ca77 --- /dev/null +++ b/src/nu/xom/canonical/URIJoiner.java @@ -0,0 +1,278 @@ +/* Copyright 2011 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.canonical; + +import nu.xom.MalformedURIException; + + +/** + * This class implements the modified RFC 3986 algorithm for URI merging + * found in section 2.4 of the canonical XML 1.1 specification + * http://www.w3.org/TR/xml-c14n11/ + * + * @author Elliotte Rusty Harold + * @version 1.2.7 + * + */ +class URIJoiner { + + // This doesn't do enough error checking to be a public API. + static String absolutize(String baseURI, String spec) { + + if ("".equals(baseURI) || baseURI == null) return spec; + + ParsedURI base = new ParsedURI(baseURI); + + // This seems to be necessary to handle base URLs like + // http://www.example.com/test/data/.. + // but I don't think it's part of the 3986 algorithm. + // ???? It may be a bug in that algorithm. Check. + if (base.path.endsWith("/..")) base.path += '/'; + + // The variable names R and T violate Java naming conventions. + // They are taken from the pseudo-code in the RFC 3986 spec. + ParsedURI R = new ParsedURI(spec); + ParsedURI T = new ParsedURI(); + + if (R.scheme != null) { + T.scheme = R.scheme; + T.authority = R.authority; + T.query = R.query; + T.path = removeDotSegments(R.path); + } + else { + if (R.authority != null) { + T.authority = R.authority; + T.query = R.query; + T.path = removeDotSegments(R.path); + } + else { + if ("".equals(R.path)) { + T.path = base.path; + if (R.query != null) { + T.query = R.query; + } + else { + T.query = base.query; + } + } + else { + if (R.path.startsWith("/")) { + T.path = removeDotSegments(R.path); + } + else { + T.path = merge(base, R.path); + T.path = removeDotSegments(T.path); + } + T.query = R.query; + } + T.authority = base.authority; + } + T.scheme = base.scheme; + } + // Fragment ID of base URI is never considered + T.fragment = R.fragment; + + return T.toString(); + + } + + + static String merge(ParsedURI base, String relativePath) { + + if (base.authority != null && "".equals(base.path) + && !"".equals(base.authority)) { + return "/" + relativePath; + } + + int lastSlash = base.path.lastIndexOf('/'); + if (lastSlash == -1) return relativePath; + String topPath = base.path.substring(0, lastSlash+1); + return topPath + relativePath; + + } + + + static String removeDotSegments(String path) { + + StringBuffer output = new StringBuffer(); + + while (path.length() > 0) { + if (path.startsWith("/./")) { + path = '/' + path.substring(3); + } + else if (path.equals("/.")) { + path = "/"; + } + /* else if (path.startsWith("/../")) { + path = '/' + path.substring(4); + int lastSlash = output.toString().lastIndexOf('/'); + if (lastSlash != -1) output.setLength(lastSlash); + }*/ + else if (path.equals("/..")) { + path = "/"; + int lastSlash = output.toString().lastIndexOf('/'); + if (lastSlash != -1) output.setLength(lastSlash); + } + else if (path.equals(".") /*|| path.equals("..")*/) { + path = ""; + } + /*else if (path.startsWith("../")) { + path = path.substring(3); + }*/ + else if (path.startsWith("./")) { + path = path.substring(2); + } + else { + int nextSlash = path.indexOf('/'); + if (nextSlash == 0) nextSlash = path.indexOf('/', 1); + if (nextSlash == -1) { + output.append(path); + path = ""; + } + else { + output.append(path.substring(0, nextSlash)); + path = path.substring(nextSlash); + } + } + } + + return output.toString(); + + } + + + // really just a struct + static class ParsedURI { + + String scheme; + String schemeSpecificPart; + String query; + String fragment; + String authority; + String path = ""; + + ParsedURI(String spec) { + + int colon = spec.indexOf(':'); + int question; + + // URIs can only contain one sharp sign + int sharp = spec.lastIndexOf('#'); + + // Fragment IDs can contain question marks so we only read + // the question mark before the fragment ID, if any + if (sharp == -1) question = spec.indexOf('?'); + else question = spec.substring(0, sharp).indexOf('?'); + + if (colon != -1) scheme = spec.substring(0, colon); + + if (question == -1 && sharp == -1) { + schemeSpecificPart = spec.substring(colon+1); + } + else if (question != -1) { + if (question < colon) { + MalformedURIException ex + = new MalformedURIException("Unparseable URI"); + ex.setData(spec); + throw ex; + } + schemeSpecificPart = spec.substring(colon+1, question); + } + else { + if (sharp < colon) { + MalformedURIException ex + = new MalformedURIException("Unparseable URI"); + ex.setData(spec); + throw ex; + } + schemeSpecificPart = spec.substring(colon+1, sharp); + } + + if (sharp != -1) { + fragment = spec.substring(sharp+1); + } + + if (question != -1) { + if (sharp == -1) { + query = spec.substring(question+1); + } + else { + query = spec.substring(question+1, sharp); + } + } + + if (schemeSpecificPart.startsWith("//")) { + int authorityBegin = 2; + int authorityEnd = schemeSpecificPart.indexOf('/', authorityBegin); + if (authorityEnd == -1) { + authority = schemeSpecificPart.substring(2); + path = ""; + } + else { + authority = schemeSpecificPart.substring(authorityBegin, authorityEnd); + path = schemeSpecificPart.substring(authorityEnd); + } + } + else { + path = schemeSpecificPart; + } + + } + + ParsedURI() {} + + public String toString() { + + StringBuffer result = new StringBuffer(30); + + if (scheme != null) { + result.append(scheme); + result.append(':'); + } + + if (schemeSpecificPart != null) { + result.append(schemeSpecificPart); + } + else { + if (scheme != null) result.append("//"); + if (authority != null) result.append(authority); + result.append(path); + } + + if (query != null) { + result.append('?'); + result.append(query); + } + + if (fragment != null) { + result.append('#'); + result.append(fragment); + } + + return result.toString(); + + } + + } + +} diff --git a/src/nu/xom/canonical/package.html b/src/nu/xom/canonical/package.html new file mode 100644 index 0000000..80e72d0 --- /dev/null +++ b/src/nu/xom/canonical/package.html @@ -0,0 +1,47 @@ + + + + + + + +

    +nu.xom.canonical +supports the output of +Canonical XML +from XOM. +For basic canonicalization of existing documents, this (or any other tree-based +solution) is likely not as efficient as a streaming, SAX-based solution. +However, this is useful for output from XOM in canonical form. +

    + +@since 1.0 + + + diff --git a/src/nu/xom/characters.dat b/src/nu/xom/characters.dat new file mode 100644 index 0000000000000000000000000000000000000000..ccfac0f96b6b33871eefe814374c0a90b19c2a7a GIT binary patch literal 65536 zcmeI)$#&Z?3_wxobg6~^|8I+d07#8?VyABW)YhUn0OS>|)M-wqsU7n?P4atv%>A(} z^ODCOZ@C>F+J2i4n{R#ON6SkLI0%1|)<#^lAB5FvSyUZ$(brb{Rq1P^R#C&^u3*wx z*H9B*1#6cX^TEP?v?tVV=dn9e*`f~}xt+gNI8J%rd~S;@=HSh8*If zZc@>J1SFgaBWy*$}8oK^i7T&_vPbTkIn<~ z<%N7{)hCJ_HenypY6KulDR8kaz3cu~kyr1lG2?dYS)U8--PGlmR934UB5&>pnZ`xl zi{===yBNEfHfcOLptz9lc>3beg2SAv~PF2fV+B^|hTw=o&3p zM$AIjgCYV5Abj>ZRH5sg?N2Odc>Mtk{qw}It5CWWU&yNA@UmMr zMFbE)009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5J2GF1P=eLlLiD3KmY**5I_I{ z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0;G+cI?SGAAJ_sOy00Iag zfB*srAn-p5eALG%7K;D^2q1s}0tg_000IagfB*srAbt4g?TD009IL_&9-A{@sGu2q1s}0tg_000IagfB*sr eAbl#<0L@)It^9UusjAOQj- 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 + * Converts XOM Document objects to and from DOM + * Document objects. This class can also + * convert many DOM node objects into the corresponding + * XOM node objects. However, the reverse is not possible because + * DOM objects cannot live outside their containing + * Document. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.2d1 + * + */ +public class DOMConverter { + + + // prevent instantiation + private DOMConverter() {} + + + /** + *

    + * DOM violates the namespaces 1.0 specification by mapping + * the xmlns prefix to the namespace URI + * http://www.w3.org/2000/xmlns/. + *

    + */ + private final static String XMLNS_NAMESPACE + = "http://www.w3.org/2000/xmlns/"; + + + /** + *

    + * Translates a DOM org.w3c.dom.Document object + * into an equivalent nu.xom.Document object. + * The original DOM document is not changed. + * Some DOM Document objects cannot + * be serialized as namespace well-formed XML, and + * thus cannot be converted to XOM. + *

    + * + * @param domDocument the DOM document to translate + * @return a XOM document + * + * @throws XMLException if the DOM document is not a well-formed + * XML document + */ + public static Document convert(org.w3c.dom.Document domDocument) { + return convert(domDocument, new NodeFactory()); + } + + + // ???? should this return a Nodes instead? + /** + *

    + * Translates a DOM org.w3c.dom.Document object + * into an equivalent nu.xom.Document object as + * controlled by a factory. + * The original DOM document is not changed. + * Some DOM Document objects cannot + * be serialized as namespace well-formed XML, and + * thus cannot be converted to XOM. + *

    + * + * @param domDocument the DOM document to translate + * @param factory the factory that converts each DOM node into + * zero or more XOM nodes + * + * @return a XOM document + * + * @throws XMLException if the DOM document is not a well-formed + * XML document + */ + public static Document convert( + org.w3c.dom.Document domDocument, NodeFactory factory) { + + org.w3c.dom.Element domRoot = domDocument.getDocumentElement(); + Element xomRoot = factory.makeRootElement( + domRoot.getTagName(), domRoot.getNamespaceURI()); + Document xomDocument = factory.startMakingDocument(); + xomDocument.setRootElement(xomRoot); + Nodes result = convert(domRoot, factory); + boolean beforeRoot = true; + int p = 0; + for (int i = 0; i < result.size(); i++) { + Node n = result.get(i); + if (beforeRoot) { + if (n instanceof Element) { + xomDocument.setRootElement((Element) n); + beforeRoot = false; + } + else { + xomDocument.insertChild(n, p++); + } + } + else { + xomDocument.appendChild(n); + } + } + + org.w3c.dom.Node current = domDocument.getFirstChild(); + + // prolog + for (int position = 0; + current.getNodeType() != org.w3c.dom.Node.ELEMENT_NODE; + current = current.getNextSibling()) { + Nodes nodes = convert(current, factory); + // FIXME fix for multiples + for (int i = 0; i < nodes.size(); i++) { + xomDocument.insertChild(nodes.get(i), position++); + } + } + // root element + current = current.getNextSibling(); + + // epilog + while (current != null) { + Nodes nodes = convert(current, factory); + for (int i = 0; i < nodes.size(); i++) { + xomDocument.appendChild(nodes.get(i)); + } + current = current.getNextSibling(); + } + + return xomDocument; + } + + + /** + *

    + * Translates a DOM org.w3c.dom.DocumentFragment + * object into an equivalent nu.xom.Nodes object. + * The original DOM document fragment is not changed. + * Some DOM DocumentFragment objects cannot + * be serialized as namespace well-balanced XML, and + * thus cannot be converted to XOM. + *

    + * + * @param fragment the DOM document fragment to translate + * + * @return a Nodes containing the converted + * fragment members + * + * @throws XMLException if the DOM object is not a well-balanced + * XML fragment + */ + public static Nodes convert(DocumentFragment fragment) { + return convert(fragment, new NodeFactory()); + } + + + /** + *

    + * Translates a DOM org.w3c.dom.DocumentFragment + * object into an equivalent nu.xom.Nodes object, + * converting each DOM node as specified by a factory. + * The original DOM document fragment is not changed. + * Some DOM DocumentFragment objects cannot + * be serialized as namespace well-balanced XML, and + * thus cannot be converted to XOM. + *

    + * + * @param fragment the DOM document fragment to translate + * @param factory the NodeFactory that converts each DOM node into + * a XOM node + * + * @return a Nodes containing the converted + * fragment members + * + * @throws XMLException if the DOM object is not a well-balanced + * XML fragment + */ + public static Nodes convert(DocumentFragment fragment, NodeFactory factory) { + + Nodes result = new Nodes(); + NodeList children = fragment.getChildNodes(); + for (int i = 0; i < children.getLength(); i++) { + Nodes nodes = convert(children.item(i), factory); + appendNodes(result, nodes); + } + + return result; + + } + + + private static void appendNodes(Nodes parent, Nodes children) { + for (int i = 0; i < children.size(); i++) { + parent.append(children.get(i)); + } + } + + + private static Nodes convert(org.w3c.dom.Node node, NodeFactory factory) { + + int type = node.getNodeType(); + switch (type) { + case org.w3c.dom.Node.ELEMENT_NODE: + return convert((org.w3c.dom.Element) node, factory); + case org.w3c.dom.Node.COMMENT_NODE: + return convert((org.w3c.dom.Comment) node, factory); + case org.w3c.dom.Node.DOCUMENT_TYPE_NODE: + return convert((org.w3c.dom.DocumentType) node, factory); + case org.w3c.dom.Node.TEXT_NODE: + return convert((org.w3c.dom.Text) node, factory); + case org.w3c.dom.Node.CDATA_SECTION_NODE: + return convert((org.w3c.dom.Text) node, factory); + case org.w3c.dom.Node.PROCESSING_INSTRUCTION_NODE: + return convert((org.w3c.dom.ProcessingInstruction) node, factory); + default: + throw new XMLException( + "Unexpected DOM node type: " + type); + } + + } + + + /** + *

    + * Translates a DOM org.w3c.dom.Comment object + * into an equivalent nu.xom.Comment object. + * The original DOM object is not changed. + * Some DOM Comment objects cannot + * be serialized as well-formed XML, and + * thus cannot be converted to XOM. + *

    + * + * @param comment the DOM comment to translate + * @return a XOM comment + * + * @throws XMLException if the DOM comment is not a well-formed + * XML comment + */ + public static Comment convert(org.w3c.dom.Comment comment) { + return new Comment(comment.getNodeValue()); + } + + private static Nodes convert(org.w3c.dom.Comment comment, NodeFactory factory) { + return factory.makeComment(comment.getNodeValue()); + } + + /** + *

    + * Translates a DOM org.w3c.dom.Text object + * into an equivalent nu.xom.Text. + * This method will also convert org.w3c.dom.CDATA + * objects. The original DOM object is not changed. + * Some DOM Text objects cannot + * be serialized as well-formed XML, and + * thus cannot be converted to XOM. + *

    + * + * @param text the DOM text to translate + * @return a XOM text + * + * @throws XMLException if the DOM text is not a well-formed + * XML text + */ + public static Text convert(org.w3c.dom.Text text) { + return new Text(text.getNodeValue()); + } + + + private static Nodes convert(org.w3c.dom.Text text, NodeFactory factory) { + return factory.makeText(text.getNodeValue()); + } + + + /** + *

    + * Translates a DOM org.w3c.dom.Attr object + * into an equivalent nu.xom.Attribute object. + * The original DOM object is not changed. + * Some DOM Attr objects cannot + * be serialized as well-formed XML, and + * thus cannot be converted to XOM. Furthermore, DOM uses + * Attr objects to represent namespace declarations. + * XOM does not. Converting an Attr object that + * represents an xmlns or + * xmlns:prefix attribute will cause an + * exception. + *

    + * + * @param attribute the DOM Attr to translate + * @return the equivalent XOM Attribute + * + * @throws XMLException if the DOM Attr + * is a namespace declaration or is not a well-formed + * XML attribute + */ + public static Attribute convert(Attr attribute) { + + String name = attribute.getName(); + String uri = attribute.getNamespaceURI(); + if (uri == null) uri = ""; + return new Attribute(name, uri, attribute.getNodeValue()); + + } + + /** + *

    + * Translates a DOM org.w3c.dom.ProcessingInstruction + * object into an equivalent + * nu.xom.ProcessingInstruction object. + * The original DOM object is not changed. + * Some DOM ProcessingInstruction objects cannot + * be serialized as well-formed XML, and + * thus cannot be converted to XOM. + *

    + * + * @param pi the DOM ProcessingInstruction to + * convert + * @return a XOM ProcessingInstruction + * + * @throws XMLException if the DOM ProcessingInstruction + * is not a well-formed XML processing instruction + */ + public static ProcessingInstruction convert( + org.w3c.dom.ProcessingInstruction pi) { + return new ProcessingInstruction(pi.getTarget(), pi.getNodeValue()); + } + + + private static Nodes convert( + org.w3c.dom.ProcessingInstruction pi, NodeFactory factory) { + return factory.makeProcessingInstruction(pi.getTarget(), pi.getNodeValue()); + } + + + /** + *

    + * Translates a DOM org.w3c.dom.DocumentType + * object into an equivalent nu.xom.DocType object. + * The original DOM object is not changed. Some DOM + * DocumentType objects cannot be serialized as + * well-formed XML, and thus cannot be converted to XOM. + *

    + * + * @param doctype the DOM DocumentType to convert + * @return the equivalent XOM DocType + * + * @throws XMLException if the DOM DocumentType + * is not a well-formed XML document type declaration + */ + public static DocType convert(org.w3c.dom.DocumentType doctype) { + + DocType result = + new DocType( + doctype.getName(), + doctype.getPublicId(), + doctype.getSystemId()); + result.setInternalDTDSubset(doctype.getInternalSubset()); + + return result; + + } + + + private static Nodes convert(org.w3c.dom.DocumentType doctype, NodeFactory factory) { + + Nodes nodes = factory.makeDocType( + doctype.getName(), + doctype.getPublicId(), + doctype.getSystemId()); + for (int i = 0; i < nodes.size(); i++) { + Node node = nodes.get(i); + if (node instanceof DocType) { + ((DocType) node).setInternalDTDSubset(doctype.getInternalSubset()); + break; + } + } + + return nodes; + + } + + + /** + *

    + * Translates a DOM org.w3c.dom.Element + * object into an equivalent nu.xom.Element object. + * The original DOM object is not changed. Some DOM + * Element objects cannot be serialized as + * namespace well-formed XML, and thus cannot be converted to XOM. + *

    + * + * @param element the DOM Element to convert + * @return the equivalent XOM Element + * + * @throws XMLException if the DOM Element + * is not a well-formed XML element + */ + public static Element convert(org.w3c.dom.Element element) { + return (Element) convert(element, new NodeFactory()).get(0); + } + + + private static Nodes convert(org.w3c.dom.Element element, NodeFactory factory) { + + org.w3c.dom.Node current = element; + Element result = makeElement(element, factory); + + if (result == null) { + Nodes out = new Nodes(); + NodeList children = element.getChildNodes(); + for (int i = 0; i < children.getLength(); i++) { + Nodes temp = convert(children.item(i), factory); + for (int j = 0; j < temp.size(); j++) { + out.append(temp.get(j)); + } + } + return out; + } + + + ParentNode parent = result; + boolean backtracking = false; + while (true) { + if (current.hasChildNodes() && !backtracking) { + current = current.getFirstChild(); + backtracking = false; + } + else if (current == element) { + break; + } + else if (current.getNextSibling() != null) { + current = current.getNextSibling(); + backtracking = false; + } + else { + current = current.getParentNode(); + backtracking = true; + if (parent.getParent() != null) parent = parent.getParent(); + continue; + } + + int type = current.getNodeType(); + if (type == org.w3c.dom.Node.ELEMENT_NODE) { + Element child = makeElement((org.w3c.dom.Element) current, factory); + if (child != null) { + parent.appendChild(child); + if (current.hasChildNodes()) parent = child; + } + } + else { + Nodes children = convert(current, factory); + for (int i = 0; i < children.size(); i++) { + parent.appendChild(children.get(i)); + } + } + + } + + return factory.finishMakingElement(result); + + } + + + private static Element makeElement(org.w3c.dom.Element element, NodeFactory factory) { + + String namespaceURI = element.getNamespaceURI(); + String tagName = element.getTagName(); + + Element result; + if (element.getParentNode() == null + || element.getParentNode().getNodeType() == org.w3c.dom.Node.DOCUMENT_NODE) { + result = factory.makeRootElement(tagName, namespaceURI); + } + else { + result = factory.startMakingElement(tagName, namespaceURI); + } + if (result == null) return null; + + // fill element's attributes and additional namespace declarations + NamedNodeMap attributes = element.getAttributes(); + for (int i = 0; i < attributes.getLength(); i++) { + org.w3c.dom.Attr attribute = (org.w3c.dom.Attr) attributes.item(i); + String name = attribute.getName(); + String uri = attribute.getNamespaceURI(); + String value = attribute.getValue(); + if (uri == null) uri = ""; + if (uri.equals(XMLNS_NAMESPACE)) { + if (name.equals("xmlns")) continue; + String prefix = name.substring(name.indexOf(':') + 1); + String currentURI = result.getNamespaceURI(prefix); + if (!value.equals(currentURI)) { + result.addNamespaceDeclaration(prefix, value); + } + } + else { + Nodes nodes = factory.makeAttribute(name, uri, value, Attribute.Type.UNDECLARED); + for (int j = 0; j < nodes.size(); j++) { + Node child = nodes.get(j); + if (child instanceof Attribute) result.addAttribute((Attribute) child); + else result.appendChild(child); + } + } + } + return result; + + } + + + /** + *

    + * Translates a XOM nu.xom.Document object + * into an equivalent org.w3c.dom.Document + * object. The original XOM document is not changed. + * Since DOM2 internal subsets are read-only, + * the internal DTD subset is not converted. + * All other aspects of the document should be + * translated without a problem. + *

    + * + * @param document the XOM document to translate + * @param impl the specific DOM implementation into which this + * document will be converted + * + * @return a DOM document + */ + public static org.w3c.dom.Document convert(Document document, + DOMImplementation impl) { + + Element root = document.getRootElement(); + String rootName = root.getQualifiedName(); + String rootNamespace = root.getNamespaceURI(); + DocType doctype = document.getDocType(); + DocumentType domDOCTYPE = null; + if (doctype != null) { + domDOCTYPE = impl.createDocumentType(rootName, + doctype.getPublicID(), doctype.getSystemID()); + } + + org.w3c.dom.Document domDoc + = impl.createDocument(rootNamespace, rootName, domDOCTYPE); + org.w3c.dom.Element domRoot = domDoc.getDocumentElement(); + + boolean beforeRoot = true; + for (int i = 0; i < document.getChildCount(); i++) { + Node original = document.getChild(i); + // Need to test positioning of doctype + if (original instanceof DocType) continue; + else if (original instanceof Element) { + convert((Element) original, domDoc); + beforeRoot = false; + } + else { + org.w3c.dom.Node domNode = convert(original, domDoc); + if (beforeRoot) domDoc.insertBefore(domNode, domRoot); + else domDoc.appendChild(domNode); + } + } + + return domDoc; + + } + + + private static org.w3c.dom.Node convert( + Node node, org.w3c.dom.Document document) { + + if (node instanceof Text) { + return convert((Text) node, document); + } + else if (node instanceof Comment) { + return convert((Comment) node, document); + } + else if (node instanceof ProcessingInstruction) { + return convert((ProcessingInstruction) node, document); + } + // The non-recursive algorithm converts elements directly. + // It does not pass through this method. + else { + throw new XMLException( + "Unexpected node type: " + node.getClass().getName() + ); + } + + } + + + private static org.w3c.dom.Comment convert( + Comment comment, org.w3c.dom.Document document) { + return document.createComment(comment.getValue()); + } + + + private static org.w3c.dom.Text convert( + Text text, org.w3c.dom.Document document) { + return document.createTextNode(text.getValue()); + } + + + private static org.w3c.dom.ProcessingInstruction convert( + ProcessingInstruction pi, org.w3c.dom.Document document) { + return document.createProcessingInstruction( + pi.getTarget(), pi.getValue()); + } + + + private static org.w3c.dom.Element convert( + Element xomElement, org.w3c.dom.Document document) { + + org.w3c.dom.Element domResult = makeElement(xomElement, document); + org.w3c.dom.Node domParent = domResult; + Node xomCurrent = xomElement; + int index = 0; + int[] indexes = new int[10]; + int top = 0; + indexes[0] = 0; + boolean end = false; + while (true) { + + if (!end && xomCurrent.getChildCount() > 0) { + xomCurrent = xomCurrent.getChild(0); + index = 0; + top++; + indexes = grow(indexes, top); + indexes[top] = 0; + } + else { + end = false; + ParentNode xomParent = xomCurrent.getParent(); + org.w3c.dom.Node grandparent = domParent.getParentNode(); + if (grandparent.getNodeType() == org.w3c.dom.Node.ELEMENT_NODE + && xomCurrent instanceof Element) { + domParent = grandparent; + } + if (xomParent.getChildCount() - 1 == index) { + xomCurrent = xomParent; + top--; + if (xomCurrent == xomElement) break; + ParentNode tp = xomCurrent.getParent(); + if (tp == null) break; + index = indexes[top]; + end = true; + continue; + } + else { + index++; + indexes[top] = index; + xomCurrent = xomParent.getChild(index); + } + } + + if (xomCurrent instanceof Element) { + Element currentElement = (Element) xomCurrent; + org.w3c.dom.Element child = makeElement(currentElement, document); + domParent.appendChild(child); + domParent = child; + } + else { + org.w3c.dom.Node child = convert(xomCurrent, document); + domParent.appendChild(child); + } + + } // end while + + return domResult; + + } + + + private static int[] grow(int[] indexes, int top) { + + if (top < indexes.length) return indexes; + int[] result = new int[indexes.length*2]; + System.arraycopy(indexes, 0, result, 0, indexes.length); + return result; + + } + + + private static org.w3c.dom.Element makeElement( + Element element, org.w3c.dom.Document document) { + + org.w3c.dom.Element result; + String namespace = element.getNamespaceURI(); + + if (element.getParent() instanceof Document) { + result = document.getDocumentElement(); + } + else if (namespace.equals("")) { + result = document.createElement( + element.getQualifiedName()); + } + else { + result = document.createElementNS( + namespace, element.getQualifiedName()); + } + + int attributeCount = element.getAttributeCount(); + for (int i = 0; i < attributeCount; i++) { + Attribute attribute = element.getAttribute(i); + String attns = attribute.getNamespaceURI(); + Attr attr; + if (attns.equals("")) { + attr = document.createAttribute(attribute.getLocalName()); + result.setAttributeNode(attr); + } + else { + attr = document.createAttributeNS( + attns, attribute.getQualifiedName() + ); + result.setAttributeNodeNS(attr); + } + attr.setValue(attribute.getValue()); + } + + int namespaceCount = element.getNamespaceDeclarationCount(); + for (int i = 0; i < namespaceCount; i++) { + String additionalPrefix = element.getNamespacePrefix(i); + String uri = element.getNamespaceURI(additionalPrefix); + + ParentNode parentNode = element.getParent(); + if (parentNode instanceof Element) { + Element parentElement = (Element) parentNode; + if (uri.equals( + parentElement.getNamespaceURI(additionalPrefix))) { + continue; + } + } + else if (uri.equals("")) { //parent is Document or null + continue; // no need to say xmlns="" + } + + if ("".equals(additionalPrefix)) { + Attr attr = document.createAttributeNS( + XMLNS_NAMESPACE, "xmlns" + ); + result.setAttributeNodeNS(attr); + attr.setValue(uri); + } + else { + Attr attr = document.createAttributeNS( + XMLNS_NAMESPACE, + "xmlns:" + additionalPrefix + ); + result.setAttributeNodeNS(attr); + attr.setValue(uri); + } + } + + return result; + + } + + +} diff --git a/src/nu/xom/converters/SAXConverter.java b/src/nu/xom/converters/SAXConverter.java new file mode 100644 index 0000000..a7d1bef --- /dev/null +++ b/src/nu/xom/converters/SAXConverter.java @@ -0,0 +1,350 @@ +/* Copyright 2002-2005 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.converters; + +import nu.xom.Attribute; +import nu.xom.Comment; +import nu.xom.DocType; +import nu.xom.Document; +import nu.xom.Element; +import nu.xom.Node; +import nu.xom.Nodes; +import nu.xom.ParentNode; +import nu.xom.ProcessingInstruction; +import nu.xom.Text; + +import org.xml.sax.ContentHandler; +import org.xml.sax.SAXException; +import org.xml.sax.ext.LexicalHandler; +import org.xml.sax.helpers.AttributesImpl; +import org.xml.sax.helpers.LocatorImpl; + +/** + *

    + * Feeds a XOM Document into a + * SAX2 ContentHandler. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.1b2 + */ +public class SAXConverter { + + + private ContentHandler contentHandler; + private LexicalHandler lexicalHandler; + private LocatorImpl locator; + private boolean stripBaseAttributes = true; + + + /** + *

    + * Creates a new SAXConverter. + *

    + * + * @param handler the SAX2 content handler + * that receives the data + * + * @throws NullPointerException if handler is null + * + */ + public SAXConverter(ContentHandler handler) { + setContentHandler(handler); + } + + + /** + *

    + * Set the content handler for this converter. + *

    + * + * @param handler SAX2 content handler that + * receives the data + * + * @throws NullPointerException if handler is null + * + */ + public void setContentHandler(ContentHandler handler) { + + if (handler == null) { + throw new NullPointerException( + "ContentHandler must be non-null." + ); + } + // unbelievably skanky hack to allow xml:base attributes + // to be passed to XSL transforms without mucking with the + // public API. This would be so much easier if Java had friend + // functions. + else if ("nu.xom.xslt.XSLTHandler".equals(handler.getClass().getName())) { + this.stripBaseAttributes = false; + } + else { + this.contentHandler = handler; + } + + } + + + /** + *

    + * Returns the content handler. + *

    + * + * @return SAX2 content handler that receives the data + */ + public ContentHandler getContentHandler() { + return this.contentHandler; + } + + + /** + *

    + * Sets the optional lexical handler for this converter. + * The only lexical events the converter supplies + * are comments. + *

    + * + * @param handler the lexical handler; + * may be null to turn off lexical events + */ + public void setLexicalHandler(LexicalHandler handler) { + this.lexicalHandler = handler; + } + + + /** + *

    + * Returns the LexicalHandler for this + * converter. This is only used for comments. + *

    + * + * @return SAX2 lexical handler that receives + * lexical events + */ + public LexicalHandler getLexicalHandler() { + return this.lexicalHandler; + } + + + // Not necessary to worry about parser exceptions passed to + // fatalError() because we're starting with a known good document. + // Only exceptions that can arise are thrown by + // the supplied ContentHandler, and we don't want to pass those + // to the ErrorHandler, or call endDocument() if such an exception + // is thrown + /** + *

    + * Feed a document through this converter. + *

    + * + * @param doc the document to pass to SAX + * + * @throws SAXException if the content handler + * or lexical handler throws an exception + */ + public void convert(Document doc) throws SAXException { + + locator = new LocatorImpl(); + locator.setSystemId(doc.getBaseURI()); + contentHandler.setDocumentLocator(locator); + contentHandler.startDocument(); + for (int i = 0; i < doc.getChildCount(); i++) { + process(doc.getChild(i)); + } + contentHandler.endDocument(); + + } + + + private void process(Node node) throws SAXException { + + if (node instanceof Element) { + convertElement((Element) node); + } + else if (node instanceof Text) { + String data = node.getValue(); + contentHandler.characters( + data.toCharArray(), 0, data.length()); + } + else if (node instanceof ProcessingInstruction) { + ProcessingInstruction instruction + = (ProcessingInstruction) node; + + contentHandler.processingInstruction( + instruction.getTarget(), instruction.getValue()); + } + else if (node instanceof Comment && lexicalHandler != null) { + String data = node.getValue(); + lexicalHandler.comment( + data.toCharArray(), 0, data.length()); + } + else if (node instanceof DocType && lexicalHandler != null) { + DocType type = (DocType) node; + lexicalHandler.startDTD(type.getRootElementName(), + type.getPublicID(), type.getSystemID()); + lexicalHandler.endDTD(); + } + // all other types are ignored + + } + + + /** + * @param element the context in which the prefix is mapped + * @param prefix the prefix to pass to statPrefixMapping + * @return true if and only if startPrefixMapping was called + * @throws SAXException if the ContentHandler throws an exception + */ + private boolean convertNamespace(Element element, String prefix) + throws SAXException { + + // XXX could store a stack of these in a namespaceSupport to avoid going up to the parent + String uri = element.getNamespaceURI(prefix); + ParentNode parentNode = element.getParent(); + Element parent = null; + if (parentNode instanceof Element) { + parent = (Element) parentNode; + } + + if (parent != null && uri.equals(parent.getNamespaceURI(prefix))) { + return false; + } + else if (parent == null && "".equals(uri)) { + // Do not fire startPrefixMapping event for no namespace + // on root element + return false; + } + contentHandler.startPrefixMapping(prefix, uri); + return true; // i.e. converted + + } + + + private void convertElement(Element element) throws SAXException { + + locator.setSystemId(element.getBaseURI()); + + // start prefix mapping + int namespaceCount = element.getNamespaceDeclarationCount(); + String[] prefixes = new String[namespaceCount]; + int prefixCount = 0; + for (int i = 0; i < namespaceCount; i++) { + String prefix = element.getNamespacePrefix(i); + boolean converted = convertNamespace(element, prefix); + if (converted) { + prefixes[prefixCount] = prefix; + prefixCount++; + } + } + + // prepare attributes + AttributesImpl saxAttributes = new AttributesImpl(); + int attributeCount = element.getAttributeCount(); + for (int i = 0; i < attributeCount; i++) { + Attribute attribute = element.getAttribute(i); + // The base URIs provided by the locator have already + // accounted for any xml:base attributes. We do not + // also pass in xml:base attributes or some relative base + // URIs could be applied twice. + if ("base".equals(attribute.getLocalName()) + && "http://www.w3.org/XML/1998/namespace".equals(attribute.getNamespaceURI()) + && stripBaseAttributes) { + continue; + } + saxAttributes.addAttribute(attribute.getNamespaceURI(), + attribute.getLocalName(), + attribute.getQualifiedName(), + getSAXType(attribute), + attribute.getValue()); + } + + contentHandler.startElement( + element.getNamespaceURI(), + element.getLocalName(), + element.getQualifiedName(), + saxAttributes); + int childCount = element.getChildCount(); + for (int i = 0; i < childCount; i++) { + process(element.getChild(i)); + } + contentHandler.endElement(element.getNamespaceURI(), + element.getLocalName(), element.getQualifiedName()); + + // end prefix mappings + for (int i = 0; i < prefixCount; i++) { + contentHandler.endPrefixMapping(prefixes[i]); + } + + } + + + private static String getSAXType(Attribute attribute) { + + Attribute.Type type = attribute.getType(); + if (type.equals(Attribute.Type.UNDECLARED)) return "CDATA"; + if (type.equals(Attribute.Type.CDATA)) return "CDATA"; + if (type.equals(Attribute.Type.ID)) return "ID"; + if (type.equals(Attribute.Type.IDREF)) return "IDREF"; + if (type.equals(Attribute.Type.IDREFS)) return "IDREFS"; + if (type.equals(Attribute.Type.NMTOKEN)) return "NMTOKEN"; + if (type.equals(Attribute.Type.NMTOKENS)) return "NMTOKENS"; + if (type.equals(Attribute.Type.ENTITY)) return "ENTITY"; + if (type.equals(Attribute.Type.ENTITIES)) return "ENTITIES"; + if (type.equals(Attribute.Type.NOTATION)) return "NOTATION"; + return "NMTOKEN"; // ENUMERATED + + } + + + /** + *

    + * Converts a Nodes list into SAX by firing events + * into the registered handlers. This method calls + * startDocument before processing the list + * of nodes, and calls endDocument after processing + * all of them. + *

    + * + * @param nodes the nodes to pass to SAX + * + * @throws SAXException if the content handler + * or lexical handler throws an exception + */ + public void convert(Nodes nodes) throws SAXException { + + if (nodes.size() == 1 && nodes.get(0) instanceof Document) { + convert((Document) nodes.get(0)); + } + else { + locator = new LocatorImpl(); + contentHandler.setDocumentLocator(locator); + contentHandler.startDocument(); + for (int i = 0; i < nodes.size(); i++) { + process(nodes.get(i)); + } + contentHandler.endDocument(); + } + + } + + +} diff --git a/src/nu/xom/converters/package.html b/src/nu/xom/converters/package.html new file mode 100644 index 0000000..5d4cf19 --- /dev/null +++ b/src/nu/xom/converters/package.html @@ -0,0 +1,44 @@ + + + + + + + +

    +nu.xom.converters contains +classes that convert XOM documents to other +models such as SAX and DOM. +

    + +@since 1.0 + + + \ No newline at end of file diff --git a/src/nu/xom/doc-files/xom.png b/src/nu/xom/doc-files/xom.png new file mode 100644 index 0000000000000000000000000000000000000000..f4478ebdce3f6ea71323244fb3e3d8da81084579 GIT binary patch literal 34156 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 literal 0 HcmV?d00001 diff --git a/src/nu/xom/integrationtests/OASISXSLTTest.java b/src/nu/xom/integrationtests/OASISXSLTTest.java new file mode 100644 index 0000000..ea6fe07 --- /dev/null +++ b/src/nu/xom/integrationtests/OASISXSLTTest.java @@ -0,0 +1,1021 @@ +/* Copyright 2002-2006, 2009, 2018 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ +package nu.xom.integrationtests; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.PrintStream; +import java.net.UnknownHostException; +import java.util.MissingResourceException; + +import nu.xom.Attribute; +import nu.xom.Builder; +import nu.xom.Document; +import nu.xom.Element; +import nu.xom.Elements; +import nu.xom.IllegalAddException; +import nu.xom.MalformedURIException; +import nu.xom.NodeFactory; +import nu.xom.Nodes; +import nu.xom.ParsingException; +import nu.xom.Text; +import nu.xom.ValidityException; +import nu.xom.XMLException; +import nu.xom.tests.XOMTestCase; +import nu.xom.xslt.XSLException; +import nu.xom.xslt.XSLTransform; + +/** + *

    + * Integration tests for the XSLT engine based on the OASISXSLT test suite + * which is not bundled and must be separately installed. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.2.11 + * + */ +public class OASISXSLTTest extends XOMTestCase { + + public OASISXSLTTest(String name) { + super(name); + } + + + // This class tests a lot of error conditions, which + // Xalan annoyingly logs to System.err. This hides System.err + // before each test and restores it after each test. + private PrintStream systemErr = System.err; + + private File inputDir; + + protected void setUp() { + + System.setErr(new PrintStream(new ByteArrayOutputStream())); + + inputDir = new File("data"); + inputDir = new File(inputDir, "xslt"); + inputDir = new File(inputDir, "input"); + + } + + + protected void tearDown() { + System.setErr(systemErr); + } + + + /** + * This test fails when run with Java 1.4.2. It passes with 1.5 or 1.6. + * I think there's some sort of bug in the XML/XSL libraries bundled with 1.4.2. + * This bug can also get triggered if the 1.4.2 classes somehow get loaded + * into a 1.5 or later VM, as I've seen happen on occasion when running this + * as part of the complete test suite. In particular, the test in DocTypeTest + * that loads Crimson may cause this test to fail. + */ + public void testKeysPerfRepro3() + throws IOException, ParsingException, XSLException { + + Builder builder = new Builder(); + File base = new File("data"); + base = new File(base, "oasis-xslt-testsuite"); + base = new File(base, "TESTS"); + + File input = new File(base, "MSFT_CONFORMANCE_TESTS/KEYS/input.xml"); + File style = new File(base, "MSFT_CONFORMANCE_TESTS/KEYS/input.xsl"); + File output = new File(base, "MSFT_CONFORMANCE_TESTS/KEYS/out/PerfRepro3.txt"); + + Document styleDoc = builder.build(style); + Document inputDoc = builder.build(input); + XSLTransform xform = new XSLTransform(styleDoc); + Nodes result = xform.transform(inputDoc); + Document expectedResult = builder.build(output); + Document actualResult = XSLTransform.toDocument(result); + assertEquals(expectedResult, actualResult); + + } + + + /** + * Like the previous test, this test fails when run with Java 1.4.2. + * It passes with 1.5 or 1.6. + */ + public void testAxes_Axes62() + throws IOException, ParsingException, XSLException { + xalanTestCase("axes/axes62"); + } + + + + private static boolean indentYes(Document styleDoc) { + + Element output = styleDoc + .getRootElement() + .getFirstChildElement("output", + "http://www.w3.org/1999/XSL/Transform"); + if (output == null) return false; + + String indent = output.getAttributeValue("indent"); + if ("yes".equals(indent)) { + return true; + } + else return false; + + } + + + private static class StrippingFactory extends NodeFactory { + + public Nodes makeText(String s) { + + String stripped = stripSpace(s); + if (stripped.length() == 0) return new Nodes(); + Text result = new Text(stripped); + return new Nodes(result); + } + + public Nodes makeAttribute(String name, String URI, + String value, Attribute.Type type) { + return new Nodes(new Attribute(name, URI, stripSpace(value), type)); + } + + private String stripSpace(String s) { + + StringBuffer sb = new StringBuffer(); + for (int i = 0; i < s.length(); i++) { + if (!Character.isWhitespace(s.charAt(i))) { + sb.append(s.charAt(i)); + } + } + + return sb.toString(); + + } + + } + + // XXX need to upgrade to final release of XSLT test suites + // and then split into individual tests + public void testOASISXalanConformanceSuite() + throws IOException, ParsingException, XSLException { + Builder builder = new Builder(); + NodeFactory stripper = new StrippingFactory(); + Builder strippingBuilder = new Builder(stripper); + + File base = new File("data"); + base = new File(base, "oasis-xslt-testsuite"); + base = new File(base, "TESTS"); + base = new File(base, "Xalan_Conformance_Tests"); + File catalog = new File(base, "catalog.xml"); + + // The test suite needs to be installed separately. If we can't + // find the catalog, we just don't run these tests. + if (catalog.exists()) { + Document doc = builder.build(catalog); + Element testsuite = doc.getRootElement(); + Elements submitters = testsuite.getChildElements("test-catalog"); + for (int i = 0; i < submitters.size(); i++) { + Element submitter = submitters.get(i); + Elements testcases = submitter.getChildElements("test-case"); + for (int j = 0; j < testcases.size(); j++) { + + Element testcase = testcases.get(j); + String id = testcase.getAttributeValue("id"); + if (id.startsWith("output_")) { + // These test cases are mostly about producing + // HTML and plain text output that isn't + // relevant to XOM + continue; + } + else if (id.equals("axes_axes62")) { + // Possible Xalan bug. Pulled out into + // separate test method and investigating + continue; + } + else if (id.equals("select_select85")) { + // Same possible Xalan bug. Pulled out into + // separate test method and investigating + continue; + } + File root = new File(base, testcase.getFirstChildElement("file-path").getValue()); + File input = null; + File style = null; + File output = null; + Element scenario = testcase.getFirstChildElement("scenario"); + Elements inputs = scenario.getChildElements("input-file"); + for (int k = 0; k < inputs.size(); k++) { + Element file = inputs.get(k); + String role = file.getAttributeValue("role"); + if ("principal-data".equals(role)) { + input = new File(root, file.getValue()); + } + else if ("principal-stylesheet".equals(role)) { + style = new File(root, file.getValue()); + } + } + Elements outputs = scenario.getChildElements("output-file"); + for (int k = 0; k < outputs.size(); k++) { + Element file = outputs.get(k); + String role = file.getAttributeValue("role"); + if ("principal".equals(role)) { + // Fix up OASIS catalog bugs + File parent = new File(root.getParent()); + parent = new File(parent, "REF_OUT"); + parent = new File(parent, root.getName()); + String outputFileName = file.getValue(); + output = new File(parent, outputFileName); + } + } + + try { + Document inputDoc = builder.build(input); + Document styleDoc = builder.build(style); + // If the transform specifies indent="yes". + // we remove all white space before comparing + XSLTransform xform; + if (indentYes(styleDoc)) { + xform = new XSLTransform(styleDoc, stripper); + } + else xform = new XSLTransform(styleDoc); + Nodes result = xform.transform(inputDoc); + if (output == null) { + // transform should have failed + //fail("Transformed " + id); + } + else { + try { + Document expectedResult; + if (indentYes(styleDoc)) { + expectedResult = strippingBuilder.build(output); + } + else { + expectedResult = builder.build(output); + } + Document actualResult = XSLTransform.toDocument(result); + + if (id.equals("attribset_attribset40")) { + // This test does not necessarily + // produce an identical infoset due + // to necessary remapping of + // namespace prefixes. + continue; + } + else if (id.equals("axes_axes129")) { + // Xalan bug. Fixed in more recent + // version than bundled with the JDK 1.4.2_05 + } + else if (id.equals("copy_copy56") + || id.equals("copy_copy58") + || id.equals("copy_copy60") + || id.equals("copy_copy59")) { + // Xalan bug; + // See http://issues.apache.org/jira/browse/XALANJ-1081 + // Also see erratum E27 to the XSLT spec. + } + else if (id.equals("expression_expression02")) { + // requires unparsed entities XOM doesn't support + } + else if (id.equals("idkey_idkey31")) { + // Known Xalan bug + // See http://issues.apache.org/jira/browse/XALANJ-1325 + } + else if (id.equals("idkey_idkey61") + || id.equals("idkey_idkey62")) { + // Xalan bug. Fixed in more recent + // version than bundled with the JDK 1.4.2_05 + // See http://issues.apache.org/jira/browse/XALANJ-1318 + } + else if (id.equals("impincl_impincl11")) { + // Test case bug; reported 2004-09-18 + // See http://lists.oasis-open.org/archives/xslt-conformance-comment/200409/msg00001.html + } + else if (id.equals("math_math110") + || id.equals("math_math111")) { + // Xalan bug. Fixed in more recent + // version than bundled with the JDK 1.4.2_05 + // See http://issues.apache.org/jira/browse/XALANJ-1278 + } + else if (id.equals("numbering_numbering17") + || id.equals("numbering_numbering79")) { + // test suite bug per XSLT 1.0 erratum 24. See + // See http://issues.apache.org/jira/browse/XALANJ-1979 + } + else if (id.equals("position_position104")) { + // Xalan bug. Fixed in more recent + // version than bundled with the JDK 1.4.2_05 + } + else if (id.equals("position_position106")) { + // Xalan bug. Fixed in more recent + // version than bundled with the JDK 1.4.2_05 + } + else if (id.equals("position_position107") + || id.equals("position_position109")) { + // Xalan bug. Fixed in more recent + // version than bundled with the JDK 1.4.2_05 + // See http://issues.apache.org/jira/browse/XALANJ-1289 + } + else { + assertEquals("Problem with " + id, + expectedResult, actualResult); + } + } + catch (ParsingException ex) { + // a few of the test cases generate + // text or HTML output rather than + // well-formed XML. For the moment, I + // just skip these. + continue; + } + catch (IllegalAddException ex) { + // A few of the test cases generate + // incomplete documents so we can't + // compare output. Perhaps I could + // wrap in an element, then get children + // to build a Nodes object rather than a + // Document???? i.e. a fragment parser? + // Could use a SequenceInputStream to hack this + } + } + + } + catch (ParsingException ex) { + // Some of the test cases contain relative + // namespace URIs XOM does not support + if (ex.getCause() instanceof MalformedURIException) continue; + throw ex; + } + catch (XSLException ex) { + // If the output was null the transformation + // was expected to fail + if (output != null) { + // a few of the test cases use relative namespace URIs + // XOM doesn't support + Throwable cause = ex.getCause(); + if (cause instanceof MalformedURIException) { + continue; + } + + if ("impincl_impincl27".equals(id)) { + // Test case uses file: URI XOM doesn't support + continue; + } + else if ("numberformat_numberformat45".equals(id) + || "numberformat_numberformat46".equals(id)) { + // This has been fixed in Xalan 2.5.2. + // However, it's a bug in earlier versions of Xalan + // including the one bundled with the JDK 1.4.2_05 + // See http://issues.apache.org/jira/browse/XALANJ-805 + // XXX I think this might need an updated version of the test cases + // and expected output at this point. + continue; + } + + System.out.println(id); + System.out.println(ex.getMessage()); + throw ex; + } + } + + } + } + + } + + } + + public void testOASISMicrosoftConformanceSuite() + throws IOException, ParsingException, XSLException { + Builder builder = new Builder(); + NodeFactory stripper = new StrippingFactory(); + Builder strippingBuilder = new Builder(stripper); + File base = new File("data"); + base = new File(base, "oasis-xslt-testsuite"); + base = new File(base, "TESTS"); + File catalog = new File(base, "catalog.xml"); + + // The test suite need to be installed separately. If we can't + // find the catalog, we just don't run these tests. + if (catalog.exists()) { + Document doc = builder.build(catalog); + Element testsuite = doc.getRootElement(); + Elements submitters = testsuite.getChildElements("test-catalog"); + Element submitter = submitters.get(1); + Elements testcases = submitter.getChildElements("test-case"); + for (int j = 0; j < testcases.size(); j++) { + Element testcase = testcases.get(j); + String id = testcase.getAttributeValue("id"); + File root = new File(base, "MSFT_Conformance_Tests"); + root = new File(root, testcase.getFirstChildElement("file-path").getValue()); + File input = null; + File style = null; + File output = null; + Element scenario = testcase.getFirstChildElement("scenario"); + Elements inputs = scenario.getChildElements("input-file"); + for (int k = 0; k < inputs.size(); k++) { + Element file = inputs.get(k); + String role = file.getAttributeValue("role"); + if ("principal-data".equals(role)) { + input = new File(root, file.getValue()); + } + else if ("principal-stylesheet".equals(role)) { + style = new File(root, file.getValue()); + } + } // end for + Elements outputs = scenario.getChildElements("output-file"); + for (int k = 0; k < outputs.size(); k++) { + Element file = outputs.get(k); + String role = file.getAttributeValue("role"); + if ("principal".equals(role)) { + // Fix up OASIS catalog bugs + File parent = new File(root.getParent()); + parent = new File(parent, "REF_OUT"); + parent = new File(parent, root.getName()); + String outputFileName = file.getValue(); + output = new File(parent, outputFileName); + } + } // end for + + try { + Document styleDoc = builder.build(style); + boolean strip = indentYes(styleDoc); + if ("BVTs_bvt002".equals(id) || "BVTs_bvt077".equals(id)) { + // This has been fixed at least as of Xalan 2.6.0. + // However, it's a bug in earlier versions of Xalan + // including the one bundled with the JDK 1.4.2_05 + continue; + } + else if ("XSLTFunctions_Bug76984".equals(id)) { + // This has been fixed at least as of Xalan 2.6.0. + // However, it's a bug in earlier versions of Xalan + // including the one bundled with the JDK 1.4.2_05 + continue; + } + else if ("BVTs_bvt020".equals(id) || "BVTs_bvt022".equals(id) + || "BVTs_bvt024".equals(id) || "BVTs_bvt058".equals(id)) { + // Either a test suite bug, or a recoverable + // error Xalan doesn't recover from. + continue; + } + else if ("BVTs_bvt038".equals(id) + || "Namespace-alias__91785".equals(id) + || "Namespace-alias__91786".equals(id)) { + // a recoverable error Xalan doesn't recover from properly + // http://issues.apache.org/jira/browse/XALANJ-1957 + continue; + } + else if ("Namespace_XPath_CopyNamespaceNodeToOutput".equals(id)) { + // Xalan bug + // http://issues.apache.org/jira/browse/XALANJ-1959 + continue; + } + else if ("Namespace-alias_Namespace-Alias_WithinRTF".equals(id)) { + // Xalan bug + // http://issues.apache.org/jira/browse/XALANJ-1960 + continue; + } + else if ("Completeness__84361".equals(id) + || "Namespace-alias__91781".equals(id) + || "Namespace-alias__91782".equals(id) + || "Namespace-alias_Namespace-Alias_Test1".equals(id) + || "Namespace-alias_Namespace-Alias_Test2".equals(id) + ) { + // a recoverable error Xalan doesn't recover from + continue; + } + else if ("Output__84008".equals(id)) { + // a recoverable error Xalan doesn't recover from + continue; + } + else if ("XSLTFunctions_ElementAvailFunctionFalseTest".equals(id)) { + // Xalan bug + // http://issues.apache.org/jira/browse/XALANJ-1961 + continue; + } + else if ("XSLTFunctions_GenereateIdAppliedToNamespaceNodesOnDifferentElements".equals(id)) { + // Xalan bug + // http://issues.apache.org/jira/browse/XALANJ-1962 + continue; + } + else if ("XSLTFunctions__specialCharInPattern".equals(id)) { + // a recoverable error Xalan doesn't recover from + continue; + } + else if ("XSLTFunctions_DocumentFunctionWithAbsoluteArgument".equals(id)) { + // test case bug; bad URL passed to document function + continue; + } + else if ("BVTs_bvt052".equals(id) || "Keys_PerfRepro2".equals(id)) { + // Requires a non-standard extension function + continue; + } + else if ("Keys_PerfRepro3".equals(id)) { + // Moved to a separate test case; investigating state dependent + // test failure; doesn't fail in isolation; only fails + // when run as part of FastTests + continue; + } + else if ("BVTs_bvt044".equals(id)) { + // a recoverable error Xalan doesn't recover from + // http://issues.apache.org/jira/browse/XALANJ-1957 + continue; + } + else if ("BVTs_bvt039".equals(id)) { + // Xalan bug + continue; + } + else if ("BVTs_bvt033".equals(id) || "BVTs_bvt034".equals(id)) { + // Test suite bug; 2.0 is not unrecognized + continue; + } + else if ("Text__78274".equals(id) || "Text__78276".equals(id)) { + // Test suite bug; no xsl:preserve-space attribute + continue; + } + else if ("XSLTFunctions__minimumValue".equals(id) + || "XSLTFunctions__minimalValue".equals(id)) { + // test suite bug + continue; + } + else if ("Errors_err073".equals(id)) { + // Xalan bug: StackOverflowError + continue; + } + else if ("Sorting_SortExprWithCurrentInsideForEach1".equals(id)) { + // Xalan bug + // http://issues.apache.org/jira/browse/XALANJ-1970 + continue; + } + else if ("BVTs_bvt041".equals(id) || "BVTs_bvt063".equals(id) + || "BVTs_bvt070".equals(id)) { + // Xalan bundled with JDK 1.4.2_05 does not recover + // from this error involving multiple conflicting + // xsl:output at same import precedence, though + // 2.6.0 does + continue; + } + Document inputDoc = builder.build(input); + XSLTransform xform; + if (strip) xform = new XSLTransform(styleDoc, stripper); + else xform = new XSLTransform(styleDoc); + Nodes result = xform.transform(inputDoc); + if (output == null) { + if ("Attributes__89463".equals(id) + || "Attributes__89465".equals(id)) { + // Processors are allowed to recover from + // this problem. + //assertEquals(0, result.size()); + } + else if ("Attributes__89464".equals(id)) { + // Processors are allowed to recover from + // this problem. + //assertEquals(0, ((Element) result.get(0)).getAttributeCount()); + } + else if ("Namespace-alias__91772".equals(id) + || "Namespace-alias__91774".equals(id) + || "Namespace-alias__91780".equals(id) + || "Namespace-alias__91790".equals(id) + || "Namespace-alias__91791".equals(id) + || "Sorting__84006".equals(id) + || "Sorting__91754".equals(id) + ) { + // Processors are allowed to recover from + // this problem. + continue; + } + else if (id.startsWith("Errors_")) { + // Processors are allowed to recover from + // most of these problems. + } + else if (id.startsWith("FormatNumber")) { + // Processors are allowed to recover from + // most of these problems. + } + else if ("BVTs_bvt074".equals(id)) { + // Processors are allowed to recover from + // this problem. + //assertEquals(0, result.get(0).getChildCount()); + } + else if ("XSLTFunctions__currency".equals(id) + || "XSLTFunctions__mixingInvalids".equals(id)) { + // Processors are allowed to recover from + // this problem. + continue; + } + else if ("Attributes_Attribute_UseXmlnsNsAsNamespaceForAttribute".equals(id) + || "Attributes_Attribute_UseXmlnsAsNamespaceForAttributeImplicitly".equals(id) + || "Elements_Element_UseXslElementWithNameSpaceAttrEqualToXmlnsUri".equalsIgnoreCase(id) + || "Elements_Element_UseXslElementWithNameSpaceEqualToXmlnsUri".equalsIgnoreCase(id) + ) { + // test follows namespace errata we don't accept + } + else if ("AttributeSets_RefToUndefinedAttributeSet".equals(id)) { + // I think the test case is wrong; I see + // nothing in the spec that says this is + // an error. + } + else if ("Namespace__77665".equals(id) + || "Namespace__77675".equals(id)) { + // I think the test case is wrong; I see + // nothing in the spec that says this is + // an error. See + // http://lists.oasis-open.org/archives/xslt-conformance-comment/200409/msg00007.html + } + else if ("Variables__84633".equals(id) + || "Variables__84634".equals(id) + || "Variables__84697".equals(id) + || "Variables__84710".equals(id) + ) { + // An error. See 11.4 + // but are processors allowed to recover? + // Hmm according to section 17, the + // processor must signal these errors + // and may but need not recover from them. + // Xalan recovers. Microsoft doesn't. + } + else if ("Output__78176".equals(id)) { + // I think the test case is wrong; I see + // nothing in the spec that says this is + // an error. + } + else if (id.startsWith("XSLTFunctions__100")) { + // I think these test cases are all wrong + // except perhaps XSLTFunctions__10026; I + // see nothing in the spec that says this + // is an error. These are all about the + // unparsed-entity-uri function. + } + else if ("Namespace__78027".equals(id)) { + // Test case is incorrect. This should + // operate in forwards compatible mode. + // Xalan gets this right. + } + else if ("Output_Output_UseStandAloneAttributeWithMultipleRoots".equals(id)) { + // Error only appears when document is serialized; + // not before + } + else { // transform should have failed + //fail("Transformed " + style + "\n id: " + // + testcase.getAttributeValue("id")); + } + } + else { + try { + if ("Attributes_xsl_attribute_dup_attr_with_namespace_conflict".equals(id) + || "BVTs_bvt057".equals(id)) { + // This test case requires namespace prefix rewriting, + // so the output won't be exactly the same between processors + continue; + } + else if ("Comment_DisableOutputEscaping_XslTextInXslComment".equals(id)) { + // Test case output is wrong + continue; + } + else if ("Output__77927".equals(id) + || "Output__77928".equals(id) + || "Output__84304".equals(id) + || "Output__84305".equals(id) + || "Output__84312".equals(id) + || "Output__84619".equals(id) + || "Output__84620".equals(id) + || "Output_EntityRefInAttribHtml".equals(id) + ) { + // These test cases have incorrect line + // breaks in the reference output. + continue; + } + else if ("Output_Modified84433".equals(id)) { + // This test case uses disable output escaping + // so the results don't match up + continue; + } + else if ("Sorting_Sort_SortTextWithNonTextCharacters".equals(id)) { + // Xalan and MSXML don't sort non alphabetic characters + // exactly the same, but that's legal + continue; + } + else if ("Text_DoeWithCdataInText".equals(id)) { + // Requires disable-output-escaping + continue; + } + else if ("Whitespaces__91443".equals(id) + || "Whitespaces__91444".equals(id)) { + // Xalan bug + // See http://issues.apache.org/jira/browse/XALANJ-1969 + continue; + } + else if ("AVTs__77591".equals(id)) { + // test suite bug; doesn't escape tabs in output. See + // http://lists.oasis-open.org/archives/xslt-conformance-comment/200409/msg00017.html + } + else if ("Keys_MultipltKeysInclude".equals(id) ) { + // Xalan bug + // http://issues.apache.org/jira/browse/XALANJ-1956 + } + /* else if ("Keys_PerfRepro3".equals(id) ) { + // Suspected Xalan bug + // http://issues.apache.org/jira/browse/XALANJ-1955 + } */ + else if ("Number__84683".equals(id)) { + // test suite bug + } + else if ("Number__84687".equals(id)) { + // test suite bug + } + else if ("Number__84692".equals(id)) { + // test suite bug + } + else if ("Number__84694".equals(id)) { + // Test suite expects Roman number for zero + // to be the empty string while Xalan uses 0 + } + else if ("Number__84699".equals(id)) { + // Xalan bug + } + else if ("Number__84700".equals(id)) { + // Xalan bug; extra whitespace. Possibly + // the same as + } + else if ("Number__84716".equals(id)) { + // Xalan doesn't support Russian + // number formatting + } + else if ("Number__84717".equals(id)) { + // Xalan supports more Japanese than the + // test case does + } + else if ("Number__84722".equals(id) + || "Number__84723".equals(id) + || "Number__84724".equals(id) + || "Number__84725".equals(id) + ) { + // Acceptable locale support differences + } + else if ("Number_NaNOrInvalidValue".equals(id)) { + // Double bug! Test case is wrong and + // Xalan gets this wrong! + } + else if ("Number_ValueAsNodesetTest1".equals(id) + || "Number_ValueAsEmptyNodeset".equals(id)) { + // Another double bug! Test case is wrong and + // Xalan gets this wrong! + } + else if (id.equals("XSLTFunctions_BooleanFunction")) { + // I think the test case is wrong; or perhaps unspecified + } + else if (id.equals("XSLTFunctions_TestIdFuncInComplexStruct")) { + // I think the Xalan output white space is wrong; + // http://issues.apache.org/jira/browse/XALANJ-1947 + } + else if (id.equals("XSLTFunctions__testOn-0.00")) { + // Possible test suite bug. See + // http://issues.apache.org/jira/browse/XALANJ-2226 + } + else { + Document expectedResult; + if (strip) expectedResult = strippingBuilder.build(output); + else expectedResult = builder.build(output); + Document actualResult = XSLTransform.toDocument(result); + assertEquals("Mismatch with " + id, + expectedResult, actualResult); + } + } // end try + catch (ParsingException ex) { + // a few of the test cases generate + // text or HTML output rather than + // well-formed XML. For the moment, I + // just skip these. + continue; + } + catch (IllegalAddException ex) { + // A few of the test cases generate + // incomplete documents so we can't + // compare output. Perhaps I could + // wrap in an element, then get children + // to build a Node object rather than a + // Document???? i.e. a fragment parser? + // Could use a SequenceInputStream to hack this + } + } // end else + + } // end try + catch (MalformedURIException ex) { + + } + catch (FileNotFoundException ex) { + // The catalog doesn't always match what's on disk + } + catch (UnknownHostException ex) { + // A few tests like ProcessingInstruction__78197 + // point to external DTD subsets that can't be loaded + } + catch (ParsingException ex) { + // several stylesheets use relative namespace URIs XOM + // does not support; skip the test + if (ex.getCause() instanceof MalformedURIException) { + continue; + } + + String operation = scenario.getAttributeValue("operation"); + if (!"execution-error".equals(operation)) { + if ("Namespace_XPath_PredefinedPrefix_XML".equals(id)) { + // uses relative namespace URIs + } + else if ("Sorting__78191".equals(id) + || "Text__78245".equals(id) + || "Text__78273".equals(id) + || "Text__78281".equals(id) + ) { + // binds XML namespace to prefix other than xml + } + else if ("ProcessingInstruction__78200".equals(id) + || "ProcessingInstruction__78202".equals(id) + || "ProcessingInstruction__78203".equals(id) + ) { + // bad test case; uses non-absolute domain name to locate DTD + } + else { + System.out.println(id + ": " + ex.getMessage()); + throw ex; + } + } + } + catch (XSLException ex) { + // If the output was null the transformation + // was expected to fail + if (output != null) { + Throwable cause = ex.getCause(); + if ("Attributes__81487".equals(id) + || "Attributes__81551".equals(id)) { + // spec inconsistency; see + // http://lists.w3.org/Archives/Public/xsl-editors/2004JulSep/0003.html + continue; + } + else if (cause instanceof MissingResourceException) { + // Xalan bug; + // http://issues.apache.org/jira/secure/ManageAttachments.jspa?id=27366 + } + else if ("Include_Include_IncludedStylesheetShouldHaveDifferentBaseUri".equals(id)) { + // This test case is wrong; Uses backslash in URI + } + else if ("Elements__89070".equals(id)) { + // bug fixed in later versions of Xalan + } + else if ("Namespace-alias_Namespace-Alias_NSAliasForDefaultWithExcludeResPref".equals(id)) { + // This test case is wrong; it uses a backslash in a URI + } + else if ("Variables_VariableWithinVariable".equals(id)) { + // Xalan does not recover from this one + } + else if ("BVTs_bvt054".equals(id)) { + // Xalan bug + // http://issues.apache.org/jira/browse/XALANJ-1952 + continue; + } + else if ("BVTs_bvt094".equals(id)) { + // Xalan bug + // http://issues.apache.org/jira/browse/XALANJ-1953 + continue; + } + else if ("Output__78177".equals(id) + || "Output__84009".equals(id)) { + // Xalan does not recover from this error + // which involves duplicate and possibly conflicting xsl:output elements + continue; + } + else if ("Comment_Comment_CDATAWithSingleHyphen".equals(id) + || "Comment_Comment_DoubleHypenEntitywithDelCharacter".equals(id) + || "Comment_Comment_LineOfAllHyphens".equals(id) + || "Comment_Comment_SingleHyphenOnly".equals(id) + || "Comment_Comment_DoubleHyphenONLY".equals(id)) { + // Begins comment data with hyphen, which XOM doesn't allow + continue; + } + else if ("ProcessingInstruction_ValueOfandTextWithDoeInProcInstr".equals(id)) { + // Begins processing instruction data with white space, which XOM doesn't allow + continue; + } + else if ("Elements__89716".equals(id) + || "Elements__89717".equals(id) + || "Elements__89718".equals(id) + || "Output__84309".equals(id) + || "Namespace__77670".equals(id)) + { + // Xalan doesn't recover from these, though recovery is allowed + continue; + } + else if ("Output__84306".equals(id)) { + // Xalan bug + // http://issues.apache.org/jira/browse/XALANJ-1954 + continue; + } + else if ("Output__84014".equals(id)) { + // Fixed in later version of Xalan than is bundled with JDK + continue; + } + else if (cause instanceof MalformedURIException) { + // Some of the tests generate relative namespace URIs + // XOM doesn't support + continue; + } + else { + System.out.println(id + ": " + ex.getMessage()); + System.out.println("in " + style); + if (cause != null) { + System.out.println("cause: " + cause.getMessage()); + } + throw ex; + } + } + } // end catch + catch (XMLException ex) { + if ("Text_modified78309".equals(id)) { + // output is not a full document + } + else { + System.err.println(id); + throw ex; + } + } + + } // end for + + } // end if + + } + + + public void testSelect_Select65() + throws IOException, ParsingException, XSLException { + xalanTestCase("select/select65"); + } + + private void xalanTestCase(String path) + throws ParsingException, ValidityException, IOException, XSLException { + + Builder builder = new Builder(); + File base = new File("data"); + base = new File(base, "oasis-xslt-testsuite"); + base = new File(base, "TESTS"); + base = new File(base, "Xalan_Conformance_Tests"); + + File input = new File(base, path + ".xml"); + File style = new File(base, path + ".xsl"); + + base = new File(base, "REF_OUT"); + File output = new File(base, path + ".out"); + + Document styleDoc = builder.build(style); + Document inputDoc = builder.build(input); + XSLTransform xform = new XSLTransform(styleDoc); + Nodes result = xform.transform(inputDoc); + Document expectedResult = builder.build(output); + Document actualResult = XSLTransform.toDocument(result); + assertEquals(expectedResult, actualResult); + + } + + + public void testSorting__89749() + throws IOException, ParsingException, XSLException { + + Builder builder = new Builder(); + File base = new File("data"); + base = new File(base, "oasis-xslt-testsuite"); + base = new File(base, "TESTS"); + + File input = new File(base, "MSFT_CONFORMANCE_TESTS/Sorting/sorttest.xml"); + File style = new File(base, "MSFT_CONFORMANCE_TESTS/Sorting/2_5_13_repeat.xsl"); + + Document styleDoc = builder.build(style); + Document inputDoc = builder.build(input); + XSLTransform xform = new XSLTransform(styleDoc); + Nodes result = xform.transform(inputDoc); + /* + File output = new File(base, "MSFT_CONFORMANCE_TESTS/Sorting/out/89749.txt"); + Document expectedResult = builder.build(output); + Document actualResult = XSLTransform.toDocument(result); + assertEquals(expectedResult, actualResult); */ + + } + +} diff --git a/src/nu/xom/integrationtests/W3CXIncludeTest.java b/src/nu/xom/integrationtests/W3CXIncludeTest.java new file mode 100644 index 0000000..cdff72b --- /dev/null +++ b/src/nu/xom/integrationtests/W3CXIncludeTest.java @@ -0,0 +1,213 @@ +/* Copyright 2002-2005, 2011, 2018 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.integrationtests; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.PrintStream; +import java.net.URL; + +import junit.framework.AssertionFailedError; +import nu.xom.Builder; +import nu.xom.DocType; +import nu.xom.Document; +import nu.xom.Element; +import nu.xom.Elements; +import nu.xom.ParsingException; +import nu.xom.Serializer; +import nu.xom.tests.XOMTestCase; +import nu.xom.xinclude.XIncludeException; +import nu.xom.xinclude.XIncluder; + +/** + *

    + * Integration tests for the XInclude and XPointer engines. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.2.11 + * + */ +public class W3CXIncludeTest extends XOMTestCase { + + + public W3CXIncludeTest(String name) { + super(name); + } + + + private Builder builder = new Builder(); + private File inputDir; + private File outputDir; + + // This class tests error conditions, which Xerces + // annoyingly logs to System.err. This hides System.err + // before each test and restores it after each test. + private PrintStream systemErr = System.err; + + + protected void setUp() { + + System.setErr(new PrintStream(new ByteArrayOutputStream())); + + inputDir = new File("data"); + inputDir = new File(inputDir, "xinclude"); + inputDir = new File(inputDir, "input"); + + outputDir = new File("data"); + outputDir = new File(outputDir, "xinclude"); + outputDir = new File(outputDir, "output"); + + } + + + protected void tearDown() { + System.setErr(systemErr); + } + + + private void dumpResult(File original, Document result) + throws IOException { + + String name = original.getName(); + File debug = new File("data"); + debug = new File(debug, "xinclude"); + debug = new File(debug, "debug"); + File output = new File(debug, name); + FileOutputStream out = new FileOutputStream(output); + Serializer serializer = new Serializer(out); + serializer.write(result); + + } + + + + // This test requires files that I have not received permission + // to distribute so for the moment you won't be able to run it. + // For my own use it checks to see if the files are present + // and runs if it does find them. You can't just install the + // XInclude-Test-Suite data as distributed by the W3C here. + // Some of those tests rely on optional features XOM does not + // support such as the xpointer() scheme and notations. + // Plus some of those tests have mistakes. You need my patched + // version of the tests. + public void testXIncludeTestSuite() + throws ParsingException, IOException, XIncludeException { + + File testDescription = new File("data"); + testDescription = new File(testDescription, "XInclude-Test-Suite"); + testDescription = new File(testDescription, "testdescr.xml"); + URL baseURL = testDescription.toURI().toURL(); + if (!testDescription.exists()) { + baseURL = new URL( + "http://dev.w3.org/cvsweb/~checkout~/2001/" + + "XInclude-Test-Suite/testdescr.xml?content-type=text/" + + "plain&only_with_tag=HEAD" + ); + } + Document master = builder.build(baseURL.toExternalForm()); + Element testsuite = master.getRootElement(); + Elements testcases = testsuite.getChildElements("testcases"); + for (int i = 0; i < testcases.size(); i++) { + Element group = testcases.get(i); + String basedir = group.getAttributeValue("basedir"); + if (basedir.startsWith("Harold")) { + // These tests are listed in the catalog but haven't + // yet been checked into CVS. besides, these are all + // based on individual tests in this class anyway, so + // running these is duplicated effort. + continue; + } + Elements cases = group.getChildElements("testcase"); + for (int j = 0; j < cases.size(); j++) { + Element testcase = cases.get(j); + String id = testcase.getAttributeValue("id"); + String features = testcase.getAttributeValue("features"); + if (features != null) { + if (features.indexOf("unexpanded-entities") >= 0) continue; + if (features.indexOf("unparsed-entities") >= 0) continue; + if (features.indexOf("xpointer-scheme") >= 0) continue; + } + String description + = testcase.getFirstChildElement("description").getValue(); + if (!basedir.endsWith("/")) basedir += '/'; + URL input = new URL(baseURL, basedir); + input = new URL(input, testcase.getAttributeValue("href")); + Element output = testcase.getFirstChildElement("output"); + if (output == null) { // test failure + try { + Document doc = builder.build(input.toExternalForm()); + XIncluder.resolveInPlace(doc); + //fail("Failed test " + id + ": " + description); + } + catch (XIncludeException success) { + //assertNotNull(success.getMessage()); + } + catch (IOException success) { + if (baseURL.getProtocol().equals("file")) { + //assertNotNull("Problem processing " + input, success.getMessage()); + } + } + catch (ParsingException success) { + //assertNotNull(success.getMessage()); + } + } + else { + URL result = new URL(baseURL, basedir); + result = new URL(result, output.getValue()); + Document expected = builder.build(result.toExternalForm()); + Document doc = builder.build(input.toExternalForm()); + XIncluder.resolveInPlace(doc); + try { + assertEquals("Error when processing " + + result, expected, doc); + } + catch (AssertionFailedError t) { + // If it fails, try it without a doctype in result. + // A lot of the test cases have incorrect DOCTYPE + // declarations. + DocType doctype = expected.getDocType(); + DocType actualDoctype = doc.getDocType(); + if (doctype != null) { + expected.removeChild(doctype); + assertEquals("Error when processing " + + input, expected, doc); + } + else if (actualDoctype != null) { + doc.removeChild(actualDoctype); + assertEquals("Error when processing " + + input, expected, doc); + } + else { + //fail(); + } + } + } + } + } + + } + + +} \ No newline at end of file diff --git a/src/nu/xom/package.html b/src/nu/xom/package.html new file mode 100644 index 0000000..a671063 --- /dev/null +++ b/src/nu/xom/package.html @@ -0,0 +1,48 @@ + + + + + + + +

    +nu.xom is the core package of XOM +that contains all the basic classes representing the different kinds +of nodes: elements, attributes, comments, text nodes, and so forth. +It also contains the basic classes for parsing and serializing +XML documents, as well as the various exception classes +representing the different things that can go wrong while working +with XOM. +

    + + + +@since 1.0 + + + + diff --git a/src/nu/xom/samples/Account.java b/src/nu/xom/samples/Account.java new file mode 100644 index 0000000..e16420b --- /dev/null +++ b/src/nu/xom/samples/Account.java @@ -0,0 +1,120 @@ +/* Copyright 2002, 2003, 2019 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.samples; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import nu.xom.Element; + + +/** + * + *

    + * Demonstrates building a structured XML document, + * from flat, tabular data. A different version of this + * example was originally developed for Chapter 4 of + * Processing + * XML with Java. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.3.1 + * + */ +public class Account { + + // An account is uniquely identified by account code, + // bureau code, agency code and BEA category + private String code; + private String name; + private String BEACategory; + private String bureauCode; + private String agencyCode; + private String year; + + private List subfunctions = new ArrayList(); + + private static Map instances = new HashMap(); + + // Use a private constructor so clients + // have to use the factory method + private Account(String name, String code, String BEACategory, + String bureauCode, String agencyCode, String year) { + + this.name = name; + this.code = code; + this.BEACategory = BEACategory; + this.bureauCode = bureauCode; + this.agencyCode = agencyCode; + this.year = year; + + } + + public static Account getInstance(String name, String code, + String BEACategory, String bureauCode, String agencyCode, + String year) { + + String key = code + " " + BEACategory + " " + bureauCode + + " " + agencyCode + " " + year; + Account account = (Account) instances.get(key); + if (account == null) { + account = new Account(name, code, BEACategory, bureauCode, + agencyCode, year); + instances.put(key, account); + } + + return account; + + } + + public void add(Subfunction sfx) { + if (!subfunctions.contains(sfx)) subfunctions.add(sfx); + } + + public Element getXML() { + + Element account = new Element("Account"); + Element name = new Element("Name"); + Element code = new Element("Code"); + Element BEACategory = new Element("BEACategory"); + name.appendChild(this.name); + code.appendChild(this.code); + BEACategory.appendChild(this.BEACategory); + account.appendChild(name); + account.appendChild(code); + account.appendChild(BEACategory); + + Iterator iterator = subfunctions.iterator(); + while (iterator.hasNext()) { + Subfunction subfunction = iterator.next(); + account.appendChild(subfunction.getXML()); + } + return account; + + } + +} diff --git a/src/nu/xom/samples/Agency.java b/src/nu/xom/samples/Agency.java new file mode 100644 index 0000000..fad9539 --- /dev/null +++ b/src/nu/xom/samples/Agency.java @@ -0,0 +1,111 @@ +/* Copyright 2002, 2003, 2019 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.samples; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import nu.xom.Element; + + +/** + * + *

    + * Demonstrates building a structured XML document, + * from flat, tabular data. A different version of this + * example was originally developed for Chapter 4 of + * Processing + * XML with Java. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.3.1 + * + */ +public class Agency { + + private String code; + private String name; + private String treasuryCode; + private String year; + + private List bureaus = new ArrayList(); + + private static Map instances = new HashMap(); + + // A private constructor so instantiators + // have to use the factory method + private Agency(String name, String code, String treasuryCode, + String year) { + + this.name = name; + this.code = code; + this.treasuryCode = treasuryCode; + this.year = year; + + } + + public static Agency getInstance(String name, String code, + String treasuryCode, String year) { + + // Agencies can be uniquely identified by code alone + String key = code+" "+year; + Agency agency = (Agency) instances.get(key); + if (agency == null) { + agency = new Agency(name, code, treasuryCode, year); + instances.put(key, agency); + } + + return agency; + + } + + public void add(Bureau b) { + if (!bureaus.contains(b)) { + bureaus.add(b); + } + } + + public Element getXML() { + + Element agency = new Element("Agency"); + Element name = new Element("Name"); + Element code = new Element("Code"); + Element treasuryAgencyCode = new Element("TreasuryAgencyCode"); + name.appendChild(this.name); + code.appendChild(this.code); + treasuryAgencyCode.appendChild(treasuryCode); + agency.appendChild(name); + agency.appendChild(code); + agency.appendChild(treasuryAgencyCode); + + for (Bureau bureau : bureaus) { + agency.appendChild(bureau.getXML()); + } + return agency; + + } + +} \ No newline at end of file diff --git a/src/nu/xom/samples/AttributesToElements.java b/src/nu/xom/samples/AttributesToElements.java new file mode 100644 index 0000000..b77cc00 --- /dev/null +++ b/src/nu/xom/samples/AttributesToElements.java @@ -0,0 +1,95 @@ +/* Copyright 2002, 2003 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ +package nu.xom.samples; + +import java.io.IOException; + +import nu.xom.Attribute; +import nu.xom.Builder; +import nu.xom.Document; +import nu.xom.Element; +import nu.xom.NodeFactory; +import nu.xom.Nodes; +import nu.xom.ParsingException; + +/** + *

    + * A filter that converts attributes to child elements. + * This does not apply to namespace declaration attributes + * such as xmlns and xmlns:prefix. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.0 + */ +public class AttributesToElements extends NodeFactory { + + private boolean maintainTypes = false; + + public AttributesToElements() {} + + public AttributesToElements(boolean maintainTypes) { + this.maintainTypes = maintainTypes; + } + + public Nodes makeAttribute(String name, String URI, + String value, Attribute.Type type) { + + Element element = new Element(name, URI); + element.appendChild(value); + if (maintainTypes + && !type.equals(Attribute.Type.UNDECLARED) + && !type.equals(Attribute.Type.ENUMERATION)) { + Attribute xsiType = new Attribute("xsi:type", + "http://www.w3.org/2001/XMLSchema-instance", type.getName()); + element.addAttribute(xsiType); + } + return new Nodes(element); + } + + public static void main(String[] args) { + + if (args.length <= 0) { + System.out.println( + "Usage: java nu.xom.samples.AttributesToElements URL" + ); + return; + } + + try { + Builder parser = new Builder(new AttributesToElements()); + Document doc = parser.build(args[0]); + EZSerializer.write(doc, System.out); + } + catch (ParsingException ex) { + System.out.println(args[0] + " is not well-formed."); + System.out.println(ex.getMessage()); + } + catch (IOException ex) { + System.out.println( + "Due to an IOException, the parser could not read " + + args[0] + ); + } + + } + +} diff --git a/src/nu/xom/samples/AttributesXMLBudget.java b/src/nu/xom/samples/AttributesXMLBudget.java new file mode 100644 index 0000000..e60829e --- /dev/null +++ b/src/nu/xom/samples/AttributesXMLBudget.java @@ -0,0 +1,136 @@ +/* Copyright 2002, 2003, 2019 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.samples; + +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import nu.xom.Attribute; +import nu.xom.Document; +import nu.xom.Element; +import nu.xom.Serializer; + +/** + * + *

    + * Demonstrates building a structured XML document, + * from flat, tabular data. A different version of this + * example was originally developed for Chapter 4 of + * Processing + * XML with Java. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.3.1 + * + */ +public class AttributesXMLBudget { + + public static void convert(List> data, OutputStream out) + throws IOException { + + Element budget = new Element("Budget"); + Document doc = new Document(budget); + + Iterator> records = data.iterator(); + while (records.hasNext()) { + Element lineItem = new Element("LineItem"); + Map record = records.next(); + + // write the attributes + setYear(lineItem, "AgencyCode", record); + setYear(lineItem, "AgencyName", record); + setYear(lineItem, "BureauCode", record); + setYear(lineItem, "BureauName", record); + setYear(lineItem, "AccountCode", record); + setYear(lineItem, "AccountName", record); + setYear(lineItem, "TreasuryAgencyCode", record); + setYear(lineItem, "SubfunctionCode", record); + setYear(lineItem, "SubfunctionTitle", record); + setYear(lineItem, "BEACategory", record); + setYear(lineItem, "BudgetIndicator", record); + setAmount(lineItem, "1976", record); + Element amount = new Element("Amount"); + amount.addAttribute(new Attribute("year", "TransitionalQuarter")); + amount.appendChild((String) record.get("TransitionalQuarter")); + for (int year=1977; year <= 2006; year++) { + setAmount(lineItem, String.valueOf(year), record); + } + } + + Serializer serializer = new Serializer(out, "UTF-8"); + serializer.write(doc); + serializer.flush(); + + } + + // Just a couple of private methods to factor out repeated code + private static void setYear(Element element, String name, + Map record) { + element.addAttribute(new Attribute(name, record.get(name))); + } + + private static void setAmount(Element element, String year, + Map record) { + Element amount = new Element("Amount"); + amount.addAttribute(new Attribute("year", String.valueOf(year))); + amount.appendChild(record.get("Y" + year)); + element.appendChild(amount); + } + + public static void main(String[] args) { + + try { + + if (args.length < 1) { + System.out.println( + "Usage: nu.xom.samples.AttributesXMLBudget infile outfile" + ); + return; + } + + InputStream in = new FileInputStream(args[0]); + OutputStream out; + if (args.length < 2) { + out = System.out; + } + else { + out = new FileOutputStream(args[1]); + } + + List> results = BudgetData.parse(in); + convert(results, out); + } + catch (IOException ex) { + System.err.println(ex); + } + + } + +} diff --git a/src/nu/xom/samples/Budget.java b/src/nu/xom/samples/Budget.java new file mode 100644 index 0000000..45a55a4 --- /dev/null +++ b/src/nu/xom/samples/Budget.java @@ -0,0 +1,116 @@ +/* Copyright 2002, 2003, 2018 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.samples; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import nu.xom.Attribute; +import nu.xom.Element; + + +/** + * + *

    + * Demonstrates the building of a structured XML document, + * from flat, tabular data. A different version of this + * example was originally developed for Chapter 4 of + * Processing + * XML with Java. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.3.1 + * + */ +public class Budget { + + private List agencies = new ArrayList(); + private String year; + + public Budget(String year) { + this.year = year; + } + + // not thread safe + public void add(Agency agency) { + if (!agencies.contains(agency)) agencies.add(agency); + } + + public void add(Map lineItem) { + + String agencyName = lineItem.get("AgencyName"); + String agencyCode = lineItem.get("AgencyCode"); + String treasuryAgencyCode + = (String) lineItem.get("TreasuryAgencyCode"); + Agency agency = Agency.getInstance(agencyName, agencyCode, + treasuryAgencyCode, year); + this.add(agency); + + String bureauName = (String) lineItem.get("BureauName"); + String bureauCode = (String) lineItem.get("BureauCode"); + Bureau bureau = Bureau.getInstance(bureauName, bureauCode, + agencyCode, year); + agency.add(bureau); + + // Names and codes of two accounts in different bureaus + // can be the same + String accountName = lineItem.get("AccountName"); + String accountCode = lineItem.get("AccountCode"); + String category = lineItem.get("BEACategory"); + Account account = Account.getInstance(accountName, + accountCode, category, bureauCode, agencyCode, year); + bureau.add(account); + + // Names and codes of two subfunctions in different accounts + // can be the same + String subfunctionTitle = lineItem.get("SubfunctionTitle"); + String subfunctionCode + = (String) lineItem.get("SubfunctionCode"); + String yearKey = year; + if (!yearKey.equals("TransitionalQuarter")) { + yearKey = "Y" + year; + } + long amount = 1000L * Long.parseLong(lineItem.get(yearKey)); + Subfunction subfunction = new Subfunction(subfunctionTitle, + subfunctionCode, amount); + account.add(subfunction); + + } + + public Element getXML() { + + Element budget = new Element("Budget"); + budget.addAttribute(new Attribute("year", String.valueOf(year))); + Iterator iterator = agencies.iterator(); + while (iterator.hasNext()) { + Agency agency = iterator.next(); + budget.appendChild(agency.getXML()); + } + return budget; + + } + +} \ No newline at end of file diff --git a/src/nu/xom/samples/BudgetData.java b/src/nu/xom/samples/BudgetData.java new file mode 100644 index 0000000..e65c36f --- /dev/null +++ b/src/nu/xom/samples/BudgetData.java @@ -0,0 +1,115 @@ +/* Copyright 2002, 2003, 2019 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.samples; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + + +/** + * + *

    + * Demonstrates building a structured XML document, + * from flat, tabular data. A different version of this + * example was originally developed for Chapter 4 of + * Processing + * XML with Java. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.3.1 + * + */ +public class BudgetData { + + public static List> parse(InputStream src) throws IOException { + + // The document as published by the OMB is encoded in Latin-1 + InputStreamReader isr = new InputStreamReader(src, "8859_1"); + BufferedReader in = new BufferedReader(isr); + List> records = new ArrayList>(); + String lineItem; + while ((lineItem = in.readLine()) != null) { + records.add(splitLine(lineItem)); + } + return records; + + } + + // the field names in order + final static String[] keys = { + "AgencyCode", + "AgencyName", + "BureauCode", + "BureauName", + "AccountCode", + "AccountName", + "TreasuryAgencyCode", + "SubfunctionCode", + "SubfunctionTitle", + "BEACategory", + "On-Off-BudgetIndicator", + "FY1976", "TransitionQuarter", "FY1977", "FY1978", "FY1979", + "FY1980", "FY1981", "FY1982", "FY1983", "FY1984", "FY1985", + "FY1986", "FY1987", "FY1988", "FY1989", "FY1990", "FY1991", + "FY1992", "FY1993", "FY1994", "FY1995", "FY1996", "FY1997", + "FY1998", "FY1999", "FY2000", "FY2001", "FY2002", "FY2003", + "FY2004", "FY2005", "FY2006" + }; + + private static Map splitLine(String record) { + + record = record.trim(); + + int index = 0; + Map result = new HashMap(); + for (int i = 0; i < keys.length; i++) { + //find the next comma + StringBuffer sb = new StringBuffer(); + char c; + boolean inString = false; + while (true) { + c = record.charAt(index); + if (!inString && c == '"') inString = true; + else if (inString && c == '"') inString = false; + else if (!inString && c == ',') break; + else sb.append(c); + index++; + if (index == record.length()) break; + } + String s = sb.toString().trim(); + result.put(keys[i], s); + index++; + } + + return result; + + } + +} \ No newline at end of file diff --git a/src/nu/xom/samples/Bureau.java b/src/nu/xom/samples/Bureau.java new file mode 100644 index 0000000..91f460d --- /dev/null +++ b/src/nu/xom/samples/Bureau.java @@ -0,0 +1,108 @@ +/* Copyright 2002, 2003, 2019 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.samples; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import nu.xom.Element; + + +/** + * + *

    + * Demonstrates building a structured XML document, + * from flat, tabular data. A different version of this + * example was originally developed for Chapter 4 of + * Processing + * XML with Java. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.3.1 + * + */ +public class Bureau { + + // Agency code plus bureau code uniquely identify a bureau + // Bureau code alone is definitely not sufficient + private String code; + private String name; + private String year; + private String agencyCode; + + private List accounts = new ArrayList(); + + private static Map instances = new HashMap(); + + // Use a private constructor so instantiators + // have to use the factory method + private Bureau(String name, String code, String agencyCode, + String year) { + + this.name = name; + this.code = code; + this.agencyCode = agencyCode; + this.year = year; + + } + + public static Bureau getInstance(String name, String code, + String agencyCode, String year) { + + String key = agencyCode+" "+code+" "+year; + Bureau bureau = (Bureau) instances.get(key); + if (bureau == null) { + bureau = new Bureau(name, code, agencyCode, year); + instances.put(key, bureau); + } + + return bureau; + + } + + public void add(Account account) { + if (!accounts.contains(account)) accounts.add(account); + } + + public Element getXML() { + + Element bureau = new Element("Bureau"); + Element name = new Element("Name"); + Element code = new Element("Code"); + + name.appendChild(this.name); + code.appendChild(this.code); + bureau.appendChild(name); + bureau.appendChild(code); + + for (Account account : accounts) { + bureau.appendChild(account.getXML()); + } + return bureau; + + } + +} \ No newline at end of file diff --git a/src/nu/xom/samples/CanonicalizerDriver.java b/src/nu/xom/samples/CanonicalizerDriver.java new file mode 100644 index 0000000..c7649e2 --- /dev/null +++ b/src/nu/xom/samples/CanonicalizerDriver.java @@ -0,0 +1,69 @@ +/* Copyright 2002-2004 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.samples; + +import nu.xom.Builder; +import nu.xom.Document; +import nu.xom.canonical.Canonicalizer; + +/** + *

    + * Demonstrates use of the Canonicalizer class. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.0 + * + */ +public class CanonicalizerDriver { + + /** + *

    + * The driver method for the Canonicalizer program. + *

    + * + * @param args args[0] contains the URL or + * file name of the first document to be processed + */ + public static void main(String[] args) { + + if (args.length == 0) { + System.out.println( + "Usage: java nu.xom.samples.CanonicalizerDriver URL" + ); + return; + } + + Builder builder = new Builder(); + try { + Canonicalizer outputter = new Canonicalizer(System.out); + Document input = builder.build(args[0]); + outputter.write(input); + } + catch (Exception ex) { + System.err.println(ex); + ex.printStackTrace(); + } + + } + +} diff --git a/src/nu/xom/samples/CommentReader.java b/src/nu/xom/samples/CommentReader.java new file mode 100644 index 0000000..9655f9b --- /dev/null +++ b/src/nu/xom/samples/CommentReader.java @@ -0,0 +1,83 @@ +/* Copyright 2002, 2003 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.samples; + +import java.io.IOException; + +import nu.xom.Builder; +import nu.xom.Comment; +import nu.xom.Document; +import nu.xom.Node; +import nu.xom.ParsingException; + +/** + *

    + * Demonstrates extracting of comments from an XML document + * and reading their contents. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.0 + * + */ +public class CommentReader { + + public static void list(Node node) { + + for (int i = 0; i < node.getChildCount(); i++) { + Node child = node.getChild(i); + if (child instanceof Comment) { + System.out.println(child.toXML()); + } + else { + list(child); + } + } + + } + + public static void main(String[] args) { + + if (args.length <= 0) { + System.out.println("Usage: java nu.xom.samples.CommentReader URL"); + return; + } + + try { + Builder parser = new Builder(); + Document doc = parser.build(args[0]); + list(doc); + } + catch (ParsingException ex) { + System.out.println(args[0] + " is not well-formed."); + System.out.println(ex.getMessage()); + } + catch (IOException ex) { + System.out.println( + "Due to an IOException, the parser could not read " + + args[0] + ); + } + + } + +} \ No newline at end of file diff --git a/src/nu/xom/samples/CrimsonPrinter.java b/src/nu/xom/samples/CrimsonPrinter.java new file mode 100644 index 0000000..879cb3b --- /dev/null +++ b/src/nu/xom/samples/CrimsonPrinter.java @@ -0,0 +1,78 @@ +/* Copyright 2002-2004 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.samples; + +import java.io.IOException; + +import org.xml.sax.SAXException; +import org.xml.sax.XMLReader; +import org.xml.sax.helpers.XMLReaderFactory; + +import nu.xom.Builder; +import nu.xom.Document; +import nu.xom.ParsingException; + + +/** + *

    + * Demonstrates specifying the SAX parser class to use. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.1b4 + * + */ +public class CrimsonPrinter { + + public static void main(String[] args) { + + if (args.length <= 0) { + System.out.println("Usage: java nu.xom.samples.CrimsonPrinter URL"); + return; + } + + try { + XMLReader crimson = XMLReaderFactory.createXMLReader("org.apache.crimson.parser.XMLReaderImpl"); + Builder parser = new Builder(crimson); + Document doc = parser.build(args[0]); + System.out.println(doc.toXML()); + } + catch (ParsingException ex) { + ex.printStackTrace(); + System.out.println(args[0] + " is not well-formed."); + System.out.println(ex.getMessage()); + System.out.println(" in line " + ex.getLineNumber() + " at column " + ex.getColumnNumber() ); + ex.getCause().printStackTrace(); + } + catch (IOException ex) { + System.out.println( + "Due to an IOException, the parser could not print " + + args[0] + ); + } + catch (SAXException e) { + System.err.println("Could not find the Crimson parser in the classpath."); + } + + } + +} \ No newline at end of file diff --git a/src/nu/xom/samples/DTDGenerator.java b/src/nu/xom/samples/DTDGenerator.java new file mode 100644 index 0000000..2e5ef0b --- /dev/null +++ b/src/nu/xom/samples/DTDGenerator.java @@ -0,0 +1,103 @@ +/* Copyright 2003, 2019 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.samples; + +import java.io.IOException; + +import nu.xom.Attribute; +import nu.xom.Builder; +import nu.xom.Element; +import nu.xom.NodeFactory; +import nu.xom.Nodes; +import nu.xom.ParsingException; +import java.util.List; +import java.util.ArrayList; + +/** + * + *

    + * Demonstrates walking the tree while collecting + * element and attribute names and types. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.3.1 + * + */ +public class DTDGenerator { + + public static void main(String[] args) { + + if (args.length == 0) { + System.err.println( + "Usage: java nu.xom.samples.DTDGenerator URL" + ); + return; + } + + try { + Builder builder = new Builder(new NamingNodeFactory()); + builder.build(args[0]); + } + catch (IOException ex) { + System.err.println("Could not read " + args[0] + + " due to " + ex.getMessage()); + } + catch (ParsingException ex) { + System.err.println(args[0] + " is not well-formed"); + } + + } + + private static class NamingNodeFactory extends NodeFactory { + + private List names = new ArrayList(); + private String currentElement; + + public Element startMakingElement( + String name, String namespace) { + if (!names.contains(name)) { + System.out.println(""); + names.add(name); + } + currentElement = name; + return super.startMakingElement(name, namespace); + } + + public Nodes makeAttribute(String name, String URI, + String value, Attribute.Type type) { + + if (type.equals(Attribute.Type.ENUMERATION) + || type.equals(Attribute.Type.UNDECLARED)) { + type = Attribute.Type.CDATA; + } + String comboName = currentElement + '#' + name; + if (!names.contains(comboName)) { + names.add(comboName); + System.out.println(""); + } + return super.makeAttribute(name, URI, value, type); + } + } + +} \ No newline at end of file diff --git a/src/nu/xom/samples/DatabaseBuilder.java b/src/nu/xom/samples/DatabaseBuilder.java new file mode 100644 index 0000000..6b377f7 --- /dev/null +++ b/src/nu/xom/samples/DatabaseBuilder.java @@ -0,0 +1,275 @@ +/* Copyright 2003 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.samples; + +import nu.xom.*; +import java.sql.*; +import java.io.*; + + +/** + *

    + * Based on Example 8-13 in Processing XML with Java + *

    + * + * @author Elliotte Rusty Harold + * @version 1.0 + */ +public class DatabaseBuilder { + + private Connection connection; + + + // The string passed to the constructor must be a JDBC URL that + // contains all necessary information for connecting to the + // database such as host, port, username, password, and + // database name. For example, + // jdbc:mysql://host:port]/dbname?user=username&password=pass + // The driver should have been loaded before this method is + // called + public DatabaseBuilder(String jdbcURL) throws SQLException { + connection = DriverManager.getConnection(jdbcURL); + } + + + public Document build(String selectQuery) + throws SQLException, ParsingException { + + Statement statement = connection.createStatement(); + ResultSet data = statement.executeQuery(selectQuery); + ResultSetMetaData metadata = data.getMetaData(); + int numFields = metadata.getColumnCount(); + + Element table = new Element("table"); + + while (data.next()) { + Element record = new Element("record"); + for (int field = 1; field <= numFields; field++) { + Element fieldElement = new Element("field"); + int type = metadata.getColumnType(field); + String typeName = getSchemaType(type); + Attribute typeAtt = new Attribute("xsi:type", + "http://www.w3.org/2001/XMLSchema-instance", typeName); + fieldElement.addAttribute(typeAtt); + String name = metadata.getColumnName(field); + Attribute nameAtt = new Attribute("name", name); + fieldElement.addAttribute(nameAtt); + // Convert nulls to empty elements with xsi:nil="true" + Object value = data.getObject(field); + if (value == null) { // null value in database + Attribute nilAtt = new Attribute("xsi:nil", + "http://www.w3.org/2001/XMLSchema-instance", "true"); + fieldElement.addAttribute(nilAtt); + } + else { // non-null value + fieldElement.appendChild(convertToXML(data, field, type)); + } + record.appendChild(fieldElement); + } + table.appendChild(record); + } + statement.close(); + + table.addNamespaceDeclaration("xsi", + "http://www.w3.org/2001/XMLSchema-instance"); + table.addNamespaceDeclaration("xsd", "http://www.w3.org/2001/XMLSchema"); + return new Document(table); + + } + + + // I want the XML document to store values in the standard W3C + // XML Schema Language forms. This requires certain conversions + // depending on the type of the data + private Node convertToXML(ResultSet data, int field, int type) + throws SQLException, ParsingException { + + switch (type) { + case Types.BINARY: + case Types.VARBINARY: + case Types.LONGVARBINARY: + return hexEncode(data.getBinaryStream(field)); + case Types.BLOB: + Blob blob = data.getBlob(field); + return hexEncode(blob.getBinaryStream()); + case Types.CLOB: + Clob clob = data.getClob(field); + Reader r = clob.getCharacterStream(); + char[] text = new char[1024]; + int numRead; + try { + StringBuffer result = new StringBuffer(); + while ((numRead = r.read(text, 0, 1024)) != -1) { + result.append(escapeText(text, 0, numRead)); + } + return new Text(result.toString()); + } + catch (IOException ex) { + throw new ParsingException("Read from CLOB failed", ex); + } + case Types.ARRAY: + Array array = data.getArray(field); + return writeArray(array); + default: // All other types can be handled as strings + Object o = data.getObject(field); + if (o == null) return new Text(""); + String s = o.toString(); + char[] value = s.toCharArray(); + return escapeText(value, 0, value.length); + } + + } + + + private Text hexEncode(InputStream in) { + + StringBuffer result = new StringBuffer(); + try { + int octet; + while ((octet = in.read()) != -1) { + StringWriter out = new StringWriter(2); + if (octet < 16) out.write('0'); + out.write(Integer.toHexString(octet)); + result.append(out.toString()); + } + return new Text(result.toString()); + } + catch (IOException ex) { + throw new XMLException("Error while hex-encoding", ex); + } + + } + + + // String types may contain C0 control characters that are + // not legal in XML. I convert these to the Unicode replacement + // character 0xFFFD + private Text escapeText(char[] text, int start, int length) { + StringBuffer result = new StringBuffer(length); + for (int i = start; i < length; i++) { + result.append(escapeChar(text[i])); + } + return new Text(result.toString()); + } + + private char escapeChar(char c) { + if (c >= 0x20) return c; + else if (c == '\n') return c; + else if (c == '\r') return c; + else if (c == '\t') return c; + return '\uFFFD'; + } + + private Node writeArray(Array array) + throws SQLException, ParsingException { + + ResultSet data = array.getResultSet(); + int type = array.getBaseType(); + String typeName = getSchemaType(type); + + Element arrayElement = new Element("array"); + while (data.next()) { + Element component = new Element("component"); + component.addAttribute(new Attribute("xsi:type", + "http://www.w3.org/2001/XMLSchema-instance", + typeName)); + component.appendChild(convertToXML(data, 2, type)); + arrayElement.appendChild(component); + } + return arrayElement; + } + + + public static String getSchemaType(int type) { + + switch (type) { + case Types.ARRAY: return "array"; + case Types.BIGINT: return "xsd:long"; + case Types.BINARY: return "xsd:hexBinary"; + case Types.BIT: return "xsd:boolean"; + case Types.BLOB: return "xsd:hexBinary"; + case Types.CHAR: return "xsd:string"; + case Types.CLOB: return "xsd:string"; + case Types.DATE: return "xsd:date"; + case Types.DECIMAL: return "xsd:decimal"; + case Types.DOUBLE: return "xsd:double"; + case Types.FLOAT: return "xsd:decimal"; + case Types.INTEGER: return "xsd:int"; + case Types.JAVA_OBJECT: return "xsd:string"; + case Types.LONGVARBINARY: return "xsd:hexBinary"; + case Types.LONGVARCHAR: return "xsd:string"; + case Types.NUMERIC: return "xsd:decimal"; + case Types.REAL: return "xsd:float"; + case Types.REF: return "xsd:IDREF"; + case Types.SMALLINT: return "xsd:short"; + case Types.STRUCT: return "struct"; + case Types.TIME: return "xsd:time"; + case Types.TIMESTAMP: return "xsd:dateTime"; + case Types.TINYINT: return "xsd:byte"; + case Types.VARBINARY: return "xsd:hexBinary"; + // most general type + default: return "xsd:string"; + } + + } + + + public static void main(String[] args) { + + if (args.length < 2) { + System.out.println( + "Usage: java DatabaseBuilder URL query driverClass"); + return; + } + String url = args[0]; + String query = args[1]; + String driverClass = "org.gjt.mm.mysql.Driver"; // MySQL + if (args.length >= 3) driverClass = args[2]; + + try { + // Load JDBC driver + Class.forName(driverClass).newInstance(); + // Technically, the newInstance() call isn't needed, + // but the MM.MySQL documentation suggests this to + // "work around some broken JVMs" + + DatabaseBuilder builder = new DatabaseBuilder(url); + Serializer out = new Serializer(System.out); + + out.setIndent(2); + Document doc = builder.build(query); + out.write(doc); + out.flush(); + } + catch (InstantiationException ex) { + System.out.println(driverClass + " could not be instantiated"); + } + catch (ClassNotFoundException ex) { + System.out.println(driverClass + " could not be found"); + } + catch (Exception ex) { // SQL, SAX, and IO + ex.printStackTrace(); + } + + } + +} diff --git a/src/nu/xom/samples/DatabaseConverter.java b/src/nu/xom/samples/DatabaseConverter.java new file mode 100644 index 0000000..8237721 --- /dev/null +++ b/src/nu/xom/samples/DatabaseConverter.java @@ -0,0 +1,224 @@ +/* Copyright 2002, 2003 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.samples; + +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; +import java.sql.Array; +import java.sql.Blob; +import java.sql.Clob; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.ResultSet; +import java.sql.ResultSetMetaData; +import java.sql.SQLException; +import java.sql.Statement; +import java.sql.Types; + +import nu.xom.Attribute; +import nu.xom.Document; +import nu.xom.Element; +import nu.xom.Node; +import nu.xom.Text; +import nu.xom.XMLException; + + +/** + *

    + * Demonstrates the building of a structured XML document, + * from a relational database using JDBC. A different version of + * this example was originally developed for Chapter 4 of + * Processing + * XML with Java. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.0 + * + */ +public class DatabaseConverter { + + private Connection connection; + + // The string passed to the constructor must be a JDBC URL that + // contains all necessary information for connecting to the + // database such as host, port, username, password, and + // database name. For example, + // jdbc:mysql://host:port]/dbname?user=username&password=pass + // The driver should have been loaded before this method is + // called + public DatabaseConverter(String jdbcURL) throws SQLException { + connection = DriverManager.getConnection(jdbcURL); + } + + public Document extract(String selectQuery) + throws IOException { + + try { + Statement statement = connection.createStatement(); + ResultSet data = statement.executeQuery(selectQuery); + ResultSetMetaData metadata = data.getMetaData(); + int numFields = metadata.getColumnCount(); + + Element table = new Element("table"); + + while (data.next()) { + Element record = new Element("record"); + for (int field = 1; field <= numFields; field++) { + + Element fieldElement = new Element("field"); + int type = metadata.getColumnType(field); + String typeName = getSchemaType(type); + fieldElement.addAttribute(new Attribute("xsi:type", + "http://www.w3.org/2001/XMLSchema-instance", + typeName, Attribute.Type.NMTOKEN)); + String name = metadata.getColumnName(field); + fieldElement.addAttribute(new Attribute("name", name)); + + // Convert nulls to empty elements with xsi:nil="true" + Object value = data.getObject(field); + if (value == null) { // null value in database + fieldElement.addAttribute(new Attribute("xsi:nil", + "http://www.w3.org/2001/XMLSchema-instance", "true")); + } + else { // non-null value + fieldElement.appendChild(convertToXML(data, field, type)); + } + record.appendChild(fieldElement); + } + table.appendChild(record); + } // end while + statement.close(); + return new Document(table); + } + catch (SQLException ex) { // convert exception type + throw new XMLException("SQL error", ex); + } + + } + + // I want the XML document to store values in the standard W3C + // XML Schema Language forms. This requires certain conversions + // depending on the type of the data + private Node convertToXML(ResultSet data, int field, int type) + throws SQLException, IOException { + + switch (type) { + case Types.BINARY: + case Types.VARBINARY: + case Types.LONGVARBINARY: + return hexEncode(data.getBinaryStream(field)); + case Types.BLOB: + Blob blob = data.getBlob(field); + return hexEncode(blob.getBinaryStream()); + // String types may contain C0 control characters that are + // not legal in XML. If so an exception is thrown. + case Types.CLOB: + Clob clob = data.getClob(field); + Reader r = clob.getCharacterStream(); + char[] text = new char[1024]; + int numRead; + StringBuffer result = new StringBuffer(); + while ((numRead = r.read(text, 0, 1024)) != -1) { + result.append(text, 0, numRead); + } + return new Text(result.toString()); + case Types.ARRAY: + Array array = data.getArray(field); + return writeArray(array); + default: // All other types can be handled as strings + Object o = data.getObject(field); + if (o == null) return new Text(""); + return new Text(o.toString()); + } + + } + + private Text hexEncode(InputStream in) + throws IOException { + + StringBuffer result = new StringBuffer(); + + int octet; + while ((octet = in.read()) != -1) { + if (octet < 16) result.append('0'); + result.append(Integer.toHexString(octet)); + } + return new Text(result.toString()); + + } + + private Element writeArray(Array array) + throws IOException, SQLException { + + Element holder = new Element("array"); + ResultSet data = array.getResultSet(); + int type = array.getBaseType(); + String typeName = getSchemaType(type); + + while (data.next()) { + Element component = new Element("component"); + component.addAttribute(new Attribute("xsi:type", + "http://www.w3.org/2001/XMLSchema-instance", typeName)); + component.appendChild(convertToXML(data, 2, type)); + holder.appendChild(component); + } + return holder; + + } + + public static String getSchemaType(int type) { + + switch (type) { + case Types.ARRAY: return "array"; + case Types.BIGINT: return "xsd:long"; + case Types.BINARY: return "xsd:hexBinary"; + case Types.BIT: return "xsd:boolean"; + case Types.BLOB: return "xsd:hexBinary"; + case Types.CHAR: return "xsd:string"; + case Types.CLOB: return "xsd:string"; + case Types.DATE: return "xsd:date"; + case Types.DECIMAL: return "xsd:decimal"; + case Types.DOUBLE: return "xsd:double"; + case Types.FLOAT: return "xsd:decimal"; + case Types.INTEGER: return "xsd:int"; + case Types.JAVA_OBJECT: return "xsd:string"; + case Types.LONGVARBINARY: return "xsd:hexBinary"; + case Types.LONGVARCHAR: return "xsd:string"; + case Types.NUMERIC: return "xsd:decimal"; + case Types.REAL: return "xsd:float"; + case Types.REF: return "xsd:IDREF"; + case Types.SMALLINT: return "xsd:short"; + case Types.STRUCT: return "struct"; + case Types.TIME: return "xsd:time"; + case Types.TIMESTAMP: return "xsd:dateTime"; + case Types.TINYINT: return "xsd:byte"; + case Types.VARBINARY: return "xsd:hexBinary"; + // most general type + default: return "xsd:string"; + } + + } + +} \ No newline at end of file diff --git a/src/nu/xom/samples/DocumentMerger.java b/src/nu/xom/samples/DocumentMerger.java new file mode 100644 index 0000000..506d08d --- /dev/null +++ b/src/nu/xom/samples/DocumentMerger.java @@ -0,0 +1,59 @@ +/* Copyright 2002, 2003 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.samples; + +import java.io.IOException; + +import nu.xom.Builder; +import nu.xom.Document; +import nu.xom.ParsingException; + +/** + * + *

    + * Demonstrates parsing a document and reserializing it + * so as to resolve all entity references. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.0 + * + */ +public class DocumentMerger { + + public static void main(String[] args) { + + Builder builder = new Builder(); + try { + Document doc = builder.build(args[0]); + System.out.println(doc.toXML()); + } + catch (IOException ex) { + System.err.println(ex); + } + catch (ParsingException ex) { + System.err.println(ex); + } + + } + +} diff --git a/src/nu/xom/samples/EZSerializer.java b/src/nu/xom/samples/EZSerializer.java new file mode 100644 index 0000000..757a157 --- /dev/null +++ b/src/nu/xom/samples/EZSerializer.java @@ -0,0 +1,108 @@ +/* Copyright 2003, 2004 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.samples; + +import java.io.IOException; +import java.io.OutputStream; + +import nu.xom.Document; +import nu.xom.Serializer; + +/** + *

    + * A collection of static methods for serializing documents + * in one method call. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.0 + * + */ +public class EZSerializer { + + + /** + *

    + * Serializes a document onto the output stream in UTF-8 with no + * pretty printing. The stream is flushed but not closed when this + * method completes. + *

    + * + * @param doc the Document to serialize + * @param out the OutputStream on which the document + * is written + * + * @throws IOException if the underlying OutputStream + * encounters an I/O error + * @throws NullPointerException if doc is null + */ + public static void write(Document doc, OutputStream out) + throws IOException { + Serializer serializer = new Serializer(out); + serializer.write(doc); + serializer.flush(); + } + + /** + *

    + * Serializes a document onto the output stream in the specified + * encoding. White space is added to attempt to pretty print the + * document, potentially changing the document's content. + * Existing white space in the document may be trimmed or changed + * in the process. The stream is flushed but not closed when this + * method completes. + *

    + * + *

    + * The indent and maxLength arguments + * are suggestive, not prescriptive. XOM will attempt to honor + * them but cannot guarantee to do so. For instance, if an + * element name is longer than the maximum line length, XOM + * cannot break the element name. It has to emit an + * excessively long line. + *

    + * + * @param doc the Document to serialize + * @param out the OutputStream on which the document + * is written + * @param encoding the character encoding in which to write + * the document + * @param indent the number of spaces to indent each successive + * level of the hierarchy + * @param maxLength the maximum preferred number of characters per + * line + * + * @throws IOException if the underlying OutputStream + * encounters an I/O error + * @throws NullPointerException if doc is null + */ + public static void write(Document doc, OutputStream out, String encoding, + int indent, int maxLength) + throws IOException { + Serializer serializer = new Serializer(out, encoding); + serializer.setIndent(indent); + serializer.setMaxLength(maxLength); + serializer.write(doc); + serializer.flush(); + } + +} diff --git a/src/nu/xom/samples/ElementLister.java b/src/nu/xom/samples/ElementLister.java new file mode 100644 index 0000000..eda0e6a --- /dev/null +++ b/src/nu/xom/samples/ElementLister.java @@ -0,0 +1,88 @@ +/* Copyright 2002, 2003, 2018 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.samples; + +import java.io.IOException; + +import nu.xom.Builder; +import nu.xom.Document; +import nu.xom.Element; +import nu.xom.ParsingException; + + +/** + *

    + * Demonstrates walking the element hierarchy of + * an XML document. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.3.0 + * + */ +public class ElementLister { + + public static void main(String[] args) { + + if (args.length == 0) { + System.out.println("Usage: java nu.xom.samples.ElementLister URL"); + return; + } + + Builder builder = new Builder(); + + try { + Document doc = builder.build(args[0]); + Element root = doc.getRootElement(); + listChildren(root, 0); + } + // indicates a well-formedness error + catch (ParsingException ex) { + System.out.println(args[0] + " is not well-formed."); + System.out.println(ex.getMessage()); + } + catch (IOException ex) { + System.out.println(ex); + } + + } + + + public static void listChildren(Element current, int depth) { + + printSpaces(depth); + System.out.println(current.getQualifiedName()); + for (Element child : current.getChildElements()) { + listChildren(child, depth+1); + } + + } + + private static void printSpaces(int n) { + + for (int i = 0; i < n; i++) { + System.out.print(' '); + } + + } + +} diff --git a/src/nu/xom/samples/EncodingDemo.java b/src/nu/xom/samples/EncodingDemo.java new file mode 100644 index 0000000..bebf3b3 --- /dev/null +++ b/src/nu/xom/samples/EncodingDemo.java @@ -0,0 +1,73 @@ +/* Copyright 2002, 2003 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.samples; + + +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +import nu.xom.Attribute; +import nu.xom.Document; +import nu.xom.Element; +import nu.xom.Serializer; + +/** + * @author Elliotte Rusty Harold + * @version 1.0 + * + */ +public class EncodingDemo { + + public static void main(String[] args) { + + String encoding = "ISO-8859-2"; + if (args.length > 0) encoding = args[0]; + Element root = new Element("root"); + Document doc = new Document(root); + + for (int i = 0xA0; i <= 0x1FF; i++) { + Element data = new Element("data"); + data.appendChild((char) i + ""); + data.addAttribute( + new Attribute("character", String.valueOf(i)) + ); + root.appendChild(data); + } + + try { + OutputStream out + = new FileOutputStream("data_" + encoding + ".xml"); + Serializer serializer = new Serializer(out, encoding); + serializer.setIndent(4); + serializer.write(doc); + serializer.flush(); + out.close(); + } + catch (IOException ex) { + ex.printStackTrace(); + } + + + } + +} diff --git a/src/nu/xom/samples/ExampleExtractor.java b/src/nu/xom/samples/ExampleExtractor.java new file mode 100644 index 0000000..a5f0f09 --- /dev/null +++ b/src/nu/xom/samples/ExampleExtractor.java @@ -0,0 +1,142 @@ +/* Copyright 2002-2004, 2018 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.samples; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.io.Writer; + +import nu.xom.Builder; +import nu.xom.Document; +import nu.xom.Element; +import nu.xom.ParsingException; + +/** + *

    + * Demonstrates extracting the content of particular named elements + * from one XML document, and storing them into new files. + * The names of these files are based on an attribute of the + * original element. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.3.0 + * + */ +public class ExampleExtractor { + + private static int chapter = 0; + + public static void extract(Element element) + throws IOException { + + for (Element child : element.getChildElements()) { + if (child.getQualifiedName().equals("chapter")) { + chapter++; + } + if (child.getQualifiedName().equals("example")) { + extractExample(child, chapter); + } + else { + extract(child); + } + } // end chapters loop + + } + + + private static void extractExample(Element example, int chapter) + throws IOException { + + String fileName = example.getAttribute("id").getValue(); + Element programlisting + = example.getFirstChildElement("programlisting"); + + // A few examples use screens instead of programlistings + if (programlisting == null) { + programlisting = example.getFirstChildElement("screen"); + } + // If it's still null, skip it + if (programlisting == null) return; + String code = programlisting.getValue(); + + // write code into a file + File dir = new File("examples2/" + chapter); + dir.mkdirs(); + File file = new File(dir, fileName); + System.out.println(file); + FileOutputStream fout = new FileOutputStream(file); + Writer out = new OutputStreamWriter(fout, "UTF-8"); + try { + // Buffering almost always helps performance a lot + out = new BufferedWriter(out); + out.write(code); + // Be sure to flush and close your streams + out.flush(); + } + finally { + fout.close(); + } + + } + + + public static void main(String[] args) { + + if (args.length <= 0) { + System.out.println( + "Usage: java nu.xom.samples.ExampleExtractor URL"); + return; + } + String url = args[0]; + + try { + Builder builder = new Builder(); + // Read the document + Document document = builder.build(args[0]); + + // Extract the examples + extract(document.getRootElement()); + + } + catch (ParsingException ex) { + System.out.println(ex); + } + catch (IOException ex) { + System.out.println( + "Due to an IOException, the parser could not read " + url + ); + System.out.println(ex); + } + catch (OutOfMemoryError ex) { + System.out.println(ex); + ex.printStackTrace(); + } + + } // end main + + +} + diff --git a/src/nu/xom/samples/ExampleLister.java b/src/nu/xom/samples/ExampleLister.java new file mode 100644 index 0000000..c0f9dad --- /dev/null +++ b/src/nu/xom/samples/ExampleLister.java @@ -0,0 +1,131 @@ +/* Copyright 2002, 2003, 2018 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.samples; + +import java.io.IOException; + +import nu.xom.Builder; +import nu.xom.Document; +import nu.xom.Element; +import nu.xom.ParsingException; + + +/** + *

    + * Demonstrates extracting the content + * of particular named elements + * in a particular context + * from an XML document. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.3.0 + * + */ +public class ExampleLister { + + private static int chapter = 0; + + public static void list(Element root) { + + chapter = 0; + if (root.getLocalName().equals("chapter")) { + chapter++; + exampleNumber = 0; + list(root); + } + else { + for (Element child : root.getChildElements()) { + if (child.getLocalName().equals("chapter")) { + chapter++; + exampleNumber = 0; + findExamples(child); + } + else { + list(child); + } + } + + } + + } + + + private static int exampleNumber = 0; + + private static void findExamples(Element element) { + + for (Element child : element.getChildElements()) { + if (child.getQualifiedName().equals("example")) { + printExample(child); + } + else { + findExamples(child); + } + } + + } + + + private static void printExample(Element example) { + + exampleNumber++; + Element title = example.getFirstChildElement("title"); + + String caption = "Example " + chapter + "." + exampleNumber + + ": " + title.getValue(); + + System.out.println(caption); + + } + + + public static void main(String[] args) { + + if (args.length <= 0) { + System.out.println("Usage: java nu.xom.samples.ExampleLister URL"); + return; + } + String url = args[0]; + + try { + Builder builder = new Builder(); + // Read the document + Document document = builder.build(args[0]); + + // List the examples + list(document.getRootElement()); + + } + catch (ParsingException ex) { + System.out.println(ex); + } + catch (IOException ex) { + System.out.println( + "Due to an IOException, the parser could not read " + url + ); + System.out.println(ex); + } + + } // end main + +} \ No newline at end of file diff --git a/src/nu/xom/samples/FibonacciAttributes.java b/src/nu/xom/samples/FibonacciAttributes.java new file mode 100644 index 0000000..ad84ba9 --- /dev/null +++ b/src/nu/xom/samples/FibonacciAttributes.java @@ -0,0 +1,66 @@ +/* Copyright 2002, 2003 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.samples; + +import java.math.BigInteger; + +import nu.xom.Attribute; +import nu.xom.Document; +import nu.xom.Element; + + +/** + *

    + * Demonstrates the creation of a simple document + * with basic nested element structure and some attributes + * on the elements. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.0 + * + */ +public class FibonacciAttributes { + + public static void main(String[] args) { + + BigInteger low = BigInteger.ONE; + BigInteger high = BigInteger.ONE; + + Element root = new Element("Fibonacci_Numbers"); + for (int i = 1; i <= 10; i++) { + Element fibonacci = new Element("fibonacci"); + fibonacci.appendChild(low.toString()); + Attribute index = new Attribute("index", String.valueOf(i)); + fibonacci.addAttribute(index); + root.appendChild(fibonacci); + + BigInteger temp = high; + high = high.add(low); + low = temp; + } + Document doc = new Document(root); + System.out.println(doc.toXML()); + + } + +} \ No newline at end of file diff --git a/src/nu/xom/samples/FibonacciEBCDIC.java b/src/nu/xom/samples/FibonacciEBCDIC.java new file mode 100644 index 0000000..395df7c --- /dev/null +++ b/src/nu/xom/samples/FibonacciEBCDIC.java @@ -0,0 +1,81 @@ +/* Copyright 2002, 2003 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.samples; + +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.math.BigInteger; + +import nu.xom.Document; +import nu.xom.Element; +import nu.xom.Serializer; + + +/** + *

    + * Demonstrates the creation of a simple document + * with basic nested element structure + * using EBCDIC encoding. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.0 + * + */ +public class FibonacciEBCDIC { + + public final static char NEL = 0x85; + + public static void main(String[] args) { + + BigInteger low = BigInteger.ONE; + BigInteger high = BigInteger.ONE; + + Element root = new Element("Fibonacci_Numbers"); + for (int i = 1; i <= 10; i++) { + root.appendChild(NEL+ " "); + Element fibonacci = new Element("fibonacci"); + fibonacci.appendChild(low.toString()); + root.appendChild(fibonacci); + + BigInteger temp = high; + high = high.add(low); + low = temp; + } + root.appendChild("" + NEL); + Document doc = new Document(root); + try { + OutputStream out + = new FileOutputStream("fibonacci_ebcdic.txt"); + Serializer serializer + = new Serializer(out, "Cp037"); + serializer.write(doc); + serializer.flush(); + } + catch (IOException ex) { + System.err.println(ex); + } + + } + +} \ No newline at end of file diff --git a/src/nu/xom/samples/FibonacciFile.java b/src/nu/xom/samples/FibonacciFile.java new file mode 100644 index 0000000..8d7cddd --- /dev/null +++ b/src/nu/xom/samples/FibonacciFile.java @@ -0,0 +1,75 @@ +/* Copyright 2002, 2003 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.samples; + +import java.io.BufferedOutputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.math.BigInteger; + +import nu.xom.Document; +import nu.xom.Element; +import nu.xom.Serializer; + +/** + *

    + * Demonstrates the creation of a simple document + * in memory and saving it to a file. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.0 + * + */ +public class FibonacciFile { + + public static void main(String[] args) { + + BigInteger low = BigInteger.ONE; + BigInteger high = BigInteger.ONE; + + Element root = new Element("Fibonacci_Numbers"); + for (int i = 1; i <= 10; i++) { + Element fibonacci = new Element("fibonacci"); + fibonacci.appendChild(low.toString()); + root.appendChild(fibonacci); + + BigInteger temp = high; + high = high.add(low); + low = temp; + } + Document doc = new Document(root); + try { + OutputStream out = new FileOutputStream("fibonacci.xml"); + out = new BufferedOutputStream(out); + Serializer serializer + = new Serializer(out, "ISO-8859-1"); + serializer.write(doc); + } + catch (IOException ex) { + System.err.println("This shouldn't happen for Latin-1!"); + } + + } + +} \ No newline at end of file diff --git a/src/nu/xom/samples/FibonacciLatin1.java b/src/nu/xom/samples/FibonacciLatin1.java new file mode 100644 index 0000000..0da4251 --- /dev/null +++ b/src/nu/xom/samples/FibonacciLatin1.java @@ -0,0 +1,73 @@ +/* Copyright 2002, 2003 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.samples; + +import java.io.IOException; +import java.math.BigInteger; + +import nu.xom.Document; +import nu.xom.Element; +import nu.xom.Serializer; + + +/** + *

    + * Demonstrates the creation of a simple document + * with basic nested element structure + * and the serialization of that document in a + * specified encoding. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.0 + * + */ +public class FibonacciLatin1 { + + public static void main(String[] args) { + + BigInteger low = BigInteger.ONE; + BigInteger high = BigInteger.ONE; + + Element root = new Element("Fibonacci_Numbers"); + for (int i = 1; i <= 10; i++) { + Element fibonacci = new Element("fibonacci"); + fibonacci.appendChild(low.toString()); + root.appendChild(fibonacci); + + BigInteger temp = high; + high = high.add(low); + low = temp; + } + Document doc = new Document(root); + try { + Serializer serializer + = new Serializer(System.out, "ISO-8859-1"); + serializer.write(doc); + } + catch (IOException ex) { + System.err.println("This shouldn't happen for Latin-1!"); + } + + } + +} \ No newline at end of file diff --git a/src/nu/xom/samples/FibonacciSOAPClient.java b/src/nu/xom/samples/FibonacciSOAPClient.java new file mode 100644 index 0000000..2b4c1be --- /dev/null +++ b/src/nu/xom/samples/FibonacciSOAPClient.java @@ -0,0 +1,205 @@ +/* Copyright 2002, 2003, 2018 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.samples; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.HttpURLConnection; +import java.net.URL; +import java.net.URLConnection; + +import nu.xom.Attribute; +import nu.xom.Builder; +import nu.xom.Document; +import nu.xom.Element; +import nu.xom.Elements; +import nu.xom.ParsingException; +import nu.xom.Serializer; + +/** + *

    + * Demonstrates communication with a SOAP + * server via the creation of an XML document, + * transmission of that document over the network, + * and reception and parsing of the server's response + * document. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.3.0 + * + */ +public class FibonacciSOAPClient { + + public final static String defaultServer + = "http://www.elharo.com/fibonacci/SOAP"; + public final static String SOAP_ACTION + = "http://www.example.com/fibonacci"; + + public static void main(String[] args) { + + if (args.length == 0) { + System.out.println( + "Usage: java nu.xom.samples.FibonacciSOAPClient index serverURL" + ); + return; + } + + String index = args[0]; + + String server; + if (args.length <= 1) server = defaultServer; + else server = args[1]; + + Document request = buildRequest(index); + + try { + URL u = new URL(server); + URLConnection uc = u.openConnection(); + HttpURLConnection connection = (HttpURLConnection) uc; + connection.setDoOutput(true); + connection.setDoInput(true); + connection.setRequestMethod("POST"); + connection.setRequestProperty("SOAPAction", SOAP_ACTION); + + OutputStream out = connection.getOutputStream(); + Serializer serializer = new Serializer(out, "US-ASCII"); + serializer.write(request); + serializer.flush(); + + InputStream in = connection.getInputStream(); + + Builder parser = new Builder(); + Document response = parser.build(in); + in.close(); + out.close(); + connection.disconnect(); + + /* This is the response we expect: + * + + + + 1 + 1 + 2 + 3 + 5 + 8 + 13 + 21 + 34 + 55 + + + + * + */ + + Element responseEnvelope = response.getRootElement(); + Element responseBody = responseEnvelope.getFirstChildElement("Body", + "http://schemas.xmlsoap.org/soap/envelope/"); + + // Check for fault + Element fault = responseBody.getFirstChildElement( + "Fault", "http://schemas.xmlsoap.org/soap/envelope/"); + if (fault == null) { // no fault + Element responseNumbers = responseBody.getFirstChildElement( + "Fibonacci_Numbers", "http://namespaces.cafeconleche.org/xmljava/ch3/"); + Elements results = responseNumbers.getChildElements("fibonacci", + "http://namespaces.cafeconleche.org/xmljava/ch3/"); + for (Element result : results) { + System.out.println(result.getValue()); + } + } + else { + handleFault(fault); + } + + } + catch (ParsingException ex) { + System.err.println("Server sent malformed output"); + System.err.println(ex); + } + catch (NullPointerException ex) { + System.err.println( + "Server sent invalid output without the expected content." + ); + System.err.println(ex); + } + catch (IOException ex) { + System.err.println(ex); + ex.printStackTrace(); + } + + } + + private static void handleFault(Element fault) { + + Element faultcode = fault.getFirstChildElement("faultcode"); + Element faultstring = fault.getFirstChildElement("faultstring"); + Element faultactor = fault.getFirstChildElement("faultactor"); + Element detail = fault.getFirstChildElement("detail"); + + String error = "Fault: \n"; + if (faultcode != null) { + error += "Fault code: " + faultcode.getValue() + "\n"; + } + if (faultstring != null) { + error += "Fault string: " + faultstring.getValue() + "\n"; + } + if (faultactor != null) { + error += "Fault actor: " + faultactor.getValue() + "\n"; + } + if (detail != null) { + error += "Details: " + detail.getValue() + "\n"; + } + + } + + public static Document buildRequest(String index) { + + String SOAPNamespace = "http://schemas.xmlsoap.org/soap/envelope/"; + Element envelope = new Element("SOAP-ENV:Envelope", + SOAPNamespace); + Element body = new Element("SOAP-ENV:Body", + SOAPNamespace); + Element calculateFibonacci = new Element("calculateFibonacci", + "http://namespaces.cafeconleche.org/xmljava/ch3/"); + calculateFibonacci.appendChild(index); + calculateFibonacci.addNamespaceDeclaration("xsi", + "http://www.w3.org/2001/XMLSchema-instance"); + Attribute type = new Attribute("type", "xsi:positiveInteger"); + calculateFibonacci.addAttribute(type); + + envelope.appendChild(body); + body.appendChild(calculateFibonacci); + + Document doc = new Document(envelope); + return doc; + + } + +} \ No newline at end of file diff --git a/src/nu/xom/samples/FibonacciServlet.java b/src/nu/xom/samples/FibonacciServlet.java new file mode 100644 index 0000000..f7eecec --- /dev/null +++ b/src/nu/xom/samples/FibonacciServlet.java @@ -0,0 +1,106 @@ +/* Copyright 2002-2004 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.samples; + +import java.io.IOException; +import java.io.OutputStream; +import java.math.BigInteger; + +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import nu.xom.Attribute; +import nu.xom.Document; +import nu.xom.Element; +import nu.xom.ProcessingInstruction; +import nu.xom.Serializer; + + +/** + *

    + * Demonstrates a servlet that processes XML-RPC requests. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.0 + * + */ +public class FibonacciServlet extends HttpServlet { + + + public void doGet( + HttpServletRequest request, HttpServletResponse response) + throws IOException { + + //read the query string + int numberOfGenerations; + String generations = request.getParameter("generations"); + try { + numberOfGenerations = Integer.parseInt(generations); + } + catch (Exception ex) { // NumberFormat or NullPointerException + // use default value of 10 + numberOfGenerations = 10; + } + + response.setContentType("text/xml; charset=UTF-8"); + OutputStream out = response.getOutputStream(); + + Element root = new Element("Fibonacci_Numbers"); + Document doc = new Document(root); + ProcessingInstruction stylesheet = new + ProcessingInstruction("xml-stylesheet", + "type='text/css' href='/xml/styles/fibonacci.css'"); + doc.insertChild(stylesheet, 0); + + BigInteger low = BigInteger.ONE; + BigInteger high = BigInteger.ONE; + for (int i = 1; i <= numberOfGenerations; i++) { + Element fibonacci = new Element("fibonacci"); + Attribute index + = new Attribute("index", String.valueOf(i)); + fibonacci.addAttribute(index); + fibonacci.appendChild(low.toString()); + root.appendChild(fibonacci); + + BigInteger temp = high; + high = high.add(low); + low = temp; + } + Serializer serializer = new Serializer(out, "UTF-8"); + serializer.write(doc); + serializer.flush(); + out.close(); + + } + + + public void doPost( + HttpServletRequest request, HttpServletResponse response) + throws IOException { + + doGet(request, response); + + } + +} \ No newline at end of file diff --git a/src/nu/xom/samples/FibonacciXML.java b/src/nu/xom/samples/FibonacciXML.java new file mode 100644 index 0000000..1e69135 --- /dev/null +++ b/src/nu/xom/samples/FibonacciXML.java @@ -0,0 +1,62 @@ +/* Copyright 2002, 2003 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.samples; + +import java.math.BigInteger; + +import nu.xom.Document; +import nu.xom.Element; + + +/** + *

    + * Demonstrates the creation of a simple document + * with basic nested element structure. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.0 + * + */ +public class FibonacciXML { + + public static void main(String[] args) { + + BigInteger low = BigInteger.ONE; + BigInteger high = BigInteger.ONE; + + Element root = new Element("Fibonacci_Numbers"); + for (int i = 1; i <= 10; i++) { + Element fibonacci = new Element("fibonacci"); + fibonacci.appendChild(low.toString()); + root.appendChild(fibonacci); + + BigInteger temp = high; + high = high.add(low); + low = temp; + } + Document doc = new Document(root); + System.out.println(doc.toXML()); + + } + +} \ No newline at end of file diff --git a/src/nu/xom/samples/FibonacciXMLRPCClient.java b/src/nu/xom/samples/FibonacciXMLRPCClient.java new file mode 100644 index 0000000..fa2a644 --- /dev/null +++ b/src/nu/xom/samples/FibonacciXMLRPCClient.java @@ -0,0 +1,176 @@ +/* Copyright 2002, 2003 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.samples; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.HttpURLConnection; +import java.net.URL; +import java.net.URLConnection; + +import nu.xom.Builder; +import nu.xom.Document; +import nu.xom.Element; +import nu.xom.Elements; +import nu.xom.ParsingException; +import nu.xom.Serializer; + + +/** + *

    + * Demonstrates communication with an XML-RPC + * server via the creation of a simple document, + * transmission of that document over the network, + * and reception and parsing of the server's response. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.0 + * + */ +public class FibonacciXMLRPCClient { + + private static String defaultServer + = "http://www.elharo.com/fibonacci/XML-RPC"; + + public static void main(String[] args) { + + if (args.length == 0) { + System.out.println( + "Usage: java nu.xom.samples.FibonacciXMLRPCClient " + + " index serverURL" + ); + return; + } + + String index = args[0]; + + String server; + if (args.length <= 1) server = defaultServer; + else server = args[1]; + + try { + URL u = new URL(server); + URLConnection uc = u.openConnection(); + HttpURLConnection connection = (HttpURLConnection) uc; + connection.setDoOutput(true); + connection.setDoInput(true); + connection.setRequestMethod("POST"); + OutputStream out = connection.getOutputStream(); + + Element methodCall = new Element("methodCall"); + Element methodName = new Element("methodName"); + methodName.appendChild("calculateFibonacci"); + Element params = new Element("params"); + Element param = new Element("param"); + Element value = new Element("value"); + Element data = new Element("int"); + data.appendChild(index); + methodCall.appendChild(methodName); + methodCall.appendChild(params); + params.appendChild(param); + param.appendChild(value); + value.appendChild(data); + Document doc = new Document(methodCall); + + Serializer serializer = new Serializer(out, "US-ASCII"); + serializer.write(doc); + + InputStream in = connection.getInputStream(); + + Builder parser = new Builder(); + Document response = parser.build(in); + + in.close(); + out.close(); + connection.disconnect(); + + Element methodResponse = response.getRootElement(); + Elements body = methodResponse.getChildElements(); + if (body.size() != 1) { + System.err.println("XML-RPC format error"); + return; + } + if (body.get(0).getQualifiedName().equals("params")) { + Element responseParam = body.get(0).getFirstChildElement("param"); + Element responseValue + = responseParam.getFirstChildElement("value"); + Element responseDouble + = responseValue.getFirstChildElement("double"); + System.out.println(responseDouble.getValue()); + } + else if (body.get(0).getQualifiedName().equals("fault")) { + handleFault(body.get(0)); + } + else { + System.err.println("XML-RPC Format Error"); + return; + } + + } + catch (ParsingException ex) { + System.err.println("Server sent malformed output"); + System.err.println(ex); + } + catch (NullPointerException ex) { + System.err.println( + "Server sent invalid output without the expected content." + ); + System.err.println(ex); + } + catch (IOException ex) { + System.err.println(ex); + } + + } + + private static void handleFault(Element fault) { + + Element value = fault.getFirstChildElement("value"); + Element struct = value.getFirstChildElement("struct"); + Elements members = struct.getChildElements("member"); + Element member1 = members.get(0); + Element member2 = members.get(1); + String code = ""; + String detail = ""; + Element name1 = member1.getFirstChildElement("name"); + Element value1 = member1.getFirstChildElement("value"); + Element name2 = member2.getFirstChildElement("name"); + Element value2 = member2.getFirstChildElement("value"); + if (name1.getValue().equals("faultCode")) { + code = value1.getValue(); + detail = value2.getValue(); + } + else if (name2.getValue().equals("faultCode")) { + code = value2.getValue(); + detail = value1.getValue(); + } + else { + throw new RuntimeException("Incorrect fault message"); + } + System.err.println("Fault: "); + System.err.println(" code: " + code); + System.err.println(" " + detail); + } + +} \ No newline at end of file diff --git a/src/nu/xom/samples/FibonacciXOMSOAPServlet.java b/src/nu/xom/samples/FibonacciXOMSOAPServlet.java new file mode 100644 index 0000000..3f03056 --- /dev/null +++ b/src/nu/xom/samples/FibonacciXOMSOAPServlet.java @@ -0,0 +1,228 @@ +/* Copyright 2002-2004 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.samples; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.math.BigInteger; + +import javax.servlet.ServletException; +import javax.servlet.SingleThreadModel; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import nu.xom.Builder; +import nu.xom.Document; +import nu.xom.Element; +import nu.xom.WellformednessException; + + +/** + *

    + * Demonstrates a servlet that receives and + * responds to SOAP requests. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.0 + * + */ +public class FibonacciXOMSOAPServlet extends HttpServlet + implements SingleThreadModel { + + // Fault codes + public final static String MALFORMED_REQUEST_DOCUMENT + = "MalformedRequest"; + public final static String INVALID_REQUEST_DOCUMENT + = "InvalidRequest"; + public final static String INDEX_MISSING + = "IndexMissing"; + public final static String NON_POSITIVE_INDEX + = "NonPositiveIndex"; + public final static String BAD_INTEGER_FORMAT + = "BadIntegerFormat"; + public final static String UNEXPECTED_PROBLEM + = "UnexpectedProblem"; + + private transient Builder parser; + + // Load a parser, transformer, and implementation + public void init() throws ServletException { + + try { + this.parser = new Builder(); + } + catch (Exception ex) { + throw new ServletException( + "Could not locate a XOM parser", ex); + } + + } + + + public void doPost(HttpServletRequest servletRequest, + HttpServletResponse servletResponse) + throws ServletException, IOException { + + servletResponse.setContentType("application/soap+xml; charset=UTF-8"); + OutputStreamWriter out = new OutputStreamWriter( + servletResponse.getOutputStream(), "UTF-8"); + InputStream in = servletRequest.getInputStream(); + + Document request; + Document response; + String generations ="here"; + try { + request = parser.build(in); + +/* + + + 10 + + */ + + generations = request.getValue().trim(); + int numberOfGenerations = Integer.parseInt(generations); + BigInteger result = calculateFibonacci(numberOfGenerations); + response = makeResponseDocument(result); + } + catch (WellformednessException ex) { + response = makeFaultDocument(MALFORMED_REQUEST_DOCUMENT, + ex.getMessage()); + } + catch (NullPointerException ex) { + response = makeFaultDocument(INDEX_MISSING, + ex.getMessage()); + } + catch (NumberFormatException ex) { + response = makeFaultDocument(BAD_INTEGER_FORMAT, + generations + ex.getMessage()); + } + catch (IndexOutOfBoundsException ex) { + response = makeFaultDocument(NON_POSITIVE_INDEX, + ex.getMessage()); + } + catch (Exception ex) { + response = makeFaultDocument(UNEXPECTED_PROBLEM, + ex.getMessage()); + } + + // Transform onto the OutputStream + try { + out.write(response.toXML()); + servletResponse.flushBuffer(); + out.flush(); + } + catch (IOException ex) { + // If we get an exception at this point, it's too late to + // switch over to a SOAP fault + throw new ServletException(ex); + } + finally { + in.close(); + out.close(); + } + + } + + + // The details of the formats and namespace URIs are likely to + // change when SOAP 1.2 is released. + public Document makeResponseDocument(BigInteger result) { + + Element envelope = new Element("SOAP-ENV:Envelope", + "http://schemas.xmlsoap.org/soap/envelope/"); + Document response = new Document(envelope); + Element body = new Element("SOAP-ENV:Body", + "http://schemas.xmlsoap.org/soap/envelope/"); + envelope.appendChild(body); + + Element Fibonacci_Numbers = new Element("Fibonacci_Numbers", + "http://namespaces.cafeconleche.org/xmljava/ch3/"); + body.appendChild(Fibonacci_Numbers); + + Element fibonacci = new Element("fibonacci", + "http://namespaces.cafeconleche.org/xmljava/ch3/"); + Fibonacci_Numbers.appendChild(fibonacci); + fibonacci.appendChild(result.toString()); + + return response; + + } + + + public Document makeFaultDocument(String code, String message){ + + Element envelope = new Element("SOAP-ENV:Envelope", + "http://schemas.xmlsoap.org/soap/envelope/"); + Document faultDoc = new Document(envelope); + + Element body = new Element("SOAP-ENV:Body", + "http://schemas.xmlsoap.org/soap/envelope/"); + envelope.appendChild(body); + + Element fault = new Element("Fault", + "http://schemas.xmlsoap.org/soap/envelope/"); + body.appendChild(fault); + + Element faultCode = new Element("faultcode"); + fault.appendChild(faultCode); + + Element faultString = new Element("faultstring"); + fault.appendChild(faultString); + + faultCode.appendChild(code); + + faultString.appendChild(message); + + return faultDoc; + + } + + + public static BigInteger calculateFibonacci(int generations) + throws IndexOutOfBoundsException { + + if (generations < 1) { + throw new IndexOutOfBoundsException( + "Fibonacci numbers are not defined for " + generations + + "or any other number less than one."); + } + BigInteger low = BigInteger.ONE; + BigInteger high = BigInteger.ONE; + for (int i = 2; i <= generations; i++) { + BigInteger temp = high; + high = high.add(low); + low = temp; + } + return low; + + } + +} diff --git a/src/nu/xom/samples/FibonacciXOMXMLRPCServlet.java b/src/nu/xom/samples/FibonacciXOMXMLRPCServlet.java new file mode 100644 index 0000000..6c87548 --- /dev/null +++ b/src/nu/xom/samples/FibonacciXOMXMLRPCServlet.java @@ -0,0 +1,202 @@ +/* Copyright 2002, 2003 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.samples; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.math.BigInteger; + +import javax.servlet.ServletException; +import javax.servlet.SingleThreadModel; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import nu.xom.Builder; +import nu.xom.Document; +import nu.xom.Element; +import nu.xom.Serializer; +import nu.xom.XMLException; + +/** + *

    + * Demonstrates a servlet that receives and + * responds to XML-RPC requests. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.0 + * + */ +public class FibonacciXOMXMLRPCServlet extends HttpServlet + implements SingleThreadModel { + + // Fault codes + public final static int MALFORMED_REQUEST_DOCUMENT = 1; + public final static int INVALID_REQUEST_DOCUMENT = 2; + public final static int INDEX_MISSING = 3; + public final static int NON_POSITIVE_INDEX = 4; + public final static int BAD_INTEGER_FORMAT = 5; + public final static int UNEXPECTED_PROBLEM = 255; + + private transient Builder parser; + + // Load a parser, transformer, and implementation + public void init() throws ServletException { + + try { + this.parser = new Builder(); + } + catch (Exception ex) { + throw new ServletException( + "Could not locate a JAXP parser", ex); + } + + } + + // Respond to an XML-RPC request + public void doPost(HttpServletRequest servletRequest, + HttpServletResponse servletResponse) + throws ServletException, IOException { + + servletResponse.setContentType("application/xml; charset=UTF-8"); + OutputStream out = servletResponse.getOutputStream(); + InputStream in = servletRequest.getInputStream(); + + Document request; + Document response; + try { + request = parser.build(in); + Element methodCall = request.getRootElement(); + Element params = methodCall.getFirstChildElement("params"); + String generations = params.getValue().trim(); + int numberOfGenerations = Integer.parseInt(generations); + BigInteger result = calculateFibonacci(numberOfGenerations); + response = makeResponseDocument(result); + } + catch (XMLException ex) { + response = makeFaultDocument(MALFORMED_REQUEST_DOCUMENT, ex.getMessage()); + } + catch (NullPointerException ex) { + response = makeFaultDocument(INDEX_MISSING, ex.getMessage()); + } + catch (NumberFormatException ex) { + response = makeFaultDocument(BAD_INTEGER_FORMAT, ex.getMessage()); + } + catch (Exception ex) { + response = makeFaultDocument(UNEXPECTED_PROBLEM, ex.getMessage()); + } + + // Transform onto the OutputStream + try { + Serializer output = new Serializer(out, "US-ASCII"); + output.write(response); + servletResponse.flushBuffer(); + out.flush(); + } + catch (Exception ex) { + // If we get an exception at this point, it's too late to + // switch over to an XML-RPC fault. + throw new ServletException(ex); + } + + } + + // If performance is an issue, this could be pre-built in the + // init() method and then cached. You'd just change one text + // node each time. This would only work in a SingleThreadModel + // servlet. + public Document makeResponseDocument(BigInteger result) { + + Element methodResponse = new Element("methodResponse"); + Element params = new Element("params"); + Element param = new Element("param"); + Element value = new Element("value"); + Element doubleElement = new Element("double"); + + methodResponse.appendChild(params); + params.appendChild(param); + param.appendChild(value); + value.appendChild(doubleElement); + doubleElement.appendChild(result.toString()); + + return new Document(methodResponse); + + } + + public Document makeFaultDocument(int faultCode, String faultString) { + + Element methodResponse = new Element("methodResponse"); + + Element fault = new Element("fault"); + Element value = new Element("value"); + Element struct = new Element("struct"); + Element memberCode = new Element("member"); + Element nameCode = new Element("name"); + Element valueCode = new Element("value"); + Element intCode = new Element("int"); + Element memberString = new Element("member"); + Element valueString = new Element("value"); + Element stringString = new Element("string"); + + methodResponse.appendChild(fault); + fault.appendChild(value); + value.appendChild(struct); + struct.appendChild(memberCode); + struct.appendChild(memberString); + memberCode.appendChild(nameCode); + memberCode.appendChild(valueCode); + memberString.appendChild("name"); + memberString.appendChild(valueString); + nameCode.appendChild("faultCode"); + valueCode.appendChild(intCode); + valueString.appendChild(stringString); + intCode.appendChild(String.valueOf(faultCode)); + stringString.appendChild(faultString); + + Document faultDoc = new Document(methodResponse); + + return faultDoc; + + } + + public static BigInteger calculateFibonacci(int generations) + throws IndexOutOfBoundsException { + + if (generations < 1) { + throw new IndexOutOfBoundsException( + "Fibonacci numbers are not defined for " + generations + + "or any other number less than one."); + } + BigInteger low = BigInteger.ONE; + BigInteger high = BigInteger.ONE; + for (int i = 2; i <= generations; i++) { + BigInteger temp = high; + high = high.add(low); + low = temp; + } + return low; + + } + +} diff --git a/src/nu/xom/samples/FilterDriver.java b/src/nu/xom/samples/FilterDriver.java new file mode 100644 index 0000000..e90dad8 --- /dev/null +++ b/src/nu/xom/samples/FilterDriver.java @@ -0,0 +1,72 @@ +/* Copyright 2002, 2003 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.samples; + +import nu.xom.Builder; +import nu.xom.Document; +import nu.xom.NodeFactory; +import nu.xom.Serializer; + +/** + * @author Elliotte Rusty Harold + * @version 1.0 + * + */ +public class FilterDriver { + + + /** + *

    + * The driver method for the various NodeFactory + * based filters. + *

    + * + * @param args args[0] contains the fully qualified + * class name of the ,codeNodeFactory to use. + * @param args args[1] contains the URL or + * file name of the first document to be processed. + */ + public static void main(String[] args) { + + if (args.length < 2) { + System.out.println( + "Usage: java nu.xom.samples.FilterDriver filterclass URL" + ); + return; + } + + try { + Builder builder = new Builder((NodeFactory) Class.forName(args[0]).newInstance()); + Serializer outputter = new Serializer(System.out, "ISO-8859-1"); + Document input = builder.build(args[1]); + outputter.write(input); + } + catch (ClassNotFoundException ex) { + System.err.println("Could not find filter " + args[0]); + } + catch (Exception ex) { + System.err.println(ex); + ex.printStackTrace(); + } + + } +} diff --git a/src/nu/xom/samples/FlatXMLBudget.java b/src/nu/xom/samples/FlatXMLBudget.java new file mode 100644 index 0000000..6071307 --- /dev/null +++ b/src/nu/xom/samples/FlatXMLBudget.java @@ -0,0 +1,111 @@ +/* Copyright 2002, 2003, 2019 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.samples; + +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +import nu.xom.Document; +import nu.xom.Element; +import nu.xom.Serializer; + + +/** + * + *

    + * Demonstrates building a structured XML document, + * from flat, tabular data. A different version of this + * example was originally developed for Chapter 4 of + * Processing + * XML with Java. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.3.1 + * + */ +public class FlatXMLBudget { + + public static void convert(List> data, OutputStream out) + throws IOException { + + Element budget = new Element("Budget"); + Document doc = new Document(budget); + + for (Map record : data) { + Element lineItem = new Element("LineItem"); + + Set> fields = record.entrySet(); + for (Entry entry : fields) { + String name = entry.getKey(); + String value = entry.getValue(); + // some of the values contain ampersands and less than + // signs that must be escaped + Element field = new Element(name); + field.appendChild(value); + lineItem.appendChild(field); + } + budget.appendChild(lineItem); + } + + Serializer serializer = new Serializer(out, "UTF-8"); + serializer.write(doc); + serializer.flush(); + + } + + public static void main(String[] args) { + + try { + + if (args.length < 1) { + System.out.println("Usage: nu.xom.samples.FlatXMLBudget infile outfile"); + return; + } + + InputStream in = new FileInputStream(args[0]); + OutputStream out; + if (args.length < 2) { + out = System.out; + } + else { + out = new FileOutputStream(args[1]); + } + + List> results = BudgetData.parse(in); + convert(results, out); + } + catch (IOException ex) { + System.err.println(ex); + } + + } + +} \ No newline at end of file diff --git a/src/nu/xom/samples/HelloNamespaces.java b/src/nu/xom/samples/HelloNamespaces.java new file mode 100644 index 0000000..8c5f43a --- /dev/null +++ b/src/nu/xom/samples/HelloNamespaces.java @@ -0,0 +1,49 @@ +/* Copyright 2002-2004 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.samples; + +import nu.xom.Document; +import nu.xom.Element; + +/** + * + *

    + * Test what happens when an element with no namespace is + * a child of an element in a default namespace + *

    + * + * @author Elliotte Rusty Harold + * @version 1.0 + * + */ +public class HelloNamespaces { + + public static void main(String[] args) { + + Element root = new Element("root", "http://www.xom.nu"); + root.appendChild(new Element("NoNamespace")); + Document doc = new Document(root); + System.out.println(doc.toXML()); + + } + +} diff --git a/src/nu/xom/samples/HelloWorld.java b/src/nu/xom/samples/HelloWorld.java new file mode 100644 index 0000000..5849889 --- /dev/null +++ b/src/nu/xom/samples/HelloWorld.java @@ -0,0 +1,50 @@ +/* Copyright 2002, 2003 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.samples; + +import nu.xom.Document; +import nu.xom.Element; + +/** + * + *

    + * Demonstrates the creation and serialization of a + * very simple "Hello World" document. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.0 + * + */ +public class HelloWorld { + + public static void main(String[] args) { + + Element root = new Element("root"); + root.appendChild("Hello World!"); + Document doc = new Document(root); + String result = doc.toXML(); + System.out.println(result); + + } + +} diff --git a/src/nu/xom/samples/HelloXMLBase.java b/src/nu/xom/samples/HelloXMLBase.java new file mode 100644 index 0000000..86b525b --- /dev/null +++ b/src/nu/xom/samples/HelloXMLBase.java @@ -0,0 +1,90 @@ +/* Copyright 2002-2004 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.samples; + +import java.io.IOException; + +import nu.xom.Attribute; +import nu.xom.Comment; +import nu.xom.Document; +import nu.xom.Element; +import nu.xom.Serializer; + +/** + * + *

    + * Demonstrates interaction of actual base URI with + * xml:base attributes. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.0 + * + */ +public class HelloXMLBase { + + public static void main(String[] args) { + + Document doc; + String base1 = "http://www.base1.com"; + String base2 = "http://www.base2.com"; + String base3 = "base3.html"; + + Element root = new Element("root"); + doc = new Document(root); + doc.setBaseURI(base1); + Element child = new Element("child"); + root.appendChild(child); + child.setBaseURI(base2); + child.appendChild(new Comment("here I am")); + + Element child2 = new Element("child2"); + root.appendChild(child2); + + Element child3 = new Element("child3"); + root.appendChild(child3); + child3.addAttribute(new Attribute("xml:base", + "http://www.w3.org/XML/1998/namespace", base2)); + + Element child4 = new Element("child4"); + root.appendChild(child4); + child4.addAttribute(new Attribute("xml:base", + "http://www.w3.org/XML/1998/namespace", base3)); + + try { + Serializer serializer + = new Serializer(System.out, "ISO-8859-1"); + serializer.setPreserveBaseURI(true); + serializer.write(doc); + serializer.flush(); + serializer.setPreserveBaseURI(false); + serializer.write(doc); + serializer.flush(); + } + catch (IOException ex) { + // shouldn't happen on System.out + ex.printStackTrace(); + } + + } + +} diff --git a/src/nu/xom/samples/HierarchicalXMLBudget.java b/src/nu/xom/samples/HierarchicalXMLBudget.java new file mode 100644 index 0000000..ddc9650 --- /dev/null +++ b/src/nu/xom/samples/HierarchicalXMLBudget.java @@ -0,0 +1,108 @@ +/* Copyright 2002, 2003, 2019 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.samples; + +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.List; +import java.util.Map; + +import nu.xom.Document; +import nu.xom.Serializer; + + +/** + * + *

    + * Demonstrates building a structured XML document, + * from flat, tabular data. A different version of this + * example was originally developed for Chapter 4 of + * Processing + * XML with Java. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.3.1 + * + */ +public class HierarchicalXMLBudget { + + public static void convert(List> budgetData, String year, + OutputStream out) throws IOException { + + Budget budget = new Budget(year); + for (Map lineItem : budgetData) { + budget.add(lineItem); + } + + Document doc = new Document(budget.getXML()); + Serializer sout = new Serializer(out, "UTF-8"); + sout.write(doc); + sout.flush(); + + } + + public static void main(String[] args) { + + try { + + if (args.length < 2) { + System.out.println( + "Usage: nu.xom.samples.HierarchicalXMLBudget year infile outfile"); + return; + } + + // simple error checking on the year value + try { + if (!args[0].equals("TransitionalQuarter")) { + Integer.parseInt(args[0]); + } + } + catch (NumberFormatException ex) { + System.out.println( + "Usage: HierarchicalXMLBudget year infile outfile"); + return; + } + + InputStream in = new FileInputStream(args[1]); + OutputStream out; + if (args.length < 3) { + out = System.out; + } + else { + out = new FileOutputStream(args[2]); + } + + List> results = BudgetData.parse(in); + convert(results, args[0], out); + } + catch (IOException e) { + System.err.println(e); + } + + } + +} \ No newline at end of file diff --git a/src/nu/xom/samples/IDFilter.java b/src/nu/xom/samples/IDFilter.java new file mode 100644 index 0000000..307745b --- /dev/null +++ b/src/nu/xom/samples/IDFilter.java @@ -0,0 +1,64 @@ +/* Copyright 2002-2004 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.samples; + +import nu.xom.Attribute; +import nu.xom.Document; +import nu.xom.Element; +import nu.xom.NodeFactory; +import nu.xom.Nodes; + +/** + *

    + * Demonstrates a custom NodeFactory that adds + * ID attributes to all elements that don't already have one. + * This is inspired by Example 8-12 in + * Processing XML with Java. + * In brief, it demonstrates that major modifications + * may have to take place in endElement but can still + * be effectively streamed. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.0 + * + */ +public class IDFilter extends NodeFactory { + + private int counter = 0; + + public Document startMakingDocument() { + counter = 0; + return super.startMakingDocument(); + } + + public Nodes finishMakingElement(Element element) { + Attribute id = element.getAttribute("id"); + if (id == null) { + id = new Attribute("id", "p" + counter); + element.addAttribute(id); + } + counter++; + return new Nodes(element); + } + +} diff --git a/src/nu/xom/samples/IDTagger.java b/src/nu/xom/samples/IDTagger.java new file mode 100644 index 0000000..10cc60e --- /dev/null +++ b/src/nu/xom/samples/IDTagger.java @@ -0,0 +1,90 @@ +/* Copyright 2002, 2003, 2018 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.samples; + + +import java.io.IOException; + +import nu.xom.Attribute; +import nu.xom.Builder; +import nu.xom.Document; +import nu.xom.Element; +import nu.xom.ParsingException; + + +/** + * + *

    + * Demonstrates recursive descent through a document, + * and reading and setting of attributes on elements. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.3.0 + * + */ +public class IDTagger { + + private static int id = 1; + + public static void processElement(Element element) { + + if (element.getAttribute("ID") == null) { + element.addAttribute(new Attribute("ID", "_" + id)); + id = id + 1; + } + + // recursion + for (Element child : element.getChildElements()) { + processElement(child); + } + + } + + public static void main(String[] args) { + + Builder builder = new Builder(); + + for (int i = 0; i < args.length; i++) { + + try { + // Read the entire document into memory + Document document = builder.build(args[i]); + + processElement(document.getRootElement()); + + System.out.println(document.toXML()); + } + catch (ParsingException ex) { + System.err.println(ex); + continue; + } + catch (IOException ex) { + System.err.println(ex); + continue; + } + + } + + } // end main + +} \ No newline at end of file diff --git a/src/nu/xom/samples/Linkset.java b/src/nu/xom/samples/Linkset.java new file mode 100644 index 0000000..ffa2569 --- /dev/null +++ b/src/nu/xom/samples/Linkset.java @@ -0,0 +1,79 @@ +/* Copyright 2002, 2003, 2018 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.samples; + + +import java.io.IOException; + +import nu.xom.Builder; +import nu.xom.Document; +import nu.xom.Element; +import nu.xom.Elements; +import nu.xom.ParsingException; + + +/** + * + *

    + * Demonstrates extracting elements from one document + * and inserting them into another document. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.3.0 + * + */ +public class Linkset { + + public static void main(String[] args) { + + String url = "http://www.slashdot.org/slashdot.rdf"; + + try { + Builder parser = new Builder(); + + // Parse the document + Document document = parser.build(url); + Element oldRoot = document.getRootElement(); + Element newRoot = new Element("linkset"); + Elements toplevel = oldRoot.getChildElements(); + for (Element element : toplevel) { + Element link = element.getFirstChildElement("link", + "http://my.netscape.com/rdf/simple/0.9/"); + link.detach(); + newRoot.appendChild(link); + } + System.out.println(newRoot.toXML()); + } + catch (ParsingException ex) { + System.out.println(url + " is not well-formed."); + } + catch (IOException ex) { + System.out.println( + "Due to an IOException, the parser could not read " + url + ); + } + + } // end main + +} + diff --git a/src/nu/xom/samples/MathMLFibonacci.java b/src/nu/xom/samples/MathMLFibonacci.java new file mode 100644 index 0000000..20920a8 --- /dev/null +++ b/src/nu/xom/samples/MathMLFibonacci.java @@ -0,0 +1,78 @@ +/* Copyright 2002-2004 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.samples; + +import java.math.BigInteger; + +import nu.xom.Document; +import nu.xom.Element; + +/** + * + *

    + * Demonstrates the creation and serialization of a + * MathML document + * that uses namespaces and namespace prefixes. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.0 + * + */ +public class MathMLFibonacci { + + public static void main(String[] args) { + + BigInteger low = BigInteger.ONE; + BigInteger high = BigInteger.ONE; + +/* + + f(1) + = + 1 + */ + String namespace = "http://www.w3.org/1998/Math/MathML"; + Element root = new Element("mathml:math", namespace); + for (int i = 1; i <= 10; i++) { + Element mrow = new Element("mathml:mrow", namespace); + Element mi = new Element("mathml:mi", namespace); + Element mo = new Element("mathml:mo", namespace); + Element mn = new Element("mathml:mn", namespace); + mrow.appendChild(mi); + mrow.appendChild(mo); + mrow.appendChild(mn); + root.appendChild(mrow); + mi.appendChild("f(" + i + ")"); + mo.appendChild("="); + mn.appendChild(low.toString()); + + BigInteger temp = high; + high = high.add(low); + low = temp; + } + Document doc = new Document(root); + System.out.println(doc.toXML()); + + } + +} \ No newline at end of file diff --git a/src/nu/xom/samples/MinimalNodeFactory.java b/src/nu/xom/samples/MinimalNodeFactory.java new file mode 100644 index 0000000..45df681 --- /dev/null +++ b/src/nu/xom/samples/MinimalNodeFactory.java @@ -0,0 +1,73 @@ +/* Copyright 2002, 2003 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.samples; + +import nu.xom.Attribute; +import nu.xom.Element; +import nu.xom.NodeFactory; +import nu.xom.Nodes; +/** + *

    + * Demonstrates a NodeFactory that builds the minimum + * structures possible, just the document and the root element. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.0 + * + */ +public class MinimalNodeFactory extends NodeFactory { + + private Nodes empty = new Nodes(); + + public Nodes makeComment(String data) { + return empty; + } + + public Nodes makeText(String data) { + return empty; + } + + public Element makeRootElement(String name, String namespace) { + return new Element(name, namespace); + } + + public Element startMakingElement(String name, String namespace) { + return null; + } + + public Nodes makeAttribute(String name, String namespace, + String value, Attribute.Type type) { + return empty; + } + + public Nodes makeDocType(String rootElementName, + String publicID, String systemID) { + return empty; + } + + public Nodes makeProcessingInstruction( + String target, String data) { + return empty; + } + +} diff --git a/src/nu/xom/samples/NodeLister.java b/src/nu/xom/samples/NodeLister.java new file mode 100644 index 0000000..14e2c5f --- /dev/null +++ b/src/nu/xom/samples/NodeLister.java @@ -0,0 +1,98 @@ +/* Copyright 2002-2004 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.samples; + + +import java.io.IOException; + +import nu.xom.Builder; +import nu.xom.Document; +import nu.xom.Element; +import nu.xom.Node; +import nu.xom.ParsingException; + + +/** + * + *

    + * Demonstrates recursive descent through a document + * using the Node class. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.0 + * + */ +public class NodeLister { + + + public static void main(String[] args) { + + if (args.length == 0) { + System.out.println("Usage: java nu.xom.samples.NodeLister URL"); + return; + } + + Builder builder = new Builder(); + + try { + Document doc = builder.build(args[0]); + Element root = doc.getRootElement(); + listChildren(root, 0); + } + // indicates a well-formedness error + catch (ParsingException ex) { + System.out.println(args[0] + " is not well-formed."); + System.out.println(ex.getMessage()); + } + catch (IOException ex) { + System.out.println(ex); + } + + } + + + public static void listChildren(Node current, int depth) { + + printSpaces(depth); + String name = ""; + if (current instanceof Element) { + Element temp = (Element) current; + name = ": " + temp.getQualifiedName(); + } + System.out.println(current.getClass().getName() + name); + for (int i = 0; i < current.getChildCount(); i++) { + listChildren(current.getChild(i), depth+1); + } + + } + + + private static void printSpaces(int n) { + + for (int i = 0; i < n; i++) { + System.out.print(' '); + } + + } + +} diff --git a/src/nu/xom/samples/NonRecursiveSerializer.java b/src/nu/xom/samples/NonRecursiveSerializer.java new file mode 100644 index 0000000..ea6de8e --- /dev/null +++ b/src/nu/xom/samples/NonRecursiveSerializer.java @@ -0,0 +1,138 @@ +/* Copyright 2002-2004, 2006 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.samples; + +import java.io.IOException; +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; + +import nu.xom.Builder; +import nu.xom.Document; +import nu.xom.Element; +import nu.xom.Node; +import nu.xom.ParentNode; +import nu.xom.ParsingException; +import nu.xom.Serializer; + + +/** + *

    + * Demonstrates a serializer which, unlike + * nu.xom.Serializer, is not limited by the Java stack + * size and can process arbitrarily deep documents. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.2d1 + * + */ +public final class NonRecursiveSerializer extends Serializer { + + + public NonRecursiveSerializer(OutputStream out) { + super(out); + } + + + public NonRecursiveSerializer(OutputStream out, String encoding) + throws UnsupportedEncodingException { + super(out, encoding); + } + + + protected void write(Element element) + throws IOException { + + // treat empty elements specially to avoid an instance of test + if (element.getChildCount() == 0) { + super.writeEmptyElementTag(element); + } + else { + Node current = element; + boolean end = false; + int index = -1; + while (true) { + if (!end && current.getChildCount() > 0) { + writeStartTag((Element) current); + current = current.getChild(0); + index = 0; + } + else { + if (end) { + writeEndTag((Element) current); + if (current == element) break; + } + else { + writeChild(current); + } + end = false; + ParentNode parent = current.getParent(); + if (parent.getChildCount() - 1 == index) { + current = parent; + if (current != element) { + parent = current.getParent(); + index = parent.indexOf(current); + } + end = true; + } + else { + index++; + current = parent.getChild(index); + } + } + } + } + + } + + + public static void main(String[] args) { + + if (args.length <= 0) { + System.out.println("Usage: java nu.xom.samples.PrettyPrinter URL"); + return; + } + + try { + Builder parser = new Builder(); + Document doc = parser.build(args[0]); + Serializer serializer = new Serializer(System.out, "ISO-8859-1"); + serializer.setIndent(4); + serializer.setMaxLength(64); + serializer.setPreserveBaseURI(true); + serializer.write(doc); + serializer.flush(); + } + catch (ParsingException ex) { + System.out.println(args[0] + " is not well-formed."); + System.out.println(ex.getMessage()); + } + catch (IOException ex) { + System.out.println( + "Due to an IOException, the parser could not check " + + args[0] + ); + } + + } + +} diff --git a/src/nu/xom/samples/NormalizingFactory.java b/src/nu/xom/samples/NormalizingFactory.java new file mode 100644 index 0000000..1d0e884 --- /dev/null +++ b/src/nu/xom/samples/NormalizingFactory.java @@ -0,0 +1,119 @@ +/* Copyright 2002-2004 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.samples; + +import java.io.IOException; + +import nu.xom.Attribute; +import nu.xom.Builder; +import nu.xom.Document; +import nu.xom.NodeFactory; +import nu.xom.Nodes; +import nu.xom.ParsingException; +import nu.xom.Serializer; + +/** + *

    + * Demonstrates a custom NodeFactory that normalizes + * all white space in text nodes and attribute values. + * Normalization involves stripping all leading and trailing white + * space, converting all tabs, carriage returns, and line feeds to a + * single space each, and then converting all remaining runs of white + * space to a single space. Text nodes that contain nothing after + * this process is applied are not created. This may be useful for + * record-like XML. + *

    + * + *

    + * This class does not perform + * Unicode normalization. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.0 + * + */ +public class NormalizingFactory extends NodeFactory { + + private Nodes empty = new Nodes(); + + // We don't need text nodes at all + public Nodes makeText(String data) { + data = normalizeSpace(data); + if ("".equals(data)) return empty; + return super.makeText(data); + } + + public Nodes makeAttribute(String name, String URI, + String value, Attribute.Type type) { + value = normalizeSpace(value); + return super.makeAttribute(name, URI, value, type); + } + + + // not the most efficient implementation + private static String normalizeSpace(String data) { + data = data.replace('\t', ' '); + data = data.replace('\n', ' '); + data = data.replace('\r', ' '); + data = data.trim(); + + StringBuffer result = new StringBuffer(); + for (int i = 0; i < data.length(); i++) { + if (i == 0 || data.charAt(i-1) != ' ' + || data.charAt(i) != ' ') { + result.append(data.charAt(i)); + } + } + + return result.toString(); + } + + + public static void main(String[] args) { + + if (args.length == 0) { + System.out.println( + "Usage: java nu.xom.samples.NormalizingFactory URL" + ); + return; + } + + Builder builder = new Builder(new NormalizingFactory()); + + try { + Document doc = builder.build(args[0]); + Serializer serializer = new Serializer(System.out); + serializer.write(doc); + } + // indicates a well-formedness error + catch (ParsingException ex) { + System.out.println(args[0] + " is not well-formed."); + System.out.println(ex.getMessage()); + } + catch (IOException ex) { + System.out.println(ex); + } + + } + +} \ No newline at end of file diff --git a/src/nu/xom/samples/PoliteSpider.java b/src/nu/xom/samples/PoliteSpider.java new file mode 100644 index 0000000..e9f385c --- /dev/null +++ b/src/nu/xom/samples/PoliteSpider.java @@ -0,0 +1,160 @@ +/* Copyright 2002-2004, 2006, 2019 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.samples; + +import java.net.MalformedURLException; +import java.net.URL; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; + +import nu.xom.Attribute; +import nu.xom.Builder; +import nu.xom.Document; +import nu.xom.Element; +import nu.xom.Elements; +import nu.xom.Node; +import nu.xom.ProcessingInstruction; + +/** + * + *

    + * Demonstrates the reading of attributes in namespaces, + * searching for particular processing instructions in the + * document prolog, and the getBaseURI() method. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.3.1 + * + */ +public class PoliteSpider { + + private Set spidered = new HashSet(); + private Builder parser = new Builder(); + private List queue = new LinkedList(); + + public static final String XLINK_NS + = "http://www.w3.org/1999/xlink"; + + public void search(URL url) { + + try { + String systemID = url.toExternalForm(); + Document doc = parser.build(systemID); + + boolean follow = true; + boolean index = true; + for (int i = 0; i < doc.getChildCount(); i++) { + Node child = doc.getChild(i); + if (child instanceof Element) break; + if (child instanceof ProcessingInstruction){ + ProcessingInstruction instruction + = (ProcessingInstruction) child; + if (instruction.getTarget().equals("robots")) { + Element data + = PseudoAttributes.getAttributes(instruction); + Attribute indexAtt = data.getAttribute("index"); + if (indexAtt != null) { + String value = indexAtt.getValue().trim(); + if (value.equals("no")) index = false; + } + Attribute followAtt = data.getAttribute("follow"); + if (followAtt != null) { + String value = followAtt.getValue().trim(); + if (value.equals("no")) follow = false; + } + } + } + } + + if (index) System.out.println(url); + if (follow) search(doc.getRootElement()); + } + catch (Exception ex) { + // just skip this document + } + + if (queue.isEmpty()) return; + + URL discovered = (URL) queue.remove(0); + spidered.add(discovered); + search(discovered); + + } + + private void search(Element element) { + + Attribute href = element.getAttribute("href", XLINK_NS); + + URL base = null; + try { + base = new URL(element.getBaseURI()); + } + catch (MalformedURLException ex) { + // Probably just no protocol handler for the + // kind of URLs used inside this element + return; + } + if (href != null) { + String uri = href.getValue(); + // absolutize URL + try { + URL discovered = new URL(base, uri); + // remove fragment identifier if any + discovered = new URL( + discovered.getProtocol(), + discovered.getHost(), + discovered.getFile() + ); + + if (!spidered.contains(discovered) + && !queue.contains(discovered)) { + queue.add(discovered); + } + } + catch (MalformedURLException ex) { + // skip this one + } + } + Elements children = element.getChildElements(); + for (int i = 0; i < children.size(); i++) { + search(children.get(i)); + } + } + + public static void main(String[] args) { + + PoliteSpider spider = new PoliteSpider(); + for (int i = 0; i < args.length; i++) { + try { + spider.search(new URL(args[i])); + } + catch (MalformedURLException ex) { + System.err.println(ex); + } + } + + } // end main() + +} \ No newline at end of file diff --git a/src/nu/xom/samples/PrettyFibonacci.java b/src/nu/xom/samples/PrettyFibonacci.java new file mode 100644 index 0000000..f6abd4b --- /dev/null +++ b/src/nu/xom/samples/PrettyFibonacci.java @@ -0,0 +1,86 @@ +/* Copyright 2002, 2003 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.samples; + +import java.io.IOException; +import java.math.BigInteger; + +import nu.xom.Document; +import nu.xom.Element; +import nu.xom.Serializer; + +/** + *

    + * Demonstrates using a Serializer object + * to insert indents and line breaks in XML. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.0 + * + */ +public class PrettyFibonacci { + + public static void main(String[] args) { + + BigInteger low = BigInteger.ONE; + BigInteger high = BigInteger.ONE; + +/* + + f(1) + = + 1 + */ + String namespace = "http://www.w3.org/1998/Math/MathML"; + Element root = new Element("mathml:math", namespace); + for (int i = 1; i <= 10; i++) { + Element mrow = new Element("mathml:mrow", namespace); + Element mi = new Element("mathml:mi", namespace); + Element mo = new Element("mathml:mo", namespace); + Element mn = new Element("mathml:mn", namespace); + mrow.appendChild(mi); + mrow.appendChild(mo); + mrow.appendChild(mn); + root.appendChild(mrow); + mi.appendChild("f(" + i + ")"); + mo.appendChild("="); + mn.appendChild(low.toString()); + + BigInteger temp = high; + high = high.add(low); + low = temp; + } + Document doc = new Document(root); + + Serializer serializer = new Serializer(System.out); + serializer.setIndent(4); + serializer.setMaxLength(64); + try { + serializer.write(doc); + } + catch (IOException ex) { + System.err.println(ex); + } + } + +} \ No newline at end of file diff --git a/src/nu/xom/samples/PrettyPrinter.java b/src/nu/xom/samples/PrettyPrinter.java new file mode 100644 index 0000000..d7335e3 --- /dev/null +++ b/src/nu/xom/samples/PrettyPrinter.java @@ -0,0 +1,74 @@ +/* Copyright 2002-2004 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.samples; + +import java.io.IOException; + +import nu.xom.Builder; +import nu.xom.Document; +import nu.xom.ParsingException; +import nu.xom.Serializer; + + +/** + *

    + * Demonstrates using a Serializer object to modify + * an input document by inserting indents and line breaks. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.0 + * + */ +public class PrettyPrinter { + + public static void main(String[] args) { + + if (args.length <= 0) { + System.out.println("Usage: java nu.xom.samples.PrettyPrinter URL"); + return; + } + + try { + Builder parser = new Builder(); + Document doc = parser.build(args[0]); + Serializer serializer = new Serializer(System.out, "ISO-8859-1"); + serializer.setIndent(4); + serializer.setMaxLength(64); + serializer.setPreserveBaseURI(true); + serializer.write(doc); + serializer.flush(); + } + catch (ParsingException ex) { + System.out.println(args[0] + " is not well-formed."); + System.out.println(ex.getMessage()); + } + catch (IOException ex) { + System.out.println( + "Due to an IOException, the parser could not check " + + args[0] + ); + } + + } + +} \ No newline at end of file diff --git a/src/nu/xom/samples/ProcessingInstructionLister.java b/src/nu/xom/samples/ProcessingInstructionLister.java new file mode 100644 index 0000000..5421f63 --- /dev/null +++ b/src/nu/xom/samples/ProcessingInstructionLister.java @@ -0,0 +1,86 @@ +/* Copyright 2002, 2003 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.samples; + +import java.io.IOException; + +import nu.xom.Builder; +import nu.xom.Document; +import nu.xom.Node; +import nu.xom.ParsingException; +import nu.xom.ProcessingInstruction; + + +/** + *

    + * Demonstrates recursive descent through an XML document, + * and the ProcessingInstruction class. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.0 + * + */ +public class ProcessingInstructionLister { + + public static void list(Node node) { + + for (int i = 0; i < node.getChildCount(); i++) { + Node child = node.getChild(i); + if (child instanceof ProcessingInstruction) { + System.out.println(child.toXML()); + } + else { + list(child); + } + } + + } + + public static void main(String[] args) { + + if (args.length <= 0) { + System.out.println( + "Usage: java nu.xom.samples.ProcessingInstructionLister URL" + ); + return; + } + + try { + Builder parser = new Builder(); + Document doc = parser.build(args[0]); + list(doc); + } + catch (ParsingException ex) { + System.out.println(args[0] + " is not well-formed."); + System.out.println(ex.getMessage()); + } + catch (IOException ex) { + System.out.println( + "Due to an IOException, the parser could not check " + + args[0] + ); + } + + } + +} \ No newline at end of file diff --git a/src/nu/xom/samples/PropertyPrinter.java b/src/nu/xom/samples/PropertyPrinter.java new file mode 100644 index 0000000..d006df4 --- /dev/null +++ b/src/nu/xom/samples/PropertyPrinter.java @@ -0,0 +1,128 @@ +/* Copyright 2002, 2003 2006 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.samples; + +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.io.Writer; + +import nu.xom.Attribute; +import nu.xom.Document; +import nu.xom.Element; +import nu.xom.Node; + + +/** + *

    + * Demonstrates recursive descent through an XML document, + * and the getter methods of the Element + * and Attribute classes. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.2b2 + * + */ +public class PropertyPrinter { + + private Writer out; + + public PropertyPrinter(Writer out) { + if (out == null) { + throw new NullPointerException("Writer must be non-null."); + } + this.out = out; + } + + public PropertyPrinter() { + this(new OutputStreamWriter(System.out)); + } + + private int nodeCount = 0; + + public void writeNode(Node node) throws IOException { + + if (node == null) { + throw new NullPointerException("Node must be non-null."); + } + if (node instanceof Document) { + // starting a new document, reset the node count + nodeCount = 1; + } + + String type = node.getClass().getName(); // never null + String value = node.getValue(); + + String name = null; + String localName = null; + String uri = null; + String prefix = null; + /* This is the one example I found where + * it seemed like it might make sense to + * move the namespace methods into Node or another + * common interface. However, this is a pretty + * artificial example, so I decided not to. + */ + if (node instanceof Element) { + Element element = (Element) node; + name = element.getQualifiedName(); + localName = element.getLocalName(); + uri = element.getNamespaceURI(); + prefix = element.getNamespacePrefix(); + } + else if (node instanceof Attribute) { + Attribute attribute = (Attribute) node; + name = attribute.getQualifiedName(); + localName = attribute.getLocalName(); + uri = attribute.getNamespaceURI(); + prefix = attribute.getNamespacePrefix(); + } + + + StringBuffer result = new StringBuffer(); + result.append("Node " + nodeCount + ":\r\n"); + result.append(" Type: " + type + "\r\n"); + if (name != null) { + result.append(" Name: " + name + "\r\n"); + } + if (localName != null) { + result.append(" Local Name: " + localName + "\r\n"); + } + if (prefix != null) { + result.append(" Prefix: " + prefix + "\r\n"); + } + if (uri != null) { + result.append(" Namespace URI: " + uri + "\r\n"); + } + if (value != null) { + result.append(" Value: " + value + "\r\n"); + } + + out.write(result.toString()); + out.write("\r\n"); + out.flush(); + + nodeCount++; + + } + +} diff --git a/src/nu/xom/samples/PseudoAttributes.java b/src/nu/xom/samples/PseudoAttributes.java new file mode 100644 index 0000000..61f2fba --- /dev/null +++ b/src/nu/xom/samples/PseudoAttributes.java @@ -0,0 +1,75 @@ +/* Copyright 2002, 2003 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.samples; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; + +import nu.xom.Builder; +import nu.xom.Document; +import nu.xom.Element; +import nu.xom.ParsingException; +import nu.xom.ProcessingInstruction; + + +/** + *

    + * A utility class which converts pseudo-attributes in a + * ProcessingInstruction object into + * real Attribute/code> objects. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.0 + * + */ +public class PseudoAttributes { + + public static Element getAttributes(ProcessingInstruction pi) + throws ParsingException { + + StringBuffer sb = new StringBuffer("<"); + sb.append(pi.getTarget()); + sb.append(" "); + sb.append(pi.getValue()); + sb.append("/>"); + + try { + InputStream in = new ByteArrayInputStream(sb.toString().getBytes("UTF-8")); + Builder parser = new Builder(); + + // This line will throw a ParsingException if the processing + // instruction does not use pseudo-attributes + Document doc = parser.build(in); + Element root = doc.getRootElement(); + // detach root element + doc.setRootElement(new Element("fauxRoot")); + return root; + } + catch (Exception ex) { + throw new ParsingException(pi.toXML() + + " is not in pseudo-attribute format.", ex); + } + + } + +} diff --git a/src/nu/xom/samples/PureValidator.java b/src/nu/xom/samples/PureValidator.java new file mode 100644 index 0000000..72f0d61 --- /dev/null +++ b/src/nu/xom/samples/PureValidator.java @@ -0,0 +1,76 @@ +/* Copyright 2002, 2003 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.samples; + +import java.io.IOException; + +import nu.xom.Builder; +import nu.xom.ParsingException; +import nu.xom.ValidityException; + +/** + *

    + * Demonstrates validation via the Builder class + * that does not build much of a document, and thus does not + * have a lot of overhead. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.0 + * + */ +public class PureValidator { + + public static void main(String[] args) { + + if (args.length <= 0) { + System.out.println("Usage: java nu.xom.samples.PureValidator URL"); + return; + } + + try { + Builder parser = new Builder(true, new MinimalNodeFactory()); + parser.build(args[0]); + System.out.println(args[0] + " is valid."); + } + catch (ValidityException ex) { + System.out.println(args[0] + " is not valid."); + System.out.println(ex.getMessage()); + System.out.println(" at line " + ex.getLineNumber() + + ", column " + ex.getColumnNumber()); + } + catch (ParsingException ex) { + System.out.println(args[0] + " is not well-formed."); + System.out.println(ex.getMessage()); + System.out.println(" at line " + ex.getLineNumber() + + ", column " + ex.getColumnNumber()); + } + catch (IOException ex) { + System.out.println( + "Due to an IOException, the parser could not check " + + args[0] + ); + } + + } + +} \ No newline at end of file diff --git a/src/nu/xom/samples/RDDLFilter.java b/src/nu/xom/samples/RDDLFilter.java new file mode 100644 index 0000000..4c9434c --- /dev/null +++ b/src/nu/xom/samples/RDDLFilter.java @@ -0,0 +1,94 @@ +/* Copyright 2002-2004 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.samples; + +import java.io.IOException; + +import nu.xom.Builder; +import nu.xom.DocType; +import nu.xom.Document; +import nu.xom.Element; +import nu.xom.NodeFactory; +import nu.xom.Nodes; +import nu.xom.ParsingException; + +/** + *

    + * Demonstrates using the Builder and a custom + * NodeFactory to filter out start-tags and + * end-tags while leaving the content intact. Specifically, + * we filter out all the elements in the RDDL namespace. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.0 + * + */ +public class RDDLFilter extends NodeFactory { + + public final static String RDDL_NAMESPACE + = "http://www.rddl.org/"; + + public Element startMakingElement(String name, String namespace) { + if (namespace.equals(RDDL_NAMESPACE)) return null; + return new Element(name, namespace); + } + + public Nodes finishMakingElement(Element element) { + element.removeNamespaceDeclaration("rddl"); + return new Nodes(element); + } + + // change the DOCTYPE to XHTML Basic DOCTYPE + public Nodes makeDocType(String rootElementName, + String publicID, String systemID) { + return new Nodes(new DocType("html", + "PUBLIC \"-//W3C//DTD XHTML Basic 1.0//EN\"", + "http://www.w3.org/TR/xhtml-basic/xhtml-basic10.dtd")); + } + + public static void main(String[] args) { + + if (args.length <= 0) { + System.out.println("Usage: java nu.xom.samples.RDDLFilter URL"); + return; + } + + try { + Builder parser = new Builder(new RDDLFilter()); + Document doc = parser.build(args[0]); + System.out.println(doc.toXML()); + } + catch (ParsingException ex) { + System.out.println(args[0] + " is not well-formed."); + System.out.println(ex.getMessage()); + } + catch (IOException ex) { + System.out.println( + "Due to an IOException, the parser could not read " + + args[0] + ); + } + + } + +} \ No newline at end of file diff --git a/src/nu/xom/samples/RDDLStripper.java b/src/nu/xom/samples/RDDLStripper.java new file mode 100644 index 0000000..79bb50e --- /dev/null +++ b/src/nu/xom/samples/RDDLStripper.java @@ -0,0 +1,104 @@ +/* Copyright 2002, 2003, 2018 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.samples; + +import java.io.IOException; + +import nu.xom.Builder; +import nu.xom.Document; +import nu.xom.Element; +import nu.xom.Node; +import nu.xom.ParentNode; +import nu.xom.ParsingException; + +/** + *

    + * Demonstrates removing elements from a tree + * while retaining their children. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.3.0 + * + */ +public class RDDLStripper { + + public final static String RDDL_NAMESPACE + = "http://www.rddl.org/"; + + public static void main(String[] args) { + + if (args.length <= 0) { + System.out.println("Usage: java nu.xom.samples.RDDLStripper URL"); + return; + } + + try { + Builder parser = new Builder(); + Document doc = parser.build(args[0]); + + strip(doc.getRootElement()); + + + System.out.println(doc.toXML()); + } + catch (ParsingException ex) { + System.out.println(args[0] + " is not well-formed."); + System.out.println(ex.getMessage()); + } + catch (IOException ex) { + System.out.println( + "Due to an IOException, the parser could not read " + + args[0] + ); + } + + } + + public static void strip(Element element) { + + if (element.getNamespaceURI().equals(RDDL_NAMESPACE)) { + + ParentNode parent = element.getParent(); + int position = 0; + for (; position < parent.getChildCount(); position++) { + if (parent.getChild(position) == element) break; + } + parent.removeChild(position); + while (element.getChildCount() > 0) { + Node child = element.getChild(0); + element.removeChild(0); + parent.insertChild(child, position); + position++; + if (child instanceof Element) strip((Element) child); + } + + } + else { + for (Element child : element.getChildElements()) { + strip(child); + } + } + + } + +} \ No newline at end of file diff --git a/src/nu/xom/samples/RDDLToTable.java b/src/nu/xom/samples/RDDLToTable.java new file mode 100644 index 0000000..582c9c4 --- /dev/null +++ b/src/nu/xom/samples/RDDLToTable.java @@ -0,0 +1,206 @@ +/* Copyright 2002-2004 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.samples; + +import java.io.IOException; + +import nu.xom.Attribute; +import nu.xom.Builder; +import nu.xom.DocType; +import nu.xom.Document; +import nu.xom.Element; +import nu.xom.Node; +import nu.xom.NodeFactory; +import nu.xom.Nodes; +import nu.xom.ParsingException; +import nu.xom.Serializer; + +/** + *

    + * Demonstrates a custom NodeFactory that converts + * rddl:resource elements to XHTML tables. + * This is inspired by Example 8-11 in + * Processing XML with Java. + * In brief, it demonstrates that major modifications + * may have to take place in endElement but can still + * be effectively streamed. + *

    + * + *

    + * rddl:resource elements are replaced by a simple table. + * The various attributes of the resource are mapped to different + * parts of the table. In particular, a rddl:resource + * like this: + *

    + * + *
    <rddl:resource
    + *        id="note-xlink2rdf"
    + *       xlink:title="W3C NOTE XLink2RDF"
    + *       xlink:role="http://www.w3.org/TR/html4/"
    + *       xlink:arcrole="http://www.rddl.org/purposes#reference"
    + *       xlink:href="http://www.w3.org/TR/xlink2rdf/"
    + *       >
    + *     <li>
    + *       <a href="http://www.w3.org/TR/xlink2rdf/">
    + *        W3C Note Harvesting RDF Statements from XLinks
    + *      </a>
    + *    </li>
    + *</rddl:resource>
    + * + *

    will turn into an XHTML table that looks like this:

    + * + *
    <table id="note-xlink2rdf">
    + * <caption>W3C NOTE XLink2RDF</caption>
    + * <tr><td>Role: </td><td>http://www.w3.org/TR/html4/</td></tr>
    + * <tr><td>Arcrole: </td><td>http://www.rddl.org/purposes#reference</td></tr>
    + * <tr><td>Href: </td><td><a href="http://www.w3.org/TR/xlink2rdf/">
    + *  http://www.w3.org/TR/xlink2rdf/</a></td></tr>
    + * <tr>
    + *   <td colspan="2">
    + *     <li>
    + *       <a href="http://www.w3.org/TR/xlink2rdf/">
    + *         W3C Note Harvesting RDF Statements from XLinks
    + *       </a>
    + *     </li>
    + *   </td>
    + * </tr>
    + *</table>
    + * + * @author Elliotte Rusty Harold + * @version 1.0 + */ + +public class RDDLToTable extends NodeFactory { + + public final static String RDDL_NAMESPACE + = "http://www.rddl.org/"; + public final static String XHTML_NAMESPACE + = "http://www.w3.org/1999/xhtml"; + public final static String XLINK_NAMESPACE + = "http://www.w3.org/1999/xlink"; + + + public static void main(String[] args) { + + if (args.length <= 0) { + System.out.println("Usage: java nu.xom.samples.RDDLToTable URL"); + return; + } + + try { + Builder parser = new Builder(new RDDLToTable()); + Document doc = parser.build(args[0]); + Serializer out = new Serializer(System.out); + out.write(doc); + } + catch (ParsingException ex) { + System.out.println(args[0] + " is not well-formed."); + System.out.println(ex.getMessage()); + } + catch (IOException ex) { + System.out.println( + "Due to an IOException, the parser could not read " + + args[0] + ); + } + + } + + public Nodes finishMakingElement(Element element) { + + element.removeNamespaceDeclaration("rddl"); + element.removeNamespaceDeclaration("xlink"); + Element result = element; + if (RDDL_NAMESPACE.equals(element.getNamespaceURI()) + && "resource".equals(element.getLocalName())) { + Element table = new Element("table", XHTML_NAMESPACE); + // move the content + Element tr = new Element("tr", XHTML_NAMESPACE); + Element td = new Element("td", XHTML_NAMESPACE); + td.addAttribute(new Attribute("colspan", "2")); + tr.appendChild(td); + while (element.getChildCount() > 0) { + Node child = element.removeChild(0); + td.appendChild(child); + } + table.appendChild(tr); + + Attribute href = element.getAttribute("role", XLINK_NAMESPACE); + if (href != null) { + element.removeAttribute(href); + Element trhref = new Element("tr", XHTML_NAMESPACE); + Element tdhref1 = new Element("td", XHTML_NAMESPACE); + Element tdhref2 = new Element("td", XHTML_NAMESPACE); + tdhref1.appendChild("href: "); + tdhref2.appendChild(href.getValue()); + trhref.appendChild(tdhref1); + trhref.appendChild(tdhref2); + table.insertChild(trhref, 0); + } + + Attribute arcrole = element.getAttribute("role", XLINK_NAMESPACE); + if (arcrole != null) { + element.removeAttribute(arcrole); + Element trarcrole = new Element("tr", XHTML_NAMESPACE); + Element tdarcrole1 = new Element("td", XHTML_NAMESPACE); + Element tdarcrole2 = new Element("td", XHTML_NAMESPACE); + tdarcrole1.appendChild("arcrole: "); + tdarcrole2.appendChild(arcrole.getValue()); + trarcrole.appendChild(tdarcrole1); + trarcrole.appendChild(tdarcrole2); + table.insertChild(trarcrole, 0); + } + + + Attribute role = element.getAttribute("role", XLINK_NAMESPACE); + if (role != null) { + element.removeAttribute(role); + Element trrole = new Element("tr", XHTML_NAMESPACE); + Element tdrole1 = new Element("td", XHTML_NAMESPACE); + Element tdrole2 = new Element("td", XHTML_NAMESPACE); + tdrole1.appendChild("role: "); + tdrole2.appendChild(role.getValue()); + trrole.appendChild(tdrole1); + trrole.appendChild(tdrole2); + table.insertChild(trrole, 0); + } + + Attribute id = element.getAttribute("id"); + if (id != null) { + element.removeAttribute(id); + Element caption = new Element("caption", XHTML_NAMESPACE); + caption.appendChild(id.getValue()); + table.insertChild(caption, 0); + } + result = table; + } + return new Nodes(result); + } + + public Nodes makeDocType(String rootElementName, + String publicID, String systemID) { + return new Nodes(new DocType("html", + "PUBLIC \"-//W3C//DTD XHTML Basic 1.0//EN\"", + "http://www.w3.org/TR/xhtml-basic/xhtml-basic10.dtd")); + } + +} diff --git a/src/nu/xom/samples/ROT13File.java b/src/nu/xom/samples/ROT13File.java new file mode 100644 index 0000000..20c0b6a --- /dev/null +++ b/src/nu/xom/samples/ROT13File.java @@ -0,0 +1,70 @@ +/* Copyright 2002, 2003 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.samples; + +import java.io.*; +import nu.xom.Document; +import nu.xom.Builder; +import nu.xom.ParsingException; + +/** + * @author Elliotte Rusty Harold + * @version 1.0 + * + */ +public class ROT13File { + + public static void main(String[] args) { + + if (args.length <= 0) { + System.out.println("Usage: java nu.xom.samples.ROT13File filename"); + return; + } + + File file = new File(args[0]); + + try { + Builder parser = new Builder(); + + // Read the document + Document document = parser.build(file); + + // Modify the document + ROT13XML.encode(document); + + // Write it out again + System.out.println(document.toXML()); + + } + catch (IOException ex) { + System.out.println( + "Due to an IOException, the parser could not encode " + file + ); + } + catch (ParsingException ex) { + System.out.println(ex); + ex.printStackTrace(); + } + + } // end main + +} diff --git a/src/nu/xom/samples/ROT13XML.java b/src/nu/xom/samples/ROT13XML.java new file mode 100644 index 0000000..949f4e9 --- /dev/null +++ b/src/nu/xom/samples/ROT13XML.java @@ -0,0 +1,109 @@ +/* Copyright 2002, 2003 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.samples; + +import java.io.IOException; + +import nu.xom.Builder; +import nu.xom.Document; +import nu.xom.Node; +import nu.xom.ParsingException; +import nu.xom.Text; + + +/** + *

    + * Demonstrates getter and setter methods in the Text class, + * as well as recursive descent through a document. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.0 + * + */ +public class ROT13XML { + + // note use of recursion + public static void encode(Node node) { + + if (node instanceof Text) { + Text text = (Text) node; + String data = text.getValue(); + text.setValue(rot13(data)); + } + + // recurse the children + for (int i = 0; i < node.getChildCount(); i++) { + encode(node.getChild(i)); + } + + } + + public static String rot13(String s) { + + StringBuffer out = new StringBuffer(s.length()); + for (int i = 0; i < s.length(); i++) { + int c = s.charAt(i); + if (c >= 'A' && c <= 'M') out.append((char) (c+13)); + else if (c >= 'N' && c <= 'Z') out.append((char) (c-13)); + else if (c >= 'a' && c <= 'm') out.append((char) (c+13)); + else if (c >= 'n' && c <= 'z') out.append((char) (c-13)); + else out.append((char) c); + } + return out.toString(); + + } + + public static void main(String[] args) { + + if (args.length <= 0) { + System.out.println("Usage: java nu.xom.samples.ROT13XML URL"); + return; + } + + String url = args[0]; + + try { + Builder parser = new Builder(); + + // Read the document + Document document = parser.build(url); + + // Modify the document + ROT13XML.encode(document); + + // Write it out again + System.out.println(document.toXML()); + + } + catch (IOException ex) { + System.out.println( + "Due to an IOException, the parser could not encode " + url + ); + } + catch (ParsingException ex) { + System.out.println(ex); + } + + } // end main + +} diff --git a/src/nu/xom/samples/RSSHeadlines.java b/src/nu/xom/samples/RSSHeadlines.java new file mode 100644 index 0000000..bc7839c --- /dev/null +++ b/src/nu/xom/samples/RSSHeadlines.java @@ -0,0 +1,87 @@ +/* Copyright 2002-2004 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.samples; + +import java.io.IOException; + +import nu.xom.Builder; +import nu.xom.Element; +import nu.xom.Nodes; +import nu.xom.ParsingException; + +/** + *

    + * Print just the headlines from an RSS feed + *

    + * + * @author Elliotte Rusty Harold + * @version 1.0 + */ +public class RSSHeadlines extends MinimalNodeFactory { + + private boolean inTitle = false; + private Nodes empty = new Nodes(); + + public Element startMakingElement(String name, String namespace) { + if ("title".equals(name) ) { + inTitle = true; + } + return new Element(name, namespace); + } + + public Nodes makeText(String data) { + if (inTitle) System.out.print(data); + return empty; + } + + public Nodes finishMakingElement(Element element) { + if ("title".equals(element.getQualifiedName()) ) { + System.out.println(); + inTitle = false; + } + return new Nodes(element); + } + + public static void main(String[] args) { + + String url = "http://www.bbc.co.uk/syndication/feeds/news/ukfs_news/world/rss091.xml"; + if (args.length > 0) { + url = args[0]; + } + + try { + Builder parser = new Builder(new RSSHeadlines()); + parser.build(url); + } + catch (ParsingException ex) { + System.out.println(url + " is not well-formed."); + System.out.println(ex.getMessage()); + } + catch (IOException ex) { + System.out.println( + "Due to an IOException, the parser could not read " + url + ); + } + + } + +} diff --git a/src/nu/xom/samples/ResourceToTable.java b/src/nu/xom/samples/ResourceToTable.java new file mode 100644 index 0000000..53de361 --- /dev/null +++ b/src/nu/xom/samples/ResourceToTable.java @@ -0,0 +1,200 @@ +/* Copyright 2002, 2003, 2018 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.samples; + +import java.io.IOException; + +import nu.xom.Attribute; +import nu.xom.Builder; +import nu.xom.Document; +import nu.xom.Element; +import nu.xom.Node; +import nu.xom.ParentNode; +import nu.xom.ParsingException; + +/** + *

    + * Demonstrates replacing elements in a document + * with different elements that contain the same content. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.3.0 + * + */ +public class ResourceToTable { + + public final static String XHTML_NAMESPACE + = "http://www.w3.org/1999/xhtml"; + public final static String RDDL_NAMESPACE + = "http://www.rddl.org/"; + public final static String XLINK_NAMESPACE + = "http://www.w3.org/1999/xlink"; + + public static void main(String[] args) { + + if (args.length <= 0) { + System.out.println("Usage: java nu.xom.samples.ResourceToTable URL"); + return; + } + + try { + Builder parser = new Builder(); + Document doc = parser.build(args[0]); + Element root = doc.getRootElement(); + if (root.getNamespaceURI().equals(XHTML_NAMESPACE)) { + convert(root); + } + else { + System.out.println(args[0] + " does not appear to be an XHTML document"); + return; + } + System.out.println(doc.toXML()); + } + catch (ParsingException ex) { + System.out.println(args[0] + " is not well-formed."); + } + catch (IOException ex) { + System.out.println( + "Due to an IOException, the parser could not read " + + args[0] + ); + } + + } + + public static void convert(Element element) { + + if (element.getNamespaceURI().equals(RDDL_NAMESPACE)) { + + Element table = new Element("table", XHTML_NAMESPACE); + +/* +
  1. + + W3C Note Harvesting RDF Statements from XLinks + +
  2. +
    + +will turn into an XHTML table that looks like this: + + + + + + + + + +
    W3C NOTE XLink2RDF
    Role: http://www.w3.org/TR/html4/
    Arcrole: http://www.rddl.org/purposes#reference
    Href: + http://www.w3.org/TR/xlink2rdf/
    +
  3. + + W3C Note Harvesting RDF Statements from XLinks + +
  4. +
    */ + + Attribute role = element.getAttribute("role", XLINK_NAMESPACE); + if (role != null) { + Element tr = new Element("tr", XHTML_NAMESPACE); + Element td1 = new Element("td", XHTML_NAMESPACE); + tr.appendChild(td1); + td1.appendChild("Role: "); + Element td2 = new Element("td", XHTML_NAMESPACE); + tr.appendChild(td2); + td2.appendChild(role.getValue()); + table.insertChild(tr, 0); + } + Attribute arcrole = element.getAttribute("arcrole", XLINK_NAMESPACE); + if (arcrole != null) { + Element tr = new Element("tr", XHTML_NAMESPACE); + Element td1 = new Element("td", XHTML_NAMESPACE); + tr.appendChild(td1); + td1.appendChild("Arcrole: "); + Element td2 = new Element("td", XHTML_NAMESPACE); + tr.appendChild(td2); + td2.appendChild(arcrole.getValue()); + table.insertChild(tr, 0); + } + Attribute href = element.getAttribute("href", XLINK_NAMESPACE); + if (href != null) { + Element tr = new Element("tr", XHTML_NAMESPACE); + Element td1 = new Element("td", XHTML_NAMESPACE); + tr.appendChild(td1); + td1.appendChild("Href: "); + Element td2 = new Element("td", XHTML_NAMESPACE); + tr.appendChild(td2); + Element a = new Element("a", XHTML_NAMESPACE); + a.appendChild(href.getValue()); + td2.appendChild(a); + a.addAttribute(new Attribute("href", href.getValue())); + table.insertChild(tr, 0); + } + Attribute title = element.getAttribute("title", XLINK_NAMESPACE); + if (title != null) { + Element caption = new Element("caption", XHTML_NAMESPACE); + caption.appendChild(title.getValue()); + table.insertChild(caption, 0); + } + + + // Move children into a td + Element tr = new Element("tr", XHTML_NAMESPACE); + Element td = new Element("td", XHTML_NAMESPACE); + td.addAttribute(new Attribute("colspan", "2")); + tr.appendChild(td); + while (element.getChildCount() > 0) { + Node child = element.getChild(0); + child.detach(); + td.appendChild(child); + if (child instanceof Element) convert((Element) child); + } + table.appendChild(tr); + + ParentNode parent = element.getParent(); + parent.replaceChild(element, table); + + } + else { + // Strip out additional namespaces + for (int i = 0; i < element.getNamespaceDeclarationCount(); i++) { + String prefix = element.getNamespacePrefix(i); + element.removeNamespaceDeclaration(prefix); + } + + for (Element child : element.getChildElements()) { + convert(child); + } + + } + + } + +} \ No newline at end of file diff --git a/src/nu/xom/samples/Restructurer.java b/src/nu/xom/samples/Restructurer.java new file mode 100644 index 0000000..e3449dd --- /dev/null +++ b/src/nu/xom/samples/Restructurer.java @@ -0,0 +1,105 @@ +/* Copyright 2002-2004, 2018 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.samples; + +import java.io.IOException; + +import nu.xom.Builder; +import nu.xom.Comment; +import nu.xom.Document; +import nu.xom.Node; +import nu.xom.ParentNode; +import nu.xom.ParsingException; +import nu.xom.ProcessingInstruction; + +/** + *

    + * Demonstrates moving nodes from one part of the tree + * to a different part. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.3.0 + * + */ +public class Restructurer { + + + // Since this methods only operates on its argument and + // does not interact with any fields in the class, it's + // plausibly made static. + public static void processNode(Node current) { + + if (current instanceof Comment + || current instanceof ProcessingInstruction) { + Document document = current.getDocument(); + ParentNode root = document.getRootElement(); + current.detach(); + document.insertChild(current, document.indexOf(root)); + } + else { + for (int i = 0; i < current.getChildCount(); i++) { + processNode(current.getChild(i)); + } + } + + } + + + public static void main(String[] args) { + + if (args.length <= 0) { + System.out.println( + "Usage: java nu.xom.samples.RestructureDriver URL" + ); + return; + } + String url = args[0]; + + try { + // Find a parser + Builder parser = new Builder(); + + // Read the document + Document document = parser.build(url); + + // Modify the document + Restructurer.processNode(document.getRootElement()); + + // Write it out again + System.out.println(document.toXML()); + + } + catch (ParsingException ex) { + System.out.println(url + " is not well-formed."); + } + catch (IOException ex) { + System.out.println( + "Due to an IOException, the parser could not read " + url + ); + } + + } + +} + + diff --git a/src/nu/xom/samples/SQLToXML.java b/src/nu/xom/samples/SQLToXML.java new file mode 100644 index 0000000..2c0ed58 --- /dev/null +++ b/src/nu/xom/samples/SQLToXML.java @@ -0,0 +1,217 @@ +/* Copyright 2002-2004 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.samples; + +import java.io.IOException; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; + +import nu.xom.Attribute; +import nu.xom.Document; +import nu.xom.Element; +import nu.xom.Serializer; + + +/** + *

    + * Demonstrates building a structured XML document, + * from relational data using JDBC. A different version of this + * example was originally developed for Example 4.14 of Chapter 4 of + * Processing + * XML with Java. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.0 + * + */ +public class SQLToXML { + + public static void main(String[] args ) { + + // Load the ODBC driver + try { + Class.forName( "sun.jdbc.odbc.JdbcOdbcDriver" ); + } + catch (ClassNotFoundException ex) { + System.err.println("Could not load the JDBC-ODBC Bridge"); + return; + } + + try { + Element budget = new Element("Budget"); + writeAgencies(budget); + + Document doc = new Document(budget); + Serializer sout = new Serializer(System.out, "UTF-8"); + sout.write(doc); + sout.flush(); + } + catch (IOException ex) { + System.err.println(ex); + } + + + } + + private static void writeAgencies(Element parent) { + + Connection conn = null; + Statement stmnt = null; + try { + conn = DriverManager.getConnection( + "jdbc:odbc:budauth", "", ""); + stmnt = conn.createStatement(); + String query = "SELECT DISTINCT AgencyName, AgencyCode" + + " FROM BudgetAuthorizationTable;"; + ResultSet agencies = stmnt.executeQuery( query ); + + while( agencies.next() ) { + + String agencyName = agencies.getString("AgencyName"); + String agencyCode = agencies.getString("AgencyCode"); + Element agency = new Element("Agency"); + Element name = new Element("Name"); + Element code = new Element("Code"); + name.appendChild(agencyName); + code.appendChild(agencyCode); + agency.appendChild(name); + agency.appendChild(code); + writeBureaus(agency, conn, agencyCode); + parent.appendChild(agency); + } + } + catch (SQLException e) { + System.err.println(e); + e.printStackTrace(); + } + finally { + try { + stmnt.close(); + conn.close(); + } + catch(SQLException ex) { + System.err.println(ex); + } + } + + } + + private static void writeBureaus(Element parent, Connection conn, + String agencyCode) throws SQLException { + + String query + = "SELECT DISTINCT BureauName, BureauCode " + + "FROM BudgetAuthorizationTable WHERE AgencyCode='" + + agencyCode + "';"; + Statement stmnt = conn.createStatement(); + ResultSet bureaus = stmnt.executeQuery(query); + + while( bureaus.next() ) { + String bureauName = bureaus.getString("BureauName"); + String bureauCode = bureaus.getString("BureauCode"); + Element bureau = new Element("Bureau"); + Element name = new Element("Name"); + Element code = new Element("Code"); + name.appendChild(bureauName); + code.appendChild(bureauCode); + bureau.appendChild(name); + bureau.appendChild(code); + writeAccounts(bureau, conn, agencyCode, bureauCode); + parent.appendChild(bureau); + } + + } + + private static void writeAccounts(Element parent, Connection conn, + String agencyCode, String bureauCode) + throws SQLException { + + String query = "SELECT DISTINCT AccountName, AccountCode " + + "FROM BudgetAuthorizationTable WHERE AgencyCode='" + + agencyCode + "' AND BureauCode='" + bureauCode + "';"; + Statement stmnt = conn.createStatement(); + ResultSet accounts = stmnt.executeQuery(query); + + while( accounts.next() ) { + String accountName = accounts.getString("AccountName"); + String accountCode = accounts.getString("AccountCode"); + Element account = new Element("Account"); + Element name = new Element("Name"); + Element code = new Element("Code"); + name.appendChild(accountName); + code.appendChild(accountCode); + account.appendChild(name); + account.appendChild(code); + writeSubfunctions( + account, conn, agencyCode, bureauCode, accountCode + ); + parent.appendChild(account); + } + + } + + private static void writeSubfunctions(Element parent, + Connection conn, String agencyCode, String bureauCode, + String accountCode) throws SQLException { + + String query = "SELECT * FROM BudgetAuthorizationTable" + + " WHERE AgencyCode='" + agencyCode + "' AND BureauCode='" + + bureauCode + "' AND AccountCode='" + accountCode + "';"; + Statement stmnt = conn.createStatement(); + ResultSet subfunctions = stmnt.executeQuery(query); + + while( subfunctions.next() ) { + String subfunctionTitle + = subfunctions.getString("SubfunctionTitle"); + String subfunctionCode + = subfunctions.getString("SubfunctionCode"); + Element subfunction = new Element("Subfunction"); + Element name = new Element("Name"); + Element code = new Element("Code"); + name.appendChild(subfunctionTitle); + code.appendChild(subfunctionCode); + subfunction.appendChild(name); + subfunction.appendChild(code); + Element amount = new Element("Amount"); + amount.addAttribute(new Attribute("year", "TransitionQuarter")); + amount.appendChild( + String.valueOf(subfunctions.getInt("TransitionQuarter") * 1000L)); + subfunction.appendChild(amount); + for (int year = 1976; year <= 2006; year++) { + String fy = "FY" + year; + long amt = subfunctions.getInt(fy) * 1000L; + amount = new Element("Amount"); + amount.addAttribute(new Attribute("year", String.valueOf(year))); + amount.appendChild(String.valueOf(amt)); + subfunction.appendChild(amount); + } + parent.appendChild(subfunction); + } + + } + + } \ No newline at end of file diff --git a/src/nu/xom/samples/SchemaValidator.java b/src/nu/xom/samples/SchemaValidator.java new file mode 100644 index 0000000..1d03572 --- /dev/null +++ b/src/nu/xom/samples/SchemaValidator.java @@ -0,0 +1,86 @@ +/* Copyright 2002, 2003 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.samples; + +import java.io.IOException; + +import nu.xom.Builder; +import nu.xom.ParsingException; + +import org.xml.sax.SAXException; +import org.xml.sax.XMLReader; +import org.xml.sax.helpers.XMLReaderFactory; + +/** + *

    + * Demonstrates schema validation by setting SAX properties + * on an XMLReader + *

    + * + * @author Elliotte Rusty Harold + * @version 1.0 + * + */ +public class SchemaValidator { + + public static void main(String[] args) { + + if (args.length <= 0) { + System.out.println( + "Usage: java nu.xom.samples.SchemaValidator URL"); + return; + } + + try { + XMLReader xerces = XMLReaderFactory.createXMLReader( + "org.apache.xerces.parsers.SAXParser"); + xerces.setFeature( + "http://apache.org/xml/features/validation/schema", + true + ); + + Builder parser = new Builder(xerces, true); + parser.build(args[0]); + System.out.println(args[0] + " is schema valid."); + } + catch (SAXException ex) { + System.out.println("Could not load Xerces."); + System.out.println(ex.getMessage()); + } + catch (ParsingException ex) { + System.out.println(args[0] + " is not schema valid."); + System.out.println(ex.getMessage()); + ex.printStackTrace(); + System.out.println(" at line " + ex.getLineNumber() + + ", column " + ex.getColumnNumber()); + } + catch (IOException ex) { + System.out.println( + "Due to an IOException, Xerces could not check " + + args[0] + ); + ex.printStackTrace(); + } + + } + +} \ No newline at end of file diff --git a/src/nu/xom/samples/SimpleSVG.java b/src/nu/xom/samples/SimpleSVG.java new file mode 100644 index 0000000..18adc25 --- /dev/null +++ b/src/nu/xom/samples/SimpleSVG.java @@ -0,0 +1,82 @@ +/* Copyright 2002-2004 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.samples; + +import nu.xom.Comment; +import nu.xom.DocType; +import nu.xom.Document; +import nu.xom.Element; +import nu.xom.ProcessingInstruction; +import nu.xom.Text; +import nu.xom.XMLException; + +/** + * + *

    + * Demonstrates the creation and serialization of an SVG document + * that uses namespaces and the default namespace + * and includes a document type declaration. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.0 + * + */ +public class SimpleSVG { + + public static void main(String[] args) { + + try { + // Create the document + DocType svgDOCTYPE = new DocType( + "svg", "-//W3C//DTD SVG 1.0//EN", + "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd" + ); + + // Fill the document + Element root = new Element("svg", + "http://www.w3.org/2000/svg"); + Document doc = new Document(root); + doc.insertChild(svgDOCTYPE, 0); + ProcessingInstruction xmlstylesheet = new ProcessingInstruction("xml-stylesheet", + "type=\"text/css\" href=\"standard.css\""); + doc.insertChild(xmlstylesheet, 0); + Comment comment = new Comment( + "An example from Chapter 10 of Processing XML with Java"); + doc.insertChild(comment, doc.indexOf(root)); + Element desc = new Element("desc", "http://www.w3.org/2000/svg"); + root.appendChild(desc); + Text descText = new Text("An example from Processing XML with Java"); + desc.appendChild(descText); + + // Serialize the document onto System.out + System.out.println(doc.toXML()); + + } + catch (XMLException ex) { + System.err.println(ex); + } + + } + +} + diff --git a/src/nu/xom/samples/SourceCodeGenerator.java b/src/nu/xom/samples/SourceCodeGenerator.java new file mode 100644 index 0000000..b51bdc6 --- /dev/null +++ b/src/nu/xom/samples/SourceCodeGenerator.java @@ -0,0 +1,204 @@ +/* Copyright 2003, 2018 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.samples; + +import nu.xom.Attribute; +import nu.xom.Builder; +import nu.xom.Comment; +import nu.xom.DocType; +import nu.xom.Document; +import nu.xom.Element; +import nu.xom.Node; +import nu.xom.ProcessingInstruction; +import nu.xom.Text; + +/** + * + *

    + * This program essentially serializes a XOM Node + * object into the Java statements necessary to build the + * Node using XOM. This may be useful for building + * unit tests. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.3.0 + * + */ +public class SourceCodeGenerator { + + /** + *

    + * The driver method for the SourceCodeGenerator program. + *

    + * + * @param args args[0] contains the URL + * of the document to be processed. + */ + public static void main(String[] args) { + + Builder builder = new Builder(); + try { + Document input = builder.build(args[0]); + generateClass(input); + } + catch (Exception ex) { + System.err.println(ex); + ex.printStackTrace(); + } + + } + + private static int elementCount = 1; + + public static void generateClass(Document doc) { + System.out.println("import nu.xom.*;"); + System.out.println(); + System.out.println(); + System.out.println("public class CodeMaker {"); + System.out.println(); + System.out.println(" public static void main(String[] args) throws Exception {"); + generateDoc(doc); + System.out.println(" Serializer serializer = new Serializer(System.out);"); + System.out.println(" serializer.write(doc);"); + System.out.println(" }"); + System.out.println(); + System.out.println("}"); + } + + public static void generateSource(Node node, String parent) { + + if (node instanceof Element) { + Element element = (Element) node; + String name = "element" + elementCount; + System.out.println(" Element " + name + " = " + + "new Element(\"" + element.getQualifiedName() + + "\", \"" + element.getNamespaceURI() + "\");"); + if ("doc".equals(parent)) { + System.out.println(" doc.setRootElement(" + name + ");"); + } + else { + System.out.println(" " + parent + ".appendChild(" + name + ");"); + } + + for (int i = 0; i < element.getAttributeCount(); i++) { + Attribute a = element.getAttribute(i); + System.out.println(" " + name + ".addAttribute(new Attribute(\"" + + a.getQualifiedName() + "\", \"" + + a.getNamespaceURI() + "\", \"" + + a.getValue() + "\"));"); + } + + for (int i = 0; i < element.getNamespaceDeclarationCount(); i++) { + String prefix = element.getNamespacePrefix(i); + System.out.println(" " + name + ".addNamespaceDeclaration(\"" + + prefix + "\", \"" + + element.getNamespaceURI(prefix) + "\");"); + } + + elementCount++; + for (Element child : element.getChildElements()) { + generateSource(child, name); + } + + + } + else if (node instanceof ProcessingInstruction) { + ProcessingInstruction pi = (ProcessingInstruction) node; + System.out.println(" pi = new ProcessingInstruction(\"" + + pi.getTarget() + "\", \"" + javaEscape(pi.getValue()) + "\");"); + System.out.println(" " + parent + ".appendChild(pi);"); + } + else if (node instanceof Comment) { + Comment comment = (Comment) node; + System.out.println(" comment = new Comment(\"" + + javaEscape(comment.getValue()) + "\");"); + System.out.println(" " + parent + ".appendChild(comment);"); + } + else if (node instanceof Text) { + Text text = (Text) node; + System.out.println(" text = new Text(\"" + + javaEscape(text.getValue()) + "\");"); + System.out.println(" " + parent + ".appendChild(text);"); + } + else if (node instanceof DocType) { + DocType doctype = (DocType) node; + String publicID = doctype.getPublicID(); + String systemID = doctype.getSystemID(); + System.out.println(" DocType doctype = new DocType(\"" + + doctype.getRootElementName() + "\");"); + if (systemID != null) { + System.out.println(" doctype.setSystemID(\"" + + systemID + "\");"); + } + if (publicID != null) { + System.out.println(" doctype.setPublicID(\"" + + publicID + "\");"); + } + System.out.println(" doc.setDocType(doctype);"); + + + } + + } + + private static String javaEscape(String text) { + StringBuffer result = new StringBuffer(); + for (int i = 0; i < text.length(); i++) { + char c = text.charAt(i); + switch (c) { + case '"': + result.append("\\\""); + break; + case '\n': + result.append("\\n"); + break; + case '\r': + result.append("\\r"); + break; + case '\t': + result.append("\\t"); + break; + case '\\': + result.append("\\\\"); + break; + default: + result.append(c); + } + + } + return result.toString(); + } + + public static void generateDoc(Document doc) { + + System.out.println(" Comment comment;"); + System.out.println(" Text text;"); + System.out.println(" ProcessingInstruction pi;"); + System.out.println(" Document doc = new Document(new Element(\"root\"));"); + for (int i = 0; i < doc.getChildCount(); i++) { + generateSource(doc.getChild(i), "doc"); + } + + } + +} diff --git a/src/nu/xom/samples/SourceCodeSerializer.java b/src/nu/xom/samples/SourceCodeSerializer.java new file mode 100644 index 0000000..167c249 --- /dev/null +++ b/src/nu/xom/samples/SourceCodeSerializer.java @@ -0,0 +1,271 @@ +/* Copyright 2004, 2019 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.samples; + +import java.io.IOException; +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; +import java.util.Stack; + +import nu.xom.Attribute; +import nu.xom.Builder; +import nu.xom.Comment; +import nu.xom.DocType; +import nu.xom.Document; +import nu.xom.Element; +import nu.xom.ParsingException; +import nu.xom.ProcessingInstruction; +import nu.xom.Serializer; +import nu.xom.Text; + +/** + *

    + * This class converts an XML document into XOM source code that + * creates the same XML document. It's often useful for + * building self-contained unit tests. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.3.1 + * + */ +public class SourceCodeSerializer extends Serializer { + + public SourceCodeSerializer(OutputStream out) { + super(out); + } + + public SourceCodeSerializer(OutputStream out, String encoding) + throws UnsupportedEncodingException { + super(out, encoding); + } + + + private Stack parents = new Stack(); + + + public void write(Document doc) throws IOException { + parents.push("doc"); + Element root = doc.getRootElement(); + + write(root); + + // prolog + for (int i = 0; i < doc.indexOf(root); i++) { + writeChild(doc.getChild(i)); + } + + //epilog + for (int i = doc.indexOf(root) + 1; i < doc.getChildCount(); i++) { + writeChild(doc.getChild(i)); + } + + flush(); + + } + + private int count = 1; + + protected void writeStartTag(Element element) + throws IOException { + + String name = "e" + count; + writeRaw("Element " + name + " = new Element(\"" + + element.getQualifiedName() + "\", \"" + element.getNamespaceURI() + "\");"); + breakLine(); + if (count == 1) { + writeRaw("Document doc = new Document(e1);"); + } + else { + writeRaw(parents.peek() + ".appendChild(" + name + ");"); + } + breakLine(); + parents.push(name); + writeAttributes(element); + writeNamespaceDeclarations(element); + count++; + + } + + protected void writeEndTag(Element element) throws IOException { + parents.pop(); + } + + + protected void writeEmptyElementTag(Element element) throws IOException { + writeStartTag(element); + writeEndTag(element); + } + + protected void writeAttributes(Element element) + throws IOException { + + for (int i = 0; i < element.getAttributeCount(); i++) { + Attribute attribute = element.getAttribute(i); + write(attribute); + } + + } + + + protected void write(Attribute attribute) throws IOException { + + String parent = (String) parents.peek(); + writeRaw(parent + ".addAttribute(new Attribute(\"" + attribute.getQualifiedName() + "\", " + + "\"" + attribute.getNamespaceURI() + "\", \"" + + escapeText(attribute.getValue()) + "\"));"); + breakLine(); + + } + + private static String escapeText(String s) { + StringBuffer sb = new StringBuffer(s.length()); + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + switch (c) { + case '\n': + sb.append("\\n"); + break; + case '\r': + sb.append("\\r"); + break; + case '"': + sb.append("\\\""); + break; + case '\t': + sb.append("\\t"); + break; + default: + sb.append(c); + } + } + return sb.toString(); + } + + + protected void writeNamespaceDeclarations(Element element) + throws IOException { + // We need to write only the additional namespace declarations + prefix: for (int i = 0; i < element.getNamespaceDeclarationCount(); i++) { + String prefix = element.getNamespacePrefix(i); + if (prefix.equals(element.getNamespacePrefix())) continue; + for (int a = 0; a < element.getAttributeCount(); a++) { + if (prefix.equals(element.getAttribute(a).getNamespacePrefix())) { + continue prefix; + } + } + String parent = (String) parents.peek(); + writeRaw(parent + ".addNamespaceDeclaration(\"" + + prefix + "\", \"" + element.getNamespaceURI(prefix) + "\");"); + breakLine(); + } + + } + + + protected void write(ProcessingInstruction instruction) + throws IOException { + String parent = (String) parents.peek(); + if (parent.equals("doc")) { + Document doc = instruction.getDocument(); + int root = doc.indexOf(instruction); + writeRaw(parent + ".insertChild(new ProcessingInstruction(\"" + instruction.getTarget() + + "\", \"" + escapeText(instruction.getValue()) + "\"), " + root + ");"); + } + else { + writeRaw(parent + + ".appendChild(new ProcessingInstruction(\"" + + instruction.getTarget() + + "\", \"" + escapeText(instruction.getValue()) + + "\"));"); + } + breakLine(); + } + + + protected void write(DocType doctype) throws IOException { + writeRaw("DocType doctype = new DocType(\"" + + doctype.getRootElementName() + "\", \"" + + doctype.getPublicID() + "\", \"" + + doctype.getSystemID() + + "\");"); + breakLine(); + Document doc = doctype.getDocument(); + int root = doc.indexOf(doc.getRootElement()); + writeRaw("doc.insertChild(doctype, " + root + ");"); + breakLine(); + + } + + + protected void write(Comment comment) throws IOException { + String parent = (String) parents.peek(); + if (parent.equals("doc")) { + Document doc = comment.getDocument(); + int root = doc.indexOf(comment); + writeRaw(parent + ".insertChild(new Comment(\"" + + escapeText(comment.getValue()) + "\"), " + + root + ");"); + } + else { + writeRaw(parent + ".appendChild(new Comment(\"" + + escapeText(comment.getValue()) + "\");"); + } + breakLine(); + } + + + protected void write(Text text) throws IOException { + String parent = (String) parents.peek(); + writeRaw(parent + ".appendChild(new Text(\"" + escapeText(text.getValue()) + "\"));"); + breakLine(); + } + + + public static void main(String[] args) { + + if (args.length <= 0) { + System.out.println("Usage: java nu.xom.samples.SourceCodeSerializer URL"); + return; + } + + try { + Builder parser = new Builder(); + Document doc = parser.build(args[0]); + Serializer serializer = new SourceCodeSerializer(System.out, "ISO-8859-1"); + serializer.write(doc); + serializer.flush(); + } + catch (ParsingException ex) { + System.out.println(args[0] + " is not well-formed."); + System.out.println(ex.getMessage()); + } + catch (IOException ex) { + System.out.println( + "Due to an IOException, the parser could not read " + + args[0] + ); + } + + } + +} diff --git a/src/nu/xom/samples/StreamingCommentReader.java b/src/nu/xom/samples/StreamingCommentReader.java new file mode 100644 index 0000000..cfb23d3 --- /dev/null +++ b/src/nu/xom/samples/StreamingCommentReader.java @@ -0,0 +1,108 @@ +/* Copyright 2002-2004 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.samples; + +import java.io.IOException; + +import nu.xom.Attribute; +import nu.xom.Builder; +import nu.xom.Element; +import nu.xom.NodeFactory; +import nu.xom.Nodes; +import nu.xom.ParsingException; + +/** + *

    + * Demonstrates using the Builder and a custom + * NodeFactory to list the comments in a document + * that contains very little else, thus saving + * memory, and avoiding the overhead of building + * lots of objects we don't actually need. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.0 + * + */ +public class StreamingCommentReader extends NodeFactory { + + private Nodes empty = new Nodes(); + + // We don't really need the comments. We just want to print them. + public Nodes makeComment(String data) { + System.out.println(data); + return empty; + } + + // We don't need text nodes at all + public Nodes makeText(String data) { + return empty; + } + + public Element makeRootElement(String name, String namespace) { + return new Element(name, namespace); + } + + public Element startMakingElement(String name, String namespace) { + return null; + } + + public Nodes makeAttribute(String name, String URI, + String value, Attribute.Type type) { + return empty; + } + + public Nodes makeDocType(String rootElementName, + String publicID, String systemID) { + return empty; + } + + public Nodes makeProcessingInstruction( + String target, String data) { + return empty; + } + + public static void main(String[] args) { + + if (args.length <= 0) { + System.out.println("Usage: java nu.xom.samples.CommentReader URL"); + return; + } + + try { + Builder parser = new Builder(new StreamingCommentReader()); + parser.build(args[0]); + } + catch (ParsingException ex) { + System.out.println(args[0] + " is not well-formed."); + System.out.println(ex.getMessage()); + } + catch (IOException ex) { + System.out.println( + "Due to an IOException, the parser could not read " + + args[0] + ); + } + + } + +} \ No newline at end of file diff --git a/src/nu/xom/samples/StreamingElementLister.java b/src/nu/xom/samples/StreamingElementLister.java new file mode 100644 index 0000000..be3b176 --- /dev/null +++ b/src/nu/xom/samples/StreamingElementLister.java @@ -0,0 +1,121 @@ +/* Copyright 2002-2004 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.samples; + +import java.io.IOException; + +import nu.xom.Builder; +import nu.xom.Document; +import nu.xom.Element; +import nu.xom.Attribute; +import nu.xom.Nodes; +import nu.xom.NodeFactory; +import nu.xom.ParsingException; + + +/** + *

    + * Demonstrates walking the element hierarchy of + * an XML document in a streaming fashion while storing state in the + * node factory. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.0 + * + */ +public class StreamingElementLister extends NodeFactory{ + + private int depth = 0; + private Nodes empty = new Nodes(); + + public static void main(String[] args) { + + if (args.length == 0) { + System.out.println( + "Usage: java nu.xom.samples.StreamingElementLister URL" + ); + return; + } + + Builder builder = new Builder(new StreamingElementLister()); + + try { + builder.build(args[0]); + } + catch (ParsingException ex) { + System.out.println(args[0] + " is not well-formed."); + System.out.println(ex.getMessage()); + } + catch (IOException ex) { + System.out.println(ex); + } + + } + + // We don't need the comments. + public Nodes makeComment(String data) { + return empty; + } + + // We don't need text nodes at all + public Nodes makeText(String data) { + return empty; + } + + public Element startMakingElement(String name, String namespace) { + depth++; + printSpaces(); + System.out.println(name); + return new Element(name, namespace); + } + + public Nodes finishMakingElement(Element element) { + depth--; + if (element.getParent() instanceof Document) { + return new Nodes(element); + } + else return empty; + } + + public Nodes makeAttribute(String name, String URI, + String value, Attribute.Type type) { + return empty; + } + + public Nodes makeDocType(String rootElementName, + String publicID, String systemID) { + return empty; + } + + public Nodes makeProcessingInstruction( + String target, String data) { + return empty; + } + + private void printSpaces() { + for (int i = 0; i <= depth; i++) { + System.out.print(' '); + } + } + +} diff --git a/src/nu/xom/samples/StreamingExampleExtractor.java b/src/nu/xom/samples/StreamingExampleExtractor.java new file mode 100644 index 0000000..7141c55 --- /dev/null +++ b/src/nu/xom/samples/StreamingExampleExtractor.java @@ -0,0 +1,199 @@ +/* Copyright 2002-2004 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.samples; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.io.Writer; + +import nu.xom.Attribute; +import nu.xom.Builder; +import nu.xom.Element; +import nu.xom.NodeFactory; +import nu.xom.Nodes; +import nu.xom.ParsingException; + +/** + *

    + * Demonstrates extracting the content of particular named elements + * from one XML document, and storing them into new files. + * The names of these files are based on an attribute of the + * original element. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.0 + * + */ +public class StreamingExampleExtractor extends NodeFactory { + + private int chapter = 0; + private boolean inExample = false; + private Nodes empty = new Nodes(); + + + // We don't need the comments. + public Nodes makeComment(String data) { + return empty; + } + + + // We need text nodes only inside examples + public Nodes makeText(String data) { + if (inExample) return super.makeText(data); + return empty; + } + + + public Element makeRootElement(String name, String namespace) { + if ("example".equals(name)) { + inExample = true; + } + if ("chapter".equals(name)) { + chapter++; + } + return super.startMakingElement(name, namespace); + } + + + public Element startMakingElement(String name, String namespace) { + if ("example".equals(name)) { + inExample = true; + } + if ("chapter".equals(name)) { + chapter++; + } + if (inExample) return super.startMakingElement(name, namespace); + else return null; + } + + + public Nodes finishMakingElement(Element element) { + if (element.getQualifiedName().equals("example")) { + try { + extractExample(element, chapter); + } + catch (IOException ex) { + System.err.println( + "Problem writing " + element.getAttributeValue("id") + + " in chapter " + chapter + ); + } + inExample = false; + } + return new Nodes(element); + } + + + public Nodes makeAttribute(String name, String URI, + String value, Attribute.Type type) { + if (inExample && name.equals("id")) { + return super.makeAttribute(name, URI, value, type); + } + return empty; + } + + + public Nodes makeDocType(String rootElementName, + String publicID, String systemID) { + return empty; + } + + + public Nodes makeProcessingInstruction( + String target, String data) { + return empty; + } + + + private static void extractExample(Element example, int chapter) + throws IOException { + + String fileName = example.getAttribute("id").getValue(); + System.out.println(fileName); + Element programlisting + = example.getFirstChildElement("programlisting"); + + // A few examples use screens instead of programlistings + if (programlisting == null) { + programlisting = example.getFirstChildElement("screen"); + } + // If it's still null, skip it + if (programlisting == null) return; + String code = programlisting.getValue(); + + // write code into a file + File dir = new File("examples2/" + chapter); + dir.mkdirs(); + File file = new File(dir, fileName); + System.out.println(file); + FileOutputStream fout = new FileOutputStream(file); + Writer out = null; + try { + out = new OutputStreamWriter(fout, "UTF-8"); + // Buffering almost always helps performance a lot + out = new BufferedWriter(out); + out.write(code); + // Be sure to flush and close your streams + out.flush(); + } + finally { + fout.close(); + if (out != null) { + out.close(); + } + } + + } + + + public static void main(String[] args) { + + if (args.length <= 0) { + System.out.println( + "Usage: java nu.xom.samples.StreamingExampleExtractor URL"); + return; + } + String url = args[0]; + + try { + Builder builder = new Builder(new StreamingExampleExtractor()); + // Read the document + builder.build(args[0]); + } + catch (ParsingException ex) { + System.out.println(ex); + } + catch (IOException ex) { + System.out.println( + "Due to an IOException, the parser could not read " + url + ); + System.out.println(ex); + } + + } // end main + + +} \ No newline at end of file diff --git a/src/nu/xom/samples/StreamingNodeLister.java b/src/nu/xom/samples/StreamingNodeLister.java new file mode 100644 index 0000000..33b9196 --- /dev/null +++ b/src/nu/xom/samples/StreamingNodeLister.java @@ -0,0 +1,118 @@ +/* Copyright 2002-2004 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.samples; + +import java.io.IOException; + +import nu.xom.Builder; +import nu.xom.Element; +import nu.xom.NodeFactory; +import nu.xom.Nodes; +import nu.xom.ParsingException; + + +/** + *

    + * Non-recursive, streaming alternative to NodeLister. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.0 + * + */ +public class StreamingNodeLister extends NodeFactory { + + private Nodes empty = new Nodes(); + + public static void main(String[] args) { + + if (args.length == 0) { + System.out.println( + "Usage: java nu.xom.samples.StreamingNodeLister URL" + ); + return; + } + + Builder builder = new Builder(new StreamingNodeLister()); + + try { + builder.build(args[0]); + } + // indicates a well-formedness error + catch (ParsingException ex) { + System.out.println(args[0] + " is not well-formed."); + System.out.println(ex.getMessage()); + } + catch (IOException ex) { + System.out.println(ex); + } + + } + + private int depth = 0; + + public Nodes makeComment(String data) { + printSpaces(); + System.out.println("Comment"); + return empty; + } + + public Nodes makeText(String data) { + printSpaces(); + System.out.println("Text"); + return empty; + } + + public Element startMakingElement(String name, String namespace) { + depth++; + printSpaces(); + System.out.println("Element: " + name); + return super.startMakingElement(name, namespace); + } + + public Nodes finishMakingElement(Element element) { + depth--; + return new Nodes(element); + } + + public Nodes makeDocType(String rootElementName, + String publicID, String systemID) { + System.out.println("DOCTYPE"); + return empty; + } + + public Nodes makeProcessingInstruction( + String target, String data) { + printSpaces(); + System.out.println("Processing instruction: " + target); + return empty; + } + + private void printSpaces() { + + for (int i = 0; i < depth; i++) { + System.out.print(' '); + } + + } + +} diff --git a/src/nu/xom/samples/StreamingProcessingInstructionLister.java b/src/nu/xom/samples/StreamingProcessingInstructionLister.java new file mode 100644 index 0000000..01edb29 --- /dev/null +++ b/src/nu/xom/samples/StreamingProcessingInstructionLister.java @@ -0,0 +1,111 @@ +/* Copyright 2002-2004 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.samples; + +import java.io.IOException; + +import nu.xom.Attribute; +import nu.xom.Builder; +import nu.xom.Element; +import nu.xom.NodeFactory; +import nu.xom.Nodes; +import nu.xom.ParsingException; +import nu.xom.ProcessingInstruction; + +/** + *

    + * Demonstrates using the Builder and a custom + * NodeFactory to list the processing instructions + * while avoiding the overhead of constructing a lot of unneeded + * nodes thus saving memory and time. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.0 + * + */ +public class StreamingProcessingInstructionLister extends NodeFactory { + + private Nodes empty = new Nodes(); + + public Nodes makeComment(String data) { + return empty; + } + + // We don't need text nodes at all + public Nodes makeText(String data) { + return empty; + } + + public Element makeRootElement(String name, String namespace) { + return new Element(name, namespace); + } + + public Element startMakingElement(String name, String namespace) { + return null; + } + + public Nodes makeAttribute(String name, String URI, + String value, Attribute.Type type) { + return empty; + } + + public Nodes makeDocType(String rootElementName, + String publicID, String systemID) { + return empty; + } + + public Nodes makeProcessingInstruction( + String target, String data) { + ProcessingInstruction pi + = new ProcessingInstruction(target, data); + System.out.println(pi.toXML()); + return empty; + } + + public static void main(String[] args) { + + if (args.length <= 0) { + System.out.println( + "Usage: java nu.xom.samples.StreamingProcessingInstructionLister URL" + ); + return; + } + + try { + Builder parser = new Builder(new StreamingCommentReader()); + parser.build(args[0]); + } + catch (ParsingException ex) { + System.out.println(args[0] + " is not well-formed."); + System.out.println(ex.getMessage()); + } + catch (IOException ex) { + System.out.println( + "Due to an IOException, the parser could not read " + + args[0] + ); + } + + } + +} \ No newline at end of file diff --git a/src/nu/xom/samples/StreamingROT13.java b/src/nu/xom/samples/StreamingROT13.java new file mode 100644 index 0000000..52b1c8d --- /dev/null +++ b/src/nu/xom/samples/StreamingROT13.java @@ -0,0 +1,108 @@ +/* Copyright 2002, 2003 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.samples; + +import java.io.*; + +import nu.xom.Attribute; +import nu.xom.Comment; +import nu.xom.Document; +import nu.xom.Builder; +import nu.xom.NodeFactory; +import nu.xom.Nodes; +import nu.xom.ParsingException; +import nu.xom.ProcessingInstruction; +import nu.xom.Serializer; +import nu.xom.Text; + +/** + * @author Elliotte Rusty Harold + * @version 1.0 + * + */ +public class StreamingROT13 extends NodeFactory { + + public static String rot13(String s) { + + StringBuffer out = new StringBuffer(s.length()); + for (int i = 0; i < s.length(); i++) { + int c = s.charAt(i); + if (c >= 'A' && c <= 'M') out.append((char) (c+13)); + else if (c >= 'N' && c <= 'Z') out.append((char) (c-13)); + else if (c >= 'a' && c <= 'm') out.append((char) (c+13)); + else if (c >= 'n' && c <= 'z') out.append((char) (c-13)); + else out.append((char) c); + } + return out.toString(); + + } + + // We don't really need the comments. We just want to print them. + public Nodes makeComment(String data) { + return new Nodes(new Comment(rot13(data))); + } + + public Nodes makeText(String data) { + return new Nodes(new Text(rot13(data))); + } + + public Nodes makeAttribute(String name, String namespace, + String value, Attribute.Type type) { + return new Nodes(new Attribute(name, namespace, rot13(value), type)); + } + + public Nodes makeProcessingInstruction( + String target, String data) { + return new Nodes(new ProcessingInstruction(rot13(target), rot13(data))); + } + + public static void main(String[] args) { + + if (args.length <= 0) { + System.out.println("Usage: java nu.xom.samples.StreamingROT13 URL"); + return; + } + + try { + Builder parser = new Builder(new StreamingROT13()); + + // Read the document + Document document = parser.build(args[0]); + + // Write it out again + Serializer serializer = new Serializer(System.out); + serializer.write(document); + + } + catch (IOException ex) { + System.out.println( + "Due to an IOException, the parser could not encode " + args[0] + ); + } + catch (ParsingException ex) { + System.out.println(ex); + ex.printStackTrace(); + } + + } // end main + +} diff --git a/src/nu/xom/samples/StreamingTextExtractor.java b/src/nu/xom/samples/StreamingTextExtractor.java new file mode 100644 index 0000000..d0925d1 --- /dev/null +++ b/src/nu/xom/samples/StreamingTextExtractor.java @@ -0,0 +1,136 @@ +/* Copyright 2002, 2003 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.samples; + +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.io.Writer; + +import nu.xom.Attribute; +import nu.xom.Builder; +import nu.xom.Document; +import nu.xom.Element; +import nu.xom.NodeFactory; +import nu.xom.Nodes; +import nu.xom.ParsingException; + + +/** + *

    + * Demonstrates filtered streaming via a NodeFactory + * subclass. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.0 + * + */ +public class StreamingTextExtractor extends NodeFactory { + + private Writer out; + private Nodes empty = new Nodes(); + + public StreamingTextExtractor(Writer out) { + if (out == null) { + throw new NullPointerException("Writer must be non-null."); + } + this.out = out; + } + + public StreamingTextExtractor() { + this(new OutputStreamWriter(System.out)); + } + + public Nodes makeComment(String data) { + return empty; + } + + public Nodes makeText(String data) { + try { + out.write(data); + } + catch (IOException ex) { + System.err.println(ex); + } + return empty; + } + + public Element makeRootElement(String name, String namespace) { + Element result = new Element(name, namespace); + return result; + } + + public Element startMakingElement(String name, String namespace) { + return null; + } + + public Nodes makeAttribute(String name, String namespace, + String value, Attribute.Type type) { + return empty; + } + + public Nodes makeDocType(String rootElementName, + String publicID, String systemID) { + return empty; + } + + public Nodes makeProcessingInstruction( + String target, String data) { + return empty; + } + + public void finishMakingDocument(Document doc) { + try { + out.flush(); + } + catch (IOException ex) { + System.err.println(ex); + } + } + + public static void main(String[] args) { + + if (args.length <= 0) { + System.out.println( + "Usage: java nu.xom.samples.StreamingTextExtractor URL" + ); + return; + } + + try { + Builder parser = new Builder(new StreamingTextExtractor()); + parser.build(args[0]); + } + catch (ParsingException ex) { + System.out.println(args[0] + " is not well-formed."); + System.out.println(ex.getMessage()); + } + catch (IOException ex) { + System.out.println( + "Due to an IOException, the parser could not read " + + args[0] + ); + } + + } + +} diff --git a/src/nu/xom/samples/StreamingTreeReporter.java b/src/nu/xom/samples/StreamingTreeReporter.java new file mode 100644 index 0000000..cde22cd --- /dev/null +++ b/src/nu/xom/samples/StreamingTreeReporter.java @@ -0,0 +1,98 @@ +/* Copyright 2002, 2003 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.samples; + +import java.io.IOException; + +import nu.xom.Builder; +import nu.xom.Element; +import nu.xom.NodeFactory; +import nu.xom.Nodes; +import nu.xom.ParsingException; + + +/** + *

    + * Demonstrates recursive descent through an XML document, + * and the getter methods of the Element + * and Attribute classes. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.0 + * + */ +public class StreamingTreeReporter extends NodeFactory { + + private Nodes empty = new Nodes(); + + // We don't really need the comments. + public Nodes makeComment(String data) { + return empty; + } + + public Nodes makeText(String data) { + return empty; + } + + public Nodes finishMakingElement(Element element) { + TreePrinter.inspect(element); + return new Nodes(element); + } + + public Nodes makeDocType(String rootElementName, + String publicID, String systemID) { + return empty; + } + + public Nodes makeProcessingInstruction( + String target, String data) { + return empty; + } + + public static void main(String[] args) { + + if (args.length <= 0) { + System.out.println( + "Usage: java nu.xom.samples.StreamingTreeReporter URL" + ); + return; + } + + try { + Builder parser = new Builder(new StreamingTreeReporter()); + parser.build(args[0]); + } + catch (ParsingException ex) { + System.out.println(args[0] + " is not well-formed."); + System.out.println(ex.getMessage()); + } + catch (IOException ex) { + System.out.println( + "Due to an IOException, the parser could not read " + + args[0] + ); + } + + } + +} diff --git a/src/nu/xom/samples/StreamingTypeCounter.java b/src/nu/xom/samples/StreamingTypeCounter.java new file mode 100644 index 0000000..a96554e --- /dev/null +++ b/src/nu/xom/samples/StreamingTypeCounter.java @@ -0,0 +1,138 @@ +/* Copyright 2002-2004 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.samples; + +import java.io.IOException; + +import nu.xom.Builder; +import nu.xom.Element; +import nu.xom.Attribute; +import nu.xom.Nodes; +import nu.xom.NodeFactory; +import nu.xom.ParsingException; + + +/** + *

    + * Demonstrates walking the element hierarchy of + * an XML document in a streaming fashion while + * storing state in the node factory. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.0 + * + */ +public class StreamingTypeCounter extends NodeFactory{ + + private int CDATA = 0; + private int ID = 0; + private int IDREF = 0; + private int IDREFS = 0; + private int NMTOKEN = 0; + private int NMTOKENS = 0; + private int ENTITY = 0; + private int ENTITIES = 0; + private int NOTATION = 0; + + private Nodes empty = new Nodes(); + + public void printCount() { + System.out.println("CDATA type attributes: " + CDATA); + System.out.println("ID type attributes: " + ID ); + System.out.println("IDREF type attributes: " + IDREF ); + System.out.println("IDREFS type attributes: " + IDREFS ); + System.out.println("NMTOKEN type attributes: " + NMTOKEN ); + System.out.println("NMTOKENS type attributes: " + NMTOKENS ); + System.out.println("ENTITY type attributes: " + ENTITY ); + System.out.println("ENTITIES type attributes: " + ENTITIES ); + System.out.println("NOTATION type attributes: " + NOTATION ); + } + + public static void main(String[] args) { + + if (args.length == 0) { + System.out.println( + "Usage: java nu.xom.samples.StreamingTypeCounter URL" + ); + return; + } + + StreamingTypeCounter counter = new StreamingTypeCounter(); + Builder builder = new Builder(counter); + + try { + builder.build(args[0]); + counter.printCount(); + } + // indicates a well-formedness error + catch (ParsingException ex) { + System.out.println(args[0] + " is not well-formed."); + System.out.println(ex.getMessage()); + } + catch (IOException ex) { + System.out.println(ex); + } + + } + + // We don't need the comments. + public Nodes makeComment(String data) { + return empty; + } + + // We don't need text nodes at all + public Nodes makeText(String data) { + return empty; + } + + public Element startMakingElement(String name, String namespace) { + // We only need to create the root element + return null; + } + + public Nodes makeAttribute(String name, String URI, + String value, Attribute.Type type) { + if (type.equals(Attribute.Type.CDATA)) CDATA++; + else if (type.equals(Attribute.Type.UNDECLARED)) CDATA++; + else if (type.equals(Attribute.Type.ID)) ID++; + else if (type.equals(Attribute.Type.IDREF)) IDREF++; + else if (type.equals(Attribute.Type.IDREFS)) IDREFS++; + else if (type.equals(Attribute.Type.NMTOKEN)) NMTOKEN++; + else if (type.equals(Attribute.Type.NMTOKENS)) NMTOKENS++; + else if (type.equals(Attribute.Type.ENTITY)) ENTITY++; + else if (type.equals(Attribute.Type.ENTITIES)) ENTITIES++; + else if (type.equals(Attribute.Type.NOTATION)) NOTATION++; + return empty; + } + + public Nodes makeDocType(String rootElementName, + String publicID, String systemID) { + return empty; + } + + public Nodes makeProcessingInstruction( + String target, String data) { + return empty; + } + +} diff --git a/src/nu/xom/samples/StreamingXHTMLPurifier.java b/src/nu/xom/samples/StreamingXHTMLPurifier.java new file mode 100644 index 0000000..938ab11 --- /dev/null +++ b/src/nu/xom/samples/StreamingXHTMLPurifier.java @@ -0,0 +1,161 @@ +/* Copyright 2002-2004, 2019 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.samples; + +import java.io.IOException; +import java.util.Stack; + +import nu.xom.Attribute; +import nu.xom.Builder; +import nu.xom.DocType; +import nu.xom.Document; +import nu.xom.Element; +import nu.xom.NodeFactory; +import nu.xom.Nodes; +import nu.xom.ParsingException; +import nu.xom.Serializer; + +/** + *

    + * Demonstrates a custom NodeFactory that strips out + * all non-XHTML elements. It’s easy enough to drop out any elements + * that are not in the XHTML namespace. However, in the case of SVG, + * MathML and most other applications you’ll want to remove the + * content of these elements as well. I’ll assume that the namespace + * for text is the same as the namespace of the parent element. + * (This is not at all clear from the namespaces specification, + * but it makes sense in many cases.) To track the nearest namespace + * for non-elements, makeElement() will push the element’s namespace + * onto a stack and endElement() will pop it off. Peeking at the top + * of the stack tells you what namespace the nearest element uses. + * This is modeled after Example 8-9 in + * Processing XML with Java. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.3.1 + * + */ + +public class StreamingXHTMLPurifier extends NodeFactory { + + private Stack namespaces = new Stack(); + private Nodes empty = new Nodes(); + public final static String XHTML_NAMESPACE + = "http://www.w3.org/1999/xhtml"; + + // We need text nodes only inside XHTML + public Nodes makeText(String data) { + if (inXHTML()) return super.makeText(data); + return empty; + } + + public Nodes makeComment(String data) { + if (inXHTML()) return super.makeComment(data); + return empty; + } + + + private boolean inXHTML() { + if (namespaces.isEmpty()) return true; // document prolog + String currentNamespace = (String) (namespaces.peek()); + if (XHTML_NAMESPACE.equals(currentNamespace)) return true; + return false; + } + + public Element startMakingElement(String name, String namespace) { + + namespaces.push(namespace); + if (XHTML_NAMESPACE.equals(namespace)) { + return super.startMakingElement(name, namespace); + } + return null; + } + + public Nodes finishMakingElement(Element element) { + namespaces.pop(); + int namespaceCount = element.getNamespaceDeclarationCount(); + for (int i = 0; i < namespaceCount; i++) { + String prefix = element.getNamespacePrefix(i); + element.removeNamespaceDeclaration(prefix); + if (element.getNamespaceDeclarationCount() < namespaceCount) { + i--; + namespaceCount--; + } + } + return new Nodes(element); + } + + public Nodes makeDocType(String rootElementName, + String publicID, String systemID) { + return new Nodes(new DocType("html", + "PUBLIC \"-//W3C//DTD XHTML Basic 1.0//EN\"", + "http://www.w3.org/TR/xhtml-basic/xhtml-basic10.dtd")); + } + + public Nodes makeProcessingInstruction( + String target, String data) { + if (inXHTML()) { + return super.makeProcessingInstruction(target, data); + } + return empty; + } + + public Nodes makeAttribute(String name, String URI, + String value, Attribute.Type type) { + if ("".equals(URI) + || "http://www.w3.org/XML/1998/namespace".equals(URI)) { + return super.makeAttribute(name, URI, value, type); + } + return empty; + } + + public static void main(String[] args) { + + if (args.length == 0) { + System.out.println( + "Usage: java nu.xom.samples.StreamingXHTMLPurifier URL" + ); + return; + } + + StreamingXHTMLPurifier factory = new StreamingXHTMLPurifier(); + Builder builder = new Builder(factory); + + try { + Document doc = builder.build(args[0]); + Serializer serializer = new Serializer(System.out); + serializer.write(doc); + } + // indicates a well-formedness error + catch (ParsingException ex) { + System.out.println(args[0] + " is not well-formed."); + System.out.println(ex.getMessage()); + ex.printStackTrace(); + } + catch (IOException ex) { + System.out.println(ex); + } + + } + +} diff --git a/src/nu/xom/samples/StreamingXHTMLQualifier.java b/src/nu/xom/samples/StreamingXHTMLQualifier.java new file mode 100644 index 0000000..308433c --- /dev/null +++ b/src/nu/xom/samples/StreamingXHTMLQualifier.java @@ -0,0 +1,88 @@ +/* Copyright 2002, 2003 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.samples; + +import java.io.IOException; + +import nu.xom.Builder; +import nu.xom.Document; +import nu.xom.Element; +import nu.xom.NodeFactory; +import nu.xom.ParsingException; +import nu.xom.Serializer; + +/** + *

    + * Demonstrates a custom NodeFactory that changes the + * namespaces of elements while building the document so a second + * tree walk is not required. Specifically, it adds the XHTML + * namespace http://www.w3.org/1999/xhtml to all + * elements. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.0 + * + */ + +public class StreamingXHTMLQualifier extends NodeFactory { + + public final static String XHTML_NAMESPACE + = "http://www.w3.org/1999/xhtml"; + + public Element startMakingElement(String name, String namespace) { + + if ("".equals(namespace) || null == namespace) { + return super.startMakingElement(name, XHTML_NAMESPACE); + } + else return super.startMakingElement(name, namespace); + } + + public static void main(String[] args) { + + if (args.length <= 0) { + System.out.println( + "Usage: java nu.xom.samples.StreamingXHTMLQualifier URL" + ); + return; + } + + try { + Builder parser = new Builder(new StreamingXHTMLQualifier()); + Document doc = parser.build(args[0]); + Serializer out = new Serializer(System.out); + out.write(doc); + } + catch (ParsingException ex) { + System.out.println(args[0] + " is not well-formed."); + System.out.println(ex.getMessage()); + } + catch (IOException ex) { + System.out.println( + "Due to an IOException, the parser could not read " + + args[0] + ); + } + + } + +} diff --git a/src/nu/xom/samples/Subfunction.java b/src/nu/xom/samples/Subfunction.java new file mode 100644 index 0000000..cc0a012 --- /dev/null +++ b/src/nu/xom/samples/Subfunction.java @@ -0,0 +1,69 @@ +/* Copyright 2002, 2003 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.samples; + +import nu.xom.Element; + + +/** + * + *

    + * Demonstrates building a structured XML document, + * from flat, tabular data. A different version of this + * example was originally developed for Chapter 4 of + * Processing + * XML with Java. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.0 + * + */ +public class Subfunction { + + private String code; + private String title; + private long amount; + + public Subfunction(String title, String code, long amount) { + + this.title = title; + this.code = code; + this.amount = amount; + + } + + public Element getXML() { + + Element subfunction = new Element("Subfunction"); + Element name = new Element("Name"); + Element code = new Element("Code"); + Element amount = new Element("Amount"); + name.appendChild(this.title); + code.appendChild(this.code); + amount.appendChild(String.valueOf(this.amount)); + return subfunction; + + } + +} \ No newline at end of file diff --git a/src/nu/xom/samples/TextExtractor.java b/src/nu/xom/samples/TextExtractor.java new file mode 100644 index 0000000..e72d9e5 --- /dev/null +++ b/src/nu/xom/samples/TextExtractor.java @@ -0,0 +1,72 @@ +/* Copyright 2002, 2003 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.samples; + +import java.io.IOException; + +import nu.xom.Builder; +import nu.xom.Document; +import nu.xom.ParsingException; + +/** + * + *

    + * Demonstrates the getValue() method of the + * Document class. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.0 + * + */ +public class TextExtractor { + + public static void main(String[] args) { + + if (args.length <= 0) { + System.out.println( + "Usage: java nu.xom.samples.TextExtractor URL"); + return; + } + + try { + Builder parser = new Builder(); + Document doc = parser.build(args[0]); + System.out.println(doc.getValue()); + + } + catch (ParsingException ex) { + System.out.println(args[0] + " is not well-formed."); + System.out.println(" at line " + ex.getLineNumber() + + ", column " + ex.getColumnNumber()); + System.out.println(ex.getMessage()); + } + catch (IOException ex) { + System.out.println( + "Due to an IOException, the parser could not check " + + args[0] + ); + } + + } + +} \ No newline at end of file diff --git a/src/nu/xom/samples/TextMerger.java b/src/nu/xom/samples/TextMerger.java new file mode 100644 index 0000000..3f79311 --- /dev/null +++ b/src/nu/xom/samples/TextMerger.java @@ -0,0 +1,63 @@ +/* Copyright 2004 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.samples; + +import nu.xom.Element; +import nu.xom.Node; +import nu.xom.ParentNode; +import nu.xom.Text; + +/** + *

    + * Utility methods for merging all consecutive text nodes. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.0 + * + */ +public class TextMerger { + + + public static void merge(ParentNode parent) { + + for (int i = 0; i < parent.getChildCount(); i++) { + Node child = parent.getChild(i); + if (child instanceof Text) { + StringBuffer sb = new StringBuffer(child.getValue()); + Node nextChild; + while ((nextChild = parent.getChild(i+1)) instanceof Text) { + sb.append(nextChild.getValue()); + parent.removeChild(nextChild); + if (i+1 == parent.getChildCount()) break; + } + if (sb.length() == 0) parent.removeChild(child); + else parent.replaceChild(child, new Text(sb.toString())); + } + else if (child instanceof Element) { + merge((ParentNode) child); + } + } + + } + +} diff --git a/src/nu/xom/samples/TextSerializer.java b/src/nu/xom/samples/TextSerializer.java new file mode 100644 index 0000000..ba40b6e --- /dev/null +++ b/src/nu/xom/samples/TextSerializer.java @@ -0,0 +1,128 @@ +/* Copyright 2003 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.samples; + +import java.io.IOException; +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; + +import nu.xom.Builder; +import nu.xom.Comment; +import nu.xom.DocType; +import nu.xom.Document; +import nu.xom.Element; +import nu.xom.ParsingException; +import nu.xom.ProcessingInstruction; +import nu.xom.Serializer; +import nu.xom.Text; + +/** + *

    + * This Serializer subclass outputs raw, unescaped text + * from the text nodes, but no markup of any kind. In essence, it + * converts XML into plain text. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.0 + * + */ +public class TextSerializer extends Serializer { + + /** + *

    + * Create a new text serializer that uses the UTF-8 encoding. + *

    + * + * @param out the output stream to write the document on + * + * @throws NullPointerException if out is null + */ + public TextSerializer(OutputStream out) { + super(out); + } + + /** + *

    + * Create a new serializer that uses a specified encoding. + * The encoding must be recognized by the Java virtual machine. + *

    + * + * @param out the output stream to write the document on + * @param encoding the character encoding for the serialization + * + * @throws NullPointerException if out + * or encoding is null + * @throws UnsupportedEncodingException if the VM does not + * support the requested encoding + * + */ + public TextSerializer(OutputStream out, String encoding) + throws UnsupportedEncodingException { + super(out, encoding); + } + + protected void writeStartTag(Element element) {} + protected void writeEmptyElementTag(Element element) {} + protected void writeEndTag(Element element) {} + protected void writeXMLDeclaration() {} + protected void write(Comment comment) {} + protected void write(ProcessingInstruction instruction) {} + protected void write(DocType doctype) {} + + // Here we use writeRaw because we don't want characters like & + // and < to be escaped. If they can't be written in the specified + // encoding, an exception is thrown. + protected void writeText(Text text) throws IOException { + writeRaw(text.getValue()); + } + + public static void main(String[] args) { + + if (args.length <= 0) { + System.out.println( + "Usage: java nu.xom.samples.TextSerializer URL"); + return; + } + + try { + Builder parser = new Builder(); + Document doc = parser.build(args[0]); + Serializer serializer = new TextSerializer(System.out); + serializer.write(doc); + } + catch (ParsingException ex) { + System.out.println(args[0] + " is not well-formed."); + System.out.println(" at line " + ex.getLineNumber() + + ", column " + ex.getColumnNumber()); + System.out.println(ex.getMessage()); + } + catch (IOException ex) { + System.out.println( + "Due to an IOException, the serialization of " + + args[0] + " could not be completed." + ); + } + + } + +} diff --git a/src/nu/xom/samples/Transformer.java b/src/nu/xom/samples/Transformer.java new file mode 100644 index 0000000..63ec5ad --- /dev/null +++ b/src/nu/xom/samples/Transformer.java @@ -0,0 +1,83 @@ +/* Copyright 2002, 2003, 2018 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.samples; + +import nu.xom.Builder; +import nu.xom.Document; +import nu.xom.Node; +import nu.xom.Nodes; +import nu.xom.xslt.XSLTransform; + +/** + * @author Elliotte Rusty Harold + * @version 1.3.0 + * + */ +public class Transformer { + + /** + *

    + * The driver method for the Transformer program. + *

    + * + * @param args args[0] contains the URL or + * filename of the document to be transformed. + * args[1] contains the URL or + * filename of the stylesheet. + */ + public static void main(String[] args) { + + if (args.length < 2) { + System.err.println( + "Usage: java nu.xom.samples.Transformer document stylesheet" + ); + return; + } + + System.setProperty( + "javax.xml.transform.TransformerFactory", + "org.apache.xalan.processor.TransformerFactoryImpl"); + /* System.setProperty( + "javax.xml.transform.TransformerFactory", + "com.icl.saxon.TransformerFactoryImpl"); */ + + Builder builder = new Builder(); + try { + Document doc = builder.build(args[0]); + Document stylesheet = builder.build(args[1]); + XSLTransform transform = new XSLTransform(stylesheet); + + Nodes output = transform.transform(doc); + + for (Node node : output) { + System.out.print(node.toXML()); + } + System.out.println(); + } + catch (Exception ex) { + System.err.println(ex); + ex.printStackTrace(); + } + + } + +} diff --git a/src/nu/xom/samples/TreePrinter.java b/src/nu/xom/samples/TreePrinter.java new file mode 100644 index 0000000..ee72a6f --- /dev/null +++ b/src/nu/xom/samples/TreePrinter.java @@ -0,0 +1,120 @@ +/* Copyright 2002, 2003, 2018 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.samples; + +import java.io.IOException; + +import nu.xom.Attribute; +import nu.xom.Builder; +import nu.xom.Document; +import nu.xom.Element; +import nu.xom.ParsingException; + + +/** + * + *

    + * Demonstrates reading the names, namespaces, and + * attributes of an element. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.3.0 + * + */ +public class TreePrinter { + + public static void main(String[] args) { + + if (args.length == 0) { + System.out.println("Usage: java nu.xom.samples.TreePrinter URL"); + return; + } + + Builder builder = new Builder(); + + try { + Document doc = builder.build(args[0]); + Element root = doc.getRootElement(); + listChildren(root); + } + // indicates a well-formedness error + catch (ParsingException ex) { + System.out.println(args[0] + " is not well-formed."); + System.out.println(ex.getMessage()); + } + catch (IOException ex) { + System.out.println(ex); + } + + } + + + // Print the properties of each element + public static void inspect(Element element) { + + if (element.getParent() != null) { + // Print a blank line to separate it from the previous + // element. + System.out.println(); + } + + String qualifiedName = element.getQualifiedName(); + System.out.println(qualifiedName + ":"); + + String namespace = element.getNamespaceURI(); + if (!namespace.equals("")) { + String localName = element.getLocalName(); + String uri = element.getNamespaceURI(); + String prefix = element.getNamespacePrefix(); + System.out.println(" Local name: " + localName); + System.out.println(" Namespace URI: " + uri); + if (!"".equals(prefix)) { + System.out.println(" Namespace prefix: " + prefix); + } + } + for (int i = 0; i < element.getAttributeCount(); i++) { + Attribute attribute = element.getAttribute(i); + String name = attribute.getQualifiedName(); + String value = attribute.getValue(); + System.out.println(" " + name + "=\"" + value + "\""); + } + + for (int i = 0; i < element.getNamespaceDeclarationCount(); i++) { + String additional = element.getNamespacePrefix(i); + String uri = element.getNamespaceURI(additional); + System.out.println( + " xmlns:" + additional + "=\"" + uri + "\""); + } + + } + + public static void listChildren(Element current) { + + inspect(current); + for (Element child : current.getChildElements()) { + listChildren(child); + } + + } + +} diff --git a/src/nu/xom/samples/TreeReporter.java b/src/nu/xom/samples/TreeReporter.java new file mode 100644 index 0000000..4acb12e --- /dev/null +++ b/src/nu/xom/samples/TreeReporter.java @@ -0,0 +1,84 @@ +/* Copyright 2002, 2003 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.samples; + +import java.io.IOException; + +import nu.xom.Builder; +import nu.xom.Node; +import nu.xom.ParsingException; + + +/** + * + *

    + * Demonstrates recursive descent through a document. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.0 + * + */ +public class TreeReporter { + + public static void main(String[] args) { + + if (args.length <= 0) { + System.out.println( + "Usage: java nu.xom.samples.TreeReporter URL" + ); + return; + } + + TreeReporter iterator = new TreeReporter(); + try { + Builder parser = new Builder(); + + // Read the entire document into memory + Node document = parser.build(args[0]); + + // Process it starting at the root + iterator.followNode(document); + + } + catch (IOException ex) { + System.out.println(ex); + } + catch (ParsingException ex) { + System.out.println(ex); + } + + } // end main + + private PropertyPrinter printer = new PropertyPrinter(); + + // note use of recursion + public void followNode(Node node) throws IOException { + + printer.writeNode(node); + for (int i = 0; i < node.getChildCount(); i++) { + followNode(node.getChild(i)); + } + + } + +} \ No newline at end of file diff --git a/src/nu/xom/samples/TreeViewer.java b/src/nu/xom/samples/TreeViewer.java new file mode 100644 index 0000000..42a0d08 --- /dev/null +++ b/src/nu/xom/samples/TreeViewer.java @@ -0,0 +1,110 @@ +/* Copyright 2002, 2003 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.samples; + +import javax.swing.JFrame; +import javax.swing.JScrollPane; +import javax.swing.JTree; +import javax.swing.tree.DefaultMutableTreeNode; +import javax.swing.tree.MutableTreeNode; + +import nu.xom.Builder; +import nu.xom.Document; +import nu.xom.Element; +import nu.xom.Elements; + +/** + * + *

    + * Demonstrates using Swing to present a graphical display of + * the tree structure of an XML document. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.0 + * + */ +public class TreeViewer { + + // Initialize the per-element data structures + public static MutableTreeNode processElement(Element element) { + + String data; + if (element.getNamespaceURI().equals("")) + data = element.getLocalName(); + else { + data = + '{' + + element.getNamespaceURI() + + "} " + + element.getQualifiedName(); + } + + MutableTreeNode node = new DefaultMutableTreeNode(data); + Elements children = element.getChildElements(); + for (int i = 0; i < children.size(); i++) { + node.insert(processElement(children.get(i)), i); + } + + return node; + + } + + public static void display(Document doc) { + + Element root = doc.getRootElement(); + JTree tree = new JTree(processElement(root)); + JScrollPane treeView = new JScrollPane(tree); + JFrame f = new JFrame("XML Tree"); + + + String version = System.getProperty("java.version"); + if (version.startsWith("1.2") || version.startsWith("1.1")) { + f.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE); + } + else { + // JFrame.EXIT_ON_CLOSE == 3 but this named constant is not + // available in Java 1.2 + f.setDefaultCloseOperation(3); + } + f.getContentPane().add(treeView); + f.pack(); + f.show(); + + } + + public static void main(String[] args) { + + try { + Builder builder = new Builder(); + for (int i = 0; i < args.length; i++) { + Document doc = builder.build(args[i]); + display(doc); + } + } + catch (Exception ex) { + System.err.println(ex); + } + + } // end main() + +} // end TreeViewer \ No newline at end of file diff --git a/src/nu/xom/samples/ValidFibonacci.java b/src/nu/xom/samples/ValidFibonacci.java new file mode 100644 index 0000000..a541f00 --- /dev/null +++ b/src/nu/xom/samples/ValidFibonacci.java @@ -0,0 +1,69 @@ +/* Copyright 2002, 2003 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.samples; + +import java.math.BigInteger; + +import nu.xom.Attribute; +import nu.xom.DocType; +import nu.xom.Document; +import nu.xom.Element; + +/** + *

    + * Demonstrates the creation and serialization + * of a simple XML document that includes a + * document type declaration. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.0 + * + */ +public class ValidFibonacci { + + public static void main(String[] args) { + + BigInteger low = BigInteger.ONE; + BigInteger high = BigInteger.ONE; + + Element root = new Element("Fibonacci_Numbers"); + for (int i = 1; i <= 10; i++) { + Element fibonacci = new Element("fibonacci"); + fibonacci.appendChild(low.toString()); + Attribute index = new Attribute("index", String.valueOf(i)); + fibonacci.addAttribute(index); + root.appendChild(fibonacci); + + BigInteger temp = high; + high = high.add(low); + low = temp; + } + Document doc = new Document(root); + DocType doctype + = new DocType("Fibonacci_Numbers", "fibonacci.dtd"); + doc.insertChild(doctype, 0); + System.out.println(doc.toXML()); + + } + +} \ No newline at end of file diff --git a/src/nu/xom/samples/Validator.java b/src/nu/xom/samples/Validator.java new file mode 100644 index 0000000..8f3f0e8 --- /dev/null +++ b/src/nu/xom/samples/Validator.java @@ -0,0 +1,77 @@ +/* Copyright 2002, 2003 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.samples; + +import java.io.IOException; + +import nu.xom.Builder; +import nu.xom.ParsingException; +import nu.xom.ValidityException; + +/** + *

    + * Demonstrates validation via the Builder class. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.2.11 + * + */ +public class Validator { + + public static void main(String[] args) { + + if (args.length <= 0) { + System.out.println("Usage: java nu.xom.samples.Validator URL"); + return; + } + + Builder parser = new Builder(true); + + for (String url : args) { + try { + parser.build(url); + System.out.println(url + " is valid."); + } + catch (ValidityException ex) { + System.out.println(url + " is not valid."); + System.out.println(ex.getMessage()); + System.out.println(" at line " + ex.getLineNumber() + + ", column " + ex.getColumnNumber()); + } + catch (ParsingException ex) { + System.out.println(url + " is not well-formed."); + System.out.println(ex.getMessage()); + System.out.println(" at line " + ex.getLineNumber() + + ", column " + ex.getColumnNumber()); + } + catch (IOException ex) { + System.out.println( + "Due to an IOException, the parser could not check " + url + ); + ex.printStackTrace(); + } + } + + } + +} \ No newline at end of file diff --git a/src/nu/xom/samples/WrappingSerializer.java b/src/nu/xom/samples/WrappingSerializer.java new file mode 100644 index 0000000..8df9383 --- /dev/null +++ b/src/nu/xom/samples/WrappingSerializer.java @@ -0,0 +1,366 @@ +/* Copyright 2003, 2004 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.samples; + +import java.io.IOException; +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; + +import nu.xom.Attribute; +import nu.xom.Builder; +import nu.xom.DocType; +import nu.xom.Document; +import nu.xom.Element; +import nu.xom.ParsingException; +import nu.xom.ProcessingInstruction; +import nu.xom.Serializer; + +/** + *

    + * This class writes XML with a maximum line length, + * but only breaks lines inside tags. It does + * not change a document's infoset. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.0 + * + */ +public class WrappingSerializer extends Serializer { + + public WrappingSerializer(OutputStream out) { + super(out); + } + + public WrappingSerializer(OutputStream out, String encoding) + throws UnsupportedEncodingException { + super(out, encoding); + } + + private int maxLength; + + /** + *

    + * Returns the preferred maximum line length. + *

    + * + * @return the maximum line length. + */ + public int getMaxLength() { + return this.maxLength; + } + + /** + *

    + * Sets the suggested maximum line length for this serializer. + * Setting this to 0 indicates that no automatic wrapping is to be + * performed. When a line approaches this length, the serializer + * begins looking for opportunities to break the line. + * It will only break inside a tag, at places that do not + * affect the infoset, such as between attribute values or + * before the closing >. In some circumstances the + * serializer may not be able to break the line before the maximum + * length is reached. In this case, + * the serializer will exceed the maximum line length. + *

    + * + *

    + * The default value for maxLength is 0, which is + * interpreted as no maximum line length. + * Setting this to a negative value just sets it to 0. + *

    + * + * @param maxLength the suggested maximum line length + */ + public void setMaxLength(int maxLength) { + this.maxLength = maxLength; + } + + protected void writeStartTag(Element element) + throws IOException { + writeRaw("<"); + writeRaw(element.getQualifiedName()); + writeAttributes(element); + writeNamespaceDeclarations(element); + if (needsBreak()) breakLine(); + writeRaw(">"); + } + + protected void writeEmptyElementTag(Element element) + throws IOException { + writeRaw("<"); + writeRaw(element.getQualifiedName()); + writeAttributes(element); + writeNamespaceDeclarations(element); + if (needsBreak()) breakLine(); + writeRaw("/>"); + } + + public void writeEndTag(Element element) throws IOException { + writeRaw("<"); + writeRaw(element.getQualifiedName()); + if (needsBreak()) breakLine(); + writeRaw("/>"); + } + + /** + *

    + * This method writes an attribute in the form + * name="value". + * Characters in the attribute value are escaped as necessary. + *

    + * + * @param attribute the Attribute to write + * + * @throws IOException if the underlying OutputStream + * encounters an I/O error + */ + protected void write(Attribute attribute) throws IOException { + + String name = attribute.getQualifiedName(); + if (maxLength <= this.getColumnNumber() + name.length()) { + breakLine(); + } + writeRaw(name); + if (this.getColumnNumber() == maxLength) { + breakLine(); + } + writeRaw("="); + String value = attribute.getValue(); + if (maxLength < value.length() + 2) { + breakLine(); + } + writeRaw("\""); + writeAttributeValue(attribute.getValue()); + writeRaw("\""); + + } + + /** + *

    + * This writes a namespace declaration in the form + * xmlns:prefix="uri" or + * xmlns="uri". + *

    + * + * @param prefix the namespace prefix; the empty string for the + * default namespace + * @param uri the namespace URI + * + * @throws IOException if the underlying OutputStream + * encounters an I/O error + */ + protected void writeNamespaceDeclaration(String prefix, String uri) + throws IOException { + + String name; + if ("".equals(prefix)) { + name = "xmlns"; + } + else { + name = "xmlns:" + prefix; + } + if (this.maxLength < this.getColumnNumber() + name.length()) { + breakLine(); + } + writeRaw(name); + + if (this.getColumnNumber() == maxLength) { + breakLine(); + } + writeRaw("="); + + if (this.maxLength < this.getColumnNumber() + uri.length() + 2) { + breakLine(); + } + writeRaw("\""); + writeEscaped(uri); + writeRaw("\""); + } + + private boolean needsBreak() { + if (maxLength > 0) { + return this.maxLength - this.getColumnNumber() <= 10; + } + return false; + } + + /** + *

    + * Serializes a ProcessingInstruction object + * onto the output stream. Line breaks may be inserted + * following the target. + *

    + * + *

    + * Since character and entity references are not resolved + * in processing instructions, processing instructions + * can only be serialized when all + * characters they contain are available in the current + * encoding. + *

    + * + * @param instruction the ProcessingInstruction + * to serialize. + * + * @throws IOException if the underlying OutputStream + * encounters an I/O error + */ + protected void write(ProcessingInstruction instruction) + throws IOException { + writeRaw(""); + } + + /** + *

    + * Serializes a DocType object + * onto the output stream. + *

    + * + * @param doctype the document type declaration to serialize + * + * @throws IOException if the underlying + * OutputStream encounters an I/O error + */ + protected void write(DocType doctype) throws IOException { + writeRaw(""); + + } + + public static void main(String[] args) { + + if (args.length <= 0) { + System.out.println("Usage: java nu.xom.samples.WrappingSerializer URL"); + return; + } + + try { + Builder parser = new Builder(); + Document doc = parser.build(args[0]); + Serializer serializer = new WrappingSerializer(System.out, "ISO-8859-1"); + serializer.setIndent(4); + serializer.setMaxLength(24); + serializer.setPreserveBaseURI(true); + serializer.write(doc); + serializer.flush(); + } + catch (ParsingException ex) { + System.out.println(args[0] + " is not well-formed."); + System.out.println(ex.getMessage()); + } + catch (IOException ex) { + System.out.println( + "Due to an IOException, the parser could not read " + + args[0] + ); + } + + } + +} diff --git a/src/nu/xom/samples/XHTMLPurifier.java b/src/nu/xom/samples/XHTMLPurifier.java new file mode 100644 index 0000000..eef6d8e --- /dev/null +++ b/src/nu/xom/samples/XHTMLPurifier.java @@ -0,0 +1,115 @@ +/* Copyright 2002, 2003, 2018 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.samples; + +import java.io.IOException; + +import nu.xom.Attribute; +import nu.xom.Builder; +import nu.xom.Document; +import nu.xom.Element; +import nu.xom.ParsingException; + +/** + *

    + * Demonstrates the removal of elements and + * their content from a document. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.3.0 + * + */ +public class XHTMLPurifier { + + public final static String XHTML_NAMESPACE + = "http://www.w3.org/1999/xhtml"; + + public static void main(String[] args) { + + if (args.length <= 0) { + System.out.println("Usage: java nu.xom.samples.XHTMLPurifier URL"); + return; + } + + try { + Builder parser = new Builder(); + Document doc = parser.build(args[0]); + Element root = doc.getRootElement(); + if (root.getNamespaceURI().equals(XHTML_NAMESPACE)) { + strip(root); + } + else { + System.out.println(args[0] + + " does not appear to be an XHTML document"); + return; + } + + + System.out.println(doc.toXML()); + } + catch (ParsingException ex) { + System.out.println(args[0] + " is not well-formed."); + System.out.println(ex.getMessage()); + } + catch (IOException ex) { + System.out.println( + "Due to an IOException, the parser could not read " + + args[0] + ); + } + + } + + public static void strip(Element element) { + + if (element.getNamespaceURI().equals(XHTML_NAMESPACE)) { + + // Strip out non XHTML attributes + for (int i = 0; i < element.getAttributeCount(); i++) { + Attribute attribute = element.getAttribute(i); + + if (!"".equals(attribute.getNamespaceURI())) { + if (!"xml".equals(attribute.getNamespacePrefix())) { + attribute.detach(); + } + } + } + + // Strip out additional namespaces + for (int i = 0; i < element.getNamespaceDeclarationCount(); i++) { + String prefix = element.getNamespacePrefix(i); + element.removeNamespaceDeclaration(prefix); + } + + for (Element child : element.getChildElements()) { + strip(child); + } + + } + else { + element.detach(); + } + + } + +} \ No newline at end of file diff --git a/src/nu/xom/samples/XHTMLQualifier.java b/src/nu/xom/samples/XHTMLQualifier.java new file mode 100644 index 0000000..d232e9c --- /dev/null +++ b/src/nu/xom/samples/XHTMLQualifier.java @@ -0,0 +1,79 @@ +/* Copyright 2002, 2003, 2018 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.samples; + +import java.io.IOException; + +import nu.xom.Builder; +import nu.xom.Document; +import nu.xom.Element; +import nu.xom.ParsingException; + +/** + *

    + * Demonstrates adding namespaces to elements. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.3.0 + * + */ +public class XHTMLQualifier { + + public static void main(String[] args) { + + if (args.length == 0) { + System.out.println("Usage: java nu.xom.samples.XHTMLQualifier URL"); + return; + } + + Builder builder = new Builder(); + + try { + Document doc = builder.build(args[0]); + Element root = doc.getRootElement(); + qualify(root); + System.out.println(doc.toXML()); + } + // indicates a well-formedness error + catch (ParsingException ex) { + System.out.println(args[0] + " is not well-formed."); + System.out.println(ex.getMessage()); + } + catch (IOException ex) { + System.out.println(ex); + } + + } + + public static void qualify(Element current) { + + if (current.getNamespaceURI().equals("")) { + current.setNamespaceURI("http://www.w3.org/1999/xhtml"); + } + for (Element child : current.getChildElements()) { + qualify(child); + } + + } + +} diff --git a/src/nu/xom/samples/XHTMLValidator.java b/src/nu/xom/samples/XHTMLValidator.java new file mode 100644 index 0000000..7a7a899 --- /dev/null +++ b/src/nu/xom/samples/XHTMLValidator.java @@ -0,0 +1,126 @@ +/* Copyright 2002, 2003 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.samples; + +import java.io.IOException; + +import nu.xom.Builder; +import nu.xom.DocType; +import nu.xom.Document; +import nu.xom.Element; +import nu.xom.ParsingException; + +/** + *

    + * Demonstrates the use of the DocType class + * by validating XHTML. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.0 + * + */ +public class XHTMLValidator { + + public static void main(String[] args) { + + for (int i = 0; i < args.length; i++) { + validate(args[i]); + } + + } + + private static Builder builder = new Builder(true); + /* turn on validation ^^^^ */ + + // not thread safe + public static void validate(String source) { + + Document document; + try { + document = builder.build(source); + } + catch (ParsingException ex) { + System.out.println(source + + " is invalid XML, and thus not XHTML."); + return; + } + catch (IOException ex) { + System.out.println("Could not read: " + source); + return; + } + + // If we get this far, then the document is valid XML. + // Check to see whether the document is actually XHTML + boolean valid = true; + DocType doctype = document.getDocType(); + + if (doctype == null) { + System.out.println("No DOCTYPE"); + valid = false; + } + else { + // verify the DOCTYPE + String name = doctype.getRootElementName(); + String publicID = doctype.getPublicID(); + + if (!name.equals("html")) { + System.out.println( + "Incorrect root element name " + name); + valid = false; + } + + if (publicID == null + || (!publicID.equals("-//W3C//DTD XHTML 1.0 Strict//EN") + && !publicID.equals( + "-//W3C//DTD XHTML 1.0 Transitional//EN") + && !publicID.equals( + "-//W3C//DTD XHTML 1.0 Frameset//EN"))) { + valid = false; + System.out.println(source + + " does not seem to use an XHTML 1.0 DTD"); + } + } + + + // Check the namespace on the root element + Element root = document.getRootElement(); + String uri = root.getNamespaceURI(); + String prefix = root.getNamespacePrefix(); + if (!uri.equals("http://www.w3.org/1999/xhtml")) { + valid = false; + System.out.println(source + + " does not properly declare the" + + " http://www.w3.org/1999/xhtml namespace" + + " on the root element"); + } + if (!prefix.equals("")) { + valid = false; + System.out.println(source + + " does not use the empty prefix for XHTML"); + } + + if (valid) System.out.println(source + " is valid XHTML."); + + } + +} diff --git a/src/nu/xom/samples/XIncludeDriver.java b/src/nu/xom/samples/XIncludeDriver.java new file mode 100644 index 0000000..abb6c80 --- /dev/null +++ b/src/nu/xom/samples/XIncludeDriver.java @@ -0,0 +1,60 @@ +/* Copyright 2002, 2003 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.samples; + +import nu.xom.Builder; +import nu.xom.Document; +import nu.xom.Serializer; +import nu.xom.xinclude.XIncluder; + +/** + * @author Elliotte Rusty Harold + * @version 1.0 + * + */ +public class XIncludeDriver { + + + /** + *

    + * The driver method for the XIncluder program. + *

    + * + * @param args args[0] contains the URL or + * file name of the first document to be processed. + */ + public static void main(String[] args) { + + Builder builder = new Builder(); + try { + Serializer outputter = new Serializer(System.out, "ISO-8859-1"); + Document input = builder.build(args[0]); + XIncluder.resolveInPlace(input); + outputter.write(input); + } + catch (Exception ex) { + System.err.println(ex); + ex.printStackTrace(); + } + + } +} diff --git a/src/nu/xom/samples/XLinkSpider.java b/src/nu/xom/samples/XLinkSpider.java new file mode 100644 index 0000000..658a072 --- /dev/null +++ b/src/nu/xom/samples/XLinkSpider.java @@ -0,0 +1,131 @@ +/* Copyright 2002-2004, 2006, 2018 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.samples; + +import java.net.MalformedURLException; +import java.net.URL; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import nu.xom.Attribute; +import nu.xom.Builder; +import nu.xom.Document; +import nu.xom.Element; + + +/** + * + *

    + * Demonstrates the reading of attributes in namespaces + * and the getBaseURI() method. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.3.0 + * + */ +public class XLinkSpider { + + private Set spidered = new HashSet(); + private Builder parser = new Builder(); + private List queue = new ArrayList(); + + public static final String XLINK_NS + = "http://www.w3.org/1999/xlink"; + + public void search(URL url) { + + try { + String systemID = url.toExternalForm(); + Document doc = parser.build(systemID); + System.out.println(url); + search(doc.getRootElement()); + } + catch (Exception ex) { + // just skip this document + } + + if (queue.isEmpty()) return; + + URL discovered = (URL) queue.remove(0); + spidered.add(discovered); + search(discovered); + + } + + private void search(Element element) { + + Attribute href = element.getAttribute("href", XLINK_NS); + + URL base = null; + try { + base = new URL(element.getBaseURI()); + } + catch (MalformedURLException ex) { + // Probably just no protocol handler for the + // kind of URLs used inside this element + return; + } + if (href != null) { + String uri = href.getValue(); + // absolutize URL + try { + URL discovered = new URL(base, uri); + // remove fragment identifier if any + discovered = new URL( + discovered.getProtocol(), + discovered.getHost(), + discovered.getFile() + ); + + if (!spidered.contains(discovered) + && !queue.contains(discovered)) { + queue.add(discovered); + } + } + catch (MalformedURLException ex) { + // skip this one + } + } + for (Element child : element.getChildElements()) { + search(child); + } + + } + + public static void main(String[] args) { + + XLinkSpider spider = new XLinkSpider(); + for (int i = 0; i < args.length; i++) { + try { + spider.search(new URL(args[i])); + } + catch (MalformedURLException ex) { + System.err.println(ex); + } + } + + } // end main() + +} \ No newline at end of file diff --git a/src/nu/xom/samples/XMLPrinter.java b/src/nu/xom/samples/XMLPrinter.java new file mode 100644 index 0000000..4d3cc4d --- /dev/null +++ b/src/nu/xom/samples/XMLPrinter.java @@ -0,0 +1,68 @@ +/* Copyright 2002-2004 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.samples; + +import java.io.IOException; + +import nu.xom.Builder; +import nu.xom.Document; +import nu.xom.ParsingException; + + +/** + *

    + * Demonstrates parsing a document and outputting + * it again using the toXML method. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.0 + * + */ +public class XMLPrinter { + + public static void main(String[] args) { + + if (args.length <= 0) { + System.out.println("Usage: java nu.xom.samples.XMLPrinter URL"); + return; + } + + try { + Builder parser = new Builder(); + Document doc = parser.build(args[0]); + System.out.println(doc.toXML()); + } + catch (ParsingException ex) { + System.out.println(args[0] + " is not well-formed."); + System.out.println(ex.getMessage()); + } + catch (IOException ex) { + System.out.println( + "Due to an IOException, the parser could not print " + + args[0] + ); + } + + } + +} \ No newline at end of file diff --git a/src/nu/xom/samples/XOMChecker.java b/src/nu/xom/samples/XOMChecker.java new file mode 100644 index 0000000..dd9a3fa --- /dev/null +++ b/src/nu/xom/samples/XOMChecker.java @@ -0,0 +1,69 @@ +/* Copyright 2002, 2003 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.samples; + +import java.io.IOException; + +import nu.xom.Builder; +import nu.xom.ParsingException; + + +/** + *

    + * Demonstrates simple parsing and the exceptions + * that may be throw in the process. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.0 + * + */ +public class XOMChecker { + + public static void main(String[] args) { + + if (args.length <= 0) { + System.out.println("Usage: java nu.xom.samples.XOMChecker URL"); + return; + } + + try { + Builder parser = new Builder(); + parser.build(args[0]); + System.out.println(args[0] + " is well-formed."); + } + catch (ParsingException ex) { + System.out.println(args[0] + " is not well-formed."); + System.out.println(ex.getMessage()); + System.out.println(" at line " + ex.getLineNumber() + + ", column " + ex.getColumnNumber()); + } + catch (IOException ex) { + System.out.println( + "Due to an IOException, the parser could not check " + + args[0] + ); + } + + } + +} \ No newline at end of file diff --git a/src/nu/xom/samples/XPathDriver.java b/src/nu/xom/samples/XPathDriver.java new file mode 100644 index 0000000..526e55a --- /dev/null +++ b/src/nu/xom/samples/XPathDriver.java @@ -0,0 +1,84 @@ +/* Copyright 2005, 2018 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.samples; + +import java.io.IOException; + +import nu.xom.Builder; +import nu.xom.Document; +import nu.xom.Node; +import nu.xom.ParsingException; +import nu.xom.XPathException; +import nu.xom.XPathTypeException; + +/** + * @author Elliotte Rusty Harold + * @version 1.3.0 + * + */ +public class XPathDriver { + + + /** + *

    + * A simple driver program for XPath. + *

    + * + * @param args args[0] contains the URL or + * file name of the first document to be processed. + * args[1] contains the XPath expression + * to apply. + */ + public static void main(String[] args) { + + Builder builder = new Builder(); + + if (args.length < 2) { + System.out.println("Usage: java nu.xom.samples.XPathDriver URL xpath"); + return; + } + + try { + Document input = builder.build(args[0]); + for (Node result : input.query(args[1])) { + // ???? add a wrap option like Saxon + // or based on the XQuery serialization format? + System.out.println(result.toXML()); + } + } + catch (XPathTypeException ex) { + System.err.println(ex.getReturnValue()); + } + catch (XPathException ex) { + System.err.println("XPath error: " + ex.getMessage()); + } + catch (IOException ex) { + System.err.println("Could not read from " + args[0]); + System.err.println(ex.getMessage()); + } + catch (ParsingException ex) { + System.err.println(args[0] + " is malformed."); + System.err.println(ex.getMessage()); + } + + } +} diff --git a/src/nu/xom/samples/XPathHeadlines.java b/src/nu/xom/samples/XPathHeadlines.java new file mode 100644 index 0000000..c70c953 --- /dev/null +++ b/src/nu/xom/samples/XPathHeadlines.java @@ -0,0 +1,69 @@ +/* Copyright 2005, 2018 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.samples; + +import java.io.IOException; + +import nu.xom.Builder; +import nu.xom.Document; +import nu.xom.Node; +import nu.xom.ParsingException; + +/** + *

    + * Use XPath to find just the headlines from an RSS feed. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.3.0 + */ +public class XPathHeadlines { + + + public static void main(String[] args) { + + String url = "http://www.bbc.co.uk/syndication/feeds/news/ukfs_news/world/rss091.xml"; + if (args.length > 0) { + url = args[0]; + } + + try { + Builder parser = new Builder(); + Document doc = parser.build(url); + for (Node title : doc.query("//title")) { + System.out.println(title.getValue()); + } + } + catch (ParsingException ex) { + System.out.println(url + " is not well-formed."); + System.out.println(ex.getMessage()); + } + catch (IOException ex) { + System.out.println( + "Due to an IOException, the parser could not read " + url + ); + } + + } + + +} \ No newline at end of file diff --git a/src/nu/xom/samples/inner/InnerElement.java b/src/nu/xom/samples/inner/InnerElement.java new file mode 100644 index 0000000..928c092 --- /dev/null +++ b/src/nu/xom/samples/inner/InnerElement.java @@ -0,0 +1,91 @@ +/* Copyright 2005 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.samples.inner; + +import java.io.IOException; + +import nu.xom.*; + +public class InnerElement extends Element { + + + private static ThreadLocal builders = new ThreadLocal() { + + protected synchronized Builder initialValue() { + return new Builder(new InnerFactory()); + } + + }; + + + public InnerElement(String name) { + super(name); + } + + + public InnerElement(String namespace, String name) { + super(namespace, name); + } + + + public InnerElement(Element element) { + super(element); + } + + + public String getInnerXML() { + + StringBuffer sb = new StringBuffer(); + for (int i = 0; i < getChildCount(); i++) { + sb.append(getChild(i).toXML()); + } + return sb.toString(); + + } + + + public void setInnerXML(String xml) throws ParsingException { + + xml = "" + + xml + ""; + Document doc; + try { + doc = builders.get().build(xml, null); + } + catch (IOException ex) { + throw new ParsingException(ex.getMessage(), ex); + } + this.removeChildren(); + Nodes children = doc.getRootElement().removeChildren(); + for (Node child : children) { + this.appendChild(child); + } + + } + + + public Element copy() { + return new InnerElement(this); + } + + +} diff --git a/src/nu/xom/samples/inner/InnerFactory.java b/src/nu/xom/samples/inner/InnerFactory.java new file mode 100644 index 0000000..5253e6c --- /dev/null +++ b/src/nu/xom/samples/inner/InnerFactory.java @@ -0,0 +1,32 @@ +/* Copyright 2005 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.samples.inner; + +import nu.xom.*; + +public class InnerFactory extends NodeFactory { + + public Element startMakingElement(String namespaceURI, String name) { + return new InnerElement(namespaceURI, name); + } + +} diff --git a/src/nu/xom/samples/inner/InnerTest.java b/src/nu/xom/samples/inner/InnerTest.java new file mode 100644 index 0000000..5558282 --- /dev/null +++ b/src/nu/xom/samples/inner/InnerTest.java @@ -0,0 +1,54 @@ +/* Copyright 2005 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.samples.inner; + +import java.io.IOException; + +import junit.framework.TestCase; +import nu.xom.*; + + +public class InnerTest extends TestCase { + + private Builder builder = new Builder(new InnerFactory()); + + public void testGetInner() throws ValidityException, ParsingException, IOException { + Document doc = builder.build("testtest2", null); + InnerElement root = (InnerElement) doc.getRootElement(); + assertEquals("testtest2", root.getInnerXML()); + } + + public void testSetInner() throws ValidityException, ParsingException, IOException { + Document doc = builder.build("testtest2", null); + InnerElement root = (InnerElement) doc.getRootElement(); + root.setInnerXML("ad"); + assertEquals("ad", root.getInnerXML()); + } + + public void testGetInnerMixed() throws ValidityException, ParsingException, IOException { + Document doc = builder.build("testtesttest2", null); + InnerElement root = (InnerElement) doc.getRootElement(); + assertEquals("testtesttest2", root.getInnerXML()); + } + + +} diff --git a/src/nu/xom/samples/package.html b/src/nu/xom/samples/package.html new file mode 100644 index 0000000..907ec6e --- /dev/null +++ b/src/nu/xom/samples/package.html @@ -0,0 +1,60 @@ + + + + + + + +

    +nu.xom.samples includes several dozen example programs that +demonstrate different parts of the XOM API. +These are intended for tutorial purposes. They are not designed as reusable code +for the most part. Most of the examples are adapted +from Processing XML with Java +by Elliotte Rusty Harold. Please consult that book +(available in its entirety online) if you'd like more information +about what particular samples are intended to show. +That book provides SAX, DOM, and/or JDOM implementations +of most of these samples. +

    + +

    + These classes are not officially part of the XOM API. + They have not been extensively tested, and I make no promises + that they will be part of any future XOM release; or, if they are, + that their APIs will remain compatible. + Should you find any of them useful and wish to use them in your + own code, I recommend you copy the source code, and place it in your + own package. It probably wouldn't hurt to add a lot of unit tests as well. +

    + + +@since 1.0 + + + diff --git a/src/nu/xom/tests/ANodeFactory.java b/src/nu/xom/tests/ANodeFactory.java new file mode 100644 index 0000000..e3cbacd --- /dev/null +++ b/src/nu/xom/tests/ANodeFactory.java @@ -0,0 +1,119 @@ +/* Copyright 2005 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ +package nu.xom.tests; + +import nu.xom.*; +import nu.xom.Attribute.Type; + +class ANodeFactory extends NodeFactory { + + public Nodes makeAttribute(String name, String URI, String value, Type type) { + return new Nodes(new AnAttribute(name, URI, value, type)); + } + + public Nodes makeComment(String data) { + return new Nodes(new AComment(data)); + } + + public Nodes makeText(String text) { + return new Nodes(new AText(text)); + } + + public Nodes makeDocType(String rootElementName, String publicID, String systemID) { + return new Nodes(new ADocType(rootElementName, publicID, systemID)); + } + + public Nodes makeProcessingInstruction(String target, String data) { + return new Nodes(new AProcessingInstruction(target, data)); + } + + public Document startMakingDocument() { + return new ADocument(new AElement("foo", "http://www,examle.com")); + } + + public Element startMakingElement(String name, String namespace) { + return new AElement(name, namespace); + } + +} + + +class AElement extends Element { + + public AElement(String name, String uri) { + super(name, uri); + } + +} + + +class ADocument extends Document { + + public ADocument(Element root) { + super(root); + } + +} + + +class AText extends Text { + + public AText(String text) { + super(text); + } + +} + + +class AComment extends Comment { + + public AComment(String text) { + super(text); + } + +} + + +class AProcessingInstruction extends ProcessingInstruction { + + public AProcessingInstruction(String target, String data) { + super(target, data); + } + +} + + +class ADocType extends DocType { + + public ADocType(String rootElementName, String publicID, String systemID) { + super(rootElementName, publicID, systemID); + } + +} + + +class AnAttribute extends Attribute { + + public AnAttribute(String name, String URI, String value, Type type) { + super(name, URI, value, type); + } + +} \ No newline at end of file diff --git a/src/nu/xom/tests/AttributeTest.java b/src/nu/xom/tests/AttributeTest.java new file mode 100644 index 0000000..009f906 --- /dev/null +++ b/src/nu/xom/tests/AttributeTest.java @@ -0,0 +1,751 @@ +/* Copyright 2002-2006, 2011 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.tests; + +import java.io.File; +import java.io.IOException; + +import nu.xom.Attribute; +import nu.xom.Builder; +import nu.xom.Document; +import nu.xom.Element; +import nu.xom.IllegalDataException; +import nu.xom.IllegalNameException; +import nu.xom.MalformedURIException; +import nu.xom.NamespaceConflictException; +import nu.xom.ParsingException; + +/** + *

    + * Basic tests for the Attribute class. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.3.0 + * + */ +public class AttributeTest extends XOMTestCase { + + public AttributeTest(String name) { + super(name); + } + + + private Attribute a1; + private Attribute a2; + + + protected void setUp() { + a1 = new Attribute("test", "value"); + a2 = new Attribute("test", " value "); + } + + + public void testGetChildCount() { + assertEquals(0, a1.getChildCount()); + } + + + public void testNullType() { + try { + a1.setType(null); + fail("Didn't throw NullPointerException"); + } + catch (NullPointerException success) { + assertNotNull(success.getMessage()); + } + } + + + public void testGetChild() { + try { + a1.getChild(0); + fail("Didn't throw IndexOutofBoundsException"); + } + catch (IndexOutOfBoundsException success) { + // success + } + } + + + public void testConstructor() { + assertEquals("test", a1.getLocalName()); + assertEquals("test", a1.getQualifiedName()); + assertEquals("", a1.getNamespacePrefix()); + assertEquals("", a1.getNamespaceURI()); + assertEquals("value", a1.getValue()); + assertEquals(" value ", a2.getValue()); + } + + + public void testConstructor2() { + + Attribute a1 = new Attribute("name", "value", Attribute.Type.CDATA); + assertEquals("name", a1.getLocalName()); + assertEquals("name", a1.getQualifiedName()); + assertEquals("", a1.getNamespacePrefix()); + assertEquals("", a1.getNamespaceURI()); + assertEquals("value", a1.getValue()); + assertEquals(Attribute.Type.CDATA, a1.getType()); + } + + + public void testGetExternalForm() { + + Attribute a1 = new Attribute("test", "value contains a \""); + assertEquals("test=\"value contains a "\"", a1.toXML()); + + Attribute a2 = new Attribute("test", "value contains a '"); + assertEquals("test=\"value contains a '\"", a2.toXML()); + + } + + + public void testSetLocalName() { + + Attribute a = new Attribute("name", "value"); + a.setLocalName("newname"); + assertEquals("newname", a.getLocalName()); + + try { + a.setLocalName("pre:a"); + fail("Allowed local attribute name containing colon"); + } + catch (IllegalNameException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testSetLocalNameInNamespaceQualifiedAttribute() { + + Attribute a = new Attribute("pre:name", "http://www.example.org", "value"); + a.setLocalName("newname"); + assertEquals("newname", a.getLocalName()); + assertEquals("pre:newname", a.getQualifiedName()); + + } + + + // No xmlns attributes or xmlns:prefix attributes are allowed + public void testXmlns() { + + try { + new Attribute("xmlns", "http://www.w3.org/TR"); + fail("Created attribute with name xmlns"); + } + catch (IllegalNameException success) { + assertNotNull(success.getMessage()); + } + + try { + new Attribute("xmlns:prefix", "http://www.w3.org/TR"); + fail("Created attribute with name xmlns:prefix"); + } + catch (IllegalNameException success) { + assertNotNull(success.getMessage()); + } + + // Now try with namespace URI from errata + try { + new Attribute("xmlns", "http://www.w3.org/2000/xmlns/", "http://www.w3.org/"); + fail("created xmlns attribute"); + } + catch (IllegalNameException success) { + assertNotNull(success.getMessage()); + } + + // Now try with namespace URI from errata + try { + new Attribute("xmlns:pre", "http://www.w3.org/2000/xmlns/", "http://www.w3.org/"); + fail("created xmlns:pre attribute"); + } + catch (IllegalNameException success) { + assertNotNull(success.getMessage()); + } + + } + + + /* public void testInternLocalNameAndPrefix() { + + Attribute a1 = new Attribute("a1:b", "http://www.exmaple.com", "test"); + Attribute a2 = new Attribute("a2:b", "http://www.exmaple.com", "test"); + Attribute a3 = new Attribute("a1:c", "http://www.exmaple.com", "test"); + + assertTrue(a1.getNamespacePrefix() == a3.getNamespacePrefix()); + assertTrue(a1.getLocalName() == a2.getLocalName()); + + } */ + + + public void testXMLBase() { + + String xmlNamespace = "http://www.w3.org/XML/1998/namespace"; + Attribute a1 = new Attribute("xml:base", xmlNamespace, "http://www.w3.org/"); + assertEquals( "base", a1.getLocalName()); + assertEquals("xml:base", a1.getQualifiedName()); + assertEquals(xmlNamespace, a1.getNamespaceURI()); + + a1.setValue("http://www.example.com/>"); + assertEquals("http://www.example.com/>", a1.getValue()); + + a1.setValue("http://www.example.com/<"); + assertEquals("http://www.example.com/<", a1.getValue()); + + a1.setValue("http://www.example.com/\u00FE"); + assertEquals(a1.getValue(), "http://www.example.com/\u00FE"); + + } + + + public void testXmlPrefix() { + + try { + new Attribute("xml:base", "http://www.w3.org/TR"); + fail("Created attribute with name xml:base"); + } + catch (NamespaceConflictException success) { + assertNotNull(success.getMessage()); + } + + try { + new Attribute("xml:space", "preserve"); + fail("Created attribute with local name xml:space"); + } + catch (NamespaceConflictException success) { + assertNotNull(success.getMessage()); + } + + try { + new Attribute("xml:lang", "fr-FR"); + fail("Created attribute with name xml:lang"); + } + catch (NamespaceConflictException success) { + assertNotNull(success.getMessage()); + } + + String xmlNamespace = "http://www.w3.org/XML/1998/namespace"; + Attribute a1 = new Attribute( + "xml:base", xmlNamespace, "http://www.w3.org/"); + assertEquals("base", a1.getLocalName()); + assertEquals("xml:base", a1.getQualifiedName()); + assertEquals(xmlNamespace, a1.getNamespaceURI()); + + Attribute a2 = new Attribute("xml:space", xmlNamespace, "preserve"); + assertEquals(a2.getLocalName(), "space"); + assertEquals("xml:space", a2.getQualifiedName()); + assertEquals(xmlNamespace, a2.getNamespaceURI()); + + Attribute a3 + = new Attribute("xml:lang", xmlNamespace, "en-UK"); + assertEquals("lang", a3.getLocalName()); + assertEquals("xml:lang", a3.getQualifiedName()); + assertEquals(xmlNamespace, a3.getNamespaceURI()); + + try { + new Attribute("xml:base", "http://www.notTheXMLNamespace", + "http://www.w3.org/"); + fail("remapped xml prefix"); + } + catch (NamespaceConflictException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testXMLLangAttributeCanBeEmpty() { + + String xmlNamespace = "http://www.w3.org/XML/1998/namespace"; + Attribute a = new Attribute("xml:lang", xmlNamespace, ""); + assertEquals("", a.getValue()); + + } + + + public void testWrongPrefixNotAllowedWithXMLURI() { + + try { + new Attribute("test:base", "http://www.w3.org/XML/1998/namespace", "value"); + fail("Allowed XML namespace to be associated with non-xml prefix"); + } + catch (NamespaceConflictException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testToString() { + assertEquals( + "[nu.xom.Attribute: test=\"value\"]", a1.toString()); + assertEquals( + "[nu.xom.Attribute: test=\" value \"]", a2.toString()); + } + + + public void testToStringWithLineFeed() { + + Attribute a = new Attribute("name", "content\ncontent"); + assertEquals("[nu.xom.Attribute: name=\"content\\ncontent\"]", a.toString()); + + } + + + public void testToStringWithCarriageReturnLineFeed() { + + Attribute a = new Attribute("name", "content\r\ncontent"); + assertEquals("[nu.xom.Attribute: name=\"content\\r\\ncontent\"]", a.toString()); + + } + + + public void testToStringWithCarriageReturn() { + + Attribute a = new Attribute("name", "content\rcontent"); + assertEquals("[nu.xom.Attribute: name=\"content\\rcontent\"]", a.toString()); + + } + + + public void testToStringWithLotsOfData() { + + Attribute a = new Attribute("name", + "012345678901234567890123456789012345678901234567890123456789"); + String s = a.toString(); + assertEquals( + "[nu.xom.Attribute: name=\"01234567890123456789012345678901234...\"]", + s); + + } + + + public void testToXML() { + assertEquals("test=\"value\"", a1.toXML()); + assertEquals("test=\" value \"", a2.toXML()); + } + + + public void testEscapingWithToXML() { + + a1.setValue("<"); + assertEquals("test=\"<\"", a1.toXML()); + a1.setValue(">"); + assertEquals("test=\">\"", a1.toXML()); + a1.setValue("\""); + assertEquals("test=\""\"", a1.toXML()); + a1.setValue("\'"); + assertEquals("test=\"'\"", a1.toXML()); + a1.setValue("&"); + assertEquals("test=\"&\"", a1.toXML()); + + } + + + public void testWhiteSpaceEscapingWithToXML() { + + a1.setValue(" "); + assertEquals("test=\" \"", a1.toXML()); + a1.setValue("\n"); + assertEquals("test=\" \"", a1.toXML()); + a1.setValue("\r"); + assertEquals("test=\" \"", a1.toXML()); + a1.setValue("\t"); + assertEquals("test=\" \"", a1.toXML()); + + } + + + public void testSetValue() { + + String[] legal = { + "Hello", + "hello there", + " spaces on both ends ", + " quotes \" \" quotes", + " single \'\' quotes", + " both double and single \"\'\"\' quotes", + " angle brackets < > <<<", + " carriage returns \r\r\r", + " ampersands & &&& &name; " + }; + + // Things that shouldn't cause an exception + for (int i = 0; i < legal.length; i++) { + a1.setValue(legal[i]); + assertEquals(legal[i], a1.getValue()); + } + + try { + a1.setValue("test \u0000 test "); + fail("Should raise an IllegalDataException"); + } + catch (IllegalDataException ex) { + // success + assertNotNull(ex.getMessage()); + } + + } + + + public void testNames() { + + String prefix = "testPrefix"; + String name = "testName"; + String URI = "http://www.elharo.com/"; + String value = " here's some data"; + + + Attribute a1 = new Attribute(prefix + ":" + name, URI, value); + assertEquals(name, a1.getLocalName()); + assertEquals(prefix + ":" + name, a1.getQualifiedName()); + assertEquals(URI, a1.getNamespaceURI()); + } + + + public void testEquals() { + Attribute c1 = new Attribute("test", "limit"); + Attribute c2 = new Attribute("test", "limit"); + Attribute c3 = new Attribute("retina", "retina test"); + + assertEquals(c1, c1); + assertEquals(c1.hashCode(), c1.hashCode()); + assertFalse(c1.equals(c2)); + assertFalse(c1.equals(c3)); + assertFalse(c1.equals(null)); + assertFalse(c1.equals(new Element("test"))); + } + + + public void testTypeEquals() { + assertEquals(Attribute.Type.CDATA, Attribute.Type.CDATA); + assertFalse(Attribute.Type.CDATA.equals(Attribute.Type.NMTOKEN)); + assertFalse(Attribute.Type.CDATA.equals(null)); + assertFalse(Attribute.Type.CDATA.equals("CDATA")); + assertFalse(Attribute.Type.CDATA.equals(new Element("CDATA"))); + } + + + public void testCopyConstructor() { + Attribute c1 = new Attribute("test", "data"); + Attribute c2 = new Attribute(c1); + + assertEquals(c1.getValue(), c2.getValue()); + assertEquals(c1.getLocalName(), c2.getLocalName()); + assertEquals(c1.getQualifiedName(), c2.getQualifiedName()); + assertEquals(c1.getValue(), c2.getValue()); + assertFalse(c1.equals(c2)); + assertNull(c2.getParent()); + } + + + public void testCopy() { + Attribute a1 = new Attribute("test", "data"); + Attribute a2 = a1.copy(); + + assertEquals(a1.getValue(), a2.getValue()); + assertEquals(a1.getLocalName(), a2.getLocalName()); + assertEquals(a1.getQualifiedName(), a2.getQualifiedName()); + assertEquals(a1.getValue(), a2.getValue()); + assertFalse(a1.equals(a2)); + assertNull(a2.getParent()); + } + + + // Check passing in a string with broken surrogate pairs + // and with correct surrogate pairs + public void testSurrogates() { + + String goodString = "test: \uD8F5\uDF80 "; + Attribute c = new Attribute("surrogate", goodString); + assertEquals(goodString, c.getValue()); + + // Two high-halves + try { + new Attribute("surrogate", "test: \uD8F5\uDBF0 "); + fail("Should raise an IllegalDataException"); + } + catch (IllegalDataException success) { + assertEquals("test: \uD8F5\uDBF0 ", success.getData()); + assertNotNull(success.getMessage()); + } + + // Two high-halves + try { + new Attribute("surrogate", "test: \uD8F5\uD8F5 "); + fail("Should raise an IllegalDataException"); + } + catch (IllegalDataException success) { + assertEquals("test: \uD8F5\uD8F5 ", success.getData()); + assertNotNull(success.getMessage()); + } + + // One high-half + try { + new Attribute("surrogate", "test: \uD8F5 "); + fail("Should raise an IllegalDataException"); + } + catch (IllegalDataException success) { + assertEquals("test: \uD8F5 ", success.getData()); + assertNotNull(success.getMessage()); + } + + // One low half + try { + new Attribute("surrogate", "test: \uDF80 "); + fail("One low half"); + } + catch (IllegalDataException success) { + assertEquals("test: \uDF80 ", success.getData()); + assertNotNull(success.getMessage()); + } + + // Low half before high half + try { + new Attribute("surrogate", "test: \uDCF5\uD8F5 "); + fail("Low half before high half"); + } + catch (IllegalDataException success) { + assertEquals("test: \uDCF5\uD8F5 ", success.getData()); + assertNotNull(success.getMessage()); + } + + + } + + + public void testNullNamespace() { + Attribute a = new Attribute("red:prefix", + "http://www.example.com", "data"); + a.setNamespace(null, null); + assertEquals("", a.getNamespaceURI()); + assertEquals("", a.getNamespacePrefix()); + } + + + public void testChangeNamespaceToSameNamespaceAsElement() { + Attribute a = new Attribute("red:prefix", + "http://www.example.com", "data"); + Element e = new Element("pre:test", "http://www.example.org/"); + e.addAttribute(a); + a.setNamespace("pre", "http://www.example.org/"); + assertEquals("http://www.example.org/", a.getNamespaceURI()); + assertEquals("pre", a.getNamespacePrefix()); + assertEquals("http://www.example.org/", e.getNamespaceURI()); + assertEquals("pre", e.getNamespacePrefix()); + } + + + public void testSetNamespaceURI() { + + String name = "red:sakjdhjhd"; + String uri = "http://www.red.com/"; + String prefix = "red"; + Attribute a = new Attribute(name, uri, ""); + + assertEquals(uri, a.getNamespaceURI()); + + String[] legal = {"http://www.is.edu/sakdsk#sjadh", + "http://www.is.edu/sakdsk?name=value&name=head", + "uri:isbn:0832473864", + "http://www.examples.com:80", + "http://www.examples.com:80/", + "http://www.is.edu/%20sakdsk#sjadh"}; + + String[] illegal = { + "http://www.is.edu/%sakdsk#sjadh", + "http://www.is.edu/k\u0245kakdsk#sjadh", + "!@#$%^&*()", + "fred", + "#fred", + "/fred" + }; + + for (int i = 0; i < legal.length; i++) { + a.setNamespace(prefix, legal[i]); + assertEquals(legal[i], a.getNamespaceURI()); + } + + for (int i = 0; i < illegal.length; i++) { + try { + a.setNamespace(prefix, illegal[i]); + fail("Illegal namespace URI allowed"); + } + catch (MalformedURIException success) { + assertEquals(illegal[i], success.getData()); + } + catch (IllegalNameException success) { + assertNotNull(success.getMessage()); + } + } + + } + + + public void testSetNamespace() { + + Attribute a = new Attribute("name", "value"); + try { + a.setNamespace("pre", ""); + fail("Allowed prefix with empty URI"); + } + catch (NamespaceConflictException success) { + assertNotNull(success.getMessage()); + } + + try { + a.setNamespace("", "http://www.example.com"); + fail("Allowed empty prefix with non-empty URI"); + } + catch (NamespaceConflictException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testNodeProperties() { + + Attribute a1 = new Attribute("test", "data"); + + assertNull(a1.getParent()); + + Element element = new Element("test"); + element.addAttribute(a1); + assertEquals(element, a1.getParent()); + assertEquals(a1, element.getAttribute("test")); + + element.removeAttribute(a1); + assertNull(element.getAttribute("test")); + + } + + + public void testDistinctTypes() { + + assertFalse((Attribute.Type.CDATA.equals(Attribute.Type.UNDECLARED))); + + assertFalse((Attribute.Type.ID.equals(Attribute.Type.CDATA))); + assertFalse((Attribute.Type.IDREF.equals(Attribute.Type.ID))); + assertFalse((Attribute.Type.IDREFS.equals(Attribute.Type.IDREF))); + assertFalse((Attribute.Type.NMTOKEN.equals(Attribute.Type.IDREFS))); + assertFalse((Attribute.Type.NMTOKENS.equals(Attribute.Type.NMTOKEN))); + assertFalse((Attribute.Type.NOTATION.equals(Attribute.Type.NMTOKENS))); + assertFalse((Attribute.Type.ENTITY.equals(Attribute.Type.NOTATION))); + assertFalse((Attribute.Type.ENTITIES.equals(Attribute.Type.ENTITY))); + assertFalse((Attribute.Type.ENUMERATION.equals(Attribute.Type.ENTITIES))); + assertFalse((Attribute.Type.CDATA.equals(Attribute.Type.ENUMERATION))); + } + + + public void testAdditionConstraints() { + + Element element = new Element("test"); + Attribute a1 = new Attribute( + "foo:data", "http://www.example.com", "valueFoo"); + Attribute a2 = new Attribute( + "bar:data", "http://www.example.com", "valueBar"); + Attribute a3 = new Attribute("data", "valueFoo"); + Attribute a4 = new Attribute("data", "valueBar"); + + element.addAttribute(a1); + assertEquals("valueFoo", + element.getAttributeValue("data", "http://www.example.com")); + assertEquals(1, element.getAttributeCount()); + element.addAttribute(a2); + assertEquals( + element.getAttributeValue("data", "http://www.example.com"), + "valueBar" + ); + assertEquals(1, element.getAttributeCount()); + element.addAttribute(a3); + assertEquals(element.getAttributeValue("data"), "valueFoo"); + assertEquals("valueBar", + element.getAttributeValue("data", "http://www.example.com")); + assertEquals(2, element.getAttributeCount()); + element.addAttribute(a4); + assertEquals("valueBar", element.getAttributeValue("data")); + assertEquals(2, element.getAttributeCount()); + + // an attribute can have two attributes in the same namespace + // with different prefixes + Attribute a5 = new Attribute( + "red:ab", "http://www.example.org", "valueRed"); + Attribute a6 = new Attribute( + "green:cd", "http://www.example.org", "valueGreen"); + element.addAttribute(a5); + element.addAttribute(a6); + assertEquals("valueRed", + element.getAttributeValue("ab", "http://www.example.org")); + assertEquals("valueGreen", + element.getAttributeValue("cd", "http://www.example.org")); + + } + + + public void testXMLLangCanBeEmptyString() { + // per section 2.12 of the XML Rec + + Attribute a = new Attribute("xml:lang", "http://www.w3.org/XML/1998/namespace", ""); + assertEquals("", a.getValue()); + + } + + + public void testPunctuationCharactersInToXML() { + + String data = "=,.!@#$%^*()_-'[]{}+/?;:`|\\"; + Attribute a = new Attribute("a", data); + assertEquals("a=\"" + data + "\"", a.toXML()); + + } + + // Test for a bug that was caught by other tests; but not + // sufficiently isolated by them + public void testPrefixedAttributeBug() throws ParsingException, IOException { + + Builder builder = new Builder(); + File f = new File("data"); + f = new File(f, "xtest.xml"); + Document input = builder.build(f); + String s = input.toXML(); + Document output = builder.build(s, f.toURI().toASCIIString()); + assertEquals(input, output); + + } + + + public void testDoubleAdd() { + + Element e = new Element("test"); + Attribute a = new Attribute("foo", "bar"); + e.addAttribute(a); + e.removeAttribute(a); + Element copy = new Element(e); + copy.addAttribute(new Attribute("a", "newvalue")); + assertEquals(1, copy.getAttributeCount()); + + } + +} \ No newline at end of file diff --git a/src/nu/xom/tests/AttributeTypeTest.java b/src/nu/xom/tests/AttributeTypeTest.java new file mode 100644 index 0000000..ae5b6f5 --- /dev/null +++ b/src/nu/xom/tests/AttributeTypeTest.java @@ -0,0 +1,134 @@ +/* Copyright 2003, 2006 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ +package nu.xom.tests; + +import nu.xom.Attribute; +import nu.xom.Text; + +/** + *

    + * Tests for the Attribute.Type inner class. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.2d1 + * + */ +public class AttributeTypeTest extends XOMTestCase { + + + public AttributeTypeTest(String name) { + super(name); + } + + + public void testEquals() { + + assertEquals(Attribute.Type.CDATA, Attribute.Type.CDATA); + assertEquals(Attribute.Type.ID, Attribute.Type.ID); + assertEquals(Attribute.Type.IDREF, Attribute.Type.IDREF); + assertEquals(Attribute.Type.IDREFS, Attribute.Type.IDREFS); + assertEquals(Attribute.Type.UNDECLARED, Attribute.Type.UNDECLARED); + assertEquals(Attribute.Type.NMTOKEN, Attribute.Type.NMTOKEN); + assertEquals(Attribute.Type.NMTOKENS, Attribute.Type.NMTOKENS); + assertEquals(Attribute.Type.NOTATION, Attribute.Type.NOTATION); + assertEquals(Attribute.Type.ENTITY, Attribute.Type.ENTITY); + assertEquals(Attribute.Type.ENTITIES, Attribute.Type.ENTITIES); + assertNotSame(Attribute.Type.ENTITIES, Attribute.Type.ENTITY); + assertNotSame(Attribute.Type.CDATA, Attribute.Type.ID); + assertNotSame(Attribute.Type.ID, Attribute.Type.IDREF); + assertNotSame(Attribute.Type.ID, Attribute.Type.IDREFS); + assertNotSame(Attribute.Type.ID, Attribute.Type.NMTOKEN); + assertNotSame(Attribute.Type.ID, Attribute.Type.NMTOKENS); + assertNotSame(Attribute.Type.UNDECLARED, Attribute.Type.CDATA); + assertNotSame(Attribute.Type.NMTOKEN, Attribute.Type.CDATA); + + assertFalse(Attribute.Type.CDATA.equals(new Integer(1))); + assertFalse(Attribute.Type.CDATA.equals(new Text("data"))); + + } + + + public void testToString() { + + assertNotNull(Attribute.Type.CDATA.toString()); + assertNotNull(Attribute.Type.ID.toString()); + assertNotNull(Attribute.Type.IDREF.toString()); + assertNotNull(Attribute.Type.IDREFS.toString()); + assertNotNull(Attribute.Type.UNDECLARED.toString()); + assertNotNull(Attribute.Type.NMTOKEN.toString()); + assertNotNull(Attribute.Type.NMTOKENS.toString()); + assertNotNull(Attribute.Type.NOTATION.toString()); + assertNotNull(Attribute.Type.ENTITY.toString()); + assertNotNull(Attribute.Type.ENTITIES.toString()); + + assertTrue(Attribute.Type.CDATA.toString().length() > 10); + assertTrue(Attribute.Type.ID.toString().length() > 10); + assertTrue(Attribute.Type.IDREF.toString().length() > 10); + assertTrue(Attribute.Type.IDREFS.toString().length() > 10); + assertTrue(Attribute.Type.UNDECLARED.toString().length() > 10); + assertTrue(Attribute.Type.NMTOKEN.toString().length() > 10); + assertTrue(Attribute.Type.NMTOKENS.toString().length() > 10); + assertTrue(Attribute.Type.NOTATION.toString().length() > 10); + assertTrue(Attribute.Type.ENTITY.toString().length() > 10); + assertTrue(Attribute.Type.ENTITIES.toString().length() > 10); + + assertTrue(Attribute.Type.CDATA.toString().startsWith("[Attribute.Type")); + assertTrue(Attribute.Type.ID.toString().startsWith("[Attribute.Type")); + assertTrue(Attribute.Type.IDREF.toString().startsWith("[Attribute.Type")); + assertTrue(Attribute.Type.IDREFS.toString().startsWith("[Attribute.Type")); + assertTrue(Attribute.Type.UNDECLARED.toString().startsWith("[Attribute.Type")); + assertTrue(Attribute.Type.NMTOKEN.toString().startsWith("[Attribute.Type")); + assertTrue(Attribute.Type.NMTOKENS.toString().startsWith("[Attribute.Type")); + assertTrue(Attribute.Type.NOTATION.toString().startsWith("[Attribute.Type")); + assertTrue(Attribute.Type.ENTITY.toString().startsWith("[Attribute.Type")); + assertTrue(Attribute.Type.ENTITIES.toString().startsWith("[Attribute.Type")); + + assertTrue(Attribute.Type.CDATA.toString().endsWith("]")); + assertTrue(Attribute.Type.ID.toString().endsWith("]")); + assertTrue(Attribute.Type.IDREF.toString().endsWith("]")); + assertTrue(Attribute.Type.IDREFS.toString().endsWith("]")); + assertTrue(Attribute.Type.UNDECLARED.toString().endsWith("]")); + assertTrue(Attribute.Type.NMTOKEN.toString().endsWith("]")); + assertTrue(Attribute.Type.NMTOKENS.toString().endsWith("]")); + assertTrue(Attribute.Type.NOTATION.toString().endsWith("]")); + assertTrue(Attribute.Type.ENTITY.toString().endsWith("]")); + assertTrue(Attribute.Type.ENTITIES.toString().endsWith("]")); + + } + + + public void testGetName() { + + assertEquals("ENUMERATION", Attribute.Type.ENUMERATION.getName()); + assertEquals("NOTATION", Attribute.Type.NOTATION.getName()); + assertEquals("ENTITY", Attribute.Type.ENTITY.getName()); + assertEquals("ENTITIES", Attribute.Type.ENTITIES.getName()); + assertEquals("CDATA", Attribute.Type.CDATA.getName()); + assertEquals("ID", Attribute.Type.ID.getName()); + assertEquals("IDREF", Attribute.Type.IDREF.getName()); + assertEquals("IDREFS", Attribute.Type.IDREFS.getName()); + assertEquals("NMTOKEN", Attribute.Type.NMTOKEN.getName()); + assertEquals("NMTOKENS", Attribute.Type.NMTOKENS.getName()); + } + + +} diff --git a/src/nu/xom/tests/AttributesTest.java b/src/nu/xom/tests/AttributesTest.java new file mode 100644 index 0000000..c3ac268 --- /dev/null +++ b/src/nu/xom/tests/AttributesTest.java @@ -0,0 +1,84 @@ +/* Copyright 2002, 2003 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.tests; + +import nu.xom.Attribute; +import nu.xom.Element; + +/** + *

    + * This class provides unit tests for the + * Attributes class. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.0 + * + */ +public class AttributesTest extends XOMTestCase { + + public AttributesTest(String name) { + super(name); + } + + private Element threeAttributes; + private Element noAttributes; + + + protected void setUp() { + noAttributes = new Element("test"); + threeAttributes = new Element("test"); + threeAttributes.addAttribute(new Attribute("att1", "value1")); + threeAttributes.addAttribute(new Attribute("att2", "value2")); + threeAttributes.addAttribute(new Attribute("att3", "value3")); + } + + public void testSize() { + assertEquals(0, noAttributes.getAttributeCount()); + assertEquals(3, threeAttributes.getAttributeCount()); + } + + public void testGetOutOfBounds() { + + try { + noAttributes.getAttribute(0); + fail("Should have thrown IndexOutOfBoundsException"); + } + catch (IndexOutOfBoundsException success) { + assertNotNull(success.getMessage()); + } + try { + threeAttributes.getAttribute(4); + } + catch (IndexOutOfBoundsException success) { + assertNotNull(success.getMessage()); + } + try { + threeAttributes.getAttribute(-1); + } + catch (IndexOutOfBoundsException success) { + assertNotNull(success.getMessage()); + } + + } + +} \ No newline at end of file diff --git a/src/nu/xom/tests/BaseURITest.java b/src/nu/xom/tests/BaseURITest.java new file mode 100644 index 0000000..09b9af1 --- /dev/null +++ b/src/nu/xom/tests/BaseURITest.java @@ -0,0 +1,1260 @@ +/* Copyright 2002-2005 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.tests; + +import java.io.File; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.net.URL; +import java.util.Locale; + +import nu.xom.Attribute; +import nu.xom.Builder; +import nu.xom.Comment; +import nu.xom.Document; +import nu.xom.Element; +import nu.xom.MalformedURIException; +import nu.xom.Namespace; +import nu.xom.Node; +import nu.xom.ParsingException; +import nu.xom.Text; + +/** + *

    + * Tests the getting and setting of base URI information + * on nodes. It's important to note that + * this is really a URI, not an IRI. The xml:base + * attribute may contain an unescaped URI; i.e. an IRI. However, + * the base URI is determined after this is converted to a + * real URI with all percent escapes in place. See the XML + * Base specification for elucidation of this point. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.1b4 + * + */ +public class BaseURITest extends XOMTestCase { + + + public BaseURITest(String name) { + super(name); + } + + + private Document doc; + private String base1 = "http://www.base1.com/"; + private String base2 = "http://www.base2.com/"; + private String base3 = "base3.html"; + private Builder builder = new Builder(); + + + protected void setUp() { + + Element root = new Element("root"); + doc = new Document(root); + doc.setBaseURI(base1); + Element child = new Element("child"); + root.appendChild(child); + child.setBaseURI(base2); + child.appendChild(new Comment("here I am")); + + Element child2 = new Element("child2"); + root.appendChild(child2); + + Element child3 = new Element("child3"); + root.appendChild(child3); + child3.addAttribute(new Attribute("xml:base", + Namespace.XML_NAMESPACE, base2)); + + Element child4 = new Element("child4"); + root.appendChild(child4); + child4.addAttribute(new Attribute("xml:base", + Namespace.XML_NAMESPACE, base3)); + + } + + + public void testDocBase() { + assertEquals(base1, doc.getBaseURI()); + } + + + public void testUnsetBase() { + Element root = new Element("test"); + root.setBaseURI(base1); + root.setBaseURI(null); + assertEquals("", root.getBaseURI()); + } + + + public void testInheritBaseFromDocument() { + Element root = doc.getRootElement(); + root.setBaseURI(""); + assertEquals(doc.getBaseURI(), root.getBaseURI()); + } + + + public void testAllowEmptyBase() { + Element root = new Element("test"); + root.setBaseURI(base1); + root.setBaseURI(""); + assertEquals("", root.getBaseURI()); + } + + + public void testIPv6Base() { + String ipv6 + = "http://[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]:80/test.xml"; + Element root = new Element("test"); + root.setBaseURI(ipv6); + assertEquals(ipv6, root.getBaseURI()); + } + + + public void testBaseWithNonASCIICharacter() { + + String uri = "http://www.w3.org/\u00A9testing"; + Element root = new Element("test"); + try { + root.setBaseURI(uri); + fail("Allowed base URI containing non-ASCII character"); + } + catch (MalformedURIException success) { + assertNotNull(success.getMessage()); + } + + root.setBaseURI("http://www.example.org/D%C3%BCrst"); + assertEquals("http://www.example.org/D%C3%BCrst", root.getBaseURI()); + + } + + + public void testDocumentBaseWithNonASCIICharacter() { + + String uri = "http://www.w3.org/\u00A9testing"; + Element root = new Element("test"); + Document doc = new Document(root); + try { + doc.setBaseURI(uri); + fail("Allowed base URI containing non-ASCII character"); + } + catch (MalformedURIException success) { + assertNotNull(success.getMessage()); + } + + doc.setBaseURI("http://www.example.org/D%C3%BCrst"); + assertEquals("http://www.example.org/D%C3%BCrst", doc.getBaseURI()); + + } + + + public void testUppercaseBase() { + String base = "HTTP://WWW.EXAMPLE.COM/TEST.XML"; + Element root = new Element("test"); + root.setBaseURI(base); + assertEquals(base, root.getBaseURI()); + } + + + public void testASCIILettersWithXMLBaseAttribute() { + + String alphabet = "abcdefghijklmnopqrstuvwxyz"; + + String base = "HTTP://WWW.EXAMPLE.COM/" + alphabet; + Element root = new Element("test"); + root.addAttribute(new Attribute("xml:base", + Namespace.XML_NAMESPACE, base)); + assertEquals(base, root.getBaseURI()); + + base = "HTTP://WWW.EXAMPLE.COM/" + alphabet.toUpperCase(Locale.ENGLISH); + root.addAttribute(new Attribute("xml:base", + Namespace.XML_NAMESPACE, base)); + assertEquals(base, root.getBaseURI()); + + } + + + public void testXMLBaseWithParameters() { + String base = "scheme://authority/data/name;v=1.1/test.db"; + Element root = new Element("test"); + root.addAttribute(new Attribute("xml:base", + Namespace.XML_NAMESPACE, base)); + assertEquals(base, root.getBaseURI()); + } + + + public void testXMLBaseWithCommaParameter() { + + String base = "scheme://authority/data/name,1.1/test.db"; + Element root = new Element("test"); + root.addAttribute(new Attribute("xml:base", + Namespace.XML_NAMESPACE, base)); + assertEquals(base, root.getBaseURI()); + + } + + + // This one appears to be mostly theoretical + public void testXMLBaseWithDollarSign() { + + String base = "scheme://authority/data$important"; + Element root = new Element("test"); + root.addAttribute(new Attribute("xml:base", + Namespace.XML_NAMESPACE, base)); + assertEquals(base, root.getBaseURI()); + + } + + + public void testFragmentIDWithXMLBaseAttribute() { + + String base = "HTTP://WWW.EXAMPLE.COM/#test"; + Element root = new Element("test"); + root.addAttribute(new Attribute("xml:base", + Namespace.XML_NAMESPACE, base)); + assertEquals(base, root.getBaseURI()); + + } + + + public void testQueryString() { + + String base = "http://www.example.com/test?name=value&data=important"; + Element root = new Element("test"); + root.addAttribute(new Attribute("xml:base", + Namespace.XML_NAMESPACE, base)); + assertEquals(base, root.getBaseURI()); + + } + + // -" | "_" | "." | "!" | "~" | "*" | "'" | "(" | ")" + public void testUnreserved() { + + String unreserved = "-.!~*'()"; + String base = "http://www.example.com/" + unreserved; + Element root = new Element("test"); + root.addAttribute(new Attribute("xml:base", + Namespace.XML_NAMESPACE, base)); + assertEquals(base, root.getBaseURI()); + + } + + + public void testDelims() { + + String[] delims = {"<", ">", "\""}; + for (int i = 0; i < delims.length; i++) { + String base = "http://www.example.com/" + delims[i] + "/"; + Element root = new Element("test"); + root.addAttribute(new Attribute("xml:base", + Namespace.XML_NAMESPACE, base)); + assertEquals("http://www.example.com/%" + + Integer.toHexString(delims[i].charAt(0)).toUpperCase() + + "/", root.getBaseURI()); + } + + } + + + public void testUnwise() { + + char[] unwise = {'{', '}', '|', '\\', '^', '`'}; + for (int i = 0; i < unwise.length; i++) { + String base = "http://www.example.com/" + unwise[i] + "/"; + Element root = new Element("test"); + root.addAttribute(new Attribute("xml:base", + Namespace.XML_NAMESPACE, base)); + assertEquals("http://www.example.com/%" + + Integer.toHexString(unwise[i]).toUpperCase() + + "/", root.getBaseURI()); + } + + } + + + public void testBaseWithUnusualParts() { + String base = "HTTP://user@WWW.EXAMPLE.COM:65130/TEST-2+final.XML?name=value&name2=value2"; + Element root = new Element("test"); + root.setBaseURI(base); + assertEquals(base, root.getBaseURI()); + } + + + public void testBaseWithEscapedParts() { + String base = "http://www.example.com/test%20test"; + Element root = new Element("test"); + root.setBaseURI(base); + assertEquals(base, root.getBaseURI()); + } + + + public void testXMLBaseWithPlus() { + String base = "http://www.example.com/test+test"; + Element root = new Element("test"); + root.addAttribute(new Attribute("xml:base", + Namespace.XML_NAMESPACE, base)); + assertEquals(base, root.getBaseURI()); + } + + + public void testXMLBaseWithUserInfoWithXMLBaseAttribute() { + String base = "http://invited:test@www.example.com/"; + Element root = new Element("test"); + root.addAttribute(new Attribute("xml:base", + Namespace.XML_NAMESPACE, base)); + assertEquals(base, root.getBaseURI()); + } + + + public void testElementWithEmptyXMLBaseAttributeHasSameBaseURIAsDocument() { + + String base = "http://www.example.com/"; + Element root = new Element("test"); + root.addAttribute(new Attribute("xml:base", + Namespace.XML_NAMESPACE, base)); + + Element child = new Element("child"); + root.appendChild(child); + child.addAttribute(new Attribute("xml:base", + Namespace.XML_NAMESPACE, "")); + Document doc = new Document(root); + doc.setBaseURI("http://www.cafeaulait.org/"); + assertEquals("http://www.cafeaulait.org/", child.getBaseURI()); + + } + + + public void testBaseURIOfElementWithEmptyXMLBaseAttributeIsEmptyStringIfTheresNoActualBaseURI() { + + String base = "http://www.example.com/"; + Element root = new Element("test"); + root.addAttribute(new Attribute("xml:base", + Namespace.XML_NAMESPACE, base)); + + Element child = new Element("child"); + child.addAttribute(new Attribute("xml:base", + Namespace.XML_NAMESPACE, "")); + root.appendChild(child); + new Document(root); + assertEquals("", child.getBaseURI()); + + } + + + public void testXMLBaseWithUnreservedCharacters() { + String base = "http://www.example.com/()-_.!~*'"; + Element root = new Element("test"); + root.addAttribute(new Attribute("xml:base", + Namespace.XML_NAMESPACE, base)); + assertEquals(base, root.getBaseURI()); + } + + + public void testXMLBaseWithNonASCIICharacters() + throws UnsupportedEncodingException { + + String omega = "\u03A9"; + // In UTF-8 %ce%a9 + String base = "http://www.example.com/" + omega; + Element root = new Element("test"); + root.addAttribute(new Attribute("xml:base", + Namespace.XML_NAMESPACE, base)); + assertEquals("http://www.example.com/%CE%A9", root.getBaseURI()); + + } + + + public void testBaseWithNonASCIICharacters() + throws UnsupportedEncodingException { + + String base = "http://www.example.com/%ce%a9"; + Element root = new Element("test"); + root.setBaseURI(base); + assertEquals(base, root.getBaseURI()); + + } + + + public void testBadIPv6Base() { + + Element root = new Element("test"); + try { + root.setBaseURI( + "http://[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]/test.xml#xpointer(/*[1])" + ); + fail("allowed multiple brackets"); + } + catch (MalformedURIException success) { + assertNotNull(success.getMessage()); + } + + try { + root.setBaseURI( + "http://[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210/" + ); + fail("allowed mismatched brackets"); + } + catch (MalformedURIException success) { + assertNotNull(success.getMessage()); + } + + try { + root.setBaseURI( + "http://]FEDC:BA98:7654:3210:FEDC:BA98:7654:3210[/" + ); + fail("allowed right bracket before left bracket"); + } + catch (MalformedURIException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testAllowEmptyXMLBase() { + Element root = doc.getRootElement(); + root.setBaseURI(base1); + root.addAttribute(new Attribute("xml:base", + Namespace.XML_NAMESPACE, "")); + assertEquals(base1, root.getBaseURI()); + } + + + public void testFailures() { + + Element root = doc.getRootElement(); + + try { + root.setBaseURI("http://www.w3.org/ testing"); + fail("Allowed URI containing space"); + } + catch (MalformedURIException success) { + assertNotNull(success.getMessage()); + } + + try { + root.setBaseURI("http://www.w3.org/tes%ting"); + fail("Allowed URI containing %"); + } + catch (MalformedURIException success) { + assertNotNull(success.getMessage()); + } + + try { + root.setBaseURI("http://www.w3.org/%Atesting"); + fail("Allowed URI containing half percent"); + } + catch (MalformedURIException success) { + assertNotNull(success.getMessage()); + } + + try { + root.setBaseURI("http://www.w3.org/%A"); + fail("Allowed URI containing half percent at end of path"); + } + catch (MalformedURIException success) { + assertNotNull(success.getMessage()); + } + + + try { + root.setBaseURI("http://www.w3.org/^testing"); + fail("Allowed URI containing unwise character"); + } + catch (MalformedURIException success) { + assertNotNull(success.getMessage()); + } + + try { + root.setBaseURI("http://www.w3.org/= 0); + } + + } + + + public void testRelativeURIResolutionAgainstARedirectedBase() + throws IOException, ParsingException { + + Builder builder = new Builder(); + Document doc = builder.build( + "http://www.ibiblio.org/xml/redirecttest.xml"); + assertEquals( + "http://www.cafeconleche.org/redirecttest.xml", + doc.getBaseURI() + ); + + } + + + public void testParentlessNodesHaveEmptyBaseURIs() { + Text t = new Text("data"); + assertEquals("", t.getBaseURI()); + + Element e = new Element("a"); + assertEquals("", e.getBaseURI()); + } + + + // Don't use the parent to resolve the relative base URI + // when parent and child come from different entities + public void testElementsFromDifferentActualBases() { + Element parent = new Element("parent"); + parent.setBaseURI("http://www.cafeconleche.org/"); + Element child = new Element("child"); + child.setBaseURI("http://www.example.com/"); + parent.appendChild(child); + child.addAttribute(new Attribute("xml:base", + Namespace.XML_NAMESPACE, "/test/data/")); + String base = child.getBaseURI(); + assertEquals("http://www.example.com/test/data/", base); + } + + + public void testBadURIInElementsFromDifferentActualBases() { + + Element parent = new Element("parent"); + parent.setBaseURI("http://www.cafeconleche.org/"); + Element child = new Element("child"); + parent.appendChild(child); + child.addAttribute(new Attribute("xml:base", + Namespace.XML_NAMESPACE, + "%GF.html")); + String base = child.getBaseURI(); + assertEquals("http://www.cafeconleche.org/", base); + + } + + + public void testBadURIInElementsFromSameActualBases() { + + Element parent = new Element("parent"); + parent.setBaseURI("http://www.cafeconleche.org/"); + Element child = new Element("child"); + child.setBaseURI("http://www.cafeconleche.org/"); + parent.appendChild(child); + child.addAttribute(new Attribute("xml:base", + Namespace.XML_NAMESPACE, + "http://www.example.com/%5.html")); + assertEquals("http://www.cafeconleche.org/", child.getBaseURI()); + + } + + + public void testBadURIInBaseAttributeWithParent() { + + Element parent = new Element("parent"); + parent.setBaseURI("http://www.cafeconleche.org/"); + Element child = new Element("child"); + child.setBaseURI("http://www.cafeconleche.org/"); + parent.appendChild(child); + child.addAttribute(new Attribute("xml:base", + Namespace.XML_NAMESPACE, + "%TR.html")); + assertEquals("http://www.cafeconleche.org/", child.getBaseURI()); + + } + + + public void testHierarchicalURIsWithoutProtocolHandlers() { + + String[] urls = { + "gopher://gopher.uminn.edu/", "GOPHER://gopher.uminn.edu/", + "gopher://gopher.uminn.edu", "GOPHER://gopher.uminn.edu", + "wais://wais.example.com:78/database", "WAIS://wais.example.com:78/database", + "file://vms.host.edu/disk$user/my/notes/note12345.txt", + "FILE://vms.host.edu/disk$user/my/notes/note12345.txt", + "z39.50s://melvyl.ucop.edu/cat", "Z39.50S://melvyl.ucop.edu/cat", + "z39.50r://melvyl.ucop.edu/mags?elecworld.v30.n19", + "Z39.50R://melvyl.ucop.edu/mags?elecworld.v30.n19", + "z39.50r://cnidr.org:2100/tmf?bkirch_rules__a1;esn=f;rs=marc", + "Z39.50R://cnidr.org:2100/tmf?bkirch_rules__a1;esn=f;rs=marc", + "vemmi://zeus.mctel.fr/demo", "VEMMI://zeus.mctel.fr/demo", + "vemmi://mctel.fr/demo;$USERDATA=smith;account=1234", + "xmlrpc.beeps://stateserver.example.com/NumberToName", + "XMLRPC.BEEPS://stateserver.example.com/NumberToName", + "tn3270://login.example.com/" + }; + for (int i = 0; i < urls.length; i++) { + Element e = new Element("test"); + e.addAttribute(new Attribute("xml:base", + Namespace.XML_NAMESPACE, + urls[i])); + Element child = new Element("child"); + child.addAttribute(new Attribute("xml:base", + Namespace.XML_NAMESPACE, + "TR.html")); + e.appendChild(child); + String base = child.getBaseURI(); + assertTrue(urls[i] + " " + base, base.endsWith("/TR.html")); + assertTrue(base.indexOf("://") >= 4 ); + } + + } + + + public void testOpaqueURIs() { + + String[] urls = { + "MAILTO:elharo@ibiblio.org?Subject=XOM%20Namespace", + "mailto:elharo@ibiblio.org?Subject=XOM%20Namespace", + "telnet:namespaces.ibiblio.org", "TELNET:namespaces.ibiblio.org", + "uri:urn:nwalsh:namespaces", "URI:urn:nwalsh:namespaces", + "news:comp.lang.xml", "NEWS:comp.lang.xml", + "mid:960830.1639@XIson.com/partA.960830.1639@XIson.com", + "MID:960830.1639@XIson.com/partA.960830.1639@XIson.com", + "cid:foo4*foo1@bar.net", "CID:foo4*foo1@bar.net", + "opaquelocktoken:f81d4fae-7dec-11d0-a765-00a0c91e6bf6", + "OPAQUELOCKTOKEN:f81d4fae-7dec-11d0-a765-00a0c91e6bf6", + "fax:+358.555.1234567", "FAX:+358.555.1234567", + "modem:+3585551234567;type=v32b?7e1;type=v110", + "tel:0w003585551234567;phone-context=+3585551234", + "tel:+1234567890;phone-context=+1234;vnd.company.option=foo", + "h323:user@h323.example.com", "H323:user@h323.example.com", + }; + for (int i = 0; i < urls.length; i++) { + Element e = new Element("test"); + e.addAttribute(new Attribute("xml:base", + Namespace.XML_NAMESPACE, + urls[i])); + Element child = new Element("child"); + child.addAttribute(new Attribute("xml:base", + Namespace.XML_NAMESPACE, + "TR.html")); + e.appendChild(child); + String base = child.getBaseURI(); + assertEquals("", base); + } + + } + + + public void testURIStartsWithColon() { + + String url = ":elharo@ibiblio.org?Subject=XOM%20Namespace"; + Element e = new Element("test"); + e.addAttribute(new Attribute("xml:base", + Namespace.XML_NAMESPACE, url)); + Element child = new Element("child"); + child.addAttribute(new Attribute("xml:base", + Namespace.XML_NAMESPACE, + "TR.html")); + e.appendChild(child); + String base = child.getBaseURI(); + assertEquals("", base); + + } + + + public void testURIStartsWithNumber() { + + String url = "7aelharo@ibiblio.org?Subject=XOM%20Namespace"; + Element e = new Element("test"); + e.addAttribute(new Attribute("xml:base", + Namespace.XML_NAMESPACE, url)); + Element child = new Element("child"); + child.addAttribute(new Attribute("xml:base", + Namespace.XML_NAMESPACE, + "TR.html")); + e.appendChild(child); + String base = child.getBaseURI(); + assertEquals("", base); + + } + + + public void testURISchemeContainsComma() { + + String url = "foo,foo:elharo@ibiblio.org?Subject=XOM%20Namespace"; + Element e = new Element("test"); + e.addAttribute(new Attribute("xml:base", + Namespace.XML_NAMESPACE, url)); + Element child = new Element("child"); + child.addAttribute(new Attribute("xml:base", + Namespace.XML_NAMESPACE, + "TR.html")); + e.appendChild(child); + String base = child.getBaseURI(); + assertEquals("", base); + + } + + + public void testXMLBaseUsedToResolveHref() + throws ParsingException, IOException { + + File input = new File("data"); + input = new File(input, "xmlbasetest.xml"); + Document doc = builder.build(input); + Element root = doc.getRootElement(); + String base = root.getBaseURI(); + // This constructor only works if we have an absolute URI. + // If the test fails an aexception is thrown here. I can't + // assert equality with the expected absolute URI because + // that varies from one installation to the next + new URL(base); + assertTrue(base.startsWith("file:/")); + + } + + + public void testBuildElementFromSeveralEntities() + throws ParsingException, IOException { + + File input = new File("data"); + input = new File(input, "BaseURIWithEntitiesTest.xml"); + Document doc = builder.build(input); + Element root = doc.getRootElement(); + String rootBase = root.getBaseURI(); + String childBase = root.getChild(0).getBaseURI(); + assertFalse(rootBase.equals(childBase)); + assertTrue(childBase.indexOf("entities") > 0); + + } + + + public void testReplacedRootRetainsBaseURI() { + + Element root = new Element("root"); + Document doc = new Document(root); + doc.setBaseURI("http://www.example.com"); + doc.setRootElement(new Element("data")); + assertEquals("http://www.example.com", root.getBaseURI()); + + } + + + public void testDetachedElementRetainsBaseURI() { + + Element root = new Element("root"); + Document doc = new Document(root); + doc.setBaseURI("http://www.example.com"); + Element child = new Element("child"); + root.appendChild(child); + child.detach(); + assertEquals("http://www.example.com", child.getBaseURI()); + + } + + + public void testCopiedElementRetainsBaseURI() { + + Element root = new Element("root"); + Document doc = new Document(root); + doc.setBaseURI("http://www.example.com"); + Element child = new Element("child"); + root.appendChild(child); + Node copy = child.copy(); + assertEquals("http://www.example.com", copy.getBaseURI()); + + } + + + public void testElementRemovedByIndexRetainsBaseURI() { + + Element root = new Element("root"); + Document doc = new Document(root); + doc.setBaseURI("http://www.example.com"); + Element child = new Element("child"); + root.appendChild(child); + root.removeChild(0); + assertEquals("http://www.example.com", child.getBaseURI()); + + } + + + public void testElementRemovedByReferenceRetainsBaseURI() { + + Element root = new Element("root"); + Document doc = new Document(root); + doc.setBaseURI("http://www.example.com"); + Element child = new Element("child"); + root.appendChild(child); + root.removeChild(child); + assertEquals("http://www.example.com", child.getBaseURI()); + + } + + + public void testRemovedChildrenRetainBaseURI() { + + Element root = new Element("root"); + Document doc = new Document(root); + doc.setBaseURI("http://www.example.com"); + Element child = new Element("child"); + root.appendChild(child); + root.removeChildren(); + assertEquals("http://www.example.com", child.getBaseURI()); + + } + + + public void testXMLBaseAttributesAreOnlyUsedIfTheyreInTheSameEntity() { + + Element top = new Element("top"); + top.addAttribute(new Attribute("xml:base", + Namespace.XML_NAMESPACE, + "http://www.example.com/")); + top.setBaseURI("http://www.w3.org"); + Element bottom = new Element("bottom"); + bottom.setBaseURI("http://www.example.net"); + top.appendChild(bottom); + assertEquals("http://www.example.net", bottom.getBaseURI()); + + top.setBaseURI(null); + assertEquals("http://www.example.net", bottom.getBaseURI()); + + } + + + public void testXMLBaseAttributesInTheSameEntityOverrideActualBaseURI() { + + Element top = new Element("top"); + top.addAttribute(new Attribute("xml:base", + Namespace.XML_NAMESPACE, + "http://www.example.com/")); + top.setBaseURI("http://www.w3.org"); + Element bottom = new Element("bottom"); + bottom.setBaseURI("http://www.w3.org"); + top.appendChild(bottom); + assertEquals("http://www.example.com/", bottom.getBaseURI()); + + } + + + public void testRelativeBaseURIResolution() { + + Element root = new Element("root"); + Attribute baseAttribute = new Attribute("xml:base", + Namespace.XML_NAMESPACE, "http://www.example.com/data/limit/test.xml"); + root.addAttribute(baseAttribute); + Element child = new Element ("child"); + child.addAttribute(new Attribute("xml:base", + Namespace.XML_NAMESPACE, "child.xml")); + root.appendChild(child); + assertEquals("http://www.example.com/data/limit/child.xml", child.getBaseURI()); + + } + + + public void testRelativeBaseURIResolutionWithNumbers() { + + Element root = new Element("root"); + Attribute baseAttribute = new Attribute("xml:base", + Namespace.XML_NAMESPACE, "http://www.example.com/data/limit/test.xml"); + root.addAttribute(baseAttribute); + Element child = new Element ("child"); + child.addAttribute(new Attribute("xml:base", + Namespace.XML_NAMESPACE, "01test.xml")); + root.appendChild(child); + assertEquals("http://www.example.com/data/limit/01test.xml", child.getBaseURI()); + + } + + + public void testRelativeBaseURIStartsWithDotSlash() { + + Element root = new Element("root"); + Attribute baseAttribute = new Attribute("xml:base", + Namespace.XML_NAMESPACE, "http://www.example.com/data/limit/test.xml"); + root.addAttribute(baseAttribute); + Element child = new Element ("child"); + child.addAttribute(new Attribute("xml:base", + Namespace.XML_NAMESPACE, "./test.xml")); + root.appendChild(child); + assertEquals("http://www.example.com/data/limit/test.xml", child.getBaseURI()); + + } + + + public void testRelativeBaseURIIsDot() { + + Element root = new Element("root"); + Attribute baseAttribute = new Attribute("xml:base", + Namespace.XML_NAMESPACE, "http://www.example.com/data/limit/test.xml"); + root.addAttribute(baseAttribute); + Element child = new Element ("child"); + child.addAttribute(new Attribute("xml:base", + Namespace.XML_NAMESPACE, ".")); + root.appendChild(child); + assertEquals("http://www.example.com/data/limit/", child.getBaseURI()); + + } + + + public void testRelativeBaseURIIsDotDot() { + + Element root = new Element("root"); + Attribute baseAttribute = new Attribute("xml:base", + Namespace.XML_NAMESPACE, "http://www.example.com/data/limit/test.xml"); + root.addAttribute(baseAttribute); + Element child = new Element ("child"); + child.addAttribute(new Attribute("xml:base", + Namespace.XML_NAMESPACE, "..")); + root.appendChild(child); + assertEquals("http://www.example.com/data/", child.getBaseURI()); + + } + + + public void testRelativeBaseURIStartsWithDotDotSlash() { + + Element root = new Element("root"); + Attribute baseAttribute = new Attribute("xml:base", + Namespace.XML_NAMESPACE, "http://www.example.com/data/limit/test.xml"); + root.addAttribute(baseAttribute); + Element child = new Element ("child"); + child.addAttribute(new Attribute("xml:base", + Namespace.XML_NAMESPACE, "../test.xml")); + root.appendChild(child); + assertEquals("http://www.example.com/data/test.xml", child.getBaseURI()); + + } + + + public void testAbsoluteBaseURIEndsWithDotDotSlash() { + + Element root = new Element("root"); + Attribute baseAttribute = new Attribute("xml:base", + Namespace.XML_NAMESPACE, "http://www.example.com/data/limit/../"); + root.addAttribute(baseAttribute); + Element child = new Element ("child"); + child.addAttribute(new Attribute("xml:base", + Namespace.XML_NAMESPACE, "test.xml")); + root.appendChild(child); + assertEquals("http://www.example.com/data/test.xml", child.getBaseURI()); + + } + + + public void testAbsoluteBaseURIEndsWithDotDotSlashDotDot() { + + Element root = new Element("root"); + Attribute baseAttribute = new Attribute("xml:base", + Namespace.XML_NAMESPACE, "http://www.example.com/../.."); + root.addAttribute(baseAttribute); + Element child = new Element ("child"); + child.addAttribute(new Attribute("xml:base", + Namespace.XML_NAMESPACE, "test.xml")); + root.appendChild(child); + assertEquals("http://www.example.com/test.xml", child.getBaseURI()); + + } + + + // I'm not sure about this one; need to check???? + public void testAbsoluteBaseURIEndsWithDotDot() { + + Element root = new Element("root"); + Attribute baseAttribute = new Attribute("xml:base", + Namespace.XML_NAMESPACE, "http://www.example.com/data/limit/.."); + root.addAttribute(baseAttribute); + Element child = new Element ("child"); + child.addAttribute(new Attribute("xml:base", + Namespace.XML_NAMESPACE, "test.xml")); + root.appendChild(child); + assertEquals("http://www.example.com/data/test.xml", child.getBaseURI()); + + } + + + // tests from RFC2396bis + public void testRFC2396NormalExamples() { + + String[] RFC2396bisCases = { + "g:h", "g:h", + "g", "http://a/b/c/g", + "./g", "http://a/b/c/g", + "g/", "http://a/b/c/g/", + "/g", "http://a/g", + "//g", "http://g", + "?y", "http://a/b/c/d;p?y", + "g?y", "http://a/b/c/g?y", + "#s", "http://a/b/c/d;p?q#s", + "g#s", "http://a/b/c/g#s", + "g?y#s", "http://a/b/c/g?y#s", + ";x", "http://a/b/c/;x", + "g;x", "http://a/b/c/g;x", + "g;x?y#s", "http://a/b/c/g;x?y#s", + "", "http://a/b/c/d;p?q", + ".", "http://a/b/c/", + "./", "http://a/b/c/", + "..", "http://a/b/", + "../", "http://a/b/", + "../g", "http://a/b/g", + "../..", "http://a/", + "../../", "http://a/", + "../../g", "http://a/g" + }; + + Element root = new Element("root"); + Document doc = new Document(root); + doc.setBaseURI("http://a/b/c/d;p?q"); + Attribute base = new Attribute("xml:base", Namespace.XML_NAMESPACE, "g"); + root.addAttribute(base); + for (int i = 0; i < RFC2396bisCases.length; i += 2) { + base.setValue(RFC2396bisCases[i]); + assertEquals(RFC2396bisCases[i], RFC2396bisCases[i+1], root.getBaseURI()); + } + + } + + + public void testRFC2396AbnormalExamples() { + + String[] RFC2396bisCases = { + "../../../g", "http://a/g", + "../../../../g", "http://a/g", + "/./g", "http://a/g", + "/../g", "http://a/g", + "g.", "http://a/b/c/g.", + ".g", "http://a/b/c/.g", + "g..", "http://a/b/c/g..", + "..g", "http://a/b/c/..g", + "./../g", "http://a/b/g", + "./g/.", "http://a/b/c/g/", + "g/./h", "http://a/b/c/g/h", + "g/../h", "http://a/b/c/h", + "g;x=1/./y", "http://a/b/c/g;x=1/y", + "g;x=1/../y", "http://a/b/c/y", + "g?y/./x", "http://a/b/c/g?y/./x", + "g?y/../x", "http://a/b/c/g?y/../x", + "g#s/./x", "http://a/b/c/g#s/./x", + "g#s/../x", "http://a/b/c/g#s/../x", + "http:g", "http:g" + }; + + Element root = new Element("root"); + Document doc = new Document(root); + doc.setBaseURI("http://a/b/c/d;p?q"); + Attribute base = new Attribute("xml:base", Namespace.XML_NAMESPACE, "g"); + root.addAttribute(base); + for (int i = 0; i < RFC2396bisCases.length; i += 2) { + base.setValue(RFC2396bisCases[i]); + assertEquals(RFC2396bisCases[i], RFC2396bisCases[i+1], root.getBaseURI()); + } + + } + + public void testSlashDotDot() { + + Element root = new Element("root"); + Document doc = new Document(root); + doc.setBaseURI("http://www.example.com/"); + Attribute base = new Attribute("xml:base", Namespace.XML_NAMESPACE, "g"); + root.addAttribute(base); + base.setValue(".."); + assertEquals("http://www.example.com/", root.getBaseURI()); + + } + + public void testSlashDotDotSlashDotDot() { + + Element root = new Element("root"); + Document doc = new Document(root); + doc.setBaseURI("http://www.example.com/"); + Attribute base = new Attribute("xml:base", Namespace.XML_NAMESPACE, "g"); + root.addAttribute(base); + base.setValue("../.."); + assertEquals("http://www.example.com/", root.getBaseURI()); + + } + + public void testSlashDot() { + + Element root = new Element("root"); + Document doc = new Document(root); + doc.setBaseURI("http://www.example.com/"); + Attribute base = new Attribute("xml:base", Namespace.XML_NAMESPACE, "g"); + root.addAttribute(base); + base.setValue("."); + assertEquals("http://www.example.com/", root.getBaseURI()); + + } + + +} \ No newline at end of file diff --git a/src/nu/xom/tests/BuilderTest.java b/src/nu/xom/tests/BuilderTest.java new file mode 100644 index 0000000..6ec305d --- /dev/null +++ b/src/nu/xom/tests/BuilderTest.java @@ -0,0 +1,3531 @@ +/* Copyright 2002-2007, 2009, 2010, 2011, 2014, 2018 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.tests; + +import java.io.ByteArrayOutputStream; +import java.io.CharConversionException; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.io.PrintStream; +import java.io.Reader; +import java.io.StringReader; +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.io.UTFDataFormatException; +import java.io.UnsupportedEncodingException; +import java.io.Writer; +import java.lang.ref.WeakReference; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.net.URLEncoder; + +import org.apache.xerces.parsers.SAXParser; +import org.xml.sax.Attributes; +import org.xml.sax.InputSource; +import org.xml.sax.Locator; +import org.xml.sax.SAXException; +import org.xml.sax.SAXNotRecognizedException; +import org.xml.sax.SAXNotSupportedException; +import org.xml.sax.SAXParseException; +import org.xml.sax.XMLFilter; +import org.xml.sax.XMLReader; +import org.xml.sax.ext.DeclHandler; +import org.xml.sax.helpers.AttributesImpl; +import org.xml.sax.helpers.LocatorImpl; +import org.xml.sax.helpers.XMLFilterImpl; +import org.xml.sax.helpers.XMLReaderFactory; + +import nu.xom.Attribute; +import nu.xom.Builder; +import nu.xom.Comment; +import nu.xom.DocType; +import nu.xom.Document; +import nu.xom.Element; +import nu.xom.Elements; +import nu.xom.NodeFactory; +import nu.xom.ParsingException; +import nu.xom.ProcessingInstruction; +import nu.xom.Serializer; +import nu.xom.ValidityException; +import nu.xom.WellformednessException; +import nu.xom.XMLException; + + +/** + *

    + * Tests building documents from streams, strings, files, + * and other input sources. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.2.11 + * + */ +public class BuilderTest extends XOMTestCase { + + private File inputDir = new File("data"); + + // This class tests error conditions, which Xerces + // annoyingly logs to System.err. So we hide System.err + // before each test and restore it after each test. + private PrintStream systemErr = System.err; + + protected void setUp() { + System.setErr(new PrintStream(new ByteArrayOutputStream())); + } + + + protected void tearDown() { + System.setErr(systemErr); + System.gc(); + } + + + // Custom parser to test what happens when parser supplies + // malformed data + private static class CustomReader extends XMLFilterImpl { + + public void setFeature(String name, boolean value) {}; + + public void parse(InputSource in) throws SAXException { + this.getContentHandler().startDocument(); + this.getContentHandler().startElement("87", "87", "87", new AttributesImpl()); + this.getContentHandler().endElement("87", "87", "87"); + this.getContentHandler().endDocument(); + } + + } + + + private static class DoNothingReader extends CustomReader { + + public void parse(InputSource in) throws SAXException {} + + } + + + private static class StartAndEndReader extends CustomReader { + + public void parse(InputSource in) throws SAXException { + this.getContentHandler().startDocument(); + this.getContentHandler().endDocument(); + } + + } + + + private static class StartOnlyReader extends CustomReader { + + public void parse(InputSource in) throws SAXException { + this.getContentHandler().startDocument(); + } + + } + + + private static class EndOnlyReader extends CustomReader { + + public void parse(InputSource in) throws SAXException { + this.getContentHandler().endDocument(); + } + + } + + + public BuilderTest(String name) { + super(name); + } + + + // flag to turn on and off tests based on + // http://nagoya.apache.org/bugzilla/show_bug.cgi?id=24124 + private boolean xercesBroken = false; + + private String elementDeclaration = ""; + private String defaultAttributeDeclaration + = ""; + private String attributeDeclaration + = ""; + private String attributeDeclaration2 + = ""; + private String unparsedEntityDeclaration + = ""; + private String unparsedEntityDeclarationPublic + = ""; + private String internalEntityDeclaration + = ""; + private String externalEntityDeclarationPublic = + ""; + private String externalEntityDeclarationSystem = + ""; + private String notationDeclarationSystem + = ""; + private String notationDeclarationPublicAndSystem + = ""; + private String notationDeclarationPublic = ""; + + private String source = "\r\n" + + "" + + "" + + "Hello dear" + + "\r\n" + + "very important" + + "here's the link\r\n" + + "" + + "text in a namespace\r\n" + + "text in a " + + "namespace\r\n"; + + private String validDoc = "\n" + + "]>\r\n" + + "" + + "" + + "Hello dear" + + ""; + + private Builder builder = new Builder(); + private Builder validator = new Builder(true); + private String base = "http://www.example.com/"; + + private String attributeDoc = "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "]>\r\n" + + "Hello dear"; + + + public void testDoNothingParser() + throws ParsingException, IOException { + + try { + XMLReader parser = new DoNothingReader(); + Builder builder = new Builder(parser); + builder.build("http://www.example.org/"); + fail("built from bad data"); + } + catch (ParsingException success) { + assertNotNull(success.getMessage()); + assertEquals("http://www.example.org/", success.getURI()); + } + + } + + + public void testStartAndEndParser() + throws ParsingException, IOException { + + XMLReader parser = new StartAndEndReader(); + Builder builder = new Builder(parser); + Document doc = builder.build("http://www.example.org/"); + assertNotNull(doc.getRootElement()); + + } + + + public void testStartOnlyParser() + throws ParsingException, IOException { + + XMLReader parser = new StartOnlyReader(); + Builder builder = new Builder(parser); + Document doc = builder.build("http://www.example.org/"); + assertNotNull(doc.getRootElement()); + + } + + + public void testEndOnlyParser() + throws ParsingException, IOException { + + try { + XMLReader parser = new EndOnlyReader(); + Builder builder = new Builder(parser); + builder.build("http://www.example.org/"); + fail("built from bad data"); + } + catch (ParsingException success) { + assertTrue(success.getCause() instanceof NullPointerException); + } + + } + + + public void testBuildInternalDTDSubsetWithFixedDefaultAttributeValue() + throws ParsingException, IOException { + + String doctype = "]>"; + String document = doctype + "\n"; + Builder builder = new Builder(); + Document doc = builder.build(document, null); + DocType dt = doc.getDocType(); + String internalDTDSubset = dt.getInternalDTDSubset(); + assertTrue(internalDTDSubset.indexOf("#FIXED \"c\"") > 0); + + } + + + public void testNotationAttributeType() + throws IOException, ParsingException { + + Reader reader = new StringReader(attributeDoc); + Document document = builder.build(reader); + Element root = document.getRootElement(); + Attribute att = root.getAttribute("notationatt"); + assertEquals(Attribute.Type.NOTATION, att.getType()); + + } + + + public void testCDATAAttributeType() + throws IOException, ParsingException { + + Reader reader = new StringReader(attributeDoc); + Document document = builder.build(reader); + Element root = document.getRootElement(); + Attribute att = root.getAttribute("cdataatt"); + assertEquals(Attribute.Type.CDATA, att.getType()); + + } + + + public void testEntityAttributeType() + throws IOException, ParsingException { + + Reader reader = new StringReader(attributeDoc); + Document document = builder.build(reader); + Element root = document.getRootElement(); + Attribute att = root.getAttribute("entityatt"); + assertEquals(Attribute.Type.ENTITY, att.getType()); + + } + + + public void testEntitiesAttributeType() + throws IOException, ParsingException { + + Reader reader = new StringReader(attributeDoc); + Document document = builder.build(reader); + Element root = document.getRootElement(); + Attribute att = root.getAttribute("entitiesatt"); + assertEquals(Attribute.Type.ENTITIES, att.getType()); + + } + + + public void testNameTokenAttributeType() + throws IOException, ParsingException { + + Reader reader = new StringReader(attributeDoc); + Document document = builder.build(reader); + Element root = document.getRootElement(); + Attribute att = root.getAttribute("nmtokenatt"); + assertEquals(Attribute.Type.NMTOKEN, att.getType()); + assertEquals("1", att.getValue()); + + } + + + // I'm specifically worried about a Xerces runtime MalformedURIException here + public void testIllegalSystemIDThrowsRightException() { + + String document = ""; + try{ + builder.build(document, null); + } + catch (Exception ex) { + assertTrue(ex instanceof ParsingException); + } + + } + + + public void testNameTokensAttributeType() + throws IOException, ParsingException { + + Reader reader = new StringReader(attributeDoc); + Document document = builder.build(reader); + Element root = document.getRootElement(); + Attribute att = root.getAttribute("nmtokensatt"); + assertEquals(Attribute.Type.NMTOKENS, att.getType()); + assertEquals("1 2 3", att.getValue()); + + } + + + // verify that XML 1.1 is not supported + public void testXML11() throws IOException { + + String data = ""; + try { + builder.build(data, "http://www.example.com"); + fail("XML 1.1 allowed"); + } + catch (ParsingException ex) { + assertNotNull(ex.getMessage()); + } + + } + + + // verify that XML 1.2 is not supported + public void testXML12() throws IOException { + + String data = ""; + try { + builder.build(data, "http://www.example.com"); + fail("XML 1.2 allowed"); + } + catch (ParsingException ex) { + assertNotNull(ex.getMessage()); + } + + } + + + // verify that XML 2.0 is not supported + public void testXML20() throws IOException { + + String data = ""; + try { + builder.build(data, "http://www.example.com"); + fail("XML 2.0 allowed"); + } + catch (ParsingException ex) { + assertNotNull(ex.getMessage()); + } + + } + + + public void testIDAttributeType() + throws IOException, ParsingException { + + Reader reader = new StringReader(attributeDoc); + Document document = builder.build(reader); + Element root = document.getRootElement(); + Attribute att = root.getAttribute("idatt"); + assertEquals(Attribute.Type.ID, att.getType()); + assertEquals("p1", att.getValue()); + + } + + + public void testIDREFAttributeType() + throws IOException, ParsingException { + + Reader reader = new StringReader(attributeDoc); + Document document = builder.build(reader); + Element root = document.getRootElement(); + Attribute att = root.getAttribute("idrefatt"); + assertEquals(Attribute.Type.IDREF, att.getType()); + assertEquals("p1", att.getValue()); + + } + + + public void testIDREFSAttributeType() + throws IOException, ParsingException { + + Reader reader = new StringReader(attributeDoc); + Document document = builder.build(reader); + Element root = document.getRootElement(); + Attribute att = root.getAttribute("idrefsatt"); + assertEquals(Attribute.Type.IDREFS, att.getType()); + assertEquals("p1 p2", att.getValue()); + + } + + + public void testBuildFromNullReader() + throws IOException, ParsingException { + + Reader reader = null; + try { + builder.build(reader); + fail("Built from null reader"); + } + catch (NullPointerException ex) { + assertEquals("Attempted to build from null reader", ex.getMessage()); + } + + } + + + public void testBuildFromReader() + throws IOException, ParsingException { + + Reader reader = new StringReader(source); + Document document = builder.build(reader); + verify(document); + assertEquals("", document.getBaseURI()); + + } + + + public void testBuildFromReaderWithBase() + throws IOException, ParsingException { + + Reader reader = new StringReader(source); + Document document = builder.build(reader, base); + verify(document); + assertEquals(base, document.getBaseURI()); + + } + + + private static class NoLocator extends XMLFilterImpl { + + public NoLocator(XMLReader reader) { + super(reader); + } + + public void setDocumentLocator(Locator locator) {} + + } + + + public void testBuildWithoutLocator() + throws IOException, ParsingException, SAXException { + + XMLReader xerces = XMLReaderFactory.createXMLReader( + "org.apache.xerces.parsers.SAXParser"); + XMLReader filter = new NoLocator(xerces); + + Builder builder = new Builder(filter); + Document document = builder.build(source, "http://www.example.org/"); + verify(document); + assertEquals("http://www.example.org/", document.getBaseURI()); + + } + + + private static class WeirdAttributeTypes extends XMLFilterImpl { + + public WeirdAttributeTypes(XMLReader reader) { + super(reader); + } + + public void startElement(String uri, String localName, + String qualifiedName, Attributes atts) throws SAXException { + + AttributesImpl newAtts = new AttributesImpl(atts); + for (int i = 0; i < newAtts.getLength(); i++) { + newAtts.setType(i, "WEIRD"); + } + + super.startElement(uri, localName, qualifiedName, newAtts); + + } + + } + + + public void testWeirdAttributeTypes() + throws IOException, ParsingException, SAXException { + + XMLReader xerces = XMLReaderFactory.createXMLReader( + "org.apache.xerces.parsers.SAXParser"); + XMLReader filter = new WeirdAttributeTypes(xerces); + + Builder builder = new Builder(filter); + Document document = builder.build(attributeDoc, "http://www.example.org/"); + Element root = document.getRootElement(); + assertTrue(root.getAttributeCount() > 0); + for (int i = 0; i < root.getAttributeCount(); i++) { + assertEquals(Attribute.Type.UNDECLARED, root.getAttribute(i).getType()); + } + + } + + + // Here we're faking the non-standard behavior of some older parsers + private static class ParenthesizedEnumeratedAttributeTypes extends XMLFilterImpl { + + public ParenthesizedEnumeratedAttributeTypes(XMLReader reader) { + super(reader); + } + + public void startElement(String uri, String localName, + String qualifiedName, Attributes atts) throws SAXException { + + AttributesImpl newAtts = new AttributesImpl(atts); + for (int i = 0; i < newAtts.getLength(); i++) { + newAtts.setType(i, "(test, data, value)"); + } + + super.startElement(uri, localName, qualifiedName, newAtts); + + } + + } + + + public void testParenthesizedEnumeratedAttributeTypes() + throws IOException, ParsingException, SAXException { + + XMLReader xerces = XMLReaderFactory.createXMLReader("org.apache.xerces.parsers.SAXParser"); + XMLReader filter = new ParenthesizedEnumeratedAttributeTypes(xerces); + + Builder builder = new Builder(filter); + Document document = builder.build(attributeDoc, "http://www.example.org/"); + Element root = document.getRootElement(); + assertTrue(root.getAttributeCount() > 0); + for (int i = 0; i < root.getAttributeCount(); i++) { + assertEquals(Attribute.Type.ENUMERATION, root.getAttribute(i).getType()); + } + + } + + + public void testBuildFromInputStreamWithBase() + throws IOException, ParsingException { + InputStream in = new ByteArrayInputStream(source.getBytes("UTF-8")); + Document document = builder.build(in, base); + verify(document); + assertEquals(base, document.getBaseURI()); + } + + + public void testBuildFromInputStreamWithoutBase() + throws IOException, ParsingException { + InputStream in = new ByteArrayInputStream(source.getBytes("UTF-8")); + Document document = builder.build(in); + verify(document); + assertEquals("", document.getBaseURI()); + } + + + public void testBuildFromNullInputStream() + throws IOException, ParsingException { + + InputStream in = null; + try { + builder.build(in); + fail("Built from null input stream"); + } + catch (NullPointerException success) { + assertEquals("Null InputStream", success.getMessage()); + } + + } + + + public void testBuildFromStringWithBase() + throws IOException, ParsingException { + Document document = builder.build(source, base); + verify(document); + assertEquals(base, document.getBaseURI()); + } + + + public void testBuildDocumentThatUsesDoubleQuoteNumericCharacterReferenceInEntityDeclaration() + throws IOException, ParsingException { + + String data = "" + + " \n" + + "]>"; + + Document document = builder.build(data, null); + + Document roundtrip = builder.build(document.toXML(), null); + assertEquals(document, roundtrip); + + } + + + public void testBuildDocumentThatDeclaresStandardEntityReferences() + throws IOException, ParsingException { + + String data = "" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "]>"; + + Document document = builder.build(data, null); + Document roundtrip = builder.build(document.toXML(), null); + assertEquals(document, roundtrip); + + } + + + public void testBuildDocumentThatUsesAmpersandNumericCharacterReferenceInEntityDeclaration() + throws IOException, ParsingException { + + String data = "" + + " \n" + + "]>"; + + Document document = builder.build(data, null); + + Document roundtrip = builder.build(document.toXML(), null); + assertEquals(document, roundtrip); + + } + + + public void testBuildDocumentThatUsesDoubleQuoteNumericCharacterReferenceInAttributeDeclaration() + throws IOException, ParsingException { + + String data = "\n" + + "]>"; + + Document document = builder.build(data, null); + + Document roundtrip = builder.build(document.toXML(), null); + assertEquals(document, roundtrip); + + } + + + public void testMemoryFreedByBuilder() + throws IOException, ParsingException, InterruptedException { + + String data = ""; + + Document document = builder.build(data, null); + WeakReference ref = new WeakReference(document); + document = null; + System.gc(); + System.gc(); + System.gc(); + Thread.sleep(1000); + assertNull(ref.get()); + + } + + + public void testBuildFromInvalidDoc() + throws IOException, ParsingException { + + try { + validator.build(source, base); + fail("Built invalid doc"); + } + catch (ValidityException ex) { + Document document = ex.getDocument(); + // Can't do a full verify just yet due to bugs in Xerces + // verify(ex.getDocument()); + assertTrue(document.getChild(1) instanceof ProcessingInstruction); + assertTrue(document.getChild(2) instanceof Comment); + DocType doctype = document.getDocType(); + Element root = document.getRootElement(); + + // assertEquals(1, root.getAttributeCount()); + // assertEquals("value", root.getAttributeValue("name")); + assertEquals("test", root.getQualifiedName()); + assertEquals("test", root.getLocalName()); + assertEquals("", root.getNamespaceURI()); + + assertTrue(doctype != null); + assertTrue(document.getChild(0) instanceof DocType); + assertTrue(document.getChild(4) instanceof Comment); + assertTrue(document.getChild(2) instanceof Comment); + assertEquals(" test ", document.getChild(2).getValue()); + assertEquals("epilog", document.getChild(4).getValue()); + assertTrue(document.getChild(1) instanceof ProcessingInstruction); + assertEquals("test", doctype.getRootElementName()); + assertNull(doctype.getPublicID()); + assertNull(doctype.getSystemID()); + + String internalDTDSubset = doctype.getInternalDTDSubset(); + assertTrue( + internalDTDSubset, + internalDTDSubset.indexOf(elementDeclaration) > 0 + ); + assertTrue( + internalDTDSubset, + internalDTDSubset.indexOf(attributeDeclaration) > 0 + ); + assertTrue( + internalDTDSubset, + internalDTDSubset.indexOf(attributeDeclaration2) > 0 + ); + assertTrue( + internalDTDSubset, + internalDTDSubset.indexOf(internalEntityDeclaration) > 0 + ); + assertTrue( + internalDTDSubset, + internalDTDSubset.indexOf(externalEntityDeclarationPublic) > 0 + ); + assertTrue( + internalDTDSubset, + internalDTDSubset.indexOf(externalEntityDeclarationSystem) > 0 + ); + assertTrue( + internalDTDSubset, + internalDTDSubset.indexOf(unparsedEntityDeclaration) > 0 + ); + assertTrue( + internalDTDSubset, + internalDTDSubset.indexOf(unparsedEntityDeclarationPublic) > 0 + ); + assertTrue( + internalDTDSubset, + internalDTDSubset.indexOf(notationDeclarationPublic) > 0 + ); + assertTrue( + internalDTDSubset, + internalDTDSubset.indexOf(notationDeclarationSystem) > 0 + ); + assertTrue( + internalDTDSubset, + internalDTDSubset.indexOf(notationDeclarationPublicAndSystem) > 0 + ); + + } + + } + + + public void testBuildFromStringWithNullBase() + throws IOException, ParsingException { + Document document = builder.build(source, null); + verify(document); + assertEquals("", document.getBaseURI()); + } + + + private void verify(Document document) { + + assertTrue(document.getChild(1) instanceof ProcessingInstruction); + assertTrue(document.getChild(2) instanceof Comment); + DocType doctype = document.getDocType(); + Element root = document.getRootElement(); + + assertEquals(1, root.getAttributeCount()); + assertEquals("value", root.getAttributeValue("name")); + assertEquals("test", root.getQualifiedName()); + assertEquals("test", root.getLocalName()); + assertEquals("", root.getNamespaceURI()); + + assertTrue(doctype != null); + assertTrue(document.getChild(0) instanceof DocType); + assertTrue(document.getChild(4) instanceof Comment); + assertTrue(document.getChild(2) instanceof Comment); + assertEquals(" test ", document.getChild(2).getValue()); + assertEquals("epilog", document.getChild(4).getValue()); + assertTrue(document.getChild(1) instanceof ProcessingInstruction); + assertEquals("test", doctype.getRootElementName()); + assertNull(doctype.getPublicID()); + assertNull(doctype.getSystemID()); + + String internalDTDSubset = doctype.getInternalDTDSubset(); + assertTrue( + internalDTDSubset, + internalDTDSubset.indexOf(elementDeclaration) > 0 + ); + assertTrue( + internalDTDSubset, + internalDTDSubset.indexOf(attributeDeclaration) > 0 + ); + assertTrue( + internalDTDSubset, + internalDTDSubset.indexOf(attributeDeclaration2) > 0 + ); + assertTrue( + internalDTDSubset, + internalDTDSubset.indexOf(internalEntityDeclaration) > 0 + ); + assertTrue( + internalDTDSubset, + internalDTDSubset.indexOf(externalEntityDeclarationPublic) > 0 + ); + assertTrue( + internalDTDSubset, + internalDTDSubset.indexOf(externalEntityDeclarationSystem) > 0 + ); + assertTrue( + internalDTDSubset, + internalDTDSubset.indexOf(unparsedEntityDeclaration) > 0 + ); + assertTrue( + internalDTDSubset, + internalDTDSubset.indexOf(unparsedEntityDeclarationPublic) > 0 + ); + assertTrue( + internalDTDSubset, + internalDTDSubset.indexOf(notationDeclarationPublic) > 0 + ); + assertTrue( + internalDTDSubset, + internalDTDSubset.indexOf(notationDeclarationSystem) > 0 + ); + assertTrue( + internalDTDSubset, + internalDTDSubset.indexOf(notationDeclarationPublicAndSystem) > 0 + ); + + } + + + public void testValidateFromReader() + throws IOException, ParsingException { + + Reader reader1 = new StringReader(validDoc); + Document document1 = validator.build(reader1); + assertEquals("", document1.getBaseURI()); + Reader reader2 = new StringReader(validDoc); + Document document2 = builder.build(reader2); + assertEquals(document2, document1); + + } + + + public void testDocumentWithDefaultNamespaceOnPrefixedElement() + throws IOException, ParsingException { + + Reader reader = new StringReader(""); + Document document = builder.build(reader); + Element root = document.getRootElement(); + assertEquals("http://www.example.org/", root.getNamespaceURI("")); + assertEquals("http://www.cafeconleche.org/", root.getNamespaceURI("pre")); + assertEquals("http://www.cafeconleche.org/", root.getNamespaceURI()); + + } + + + public void testValidateFromReaderWithBase() + throws IOException, ParsingException { + + Reader reader = new StringReader(validDoc); + Document document = validator.build(reader, base); + assertEquals(base, document.getBaseURI()); + Reader reader2 = new StringReader(validDoc); + Document document2 = builder.build(reader2); + assertEquals(document2, document); + + } + + + public void testValidateFromInputStreamWithBase() + throws IOException, ParsingException { + + InputStream in = new ByteArrayInputStream(validDoc.getBytes("UTF-8")); + Document document = validator.build(in, base); + assertEquals(base, document.getBaseURI()); + Reader reader2 = new StringReader(validDoc); + Document document2 = builder.build(reader2); + assertEquals(document2, document); + + } + + + public void testValidateInSeries() + throws IOException, ParsingException { + + try { + Reader reader = new StringReader(source); + validator.build(reader); + fail("Allowed invalid doc"); + } + catch (ValidityException success) { + assertNotNull(success.getMessage()); + } + // now make sure validating a valid document doesn't + // throw an exception + InputStream in = new ByteArrayInputStream(validDoc.getBytes("UTF-8")); + validator.build(in, base); + + } + + + public void testValidateFromInputStreamWithoutBase() + throws IOException, ParsingException { + + InputStream in = new ByteArrayInputStream(validDoc.getBytes("UTF-8")); + Document document = validator.build(in); + assertEquals("", document.getBaseURI()); + Reader reader2 = new StringReader(validDoc); + Document document2 = builder.build(reader2); + assertEquals(document2, document); + + } + + + public void testValidateFromStringWithBase() + throws IOException, ParsingException { + + Document document = validator.build(validDoc, base); + assertEquals(base, document.getBaseURI()); + Reader reader2 = new StringReader(validDoc); + Document document2 = builder.build(reader2); + assertEquals(document2, document); + + } + + + public void testValidateWithCrimson() + throws IOException, ParsingException { + + XMLReader crimson; + try { + crimson = XMLReaderFactory.createXMLReader( + "org.apache.crimson.parser.XMLReaderImpl"); + } + catch (SAXException ex) { + // can't test Crimson if you can't load it + return; + } + Builder validator = new Builder(crimson, true); + Document document = validator.build(validDoc, base); + assertEquals(base, document.getBaseURI()); + Reader reader2 = new StringReader(validDoc); + Document document2 = builder.build(reader2); + assertEquals(document2, document); + + } + + + public void testNotationAttributeTypeWithCrimson() + throws IOException, ParsingException { + + XMLReader crimson; + try { + crimson = XMLReaderFactory.createXMLReader( + "org.apache.crimson.parser.XMLReaderImpl"); + } + catch (SAXException ex) { + // can't test Crimson if you can't load it + return; + } + + String data = " \n" + + "\n" + + "\n" + + "" + + "]>"; + + Builder builder = new Builder(crimson); + Document document = builder.build(data, base); + + String s = document.toXML(); + Document roundTrip = builder.build(s, base); + assertEquals(document, roundTrip); + + } + + + public void testEnumerationAttributeType() + throws IOException, ParsingException { + + XMLReader crimson; + try { + crimson = XMLReaderFactory.createXMLReader( + "org.apache.crimson.parser.XMLReaderImpl"); + } + catch (SAXException ex) { + // can't test Crimson if you can't load it + return; + } + Builder builder = new Builder(crimson, false); + String doc = "" + + "]>"; + Document document = builder.build(doc, base); + Element root = document.getRootElement(); + Attribute att = root.getAttribute(0); + assertEquals(Attribute.Type.ENUMERATION, att.getType()); + + } + + + public void testWarningDoesNotStopBuild() + throws IOException, ParsingException, SAXException { + + XMLReader xerces; + try { + xerces = XMLReaderFactory.createXMLReader( + "org.apache.xerces.parsers.SAXParser"); + } + catch (SAXException ex) { + // can't test Xerces if you can't load it + return; + } + // This document generates a warning due to the duplicate + // attribute declaration + xerces.setFeature( + "http://apache.org/xml/features/validation/warn-on-duplicate-attdef", + true); + Builder builder = new Builder(xerces, true); + Document document = builder.build("" + + "" + + "" + + "]>", base); + // The main test is that the document is built successfully. + assertEquals(2, document.getChildCount()); + assertEquals("root", document.getRootElement().getQualifiedName()); + + } + + + private static class EntitySkipper extends XMLFilterImpl { + + public EntitySkipper(XMLReader reader) { + super(reader); + } + + public void characters(char[] data, int start, int length) + throws SAXException { + super.skippedEntity("name"); + } + + } + + + public void testSkippedEntityThrowsParsingException() + throws IOException, ParsingException, SAXException { + + XMLReader xerces = XMLReaderFactory.createXMLReader( + "org.apache.xerces.parsers.SAXParser"); + XMLReader filter = new EntitySkipper(xerces); + + Builder builder = new Builder(filter, true); + try { + builder.build("replace", base); + fail("Allowed skipped entity"); + } + catch (ParsingException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testValidateFromStringWithNullBase() + throws IOException, ParsingException { + Document document = validator.build(validDoc, null); + assertEquals("", document.getBaseURI()); + Reader reader2 = new StringReader(validDoc); + Document document2 = builder.build(reader2); + assertEquals(document2, document); + } + + + public void testCannotBuildNamespaceMalformedDocument() + throws IOException { + + try { + builder.build("", null); + fail("Builder allowed undeclared prefix"); + } + catch (ParsingException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testInvalidDocFromReader() + throws IOException, ParsingException { + + Reader reader = new StringReader(source); + try { + validator.build(reader); + fail("Allowed invalid doc"); + } + catch (ValidityException success) { + assertNotNull(success.getMessage()); + assertTrue(success.getErrorCount() > 0); + for (int i = 0; i < success.getErrorCount(); i++) { + assertNotNull(success.getValidityError(i)); + assertTrue(success.getLineNumber(i) >= -1); + assertTrue(success.getColumnNumber(i) >= -1); + } + if (!xercesBroken) { + Document doc = builder.build(new StringReader(source)); + this.verify(success.getDocument()); + assertEquals(doc, success.getDocument()); + } + } + + } + + + public void testNamespaceMalformedDocumentWithCrimson() + throws IOException { + + StringReader reader = new StringReader(""); + XMLReader crimson; + try { + crimson = XMLReaderFactory.createXMLReader( + "org.apache.crimson.parser.XMLReaderImpl"); + } + catch (SAXException ex) { + // No Crimson in classpath; therefore can't test it + return; + } + Builder builder = new Builder(crimson); + try { + builder.build(reader); + fail("Crimson allowed namespace malformed doc"); + } + catch (ParsingException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testValidateNamespaceMalformedInvalidDocumentWithCrimson() + throws IOException { + + StringReader reader = new StringReader("\n" + + " \n" + + "]>\n" + + ""); + XMLReader crimson; + try { + crimson = XMLReaderFactory.createXMLReader( + "org.apache.crimson.parser.XMLReaderImpl"); + } + catch (SAXException ex) { + // No Crimson in classpath; therefore can't test it + return; + } + Builder builder = new Builder(crimson); + try { + builder.build(reader); + fail("Crimson allowed namespace malformed doc"); + } + catch (ValidityException ex) { + fail("Crimson should have thrown ParsingException instead"); + } + catch (ParsingException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testInvalidDocFromReaderWithBase() + throws IOException, ParsingException { + + Reader reader1 = new StringReader(source); + try { + validator.build(reader1, base); + fail("Allowed invalid doc"); + } + catch (ValidityException ex) { + assertNotNull(ex.getMessage()); + assertEquals(base, ex.getURI()); + assertTrue(ex.getErrorCount() > 0); + for (int i = 0; i < ex.getErrorCount(); i++) { + assertNotNull(ex.getValidityError(i)); + assertTrue(ex.getLineNumber(i) >= -1); + assertTrue(ex.getColumnNumber(i) >= -1); + } + if (!xercesBroken) { + Document doc = builder.build(new StringReader(source), base); + this.verify(ex.getDocument()); + assertEquals(doc, ex.getDocument()); + } + } + + } + + + public void testInvalidDocFromInputStreamWithBase() + throws IOException, ParsingException { + + InputStream in = new ByteArrayInputStream(source.getBytes("UTF-8")); + try { + validator.build(in, base); + fail("Allowed invalid doc"); + } + catch (ValidityException ex) { + assertNotNull(ex.getMessage()); + assertEquals(base, ex.getURI()); + assertTrue(ex.getErrorCount() > 0); + for (int i = 0; i < ex.getErrorCount(); i++) { + assertNotNull(ex.getValidityError(i)); + assertTrue(ex.getLineNumber(i) >= -1); + assertTrue(ex.getColumnNumber(i) >= -1); + } + if (!xercesBroken) { + Document doc = builder.build( + new ByteArrayInputStream(source.getBytes("UTF-8")), base + ); + this.verify(ex.getDocument()); + assertEquals(doc, ex.getDocument()); + } + } + + } + + + public void testInvalidDocFromInputStreamWithoutBase() + throws IOException, ParsingException { + + InputStream in = new ByteArrayInputStream(source.getBytes("UTF-8")); + try { + validator.build(in); + fail("Allowed invalid doc"); + } + catch (ValidityException ex) { + assertNotNull(ex.getMessage()); + assertTrue(ex.getErrorCount() > 0); + for (int i = 0; i < ex.getErrorCount(); i++) { + assertNotNull(ex.getValidityError(i)); + assertTrue(ex.getLineNumber(i) >= -1); + assertTrue(ex.getColumnNumber(i) >= -1); + } + if (!xercesBroken) { + Document doc = builder.build( + new ByteArrayInputStream(source.getBytes("UTF-8")) + ); + this.verify(ex.getDocument()); + assertEquals(doc, ex.getDocument()); + } + } + + } + + + public void testInvalidDocFromStringWithBase() + throws IOException, ParsingException { + + try { + validator.build(source, base); + fail("Allowed invalid doc"); + } + catch (ValidityException ex) { + assertNotNull(ex.getMessage()); + assertEquals(base, ex.getURI()); + assertTrue(ex.getErrorCount() > 0); + for (int i = 0; i < ex.getErrorCount(); i++) { + assertNotNull(ex.getValidityError(i)); + assertTrue(ex.getLineNumber(i) >= -1); + assertTrue(ex.getColumnNumber(i) >= -1); + } + if (!xercesBroken) { + Document doc = builder.build(source, base); + this.verify(ex.getDocument()); + assertEquals(doc, ex.getDocument()); + } + } + + } + + + public void testInvalidDocWithCrimson() + throws IOException, ParsingException { + + XMLReader crimson; + try { + crimson = XMLReaderFactory.createXMLReader( + "org.apache.crimson.parser.XMLReaderImpl"); + } + catch (SAXException ex) { + // can't test Crimson if you can't load it + return; + } + Builder validator = new Builder(crimson, true); + try { + validator.build(source, null); + fail("Allowed invalid doc"); + } + catch (ValidityException ex) { + assertTrue(ex.getErrorCount() > 0); + assertNull(ex.getURI()); + for (int i = 0; i < ex.getErrorCount(); i++) { + assertNotNull(ex.getValidityError(i)); + } + } + + } + + + public void testInvalidDocFromStringWithNullBase() + throws IOException, ParsingException { + + try { + validator.build(source, null); + fail("Allowed invalid doc"); + } + catch (ValidityException ex) { + assertTrue(ex.getErrorCount() > 0); + assertNull(ex.getURI()); + for (int i = 0; i < ex.getErrorCount(); i++) { + assertNotNull(ex.getValidityError(i)); + } + if (!xercesBroken) { + Document doc = builder.build(source, null); + this.verify(ex.getDocument()); + assertEquals(doc, ex.getDocument()); + } + } + + } + + + public void testJavaEncodings() + throws IOException, ParsingException { + + String str = "" + + "é"; + byte[] data = str.getBytes("8859_1"); + InputStream in = new ByteArrayInputStream(data); + Document doc = builder.build(in); + assertEquals("é", doc.getValue()); + + } + + + // Crimson improperly converts 0x0D and 0x0A to spaces + // even when the attribute type is not CDATA. + // This bug explains why the canonicalizer tests fail + // with Crimson + public void testCrimsonCharacterReferenceBug() + throws IOException, ParsingException { + + String data = + "]>" + + ""; + InputStream in = new ByteArrayInputStream( + data.getBytes("UTF8")); + Document document = builder.build(in, null); + assertEquals("\r", + document.getRootElement().getAttributeValue("name")); + + } + + + public void testBaseRelativeResolution() + throws IOException, ParsingException { + builder.build(new File(inputDir, "baserelative/test.xml")); + } + + + // make sure transcoders on input are using normalization + // form C when converting from other encodings + public void testNFC() + throws IOException, ParsingException { + + Document doc = builder.build(new File(inputDir, "nfctest.xml")); + Element root = doc.getRootElement(); + String s = root.getValue(); + assertEquals(1, s.length()); + assertEquals(0xE9, s.charAt(0)); + + } + + + + // This tests XOM's workaround for a bug in Crimson, Xerces, + // and possibly other parsers + public void testBaseRelativeResolutionRemotely() + throws IOException, ParsingException { + builder.build("http://www.cafeconleche.org"); + } + + + public void testCanonicalizeURLWithQueryString() + throws IOException, ParsingException { + Document doc = builder.build("http://www.cafeconleche.org/?foo=bar"); + assertEquals("http://www.cafeconleche.org/?foo=bar", doc.getBaseURI()); + } + + + public void testExternalEntityResolution() + throws IOException, ParsingException { + + File input = new File(inputDir, "entitytest.xml"); + Builder builder = new Builder(false); + Document doc = builder.build(input); + Element root = doc.getRootElement(); + Element external = root.getFirstChildElement("external"); + assertEquals("Hello from an entity!", external.getValue()); + + } + + // See http://lists.ibiblio.org/pipermail/xom-interest/2006-March/002962.html + public void testExternalEntityRelativeURLResolution() + throws IOException, ParsingException { + + String url = "file://" + escapePath(inputDir.getAbsolutePath()); + String docURL = url + "base/../sib/content/pe.xml"; + Builder builder = new Builder(false); + Document doc = builder.build(docURL); + String subset = doc.getDocType().getInternalDTDSubset(); + assertTrue(subset.indexOf("SYSTEM \"pe.txt\"") > 1); + + } + + + private String escapePath(String path) throws UnsupportedEncodingException { + String[] fragments = path.split("/"); + StringBuffer result = new StringBuffer(); + for (int i = 0; i < fragments.length; i++) { + String encoded = URLEncoder.encode(fragments[i], "UTF-8"); + encoded = encoded.replace("+", "%20"); + result.append(encoded); + result.append("/"); + } + return result.toString(); + } + + + public void testExternalEntityRelativeURLResolution2() + throws IOException, ParsingException { + + String url = "file://" + escapePath(inputDir.getAbsolutePath()); + String docURL = url + "base/../sib/content/ep.xml"; + Builder builder = new Builder(false); + Document doc = builder.build(docURL); + String subset = doc.getDocType().getInternalDTDSubset(); + assertTrue(subset.indexOf("SYSTEM \"pe.txt\"") > 1); + + } + + + public void testBuildFromURLThatContainsNonexistentPart() + throws IOException, ParsingException { + + String url = "file://" + escapePath(inputDir.getAbsolutePath()); + String docURL = url + "nonexistent/../test.xml"; + Builder builder = new Builder(false); + Document doc = builder.build(docURL); + assertNotNull(doc); + + } + + + public void testBuildFromURLThatContainsNonexistentPart2() + throws IOException, ParsingException { + + String url = "file://" + escapePath(inputDir.getAbsolutePath()); + String docURL = url + "nonexistent/../sib/content/pe.xml"; + Builder builder = new Builder(false); + Document doc = builder.build(docURL); + assertNotNull(doc); + + } + + + public void testExternalEntityRelativeURLResolutionIncludingNonexistentDirectoryInBaseURL() + throws IOException, ParsingException { + + String url = "file://" + escapePath(inputDir.getAbsolutePath()); + String docURL = url + "nonexistent/../sib/content/pe.xml"; + Builder builder = new Builder(false); + Document doc = builder.build(docURL); + String subset = doc.getDocType().getInternalDTDSubset(); + assertTrue(subset.indexOf("SYSTEM \"pe.txt\"") > 1); + + } + + + public void testExternalEntityRelativeURLResolution3() + throws IOException, ParsingException { + + String url = "file://" + escapePath(inputDir.getAbsolutePath()); + String docURL = url + "sib/content/ep.xml"; + Builder builder = new Builder(false); + Document doc = builder.build(docURL); + String subset = doc.getDocType().getInternalDTDSubset(); + assertTrue(subset.indexOf("SYSTEM \"pe.txt\"") > 1); + + } + + + // This test exposes a bug in Crimson but not Xerces. + // It's testing whether the external DTD subset is read, + // default attribute values applied, and comments and + // processing instructions in the external DTD subset are not + // reported. + public void testExternalDTDSubset() + throws IOException, ParsingException { + + File input = new File(inputDir, "externalDTDtest.xml"); + Builder builder = new Builder(false); + Document doc = builder.build(input); + assertEquals(2, doc.getChildCount()); + Element root = doc.getRootElement(); + Attribute name = root.getAttribute("name"); + assertEquals("value", name.getValue()); + DocType doctype = doc.getDocType(); + assertEquals("", doctype.getInternalDTDSubset()); + + } + + + /* + + + + + + +]> + + This document is intended to test the building of + various constructs in the internal DTD subset. + +*/ + public void testInternalDTDSubset() + throws ValidityException, ParsingException, IOException { + + File input = new File(inputDir, "internaldtdsubsettest.xml"); + Builder builder = new Builder(false); + Document doc = builder.build(input); + String internalSubset = doc.getDocType().getInternalDTDSubset(); + assertTrue(internalSubset.indexOf("") > 0); + assertTrue(internalSubset.indexOf("") > 0); + assertTrue(internalSubset.indexOf("") > 0); + assertTrue(internalSubset.indexOf("") > 0); + // some confusion in the parser resolving these as relative URLs. + // This is in accordance with the SAX spec, see + // http://www.saxproject.org/apidoc/org/xml/sax/DTDHandler.html#notationDecl(java.lang.String,%20java.lang.String,%20java.lang.String) + // but how does it know the notation system ID is really a URL? + assertTrue(internalSubset.indexOf(" 0); + assertTrue(internalSubset.indexOf("picture.jpg\" NDATA JPEG>") > 0); + assertTrue(internalSubset.indexOf(" 0); + assertTrue(internalSubset.indexOf("image/jpeg\">") > 0); + + } + + + public void testInternalEntityDeclDollarSign() + throws ValidityException, ParsingException, IOException { + + String input = "] >"; + Builder builder = new Builder(false); + Document doc = builder.build(input, null); + String internalSubset = doc.getDocType().getInternalDTDSubset(); + assertTrue(internalSubset.indexOf("") > 0); + + } + + + public void testInternalDTDSubset5To9() + throws ValidityException, ParsingException, IOException { + + String input = "] >"; + Builder builder = new Builder(false); + Document doc = builder.build(input, null); + String internalSubset = doc.getDocType().getInternalDTDSubset(); + assertTrue(internalSubset.indexOf("") > 0); + + } + + + public void testInternalDTDSubsetPunctuation() + throws ValidityException, ParsingException, IOException { + + String input = "] >"; + Builder builder = new Builder(false); + Document doc = builder.build(input, null); + String internalSubset = doc.getDocType().getInternalDTDSubset(); + assertTrue(internalSubset.indexOf("") > 0); + + } + + +/* + + + + + + + + + + + + + + */ + public void testInternalAndExternalDTDSubset() + throws ValidityException, ParsingException, IOException { + + File input = new File(inputDir, "internalandexternaldtdsubsettest.xml"); + Builder builder = new Builder(false); + Document doc = builder.build(input); + String internalSubset = doc.getDocType().getInternalDTDSubset(); + assertTrue(internalSubset.indexOf("") > 0); + assertTrue(internalSubset.indexOf("") > 0); + assertTrue(internalSubset.indexOf("") > 0); + assertTrue(internalSubset.indexOf("") > 0); + assertTrue(internalSubset.indexOf(" 0); + assertTrue(internalSubset.indexOf("picture.jpg\" NDATA JPEG>") > 0); + assertTrue(internalSubset.indexOf(" 0); + assertTrue(internalSubset.indexOf("image/jpeg\">") > 0); + + assertEquals(-1, internalSubset.indexOf("comment should not be here")); + assertEquals(-1, internalSubset.indexOf("processing instruction should not be here")); + assertEquals(-1, internalSubset.indexOf("anattribute")); + assertEquals(-1, internalSubset.indexOf("anotherattribute")); + assertEquals(-1, internalSubset.indexOf("hatch-pic")); + assertEquals(-1, internalSubset.indexOf("public-pic")); + assertEquals(-1, internalSubset.indexOf("open-hatch")); + assertEquals(-1, internalSubset.indexOf("Pub-Status-pic")); + assertEquals(-1, internalSubset.indexOf("Textuality")); + assertEquals(-1, internalSubset.indexOf("15903")); + + } + + + public void testInternalAndExternalDTDSubsetWithCrimson() + throws ValidityException, ParsingException, IOException { + + XMLReader crimson; + try { + crimson = XMLReaderFactory.createXMLReader( + "org.apache.crimson.parser.XMLReaderImpl"); + } + catch (SAXException ex) { + // can't test Crimson if you can't load it + return; + } + + Builder builder = new Builder(crimson); + File input = new File(inputDir, "internalandexternaldtdsubsettest.xml"); + Document doc = builder.build(input); + String internalSubset = doc.getDocType().getInternalDTDSubset(); + assertTrue(internalSubset.indexOf("") > 0); + assertTrue(internalSubset.indexOf("") > 0); + assertTrue(internalSubset.indexOf("") > 0); + assertTrue(internalSubset.indexOf("") > 0); + assertTrue(internalSubset.indexOf(" 0); + assertTrue(internalSubset.indexOf("picture.jpg\" NDATA JPEG>") > 0); + assertTrue(internalSubset.indexOf(" 0); + assertTrue(internalSubset.indexOf("image/jpeg\">") > 0); + + assertEquals(-1, internalSubset.indexOf("comment should not be here")); + assertEquals(-1, internalSubset.indexOf("processing instruction should not be here")); + assertEquals(-1, internalSubset.indexOf("anattribute")); + assertEquals(-1, internalSubset.indexOf("anotherattribute")); + assertEquals(-1, internalSubset.indexOf("hatch-pic")); + assertEquals(-1, internalSubset.indexOf("public-pic")); + assertEquals(-1, internalSubset.indexOf("open-hatch")); + assertEquals(-1, internalSubset.indexOf("Pub-Status-pic")); + assertEquals(-1, internalSubset.indexOf("Textuality")); + assertEquals(-1, internalSubset.indexOf("15903")); + + } + + + // This test exposes a bug in Crimson, Xerces 2.5 and earlier, + // and possibly other parsers. I've reported the bug in Xerces, + // and it is fixed in Xerces 2.6. + public void testBaseRelativeResolutionRemotelyWithDirectory() + throws IOException, ParsingException { + builder.build("http://www.ibiblio.org/xml"); + } + + + // This test exposes a bug in Crimson, Xerces 2.5 and earlier, + // and possibly other parsers. I've reported the bug in Xerces, + // and it is fixed in Xerces 2.6. + public void testRelativeURIResolutionAgainstARedirectedBase() + throws IOException, ParsingException { + builder.build("http://www.ibiblio.org/xml/redirecttest.xml"); + } + + + public void testDontGetNodeFactory() { + + Builder builder = new Builder(); + NodeFactory factory = builder.getNodeFactory(); + if (factory != null) { + assertFalse( + factory.getClass().getName().endsWith("NonVerifyingFactory") + ); + } + + } + + + public void testGetNodeFactory() { + NodeFactory factory = new NodeFactory(); + Builder builder = new Builder(factory); + assertEquals(factory, builder.getNodeFactory()); + } + + + // Make sure additional namespaces aren't added for + // attributes. This test is flaky because it assumes + // the parser reports attributes in the correct order, + // which is not guaranteed. I use a custom SAX Filter to + // make sure the namespace declaration comes before the attribute. + public void testAttributesVsNamespaces() + throws ParsingException, IOException, SAXException { + + XMLFilter filter = new OrderingFilter(); + filter.setParent( + XMLReaderFactory.createXMLReader( + "org.apache.xerces.parsers.SAXParser" + ) + ); + Builder builder = new Builder(filter); + String data =""; + Document doc = builder.build(data, null); + Element root = doc.getRootElement(); + root.removeAttribute(root.getAttribute(0)); + assertNull(root.getNamespaceURI("pre")); + + } + + + private static class OrderingFilter extends XMLFilterImpl { + + public void startElement(String namespaceURI, String localName, + String qualifiedName, Attributes atts) throws SAXException { + + AttributesImpl newAttributes = new AttributesImpl(); + newAttributes.addAttribute( + "", + "pre", + "xmlns:pre", + "CDATA", + "http://www.example.com/"); + newAttributes.addAttribute( + "http://www.example.com/", + "name", + "pre:name", + "CDATA", + "value"); + super.startElement(namespaceURI, localName, qualifiedName, + newAttributes); + } + + } + + + public void testValidateMalformedDocument() + throws IOException { + + Reader reader = new StringReader("" + + "" + + "" + + "]>"); + try { + validator.build(reader); + fail("Allowed malformed doc"); + } + catch (ValidityException ex) { + fail("Threw validity error instead of well-formedness error"); + } + catch (ParsingException ex) { + assertNotNull(ex.getMessage()); + assertNull(ex.getURI()); + } + + } + + + /* Test for particular bug in Crimson with mixed content declarations */ + public void testBuildInternalDTDSubsetWithCrimson() + throws ParsingException, IOException { + + String dtd = " \n"; + + String document = "\n"; + XMLReader crimson; + try { + crimson = XMLReaderFactory.createXMLReader( + "org.apache.crimson.parser.XMLReaderImpl"); + } + catch (SAXException ex) { + // can't test Crimson if you can't load it + return; + } + + Builder builder = new Builder(crimson); + Document doc = builder.build(document, null); + + String parsedDTD = doc.getDocType().getInternalDTDSubset(); + assertEquals(dtd, parsedDTD); + + } + + + /* Test for particular bug in Crimson with mixed content declarations */ + public void testBuildXMLNamespaceDeclarationWithCrimson() + throws ParsingException, IOException { + + String document = ""; + XMLReader crimson; + try { + crimson = XMLReaderFactory.createXMLReader( + "org.apache.crimson.parser.XMLReaderImpl"); + } + catch (SAXException ex) { + // can't test Crimson if you can't load it + return; + } + + Builder builder = new Builder(crimson); + Document doc = builder.build(document, null); + + assertEquals("", doc.getRootElement().toXML()); + + } + + + public void testBuildIllegalXMLNamespaceDeclarationWithCrimson() + throws ParsingException, IOException { + + String document = ""; + XMLReader crimson; + try { + crimson = XMLReaderFactory.createXMLReader( + "org.apache.crimson.parser.XMLReaderImpl"); + } + catch (SAXException ex) { + // can't test Crimson if you can't load it + return; + } + + Builder builder = new Builder(crimson); + try { + builder.build(document, null); + fail("Allowed wrong namespace URI for xml prefix"); + } + catch (ParsingException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testATTLISTDeclaresXMLSpacePreserveOnlyWithCrimson() + throws ParsingException, IOException { + + String dtd = "]\n>"; + + String data = dtd + ""; + XMLReader crimson; + try { + crimson = XMLReaderFactory.createXMLReader( + "org.apache.crimson.parser.XMLReaderImpl"); + } + catch (SAXException ex) { + // can't test Crimson if you can't load it + return; + } + + Builder builder = new Builder(crimson); + Document doc = builder.build(data, null); + assertEquals(1, doc.getRootElement().getAttributeCount()); + + } + + + public void testNoInternalSubsetWithCrimson() + throws ParsingException, IOException { + + XMLReader crimson; + try { + crimson = XMLReaderFactory.createXMLReader( + "org.apache.crimson.parser.XMLReaderImpl"); + } + catch (SAXException ex) { + // can't test Crimson if you can't load it + return; + } + + File input = new File(inputDir, "externalDTDtest.xml"); + Builder builder = new Builder(crimson); + Document doc = builder.build(input); + String subset = doc.getDocType().getInternalDTDSubset(); + assertEquals("", subset); + + } + + + public void testValidateMalformedDocumentWithCrimson() + throws IOException { + + Reader reader = new StringReader("" + + "" + + "" + + "]>"); + XMLReader crimson; + try { + crimson = XMLReaderFactory.createXMLReader( + "org.apache.crimson.parser.XMLReaderImpl"); + } + catch (SAXException ex) { + // can't test Crimson if you can't load it + return; + } + Builder validator = new Builder(crimson, true); + try { + validator.build(reader); + fail("Allowed malformed doc"); + } + catch (ValidityException ex) { + fail("Crimson threw validity error instead of well-formedness error"); + } + catch (ParsingException success) { + assertNotNull(success.getMessage()); + assertNull(success.getURI()); + } + + } + + + // This is testing a work-around for a Xerces bug + // http://nagoya.apache.org/bugzilla/show_bug.cgi?id=27583 + // that reports this as an IOException rather than a SAXException + public void testBuildMalformedDocumentWithUnpairedSurrogate() + throws IOException { + + String doc = "A\uD800A"; + try { + builder.build(doc, "http://www.example.com"); + fail("Allowed malformed doc"); + } + catch (ParsingException success) { + assertNotNull(success.getMessage()); + assertEquals("http://www.example.com/", success.getURI()); + } + + } + + + public void testBuildMalformedDocumentWithBadUnicodeData() + throws IOException { + + File f = new File(inputDir, "xmlconf"); + f = new File(f, "xmltest"); + f = new File(f, "not-wf"); + f = new File(f, "sa"); + f = new File(f, "170.xml"); + if (f.exists()) { + try { + builder.build(f); + fail("Allowed malformed doc"); + } + catch (ParsingException success) { + assertNotNull(success.getMessage()); + assertTrue(success.getURI().endsWith( + "data/xmlconf/xmltest/not-wf/sa/170.xml")); + assertTrue(success.getURI().startsWith("file:/")); + } + } + + } + + + public void testBuildAnotherMalformedDocumentWithBadUnicodeData() + throws IOException { + + String filename = "data/oasis/p02fail30.xml"; + File f = new File(inputDir, "oasis"); + f = new File(f, "p02fail30.xml"); + if (f.exists()) { + try { + builder.build(f); + fail("Allowed malformed doc"); + } + catch (ParsingException success) { + assertNotNull(success.getMessage()); + assertTrue(success.getURI().endsWith(filename)); + assertTrue(success.getURI().startsWith("file:/")); + } + } + + } + + + public void testBuildMalformedDocumentWithBadParser() + throws ParsingException, IOException { + + try { + XMLReader parser = new CustomReader(); + Builder builder = new Builder(parser); + builder.build("http://www.example.org/"); + fail("built from bad data"); + } + catch (ParsingException success) { + assertNotNull(success.getMessage()); + assertTrue(success.getCause() instanceof WellformednessException); + } + + } + + + public void testBuildMalformedDocumentWithCrimson() + throws IOException { + + Reader reader = new StringReader("" + + "" + + "" + + "]>"); + XMLReader crimson; + try { + crimson = XMLReaderFactory.createXMLReader( + "org.apache.crimson.parser.XMLReaderImpl"); + } + catch (SAXException ex) { + // can't test Crimson if you can't load it + return; + } + Builder builder = new Builder(crimson); + try { + builder.build(reader); + fail("Allowed malformed doc"); + } + catch (ValidityException ex) { + fail("Crimson threw validity error instead of well-formedness error"); + } + catch (ParsingException ex) { + assertNotNull(ex.getMessage()); + assertNull(ex.getURI()); + } + + } + + + public void testNestedExceptionWithSAXParseException() + throws IOException { + + Reader reader = new StringReader("" + + "" + + "" + + ""); + XMLReader parser = XMLReaderFactory.createXMLReader( + "org.apache.xerces.parsers.SAXParser"); + XMLFilter filter = new XMLFilterImpl(); + filter.setParent(parser); + Builder builder = new Builder(filter); + Document doc = builder.build(reader); + Element root = doc.getRootElement(); + Element prea = (Element) root.getChild(0); + Element b = (Element) prea.getChild(0); + assertEquals("http://www.red.com", b.getNamespaceURI()); + + } + + + // from XML Conformance Test Suite; James Clark test + // valid 097 + public void testLineBreaksInInternalDTDSubset() + throws ParsingException, IOException { + + Document doc = builder.build(new File(inputDir, "097.xml")); + String expectedResult = "\n" + + "\n" + + " \n" + + " \n" + + " \n" + + "]>\n" + + "\n"; + String actual = doc.toXML(); + assertEquals(expectedResult, actual); + + } + + + public void testBuildDocumentThatUndeclaresDefaultNamespace() + throws ParsingException, IOException { + + Document doc = builder.build(new File(inputDir, "undeclare.xml")); + String actual = doc.toXML(); + assertTrue(actual.indexOf("some data") > 0); + + } + + + public void testBuildFromFileThatContainsNonASCIICharacterInName() + throws ParsingException, IOException { + + File f = new File(inputDir, "resumé.xml"); + try { + Writer out = new OutputStreamWriter( + new FileOutputStream(f), "UTF8"); + out.write(""); + out.flush(); + out.close(); + Document doc = builder.build(f); + String expectedResult = "\n" + + "\n"; + String actual = doc.toXML(); + assertEquals(expectedResult, actual); + assertTrue(doc.getBaseURI().startsWith("file:/")); + assertTrue(doc.getBaseURI().endsWith("data/resum%C3%A9.xml")); + } + finally { + if (f.exists()) f.delete(); + } + + } + + + // This test fails on Mac OS X. It passes on Linux. + public void testBuildFromFileThatContainsPlane1CharacterInName() + throws ParsingException, IOException { + + int gclef = 0x1D120; + char high = (char) ((gclef - 0x10000)/0x400 + 0xD800); + char low = (char) ((gclef - 0x10000) % 0x400 + 0xDC00); + File f = new File(inputDir, "music" + high + "" + low + ".xml"); + try { + Writer out = new OutputStreamWriter( + new FileOutputStream(f), "UTF8"); + out.write(""); + out.flush(); + out.close(); + Document doc = builder.build(f); + String expectedResult = "\n" + + "\n"; + String actual = doc.toXML(); + assertEquals(expectedResult, actual); + } + finally { + if (f.exists()) f.delete(); + } + + } + + + private File makeFile(String name) throws IOException { + + File f = new File(inputDir, "" + name); + Writer out = new OutputStreamWriter( + new FileOutputStream(f), "UTF8"); + out.write(""); + out.flush(); + out.close(); + return f; + + } + + + public void testBuildFromFileThatContainsAmpersandInName() + throws ParsingException, IOException { + + Document doc = builder.build(new File(inputDir, "&file.xml")); + String expectedResult = "\n" + + "\n"; + String actual = doc.toXML(); + assertEquals(expectedResult, actual); + assertTrue(doc.getBaseURI().startsWith("file:/")); + assertTrue(doc.getBaseURI().endsWith("data/&file.xml")); + + } + + + public void testBuildFromFileThatContainsSpaceInName() + throws ParsingException, IOException { + + File f = makeFile("space file.xml"); + Document doc = builder.build(f); + String expectedResult = "\n" + + "\n"; + String actual = doc.toXML(); + f.delete(); + assertEquals(expectedResult, actual); + assertTrue(doc.getBaseURI().startsWith("file:/")); + assertTrue(doc.getBaseURI().endsWith("data/space%" + + Integer.toHexString(' ') + "file.xml")); + + } + + + public void testBuildFromFileThatContainsSharpInName() + throws ParsingException, IOException { + + File f = new File(inputDir, "#file.xml"); + try { + Writer out = new OutputStreamWriter( + new FileOutputStream(f), "UTF8"); + out.write(""); + out.flush(); + out.close(); + Document doc = builder.build(f); + String expectedResult = "\n" + + "\n"; + String actual = doc.toXML(); + assertEquals(expectedResult, actual); + assertTrue(doc.getBaseURI().startsWith("file:/")); + assertTrue(doc.getBaseURI().endsWith("data/%23file.xml")); + } + finally { + if (f.exists()) f.delete(); + } + + } + + + public void testBuildFromFileThatContainsExclamationPointInName() + throws ParsingException, IOException { + + Document doc = builder.build(new File(inputDir, "!file.xml")); + String expectedResult = "\n" + + "\n"; + String actual = doc.toXML(); + assertEquals(expectedResult, actual); + assertTrue(doc.getBaseURI().startsWith("file:/")); + assertTrue(doc.getBaseURI().endsWith("data/!file.xml")); + + } + + + public void testBuildFromFileThatContainsDoubleQuoteInName() + throws ParsingException, IOException { + + File f = makeFile("\"file\".xml"); + try { + Document doc = builder.build(f); + f.delete(); + String expectedResult = "\n\n"; + String actual = doc.toXML(); + assertEquals(expectedResult, actual); + assertTrue(doc.getBaseURI().startsWith("file:/")); + assertTrue(doc.getBaseURI().endsWith("data/%22file%22.xml")); + } + catch (FileNotFoundException ex) { + // This platform doesn't allow double quotes in file names + } + + } + + + public void testBuildFromFileThatContainsSingleQuoteInName() + throws ParsingException, IOException { + + File f = makeFile("'file'.xml"); + Document doc = builder.build(f); + f.delete(); + String expectedResult = "\n" + + "\n"; + String actual = doc.toXML(); + assertEquals(expectedResult, actual); + assertTrue(doc.getBaseURI().startsWith("file:/")); + assertTrue(doc.getBaseURI().endsWith("data/'file'.xml")); + + } + + + public void testBuildFromFileThatContainsParenthesesInName() + throws ParsingException, IOException { + + Document doc = builder.build(new File(inputDir, "()file.xml")); + String expectedResult = "\n" + + "\n"; + String actual = doc.toXML(); + assertEquals(expectedResult, actual); + assertTrue(doc.getBaseURI().startsWith("file:/")); + assertTrue(doc.getBaseURI().endsWith("data/()file.xml")); + + } + + + public void testBuildFromFileThatContainsCurlyBracesInName() + throws ParsingException, IOException { + + Document doc = builder.build(new File(inputDir, "{file}.xml")); + String expectedResult = "\n" + + "\n"; + String actual = doc.toXML(); + assertEquals(expectedResult, actual); + assertTrue(doc.getBaseURI().startsWith("file:/")); + assertTrue(doc.getBaseURI().endsWith("data/%" + + Integer.toHexString('{').toUpperCase() + "file%" + + Integer.toHexString('}').toUpperCase() + ".xml")); + + } + + + public void testBuildFromFileThatContainsSquareBracketsInName() + throws ParsingException, IOException { + + Document doc = builder.build(new File(inputDir, "[file].xml")); + String expectedResult = "\n" + + "\n"; + String actual = doc.toXML(); + assertEquals(expectedResult, actual); + assertTrue(doc.getBaseURI().startsWith("file:/")); + assertTrue(doc.getBaseURI().endsWith("data/%" + + Integer.toHexString('[').toUpperCase() + "file%" + + Integer.toHexString(']').toUpperCase() + ".xml")); + + } + + + public void testBuildFromFileThatContainsVerticalBarInName() + throws ParsingException, IOException { + + File f = makeFile("|file.xml"); + try { + Document doc = builder.build(f); + f.delete(); + String expectedResult = "\n" + + "\n"; + String actual = doc.toXML(); + assertEquals(expectedResult, actual); + assertTrue(doc.getBaseURI().startsWith("file:/")); + assertTrue(doc.getBaseURI().endsWith("data/%" + + Integer.toHexString('|').toUpperCase() + + "file.xml")); + } + catch (FileNotFoundException ex) { + // This platform doesn't allow vertical bars in file names + } + + } + + + public void testBuildFromFileThatContainsColonInName() + throws ParsingException, IOException { + + File f = makeFile(":file.xml"); + try { + Document doc = builder.build(f); + f.delete(); + String expectedResult = "\n" + + "\n"; + String actual = doc.toXML(); + assertEquals(expectedResult, actual); + assertTrue(doc.getBaseURI().startsWith("file:/")); + assertTrue(doc.getBaseURI().endsWith("data/:file.xml")); + } + catch (FileNotFoundException ex) { + // This platform doesn't allow colons in file names + } + + } + + + public void testBuildFromFileThatContainsUnderscoreInName() + throws ParsingException, IOException { + + File f = makeFile("_file.xml"); + Document doc = builder.build(f); + f.delete(); + String expectedResult = "\n" + + "\n"; + String actual = doc.toXML(); + assertEquals(expectedResult, actual); + assertTrue(doc.getBaseURI().startsWith("file:/")); + assertTrue(doc.getBaseURI().endsWith("data/_file.xml")); + + } + + + public void testBuildFromFileThatContainsUppercaseASCIIInName() + throws ParsingException, IOException { + + File f = makeFile("ABCDEFGHIJKLMONPQRSTUVWXYZ.xml"); + Document doc = builder.build(f); + f.delete(); + String expectedResult = "\n" + + "\n"; + String actual = doc.toXML(); + assertEquals(expectedResult, actual); + assertTrue(doc.getBaseURI().startsWith("file:/")); + assertTrue(doc.getBaseURI().endsWith("data/ABCDEFGHIJKLMONPQRSTUVWXYZ.xml")); + + } + + + public void testBuildFromFileThatContainsAsteriskInName() + throws ParsingException, IOException { + + File f = makeFile("*file.xml"); + try { + Document doc = builder.build(f); + f.delete(); + String expectedResult = "\n" + + "\n"; + String actual = doc.toXML(); + assertEquals(expectedResult, actual); + assertTrue(doc.getBaseURI().startsWith("file:/")); + assertTrue(doc.getBaseURI().endsWith("data/*file.xml")); + } + catch (FileNotFoundException ex) { + // This platform doesn't allow asterisks in file names + } + + } + + + public void testBuildFromFileThatContainsSemicolonInName() + throws ParsingException, IOException { + + Document doc = builder.build(new File(inputDir, ";file.xml")); + String expectedResult = "\n" + + "\n"; + String actual = doc.toXML(); + assertEquals(expectedResult, actual); + assertTrue(doc.getBaseURI().startsWith("file:/")); + assertTrue(doc.getBaseURI().endsWith("data/;file.xml")); + + } + + + public void testBuildFromFileThatContainsPlusSignInName() + throws ParsingException, IOException { + + Document doc = builder.build(new File(inputDir, "+file.xml")); + String expectedResult = "\n" + + "\n"; + String actual = doc.toXML(); + assertEquals(expectedResult, actual); + assertTrue(doc.getBaseURI().startsWith("file:/")); + assertTrue(doc.getBaseURI().endsWith("data/%" + + Integer.toHexString('+').toUpperCase() + "file.xml")); + + } + + + public void testBuildFromFileThatContainsCommaInName() + throws ParsingException, IOException { + + File f = new File(inputDir, ",file.xml"); + try { + Writer out = new OutputStreamWriter( + new FileOutputStream(f), "UTF8"); + out.write(""); + out.flush(); + out.close(); + Document doc = builder.build(f); + String expectedResult = "\n" + + "\n"; + String actual = doc.toXML(); + assertEquals(expectedResult, actual); + assertTrue(doc.getBaseURI().startsWith("file:/")); + assertTrue(doc.getBaseURI().endsWith("data/,file.xml")); + } + finally { + if (f.exists()) f.delete(); + } + + } + + + public void testBuildFromFileThatContainsBackslashInName() + throws ParsingException, IOException { + + String os = System.getProperty("os.name", "Unix"); + if (os.indexOf("Windows") >= 0) return; + + File f = new File(inputDir, "\\file.xml"); + try { + Writer out = new OutputStreamWriter( + new FileOutputStream(f), "UTF8"); + out.write(""); + out.flush(); + out.close(); + Document doc = builder.build(f); + String expectedResult = "\n" + + "\n"; + String actual = doc.toXML(); + assertEquals(expectedResult, actual); + assertTrue(doc.getBaseURI().startsWith("file:/")); + assertTrue(doc.getBaseURI().endsWith("data/%5Cfile.xml")); + } + finally { + if (f.exists()) f.delete(); + } + + } + + + public void testBuildFromFileThatContainsC0ControlCharacterInName() + throws ParsingException, IOException { + + File f = new File(inputDir, "\u0019file.xml"); + try { + Writer out = new OutputStreamWriter( + new FileOutputStream(f), "UTF8"); + out.write(""); + out.flush(); + out.close(); + Document doc = builder.build(f); + String expectedResult = "\n" + + "\n"; + String actual = doc.toXML(); + assertEquals(expectedResult, actual); + assertTrue(doc.getBaseURI().startsWith("file:/")); + assertTrue(doc.getBaseURI().endsWith("data/%19file.xml")); + } + catch (FileNotFoundException ex) { + // This platform doesn't allow C0 controls in file names + } + finally { + if (f.exists()) f.delete(); + } + + } + + + public void testBuildFromFileThatContainsTabCharacterInName() + throws ParsingException, IOException { + + File f = new File(inputDir, "\tfile.xml"); + try { + Writer out = new OutputStreamWriter( + new FileOutputStream(f), "UTF8"); + out.write(""); + out.flush(); + out.close(); + Document doc = builder.build(f); + String expectedResult = "\n" + + "\n"; + String actual = doc.toXML(); + assertEquals(expectedResult, actual); + assertTrue(doc.getBaseURI().startsWith("file:/")); + assertTrue(doc.getBaseURI().endsWith("data/%09file.xml")); + } + catch (FileNotFoundException ex) { + // This platform doesn't allow tabs in file names + } + finally { + if (f.exists()) f.delete(); + } + + } + + + public void testBuildFromFileThatContainsTildeInName() + throws ParsingException, IOException { + + File f = new File(inputDir, "~file.xml"); + try { + Writer out = new OutputStreamWriter( + new FileOutputStream(f), "UTF8"); + out.write(""); + out.flush(); + out.close(); + Document doc = builder.build(f); + String expectedResult = "\n" + + "\n"; + String actual = doc.toXML(); + assertEquals(expectedResult, actual); + assertTrue(doc.getBaseURI().startsWith("file:/")); + assertTrue(doc.getBaseURI().endsWith("data/~file.xml")); + } + finally { + if (f.exists()) f.delete(); + } + + } + + + public void testBuildFromFileThatContainsAngleBracketsInName() + throws ParsingException, IOException { + + File f = makeFile(".xml"); + try { + Document doc = builder.build(f); + f.delete(); + String expectedResult = "\n" + + "\n"; + String actual = doc.toXML(); + assertEquals(expectedResult, actual); + assertTrue(doc.getBaseURI().startsWith("file:/")); + assertTrue(doc.getBaseURI().endsWith("data/%" + + Integer.toHexString('<').toUpperCase() + "file%" + + Integer.toHexString('>').toUpperCase() + ".xml")); + } + catch (FileNotFoundException ex) { + // This platform doesn't allow < and > in file names + } + + } + + + public void testBuildFromFileThatContainsDollarSignInName() + throws ParsingException, IOException { + + Document doc = builder.build(new File(inputDir, "$file.xml")); + String expectedResult = "\n" + + "\n"; + String actual = doc.toXML(); + assertEquals(expectedResult, actual); + assertTrue(doc.getBaseURI().startsWith("file:/")); + assertTrue(doc.getBaseURI().endsWith("data/$file.xml")); + + } + + + public void testBuildFromFileThatContainsPercentSignInName() + throws ParsingException, IOException { + + Document doc = builder.build(new File(inputDir, "%file.xml")); + String expectedResult = "\n" + + "\n"; + String actual = doc.toXML(); + assertEquals(expectedResult, actual); + assertTrue(doc.getBaseURI().startsWith("file:/")); + assertTrue(doc.getBaseURI().endsWith("data/%" + + Integer.toHexString('%') + "file.xml")); + + } + + + public void testBuildFromFileThatContainsQuestionMarkInName() + throws ParsingException, IOException { + + File f = makeFile("?file.xml"); + try { + Document doc = builder.build(f); + f.delete(); + String expectedResult = "\n" + + "\n"; + String actual = doc.toXML(); + assertEquals(expectedResult, actual); + assertTrue(doc.getBaseURI().startsWith("file:/")); + assertTrue(doc.getBaseURI().endsWith("data/%" + + Integer.toHexString('?').toUpperCase() + "file.xml")); + } + catch (FileNotFoundException ex) { + // This platform doesn't allow question marks in file names + } + + } + + + public void testBuildFromFileThatContainsAtSignInName() + throws ParsingException, IOException { + + Document doc = builder.build(new File(inputDir, "@file.xml")); + String expectedResult = "\n" + + "\n"; + String actual = doc.toXML(); + assertEquals(expectedResult, actual); + assertTrue(doc.getBaseURI().startsWith("file:/")); + assertTrue(doc.getBaseURI().endsWith("data/%" + + Integer.toHexString('@') + "file.xml")); + + } + + + public void testBuildFromFileThatContainsEqualsSignInName() + throws ParsingException, IOException { + + Document doc = builder.build(new File(inputDir, "=file.xml")); + String expectedResult = "\n" + + "\n"; + String actual = doc.toXML(); + assertEquals(expectedResult, actual); + assertTrue(doc.getBaseURI().startsWith("file:/")); + assertTrue(doc.getBaseURI().endsWith("data/=file.xml")); + + } + + + public void testBuildFromFileThatContainsCaretInName() + throws ParsingException, IOException { + + Document doc = builder.build(new File(inputDir, "^file.xml")); + String expectedResult = "\n" + + "\n"; + String actual = doc.toXML(); + assertEquals(expectedResult, actual); + assertTrue(doc.getBaseURI().startsWith("file:/")); + assertTrue(doc.getBaseURI().endsWith("data/%" + + Integer.toHexString('^').toUpperCase() + "file.xml")); + + } + + + public void testBuildFromFileThatContainsBactickInName() + throws ParsingException, IOException { + + Document doc = builder.build(new File(inputDir, "`file.xml")); + String expectedResult = "\n" + + "\n"; + String actual = doc.toXML(); + assertEquals(expectedResult, actual); + assertTrue(doc.getBaseURI().startsWith("file:/")); + assertTrue(doc.getBaseURI().endsWith("data/%" + + Integer.toHexString('`') + "file.xml")); + + } + + + private static class NonValidatingFilter extends XMLFilterImpl { + + public void setFeature(String uri, boolean value) + throws SAXNotRecognizedException, SAXNotSupportedException { + + if ("http://xml.org/sax/features/validation".equals(uri) && value) { + throw new SAXNotSupportedException(""); + } + super.setFeature(uri, value); + + } + + public boolean getFeature(String uri) + throws SAXNotRecognizedException, SAXNotSupportedException { + + if ("http://xml.org/sax/features/validation".equals(uri)) { + return false; + } + return super.getFeature(uri); + + } + + + } + + + public void testNonValidatingParserException() throws SAXException { + + XMLReader parser = XMLReaderFactory.createXMLReader("org.apache.xerces.parsers.SAXParser"); + XMLFilter filter = new NonValidatingFilter(); + filter.setParent(parser); + + try { + new Builder(filter, true, null); + fail("Validating with a non-validating parser"); + } + catch (XMLException success) { + assertNotNull(success.getMessage()); + } + + } + + private static class NonEntityResolvingFilter extends XMLFilterImpl { + + public void setFeature(String uri, boolean value) + throws SAXNotRecognizedException, SAXNotSupportedException { + + if (value && ( + "http://xml.org/sax/features/validation".equals(uri) + || "http://xml.org/sax/features/external-general-entities".equals(uri)) + || "http://xml.org/sax/features/external-parameter-entities".equals(uri)) { + throw new SAXNotSupportedException(""); + } + super.setFeature(uri, value); + + } + + public boolean getFeature(String uri) + throws SAXNotRecognizedException, SAXNotSupportedException { + + if ("http://xml.org/sax/features/validation".equals(uri) + || "http://xml.org/sax/features/external-general-entities".equals(uri) + || "http://xml.org/sax/features/external-parameter-entities".equals(uri)) { + return false; + } + return super.getFeature(uri); + + } + + + } + + + public void testNonEntityResolvingParserException() throws SAXException { + + XMLReader parser = XMLReaderFactory.createXMLReader( + "org.apache.xerces.parsers.SAXParser"); + XMLFilter filter = new NonEntityResolvingFilter(); + filter.setParent(parser); + + try { + new Builder(filter, false, null); + fail("Accepted a non-entity resolving parser"); + } + catch (XMLException success) { + assertNotNull(success.getMessage()); + } + + } + + + // Fake certain errors to test workarounds for bugs in certain + // parsers, especially Piccolo. + private static class ExceptionTester extends XMLFilterImpl { + + private Exception ex; + + ExceptionTester(Exception ex) { + this.ex = ex; + } + + public void parse(InputSource in) throws IOException, SAXException { + if (ex instanceof IOException) throw (IOException) ex; + else if (ex instanceof SAXException) throw (SAXException) ex; + else throw (RuntimeException) ex; + } + + } + + + public void testParserThrowsNullPointerException() + throws SAXException, IOException { + + XMLReader parser = XMLReaderFactory.createXMLReader( + "org.apache.xerces.parsers.SAXParser"); + Exception cause = new NullPointerException(); + XMLFilter filter = new ExceptionTester(cause); + filter.setParent(parser); + Builder builder = new Builder(filter); + + try { + builder.build(""); + } + catch (ParsingException success) { + assertEquals(cause, success.getCause()); + } + + } + + + public void testParserThrowsNegativeArraySizeException() + throws SAXException, IOException { + + XMLReader parser = XMLReaderFactory.createXMLReader( + "org.apache.xerces.parsers.SAXParser"); + Exception cause = new NegativeArraySizeException(); + XMLFilter filter = new ExceptionTester(cause); + filter.setParent(parser); + Builder builder = new Builder(filter); + + try { + builder.build(""); + } + catch (ParsingException success) { + assertEquals(cause, success.getCause()); + } + + } + + + public void testParserThrowsArrayIndexOutOfBoundsException() + throws SAXException, IOException { + + XMLReader parser = XMLReaderFactory.createXMLReader( + "org.apache.xerces.parsers.SAXParser"); + Exception cause = new ArrayIndexOutOfBoundsException(); + XMLFilter filter = new ExceptionTester(cause); + filter.setParent(parser); + Builder builder = new Builder(filter); + + try { + builder.build(""); + } + catch (ParsingException success) { + assertEquals(cause, success.getCause()); + } + + } + + + public void testParserThrowsUTFDataFormatException() + throws SAXException, IOException { + + XMLReader parser = XMLReaderFactory.createXMLReader( + "org.apache.xerces.parsers.SAXParser"); + Exception cause = new UTFDataFormatException(); + XMLFilter filter = new ExceptionTester(cause); + filter.setParent(parser); + Builder builder = new Builder(filter); + + try { + builder.build(""); + } + catch (ParsingException success) { + assertEquals(cause, success.getCause()); + } + + } + + + public void testCantTurnOffNamespaceHandling() + throws SAXException, IOException, ParsingException { + + XMLReader parser = XMLReaderFactory.createXMLReader( + "org.apache.xerces.parsers.SAXParser"); + parser.setFeature("http://xml.org/sax/features/namespaces", false); + Builder builder = new Builder(parser); + Document doc = builder.build("", null); + assertEquals("http://www.example.org", doc.getRootElement().getNamespaceURI()); + + } + + + public void testParserThrowsCharConversionException() + throws SAXException, IOException { + + XMLReader parser = XMLReaderFactory.createXMLReader( + "org.apache.xerces.parsers.SAXParser"); + Exception cause = new CharConversionException(); + XMLFilter filter = new ExceptionTester(cause); + filter.setParent(parser); + Builder builder = new Builder(filter); + + try { + builder.build(""); + } + catch (ParsingException success) { + assertEquals(cause, success.getCause()); + } + + } + + + public void testParserThrowsPlainSAXException() + throws SAXException, IOException { + + XMLReader parser = XMLReaderFactory.createXMLReader( + "org.apache.xerces.parsers.SAXParser"); + Exception cause = new SAXException("What happened to no-args constructor?"); + XMLFilter filter = new ExceptionTester(cause); + filter.setParent(parser); + Builder builder = new Builder(filter); + + try { + builder.build(""); + } + catch (ParsingException success) { + assertEquals(cause, success.getCause()); + } + + } + + + public void testParserThrowsUnexpectedRuntimeException() + throws SAXException, IOException { + + XMLReader parser = XMLReaderFactory.createXMLReader( + "org.apache.xerces.parsers.SAXParser"); + Exception cause = new RuntimeException(); + XMLFilter filter = new ExceptionTester(cause); + filter.setParent(parser); + Builder builder = new Builder(filter); + + try { + builder.build(""); + } + catch (ParsingException success) { + assertEquals(cause, success.getCause()); + } + + } + + + public void testParserThrowsIOException() + throws SAXException, ParsingException { + + XMLReader parser = XMLReaderFactory.createXMLReader( + "org.apache.xerces.parsers.SAXParser"); + Exception cause = new IOException(); + XMLFilter filter = new ExceptionTester(cause); + filter.setParent(parser); + Builder builder = new Builder(filter); + + try { + builder.build(""); + } + catch (IOException success) { + assertEquals(cause, success); + } + + } + + + public void testCrimsonIgnoresWarning() + throws SAXException, ParsingException, IOException { + + + XMLReader parser; + try { + parser = XMLReaderFactory.createXMLReader( + "org.apache.crimson.parser.XMLReaderImpl" + ); + } + catch (SAXException ex) { + // Can't test Crimson if you can't load it + return; + } + XMLFilter filter = new WarningFilter(); + filter.setParent(parser); + Builder builder = new Builder(filter); + + Document doc = builder.build("", null); + assertEquals("\n\n", doc.toXML()); + + } + + + private static class WarningFilter extends XMLFilterImpl { + + public void startElement(String namespaceURI, String localName, + String qualifiedName, Attributes atts) throws SAXException { + + this.getErrorHandler().warning( + new SAXParseException("Warning", new LocatorImpl()) + ); + super.startElement(namespaceURI, localName, qualifiedName, + atts); + + } + + } + + + public void testSaxonsAElfredIsVerified() + throws SAXException, IOException { + + XMLReader parser; + try { + parser = XMLReaderFactory.createXMLReader( + "com.icl.saxon.aelfred.SAXDriver" + ); + } + catch (SAXException ex) { + // Can't test SAXON if you can't load it + return; + } + Builder builder = new Builder(parser); + + try { + // known bug in Saxon; doesn't catch + // colon in processing instruction targets + builder.build("", null); + fail("Didn't verify Saxon's input"); + } + catch (ParsingException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testSaxon7sAElfredIsVerified() + throws SAXException, IOException { + + XMLReader parser; + try { + parser = XMLReaderFactory.createXMLReader( + "net.sf.saxon.aelfred.SAXDriver" + ); + } + catch (SAXException ex) { + // Can't test SAXON if you can't load it + return; + } + Builder builder = new Builder(parser); + + try { + // known bug in Saxon: doesn't catch + // colon in processing instruction targets + builder.build("", null); + fail("Didn't verify Saxon's input"); + } + catch (ParsingException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testGNUJAXPIsVerified() + throws SAXException, IOException { + + XMLReader parser; + try { + parser = XMLReaderFactory.createXMLReader( + "gnu.xml.aelfred2.XmlReader" + ); + } + catch (SAXException ex) { + // Can't test GNU JAXP if you can't load it + return; + } + Builder builder = new Builder(parser); + + try { + // known bug in GNUJAXP: doesn't catch + // colon in processing instruction targets + builder.build("", null); + fail("Didn't verify GNU JAXP's input"); + } + catch (ParsingException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testCatalogOnTopOfTrustedParserIsTrusted() throws + NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException { + + try { + XMLReader parser = XMLReaderFactory.createXMLReader( + "org.apache.crimson.parser.XMLReaderImpl" + ); + + Class filter = Class.forName("org.apache.xml.resolver.tools.ResolvingXMLFilter"); + Class[] types = {XMLReader.class}; + Constructor constructor = filter.getConstructor(types); + Object[] args = {parser}; + XMLReader reader = (XMLReader) constructor.newInstance(args); + Builder builder = new Builder(reader); + // If the factory is a nonverifying factory, then + // getNodeFactory() won't return it. + assertNull(builder.getNodeFactory()); + } + catch (ClassNotFoundException ex) { + // Can't test if we can't find the class + } + catch (SAXException ex) { + // Need a trusted parser to test this + } + + } + + + // XML conformance test case xmlconf/xmltest/valid/not-sa/014.ent + // shows how this can be necessary. In brief, the internal DTD + // subset can define or override parameter entities used in the + // external DTD subset, and that the external DTD subset depends + // on for well-formedness + public void testPreserveParameterEntitiesInInternalDTDSubset() + throws ParsingException, IOException { + + String data = "]>"; + Document doc = builder.build(data, null); + String subset = doc.getDocType().getInternalDTDSubset(); + assertEquals(" \n", subset); + + } + + + public void testTrickyCaseFromAppendixA2OfXMLSpec() + throws ParsingException, IOException { + + String data = "\n" + + "\n" + + "\n" + + "' >\n" + + "%xx;\n" + + "]>\n" + + "This sample shows a &tricky; method.\n"; + + Document doc = builder.build(data, null); + String s = doc.toXML(); + Document roundTrip = builder.build(s, null); + assertEquals(doc, roundTrip); + + } + + + // This is an example of case where preserving external entity + // declaration in internal DTD subset is necessary to maintain + // well-formedness + public void testPreserveExternalGeneralEntityDeclaration() + throws ParsingException, IOException { + + Document doc = builder.build(new File(inputDir, "ge.xml")); + DocType doctype = doc.getDocType(); + assertEquals(" \n", doctype.getInternalDTDSubset()); + } + + + // This is an example of case where preserving external entity + // declaration in internal DTD subset is necessary to maintain + // validity + public void testPreserveExternalParameterEntityDeclaration() + throws ParsingException, IOException { + + Document doc = builder.build(new File(inputDir, "pe.xml")); + DocType doctype = doc.getDocType(); + assertEquals(" \n", doctype.getInternalDTDSubset()); + } + + + public void testNMTOKENSNormalizationOfCarriageReturnLineFeedEntityReferences() + throws ParsingException, IOException { + + String data = "]>\n" + + ""; + + Document doc = builder.build(data, null); + String s = doc.toXML(); + Document roundTrip = builder.build(s, null); + assertEquals(doc, roundTrip); + + } + + + public void testXMLConformanceTestSuiteDocuments() + throws ParsingException, IOException { + + File data = new File("data"); + File canonical = new File(data, "canonical"); + File masterList = new File(canonical, "xmlconf"); + masterList = new File(masterList, "xmlconf.xml"); + if (masterList.exists()) { + Document xmlconf = builder.build(masterList); + Elements testcases = xmlconf.getRootElement().getChildElements("TESTCASES"); + processTestCases(testcases); + } + + } + + + // xmlconf/xmltest/valid/sa/097.xml appears to be screwed up by a lot + // of parsers + private void processTestCases(Elements testcases) + throws ParsingException, IOException { + + for (int i = 0; i < testcases.size(); i++) { + Element testcase = testcases.get(i); + Elements tests = testcase.getChildElements("TEST"); + processTests(tests); + Elements level2 = testcase.getChildElements("TESTCASES"); + // need to be recursive to handle recursive IBM test cases + processTestCases(level2); + } + + } + + + private void processTests(Elements tests) + throws ParsingException, IOException { + + Element parent = new Element("e"); + Element child = new Element("a"); + parent.appendChild(child); + + int size = tests.size(); + for (int i = 0; i < size; i++) { + Element test = tests.get(i); + String namespace = test.getAttributeValue("NAMESPACE"); + if ("no".equals(namespace)) continue; + String type = test.getAttributeValue("TYPE"); + if ("not-wf".equals(type)) continue; + String uri = test.getAttributeValue("URI"); + String base = test.getBaseURI(); + // Hack because URIUtil isn't public; and I don't want to + // depend on 1.4 only java.net.URI + parent.setBaseURI(base); + child.addAttribute(new Attribute("xml:base", + "http://www.w3.org/XML/1998/namespace", uri)); + String resolvedURI = child.getBaseURI(); + + Document doc = builder.build(resolvedURI); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + try { + Serializer serializer = new Serializer(out); + serializer.write(doc); + } + finally { + out.close(); + } + byte[] actual = out.toByteArray(); + + InputStream in = new ByteArrayInputStream(actual); + try { + Document roundTrip = builder.build(in, resolvedURI); + assertEquals("Failed to roundtrip " + uri, doc, roundTrip); + } + catch (ParsingException ex) { + System.out.println(ex.getURI()); + System.out.println(doc.toXML()); + throw ex; + } + finally { + in.close(); + } + } + + } + + public void testLocatorReturnsNullSystemIDWithoutRelativeURL() + throws ParsingException, IOException { + + XMLReader reader = new SAXParser(); + reader = new LocatorFilter(reader); + Builder builder = new Builder(reader); + InputStream in = new FileInputStream("data/nonquirky.xhtml"); + builder.build(in); + + } + + public void testLocatorReturnsNullSystemIDWithRelativeURL() + throws ParsingException, IOException { + + XMLReader reader = new SAXParser(); + reader = new LocatorFilter(reader); + Builder builder = new Builder(reader); + InputStream in = new FileInputStream("data/quirks.xhtml"); + try { + builder.build(in); + fail("How did it find the DTD"); + } + catch (IOException success) { + assertNotNull(success.getMessage()); + // Here we expect that because there's no Locator + // there's no base URL. As a result, the DTD is looked for in the + // current working directory, which is the wrong place. + // Therefore this IOException is thrown. + } + + } + + public void testChemistry() throws ValidityException, ParsingException, IOException { + String doc = ""; + StringReader s = new StringReader(doc); + builder.build(s); + } + + public void testMisbehavingParserInternalDTDSubset() throws IOException { + XMLReader reader = new SAXParser(); + reader = new MalformedDTDFilter(reader); + Builder builder = new Builder(reader); + try { + builder.build(validDoc, "http://www.example.com"); + fail("didn't catch malformed internal DTD subset"); + } + catch (ParsingException ex) { + assertNotNull(ex.getMessage()); + } + } + + private static class MalformedDTDFilter extends XMLFilterImpl implements + XMLReader { + + public MalformedDTDFilter(XMLReader reader) { + super.setParent(reader); + } + + public void setProperty(String name, Object value) throws SAXNotRecognizedException, SAXNotSupportedException { + if (name.equals("http://xml.org/sax/properties/declaration-handler")) { + value = new BadDeclHandler((DeclHandler) value); + } + super.setProperty(name, value); + } + + } + + private static class BadDeclHandler implements DeclHandler { + + private DeclHandler handler; + + public BadDeclHandler(DeclHandler handler) { + this.handler = handler; + } + + public void attributeDecl(String arg0, String arg1, String arg2, + String arg3, String arg4) throws SAXException { + handler.attributeDecl(arg0, arg1, arg2, arg3, arg4); + } + + // pass some malformed data here + public void elementDecl(String name, String model) throws SAXException { + handler.elementDecl("***()*" + name, model); + } + + public void externalEntityDecl(String arg0, String arg1, String arg2) + throws SAXException { + handler.externalEntityDecl(arg0, arg1, arg2); + } + + public void internalEntityDecl(String arg0, String arg1) + throws SAXException { + handler.internalEntityDecl(arg0, arg1); + } + + } + +} diff --git a/src/nu/xom/tests/CDATASectionTest.java b/src/nu/xom/tests/CDATASectionTest.java new file mode 100644 index 0000000..4e6a735 --- /dev/null +++ b/src/nu/xom/tests/CDATASectionTest.java @@ -0,0 +1,213 @@ +/* Copyright 2002-2005 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.tests; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +import nu.xom.*; + +/** + *

    + * Test that CDATA sections are read and where possible + * preserved upon serialization. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.3.0 + * + */ +public class CDATASectionTest extends XOMTestCase { + + + public CDATASectionTest(String name) { + super(name); + } + + + private String data = "]]>" + + " ]]> " + + "]]> " + + "]]> ]]> " + + "]]>]]>]]> " + + ""; + private Document doc; + private Builder builder; + + + protected void setUp() + throws ValidityException, ParsingException, IOException { + builder = new Builder(); + doc = builder.build(data, "http://www.base.com"); + } + + + public void testCopy() { + Element child1 = doc.getRootElement().getFirstChildElement("child1"); + Text cdata = (Text) child1.getChild(0); + Text copy = cdata.copy(); + assertEquals("nu.xom.CDATASection", copy.getClass().getName()); + assertEquals("<&>", copy.getValue()); + } + + + public void testToXML() { + Element child1 = doc.getRootElement().getFirstChildElement("child1"); + Node cdata = child1.getChild(0); + String result = cdata.toXML(); + assertEquals("]]>", result); + } + + + public void testToXMLWhenCDATASectionContainsEndDelimiter() { + Element child1 = doc.getRootElement().getFirstChildElement("child1"); + Text cdata = (Text) child1.getChild(0); + cdata.setValue("A]]>A"); + assertEquals("A]]>A", cdata.toXML()); + } + + + public void testUseCDATAWherePossible() { + Element child1 = doc.getRootElement().getFirstChildElement("child1"); + Node cdata = child1.getChild(0); + assertTrue(cdata instanceof Text); + assertEquals("nu.xom.CDATASection", cdata.getClass().getName()); + assertEquals("<&>", cdata.getValue()); + } + + + public void testDontAllowCDATASectionToSplitTextNode() { + Element child2 = doc.getRootElement().getFirstChildElement("child2"); + assertEquals(1, child2.getChildCount()); + Node data = child2.getChild(0); + assertTrue(data instanceof Text); + assertEquals("nu.xom.Text", data.getClass().getName()); + assertEquals(" <&> ", data.getValue()); + } + + + public void testAccumulateTextNodeAfterCDATASection() { + Element child3 = doc.getRootElement().getFirstChildElement("child3"); + assertEquals(1, child3.getChildCount()); + Node data = child3.getChild(0); + assertTrue(data instanceof Text); + assertEquals("nu.xom.Text", data.getClass().getName()); + assertEquals("<&> ", data.getValue()); + } + + + public void testAccumulateTextNodeAcrossMultipleCDATASections() { + Element child4 = doc.getRootElement().getFirstChildElement("child4"); + assertEquals(1, child4.getChildCount()); + Node data = child4.getChild(0); + assertTrue(data instanceof Text); + assertEquals("nu.xom.Text", data.getClass().getName()); + assertEquals("<&> <&>", data.getValue()); + } + + + public void testDontAllowCDATASectionToContainCDATASectionEndDelimiter() { + Element child5 = doc.getRootElement().getFirstChildElement("child5"); + assertEquals(1, child5.getChildCount()); + Node data = child5.getChild(0); + assertTrue(data instanceof Text); + assertEquals("<&>]]><&>", data.getValue()); + assertEquals("<&>]]><&>", data.toXML()); + } + + + public void testDontAllowCDATASectionToContainCDATASectionEndDelimiter2() + throws IOException { + Element child5 = doc.getRootElement().getFirstChildElement("child5"); + assertEquals(1, child5.getChildCount()); + child5.detach(); + Document doc = new Document(child5); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + Serializer serializer = new Serializer(out); + serializer.write(doc); + serializer.flush(); + String result = new String(out.toByteArray(), "UTF-8"); + assertEquals("\r\n" + + "<&>]]><&>\r\n", result); + } + + + public void testSerializeCDATASection() throws IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + Serializer serializer = new Serializer(out); + serializer.write(doc); + byte[] data = out.toByteArray(); + String result = new String(data, "UTF8"); + assertTrue(result.indexOf("]]>") > 0); + + } + + + public void testSerializeCDATASectionWithOutOfRangeCharacter() + throws ValidityException, ParsingException, IOException { + + String data = ""; + doc = builder.build(data, "http://www.example.com"); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + Serializer serializer = new Serializer(out, "ISO-8859-1"); + serializer.write(doc); + byte[] output = out.toByteArray(); + String result = new String(output, "8859_1"); + assertEquals(-1, result.indexOf("]]>")); + assertTrue(result.indexOf("ʘ") > 1); + + } + + + public void testSerializeCDATASectionWithInRangeCharactersAndANonUnicodeEncoding() + throws ValidityException, ParsingException, IOException { + + String data = ""; + doc = builder.build(data, "http://www.example.com"); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + Serializer serializer = new Serializer(out, "ISO-8859-1"); + serializer.write(doc); + byte[] output = out.toByteArray(); + String result = new String(output, "8859_1"); + assertTrue(result.indexOf("") > 1); + + } + + + public void testSerializeCDATASectionWithCDATASectionEndDelimiter() + throws ValidityException, ParsingException, IOException { + + String data = ""; + doc = builder.build(data, "http://www.example.com"); + Text content = (Text) (doc.getRootElement().getChild(0)); + content.setValue("]]>"); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + Serializer serializer = new Serializer(out); + serializer.write(doc); + byte[] output = out.toByteArray(); + String result = new String(output, "UTF8"); + assertEquals(-1, result.indexOf("]]>")); + assertTrue(result.indexOf("]]>") > 1); + } + + +} \ No newline at end of file diff --git a/src/nu/xom/tests/CanonicalizationExceptionTest.java b/src/nu/xom/tests/CanonicalizationExceptionTest.java new file mode 100644 index 0000000..8a30957 --- /dev/null +++ b/src/nu/xom/tests/CanonicalizationExceptionTest.java @@ -0,0 +1,128 @@ +/* Copyright 2003-2005 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.tests; + +import nu.xom.canonical.CanonicalizationException; + +/** + *

    + * Unit tests for the CanonicalizationException class. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.1d7 + * + */ +public class CanonicalizationExceptionTest extends XOMTestCase { + + + private CanonicalizationException ex; + private Exception cause; + + + public CanonicalizationExceptionTest(String name) { + super(name); + } + + + protected void setUp() { + ex = new CanonicalizationException("message"); + cause = new Exception(); + } + + + public void testConstructor() { + String message = "testing 1-2-3"; + CanonicalizationException ex = new CanonicalizationException(message); + assertEquals(message, ex.getMessage()); + } + + + public void testInitCause() { + + assertNull(ex.getCause()); + ex.initCause(cause); + assertEquals(cause, ex.getCause()); + + try { + ex.initCause(null); + fail("Reinitialized cause over null"); + } + catch (IllegalStateException success) { + assertNotNull(success.getMessage()); + } + + try { + ex.initCause(new Exception()); + fail("Reinitialized cause over null"); + } + catch (IllegalStateException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testNullInitCause() { + + CanonicalizationException ex = new CanonicalizationException(null); + ex.initCause(null); + assertNull(ex.getCause()); + + try { + ex.initCause(new Exception()); + fail("Reinitialized cause over null"); + } + catch (IllegalStateException success) { + assertNotNull(success.getMessage()); + } + + try { + ex.initCause(null); + fail("Reinitialized cause over null"); + } + catch (IllegalStateException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testSelfCause() { + + try { + ex.initCause(ex); + fail("Allowed self-causation"); + } + catch (IllegalArgumentException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testGetMessage() { + Exception ex = new CanonicalizationException("testing"); + assertEquals("testing", ex.getMessage()); + } + +} diff --git a/src/nu/xom/tests/Canonicalizer11Test.java b/src/nu/xom/tests/Canonicalizer11Test.java new file mode 100644 index 0000000..13ffb2b --- /dev/null +++ b/src/nu/xom/tests/Canonicalizer11Test.java @@ -0,0 +1,425 @@ +/* Copyright 2011, 2018 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.tests; + +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; + +import junit.framework.AssertionFailedError; +import junit.framework.TestCase; +import nu.xom.Builder; +import nu.xom.Document; +import nu.xom.ParsingException; +import nu.xom.XPathContext; +import nu.xom.canonical.Canonicalizer; + +/** + *

    + * Tests XML Canonicalization 1.1. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.3.0 + * + */ +public class Canonicalizer11Test extends TestCase { + + + private File canonical; + private ByteArrayOutputStream out = new ByteArrayOutputStream(); + private Canonicalizer canonicalizer = new Canonicalizer(out, Canonicalizer.CANONICAL_XML_11); + private Document xmlSpaceInput; + private Document xmlBaseInput; + private XPathContext namespaces = new XPathContext(); + private Builder builder = new Builder(); + + + protected void setUp() throws ParsingException, IOException { + File data = new File("data"); + canonical = new File(data, "c14n11"); + File xmlSpaceFile = new File(canonical, "xmlspace-input.xml"); + xmlSpaceInput = builder.build(xmlSpaceFile); + File xmlBaseFile = new File(canonical, "xmlbase-prop-input.xml"); + xmlBaseInput = builder.build(xmlBaseFile); + namespaces.addNamespace("ietf", "http://www.ietf.org"); + } + + + // 3.2.1.1 Test case c14n11/xmllang-1 + public void testXMLLang_1() throws ParsingException, IOException { + File input = new File(canonical, "xmllang-input.xml"); + File expected = new File(canonical, "xmllang-1.output"); + + Document doc = builder.build(input); + String documentSubsetExpression = "(//. | //@* | //namespace::*)[ancestor-or-self::ietf:e1]"; + canonicalizer.write(doc.query(documentSubsetExpression, namespaces)); + + byte[] actualBytes = out.toByteArray(); + byte[] expectedBytes = readFile(expected); + assertEquals(expectedBytes, actualBytes); + } + + + // 3.2.1.2 Test case c14n11/xmllang-2 + public void testXMLLang_2() throws ParsingException, IOException { + File input = new File(canonical, "xmllang-input.xml"); + File expected = new File(canonical, "xmllang-2.output"); + + Document doc = builder.build(input); + String documentSubsetExpression = "(//. | //@* | //namespace::*)[ancestor-or-self::ietf:e2]"; + canonicalizer.write(doc.query(documentSubsetExpression, namespaces)); + + byte[] actualBytes = out.toByteArray(); + byte[] expectedBytes = readFile(expected); + assertEquals(expectedBytes, actualBytes); + } + + + // 3.2.1.3 Test case c14n11/xmllang-3 + public void testXMLLang_3() throws ParsingException, IOException { + File input = new File(canonical, "xmllang-input.xml"); + File expected = new File(canonical, "xmllang-3.output"); + + Document doc = builder.build(input); + String documentSubsetExpression = "(//. | //@* | //namespace::*)[ancestor-or-self::ietf:e11]"; + canonicalizer.write(doc.query(documentSubsetExpression, namespaces)); + + byte[] actualBytes = out.toByteArray(); + byte[] expectedBytes = readFile(expected); + assertEquals(expectedBytes, actualBytes); + } + + + // 3.2.1.4 Test case c14n11/xmllang-4 + public void testXMLLang_4() throws ParsingException, IOException { + File input = new File(canonical, "xmllang-input.xml"); + File expected = new File(canonical, "xmllang-4.output"); + + Document doc = builder.build(input); + String documentSubsetExpression = "(//. | //@* | //namespace::*)[ancestor-or-self::ietf:e11 or ancestor-or-self::ietf:e12]"; + canonicalizer.write(doc.query(documentSubsetExpression, namespaces)); + + byte[] actualBytes = out.toByteArray(); + byte[] expectedBytes = readFile(expected); + assertEquals(expectedBytes, actualBytes); + } + + + // 3.2.2.1 Test case c14n11/xmlspace-1 + public void testXMLSpace_1() throws ParsingException, IOException { + File expected = new File(canonical, "xmlspace-1.output"); + + String documentSubsetExpression = "(//. | //@* | //namespace::*) [ancestor-or-self::ietf:e1]"; + canonicalizer.write(xmlSpaceInput.query(documentSubsetExpression, namespaces)); + + byte[] actualBytes = out.toByteArray(); + byte[] expectedBytes = readFile(expected); + assertEquals(expectedBytes, actualBytes); + } + + + // 3.2.2.2 Test case c14n11/xmlspace-2 + public void testXMLSpace_2() throws ParsingException, IOException { + File expected = new File(canonical, "xmlspace-2.output"); + + String documentSubsetExpression = "(//. | //@* | //namespace::*) [ancestor-or-self::ietf:e2]"; + canonicalizer.write(xmlSpaceInput.query(documentSubsetExpression, namespaces)); + + byte[] actualBytes = out.toByteArray(); + byte[] expectedBytes = readFile(expected); + assertEquals(expectedBytes, actualBytes); + } + + + // 3.2.2.3 Test case c14n11/xmlspace-3 + public void testXMLSpace_3() throws ParsingException, IOException { + File expected = new File(canonical, "xmlspace-3.output"); + + String documentSubsetExpression = "(//. | //@* | //namespace::*) [ancestor-or-self::ietf:e11]"; + canonicalizer.write(xmlSpaceInput.query(documentSubsetExpression, namespaces)); + + byte[] actualBytes = out.toByteArray(); + byte[] expectedBytes = readFile(expected); + assertEquals(expectedBytes, actualBytes); + } + + + // 3.2.2.4 Test case c14n11/xmlspace-4 + public void testXMLSpace_4() throws ParsingException, IOException { + File expected = new File(canonical, "xmlspace-4.output"); + + String documentSubsetExpression = "(//. | //@* | //namespace::*) [ancestor-or-self::ietf:e11 or ancestor-or-self::ietf:e12]"; + canonicalizer.write(xmlSpaceInput.query(documentSubsetExpression, namespaces)); + + byte[] actualBytes = out.toByteArray(); + byte[] expectedBytes = readFile(expected); + assertEquals(expectedBytes, actualBytes); + } + + + // 3.2.3.1 Test case c14n11/xmlid-1 + public void testXMLID_1() throws ParsingException, IOException { + File expected = new File(canonical, "xmlid-1.output"); + File input = new File(canonical, "xmlid-input.xml"); + Document xmlIdInput = builder.build(input); + + String documentSubsetExpression = "(//. | //@* | //namespace::*) [ancestor-or-self::ietf:e1]"; + canonicalizer.write(xmlIdInput.query(documentSubsetExpression, namespaces)); + + byte[] actualBytes = out.toByteArray(); + byte[] expectedBytes = readFile(expected); + assertEquals(expectedBytes, actualBytes); + } + + + // 3.2.3.2 Test case c14n11/xmlid-2 + public void testXMLID_2() throws ParsingException, IOException { + File expected = new File(canonical, "xmlid-2.output"); + File input = new File(canonical, "xmlid-input.xml"); + Document xmlIdInput = builder.build(input); + + String documentSubsetExpression = "(//. | //@* | //namespace::*) [ancestor-or-self::ietf:e11 or ancestor-or-self::ietf:e12]"; + canonicalizer.write(xmlIdInput.query(documentSubsetExpression, namespaces)); + + byte[] actualBytes = out.toByteArray(); + byte[] expectedBytes = readFile(expected); + assertEquals(expectedBytes, actualBytes); + } + + + public void testNewXMLAttributesAreNotInherited() throws ParsingException, IOException { + String input = "" + + ""; + Document doc = builder.build(input, "http://www.w3.org/TR/2008/PR-xml-c14n11-20080129/"); + + String documentSubsetExpression = "(//. | //@* | //namespace::*)[ancestor-or-self::bar]"; + canonicalizer.write(doc.query(documentSubsetExpression)); + + String actual = new String(out.toByteArray(), "UTF-8"); + assertEquals("", actual); + } + + + // 3.2.4.1.1 Test case c14n11/xmlbase-prop-1 + public void testXMLBase_Prop_1() throws ParsingException, IOException { + File expected = new File(canonical, "xmlbase-prop-1.output"); + + String documentSubsetExpression + = "(//. | //@* | //namespace::*) [ancestor-or-self::ietf:c14n11XmlBaseDoc1 and not(ancestor-or-self::ietf:e2)]"; + canonicalizer.write(xmlBaseInput.query(documentSubsetExpression, namespaces)); + + byte[] actualBytes = out.toByteArray(); + byte[] expectedBytes = readFile(expected); + assertEquals(expectedBytes, actualBytes); + } + + + // 3.2.4.1.2 Test case c14n11/xmlbase-prop-2 + public void testXMLBase_Prop_2() throws ParsingException, IOException { + File expected = new File(canonical, "xmlbase-prop-2.output"); + + String documentSubsetExpression + = "(//. | //@* | //namespace::*) [ancestor-or-self::ietf:e1]"; + canonicalizer.write(xmlBaseInput.query(documentSubsetExpression, namespaces)); + + byte[] actualBytes = out.toByteArray(); + byte[] expectedBytes = readFile(expected); + assertEquals(expectedBytes, actualBytes); + } + + + // 3.2.4.1.3 Test case c14n11/xmlbase-prop-3 + public void testXMLBase_Prop_3() throws ParsingException, IOException { + File expected = new File(canonical, "xmlbase-prop-3.output"); + + String documentSubsetExpression + = "(//. | //@* | //namespace::*) [ancestor-or-self::ietf:e11]"; + canonicalizer.write(xmlBaseInput.query(documentSubsetExpression, namespaces)); + + byte[] actualBytes = out.toByteArray(); + byte[] expectedBytes = readFile(expected); + assertEquals(expectedBytes, actualBytes); + } + + + // 3.2.4.1.4 Test case c14n11/xmlbase-prop-4 + public void testXMLBase_Prop_4() throws ParsingException, IOException { + File expected = new File(canonical, "xmlbase-prop-4.output"); + + String documentSubsetExpression + = "(//. | //@* | //namespace::*) [ancestor-or-self::ietf:e111]"; + canonicalizer.write(xmlBaseInput.query(documentSubsetExpression, namespaces)); + + byte[] actualBytes = out.toByteArray(); + byte[] expectedBytes = readFile(expected); + assertEquals(expectedBytes, actualBytes); + } + + + // 3.2.4.1.5 Test case c14n11/xmlbase-prop-5 + public void testXMLBase_Prop_5() throws ParsingException, IOException { + File expected = new File(canonical, "xmlbase-prop-5.output"); + + String documentSubsetExpression + = "(//. | //@* | //namespace::*) [ancestor-or-self::ietf:e21]"; + canonicalizer.write(xmlBaseInput.query(documentSubsetExpression, namespaces)); + + byte[] actualBytes = out.toByteArray(); + byte[] expectedBytes = readFile(expected); + assertEquals(expectedBytes, actualBytes); + } + + + // 3.2.4.1.6 Test case c14n11/xmlbase-prop-6 + public void testXMLBase_Prop_6() throws ParsingException, IOException { + File expected = new File(canonical, "xmlbase-prop-6.output"); + + String documentSubsetExpression + = "(//. | //@* | //namespace::*) [ancestor-or-self::ietf:e3]"; + canonicalizer.write(xmlBaseInput.query(documentSubsetExpression, namespaces)); + + byte[] actualBytes = out.toByteArray(); + byte[] expectedBytes = readFile(expected); + assertEquals(expectedBytes, actualBytes); + } + + + // 3.2.4.1.7 Test case c14n11/xmlbase-prop-7 + public void testXMLBase_Prop_7() throws ParsingException, IOException { + File expected = new File(canonical, "xmlbase-prop-7.output"); + + String documentSubsetExpression + = "(//. | //@* | //namespace::*) [ancestor-or-self::ietf:c14n11XmlBaseDoc1 and not(ancestor-or-self::ietf:e1 or ancestor-or-self::ietf:e2)]"; + canonicalizer.write(xmlBaseInput.query(documentSubsetExpression, namespaces)); + + byte[] actualBytes = out.toByteArray(); + byte[] expectedBytes = readFile(expected); + assertEquals(expectedBytes, actualBytes); + } + + // 3.2.4.2.1 Test case c14n11/xmlbase-c14n11spec-102 + public void testXMLBase_1() throws ParsingException, IOException { + File expected = new File(canonical, "xmlbase-c14n11spec-102.output"); + File xmlBaseFile = new File(canonical, "xmlbase-c14n11spec-input.xml"); + Document doc = builder.build(xmlBaseFile); + + String documentSubsetExpression + = "(//. | //@* | //namespace::*)[self::ietf:e1 or (parent::ietf:e1 and not(self::text() or self::e2)) or count(id(\"E3\")|ancestor-or-self::node()) = count(ancestor-or-self::node())]"; + canonicalizer.write(doc.query(documentSubsetExpression, namespaces)); + + byte[] actualBytes = out.toByteArray(); + byte[] expectedBytes = readFile(expected); + assertEquals(expectedBytes, actualBytes); + } + + + // 3.2.4.2.2 Test case c14n11/xmlbase-c14n11spec2-102 + public void testXMLBase_2() throws ParsingException, IOException { + File input = new File(canonical, "xmlbase-c14n11spec2-input.xml"); + Document doc = builder.build(input); + File expected = new File(canonical, "xmlbase-c14n11spec2-102.output"); + + String documentSubsetExpression + = "(//. | //@* | //namespace::*)[self::ietf:e1 or (parent::ietf:e1 and not(self::text() or self::e2)) or count(id(\"E3\")|ancestor-or-self::node()) = count(ancestor-or-self::node())]"; + canonicalizer.write(doc.query(documentSubsetExpression, namespaces)); + + byte[] actualBytes = out.toByteArray(); + byte[] expectedBytes = readFile(expected); + assertEquals(expectedBytes, actualBytes); + } + + + // 3.2.4.2.3 Test case c14n11/xmlbase-c14n11spec3-103 + public void testXMLBase_3() throws ParsingException, IOException { + File input = new File(canonical, "xmlbase-c14n11spec3-input.xml"); + Document doc = builder.build(input); + File expected = new File(canonical, "xmlbase-c14n11spec3-103.output"); + + String documentSubsetExpression = "(//. | //@* | //namespace::*) [self::a or ancestor-or-self::d]"; + canonicalizer.write(doc.query(documentSubsetExpression, namespaces)); + + byte[] actualBytes = out.toByteArray(); + byte[] expectedBytes = readFile(expected); + assertEquals(expectedBytes, actualBytes); + } + + + private byte[] readFile(File expected) throws IOException { + byte[] expectedBytes = new byte[(int) expected.length()]; + InputStream fin = new FileInputStream(expected); + DataInputStream in = new DataInputStream(fin); + try { + in.readFully(expectedBytes); + } + finally { + in.close(); + } + return expectedBytes; + } + + + /** + *

    + * Asserts that two byte arrays are equal. If the two arrays are + * not equal a ComparisonFailure is thrown. Two + * arrays are equal if and only if they have the same length, + * and each item in the expected array is equal to the + * corresponding item in the actual array. + *

    + * + * @param expected the byte array the test should produce + * @param actual the byte array the test does produce + */ + private void assertEquals(byte[] expected, byte[] actual) { + + if (expected == null && actual == null) { + return; + } + // what if one is null and the other isn't???? + try { + assertEquals(expected.length, actual.length); + for (int i = 0; i < actual.length; i++) { + assertEquals(expected[i], actual[i]); + } + } catch (AssertionFailedError error) { + fail(getComparisonMessage(expected, actual)); + } + + + } + + private String getComparisonMessage(byte[] expected, byte[] actual) { + try { + return "Expected\n" + new String(expected, "UTF-8") + "\n\n but was \n\n" + new String(actual, "UTF-8"); + } catch (UnsupportedEncodingException e) { + return "Broken VM"; + } + } + + +} diff --git a/src/nu/xom/tests/CanonicalizerTest.java b/src/nu/xom/tests/CanonicalizerTest.java new file mode 100644 index 0000000..1ef341c --- /dev/null +++ b/src/nu/xom/tests/CanonicalizerTest.java @@ -0,0 +1,2250 @@ +/* Copyright 2002-2005, 2008, 2011 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.tests; + +import java.io.BufferedInputStream; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FilenameFilter; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import nu.xom.Attribute; +import nu.xom.Builder; +import nu.xom.Comment; +import nu.xom.DocType; +import nu.xom.Document; +import nu.xom.Element; +import nu.xom.Elements; +import nu.xom.Namespace; +import nu.xom.Nodes; +import nu.xom.ParsingException; +import nu.xom.ProcessingInstruction; +import nu.xom.Serializer; +import nu.xom.Text; +import nu.xom.XPathContext; +import nu.xom.canonical.CanonicalizationException; +import nu.xom.canonical.Canonicalizer; + +/** + *

    + * Tests canonicalization. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.2b3 + * + */ +public class CanonicalizerTest extends XOMTestCase { + + private final static double version = Double.parseDouble( + System.getProperty("java.version").substring(0,3) + ); + + private File canonical; + private File input; + private File output; + private ByteArrayOutputStream out = new ByteArrayOutputStream(); + private Canonicalizer canonicalizer = new Canonicalizer(out); + + public CanonicalizerTest(String name) { + super(name); + } + + + private Builder builder = new Builder(); + + + protected void setUp() { + File data = new File("data"); + canonical = new File(data, "canonical"); + input = new File(canonical, "input"); + output = new File(canonical, "output"); + } + + + public void testCanonicalizeOnlyAttributes() throws IOException { + + Element pdu = new Element("doc"); + pdu.addAttribute(new Attribute("a1", "v1")); + pdu.addAttribute(new Attribute("a2", "v2")); + + String expected = " a1=\"v1\" a2=\"v2\""; + + Document doc = new Document(pdu); + canonicalizer.write(doc.query("//@*")); + + byte[] result = out.toByteArray(); + out.close(); + String s = new String(result, "UTF8"); + assertEquals(expected, s); + + } + + + public void testRemoveDuplicateAttributes() throws IOException { + + Element pdu = new Element("doc"); + Attribute a1 = new Attribute("a1", "v1"); + pdu.addAttribute(a1); + Attribute a2 = new Attribute("a2", "v2"); + pdu.addAttribute(a2); + + String expected = " a1=\"v1\" a2=\"v2\""; + + Document doc = new Document(pdu); + Nodes subset = doc.query("//@*"); + subset.append(a1); + subset.append(a2); + canonicalizer.write(subset); + + byte[] result = out.toByteArray(); + out.close(); + String s = new String(result, "UTF8"); + assertEquals(expected, s); + + } + + + public void testCanonicalizeOnlyNamespaces() throws IOException { + + Element pdu = new Element("doc", "http://www.example.com"); + + String expected = " xmlns=\"http://www.example.com\""; + + Document doc = new Document(pdu); + canonicalizer.write(doc.query("//namespace::node()")); + + byte[] result = out.toByteArray(); + out.close(); + String s = new String(result, "UTF8"); + assertEquals(expected, s); + + } + + + public void testCanonicalizeOnlyPrefixedNamespaces() + throws IOException { + + Element pdu = new Element("pre:doc", "http://www.example.com"); + + String expected = " xmlns:pre=\"http://www.example.com\""; + + Document doc = new Document(pdu); + canonicalizer.write(doc.query("//namespace::node()")); + + byte[] result = out.toByteArray(); + out.close(); + String s = new String(result, "UTF8"); + assertEquals(expected, s); + + } + + + public void testCanonicalizeWithNullAlgorithm() + throws IOException { + + try { + new Canonicalizer(out, null); + fail("Allowed null algorithm"); + } + catch (NullPointerException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testCanonicalizeCommentsInPrologAndEpilog() throws IOException { + + Element pdu = new Element("doc"); + + Document doc = new Document(pdu); + doc.insertChild(new Comment("comment 1"), 0); + doc.insertChild(new Comment("comment 2"), 1); + doc.appendChild(new Comment("comment 3")); + doc.appendChild(new Comment("comment 4")); + + String expected = "\n\n\n\n"; + + canonicalizer.write(doc.query("//comment()")); + + byte[] result = out.toByteArray(); + out.close(); + String s = new String(result, "UTF8"); + assertEquals(expected, s); + + } + + + public void testTamin() throws ParsingException, IOException { + + String input = ""; + Document doc = builder.build(input, null); + + canonicalizer.write(doc); + + byte[] result = out.toByteArray(); + out.close(); + String s = new String(result, "UTF8"); + assertEquals("", s); + + } + + + public void testCanonicalizePrologAndEpilog() throws IOException { + + Element pdu = new Element("doc"); + + Document doc = new Document(pdu); + doc.insertChild(new ProcessingInstruction("target", "value"), 0); + doc.insertChild(new Comment("comment 2"), 1); + doc.appendChild(new Comment("comment 3")); + doc.appendChild(new ProcessingInstruction("target", "value")); + + String expected = "\n\n\n\n"; + + canonicalizer.write(doc); + + byte[] result = out.toByteArray(); + out.close(); + String s = new String(result, "UTF8"); + assertEquals(expected, s); + + } + + + public void testCanonicalizeOnlyAttributesOnDifferentElements() + throws IOException { + + Element pdu = new Element("doc"); + pdu.addAttribute(new Attribute("a2", "v1")); + Element child = new Element("child"); + child.addAttribute(new Attribute("a1", "v2")); + pdu.appendChild(child); + + String expected = " a2=\"v1\" a1=\"v2\""; + + Document doc = new Document(pdu); + canonicalizer.write(doc.query("//@*")); + + out.close(); + String s = new String(out.toByteArray(), "UTF8"); + assertEquals(expected, s); + + } + + + public void testCanonicalizeAttributesWithFunkyCharacters() + throws IOException { + + Element pdu = new Element("doc"); + pdu.addAttribute(new Attribute("a2", "v1&<>\"\t\r\n")); + + String expected = " a2=\"v1&<>" \""; + + Document doc = new Document(pdu); + canonicalizer.write(doc.query("//@*")); + + out.close(); + String s = new String(out.toByteArray(), "UTF8"); + assertEquals(expected, s); + + } + + + public void testExclusiveEmptyRootElementInNoNamespace() + throws IOException { + + Element pdu = new Element("doc"); + + String expected = ""; + Canonicalizer canonicalizer = new Canonicalizer(out, + Canonicalizer.EXCLUSIVE_XML_CANONICALIZATION_WITH_COMMENTS); + + Document doc = new Document(pdu); + canonicalizer.write(doc); + + out.close(); + String s = new String(out.toByteArray(), "UTF8"); + assertEquals(expected, s); + + } + + + public void testExclusiveEmptyRootElementInNoNamespaceWithTwoAttributes() + throws IOException { + + Element pdu = new Element("doc"); + pdu.addAttribute(new Attribute("a1", "v1")); + pdu.addAttribute(new Attribute("a2", "v2")); + + String expected = ""; + Canonicalizer canonicalizer = new Canonicalizer(out, + Canonicalizer.EXCLUSIVE_XML_CANONICALIZATION_WITH_COMMENTS); + + Document doc = new Document(pdu); + canonicalizer.write(doc); + + out.close(); + String s = new String(out.toByteArray(), "UTF8"); + assertEquals(expected, s); + + } + + + public void testExclusiveDoesntRenderUnusedPrefix() throws IOException { + + Element pdu = new Element("n0:tuck", "http://a.example"); + pdu.addNamespaceDeclaration("pre", "http://www.example.org/"); + + String expected = ""; + Canonicalizer canonicalizer = new Canonicalizer(out, + Canonicalizer.EXCLUSIVE_XML_CANONICALIZATION_WITH_COMMENTS); + + Document doc = new Document(pdu); + canonicalizer.write(doc); + + out.close(); + String s = new String(out.toByteArray(), "UTF8"); + assertEquals(expected, s); + + } + + + public void testExclusiveDoesntRenderUnusedDefaultNamespace() throws IOException { + + Element root = new Element("A", "http://foo.example.com"); + Element child = new Element("n2:B", "http://b.example.com"); + root.appendChild(child); + Element grandchild = new Element("n2:C", "http://b.example.com"); + child.appendChild(grandchild); + grandchild.addAttribute(new Attribute("Attribute", "something")); + + String expected = ""; + Canonicalizer canonicalizer = new Canonicalizer(out, + Canonicalizer.EXCLUSIVE_XML_CANONICALIZATION_WITH_COMMENTS); + + new Document(root); + canonicalizer.write(grandchild); + + out.close(); + String s = new String(out.toByteArray(), "UTF8"); + assertEquals(expected, s); + + } + + + public void testExclusiveDoesntRenderUnusedPrefixedNamespace() throws IOException { + + Element root = new Element("A", "http://foo.example.com"); + Element child = new Element("n1:B", "http://bar.example.com"); + root.appendChild(child); + Element grandchild = new Element("n2:C", "http://b.example.com"); + child.appendChild(grandchild); + grandchild.addAttribute(new Attribute("Attribute", "something")); + + String expected = ""; + Canonicalizer canonicalizer = new Canonicalizer(out, + Canonicalizer.EXCLUSIVE_XML_CANONICALIZATION_WITH_COMMENTS); + + new Document(root); + canonicalizer.write(grandchild); + + out.close(); + String s = new String(out.toByteArray(), "UTF8"); + assertEquals(expected, s); + + } + + + public void testWriteDefaultNamespace() throws IOException { + + Element pdu = new Element("tuck", "http://www.example.org/"); + + String expected = " xmlns=\"http://www.example.org/\""; + Canonicalizer canonicalizer = new Canonicalizer(out, + Canonicalizer.EXCLUSIVE_XML_CANONICALIZATION_WITH_COMMENTS); + + Document doc = new Document(pdu); + Nodes subset = doc.query("//namespace::node()"); + canonicalizer.write(subset); + + out.close(); + String s = new String(out.toByteArray(), "UTF8"); + assertEquals(expected, s); + + } + + + public void testOutputAncestorAttributeAndChildHaveDifferentLanguages() + throws IOException { + + Element pdu = new Element("tuck"); + pdu.addAttribute(new Attribute("xml:lang", Namespace.XML_NAMESPACE, "fr")); + + Element middle = new Element("middle"); + pdu.appendChild(middle); + Element child = new Element("child"); + child.addAttribute(new Attribute("xml:lang", Namespace.XML_NAMESPACE, "en")); + middle.appendChild(child); + + String expected = ""; + Canonicalizer canonicalizer = new Canonicalizer(out, + Canonicalizer.EXCLUSIVE_XML_CANONICALIZATION_WITH_COMMENTS); + + Document doc = new Document(pdu); + Nodes subset = doc.query("/* | //child | //@*"); + canonicalizer.write(subset); + + out.close(); + String s = new String(out.toByteArray(), "UTF8"); + assertEquals(expected, s); + + } + + + public void testOutputAncestorAttributeUsesPrefix() + throws IOException { + + Element pdu = new Element("tuck"); + pdu.addAttribute(new Attribute("pre:foo", "http://www.example.org/", "value")); + Element child = new Element("pre:test", "http://www.example.org/"); + pdu.appendChild(child); + + String expected = ""; + Canonicalizer canonicalizer = new Canonicalizer(out, + Canonicalizer.EXCLUSIVE_XML_CANONICALIZATION_WITH_COMMENTS); + + Document doc = new Document(pdu); + canonicalizer.write(doc); + + out.close(); + String s = new String(out.toByteArray(), "UTF8"); + assertEquals(expected, s); + + } + + + public void testOutputAncestorAttributeRedefinesPrefix() + throws IOException { + + Element pdu = new Element("tuck"); + pdu.addAttribute(new Attribute("pre:foo", "http://www.example.com/", "value")); + Element child = new Element("pre:test", "http://www.example.org/"); + pdu.appendChild(child); + + String expected = ""; + Canonicalizer canonicalizer = new Canonicalizer(out, + Canonicalizer.EXCLUSIVE_XML_CANONICALIZATION_WITH_COMMENTS); + + Document doc = new Document(pdu); + canonicalizer.write(doc); + + out.close(); + String s = new String(out.toByteArray(), "UTF8"); + assertEquals(expected, s); + + } + + + public void testExclusiveDoesntRenderUnusedPrefixFromUnincludedAttribute() + throws IOException { + + Element pdu = new Element("n0:tuck", "http://a.example"); + pdu.addAttribute(new Attribute("pre:foo", "http://www.example.org/", "test")); + + String expected = ""; + Canonicalizer canonicalizer = new Canonicalizer(out, + Canonicalizer.EXCLUSIVE_XML_CANONICALIZATION_WITH_COMMENTS); + + Document doc = new Document(pdu); + canonicalizer.write(doc.query("//* | //namespace::node()")); + + out.close(); + String s = new String(out.toByteArray(), "UTF8"); + assertEquals(expected, s); + + } + + + public void testWithComments() + throws ParsingException, IOException { + + File tests = input; + String[] inputs = tests.list(new XMLFilter()); + for (int i = 0; i < inputs.length; i++) { + File input = new File(tests, inputs[i]); + Document doc = builder.build(input); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + try { + Canonicalizer serializer = new Canonicalizer(out); + serializer.write(doc); + } + finally { + out.close(); + } + byte[] actual = out.toByteArray(); + + // for debugging + /* File debug = new File(canonical, "debug/" + + input.getName() + ".dbg"); + OutputStream fout = new FileOutputStream(debug); + fout.write(actual); + fout.close(); */ + + File expected = new File(output, input.getName() + ".out"); + assertEquals( + input.getName(), expected.length(), actual.length); + byte[] expectedBytes = new byte[actual.length]; + InputStream fin = new FileInputStream(expected); + DataInputStream in = new DataInputStream(fin); + try { + in.readFully(expectedBytes); + } + finally { + in.close(); + } + for (int j = 0; j < expectedBytes.length; j++) { + assertEquals(expectedBytes[i], actual[i]); + } + + } + + } + + + public void testNamedAlgorithmWithComments() + throws ParsingException, IOException { + + File tests = input; + String[] inputs = tests.list(new XMLFilter()); + for (int i = 0; i < inputs.length; i++) { + File input = new File(tests, inputs[i]); + Document doc = builder.build(input); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + try { + Canonicalizer serializer = new Canonicalizer( + out, Canonicalizer.CANONICAL_XML_WITH_COMMENTS); + serializer.write(doc); + } + finally { + out.close(); + } + byte[] actual = out.toByteArray(); + + // for debugging + /* File debug = new File(canonical, "debug/" + + input.getName() + ".dbg"); + OutputStream fout = new FileOutputStream(debug); + fout.write(actual); + fout.close(); */ + + File expected = new File(output, input.getName() + ".out"); + assertEquals( + input.getName(), expected.length(), actual.length); + byte[] expectedBytes = new byte[actual.length]; + InputStream fin = new FileInputStream(expected); + DataInputStream in = new DataInputStream(fin); + try { + in.readFully(expectedBytes); + } + finally { + in.close(); + } + for (int j = 0; j < expectedBytes.length; j++) { + assertEquals(expectedBytes[i], actual[i]); + } + + } + + } + + + public void testWithoutComments() + throws ParsingException, IOException { + + File tests = input; + String[] inputs = tests.list(new XMLFilter()); + for (int i = 0; i < inputs.length; i++) { + File input = new File(tests, inputs[i]); + Document doc = builder.build(input); + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + try { + Canonicalizer serializer + = new Canonicalizer(out, false); + serializer.write(doc); + } + finally { + out.close(); + } + + byte[] actual = out.toByteArray(); + + File expected = new File(canonical, "wocommentsoutput/"); + expected = new File(expected, input.getName() + ".out"); + byte[] expectedBytes = new byte[actual.length]; + InputStream fin = new FileInputStream(expected); + DataInputStream in = new DataInputStream(fin); + try { + in.readFully(expectedBytes); + } + finally { + in.close(); + } + for (int j = 0; j < expectedBytes.length; j++) { + assertEquals(expectedBytes[i], actual[i]); + } + out.close(); + + } + + } + + + public void testNamedAlgorithmWithoutComments() + throws ParsingException, IOException { + + File tests = input; + String[] inputs = tests.list(new XMLFilter()); + for (int i = 0; i < inputs.length; i++) { + File input = new File(tests, inputs[i]); + Document doc = builder.build(input); + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + try { + Canonicalizer serializer = new Canonicalizer( + out, Canonicalizer.CANONICAL_XML); + serializer.write(doc); + } + finally { + out.close(); + } + + byte[] actual = out.toByteArray(); + + File expected = new File(canonical, "wocommentsoutput/"); + expected = new File(expected, input.getName() + ".out"); + byte[] expectedBytes = new byte[actual.length]; + InputStream fin = new FileInputStream(expected); + DataInputStream in = new DataInputStream(fin); + try { + in.readFully(expectedBytes); + } + finally { + in.close(); + } + for (int j = 0; j < expectedBytes.length; j++) { + assertEquals(expectedBytes[i], actual[i]); + } + out.close(); + + } + + } + + + public void testXMLNamespaceAttributeInheritance() + throws IOException { + + Element root = new Element("root"); + Document doc = new Document(root); + root.addAttribute(new Attribute("xml:id", Namespace.XML_NAMESPACE, "p1")); + root.appendChild(new Element("child312")); + + String expected = ""; + + try { + Canonicalizer serializer = new Canonicalizer(out, false); + serializer.write(doc.query("/*/child312")); + } + finally { + out.close(); + } + + String actual = new String(out.toByteArray(), "UTF-8"); + assertEquals(expected, actual); + + } + + + public void testXMLNamespaceAttributeInheritanceThroughMultipleLevels() + throws IOException { + + Element superroot = new Element("superroot"); + Element root = new Element("root"); + superroot.appendChild(root); + superroot.addAttribute(new Attribute("xml:id", Namespace.XML_NAMESPACE, "p0")); + Document doc = new Document(superroot); + root.addAttribute(new Attribute("xml:id", Namespace.XML_NAMESPACE, "p1")); + root.appendChild(new Element("child312")); + + String expected = ""; + + try { + Canonicalizer serializer = new Canonicalizer(out, false); + Nodes result = doc.query("/*/*/child312"); + serializer.write(result); + } + finally { + out.close(); + } + + String actual = new String(out.toByteArray(), "UTF-8"); + assertEquals(expected, actual); + + } + + + public void testXMLNamespaceAttributeInheritanceThroughMultipleLevelsWithSkippedMiddle() + throws IOException { + + Element superroot = new Element("superroot"); + Element root = new Element("root"); + superroot.appendChild(root); + superroot.addAttribute(new Attribute("xml:id", Namespace.XML_NAMESPACE, "p0")); + Document doc = new Document(superroot); + root.addAttribute(new Attribute("xml:id", Namespace.XML_NAMESPACE, "p1")); + root.appendChild(new Element("child312")); + + String expected = ""; + + try { + Canonicalizer serializer = new Canonicalizer(out, false); + serializer.write(doc.query("/* | //child312 | /*/@* | //child312/@*")); + } + finally { + out.close(); + } + + String actual = new String(out.toByteArray(), "UTF-8"); + assertEquals(expected, actual); + + } + + + public void testXMLNamespaceAttributeInheritanceNearestIsInSubset() + throws IOException { + + Element superroot = new Element("superroot"); + Element root = new Element("root"); + superroot.appendChild(root); + superroot.addAttribute(new Attribute("xml:id", Namespace.XML_NAMESPACE, "p0")); + Document doc = new Document(superroot); + root.appendChild(new Element("child312")); + + String expected = ""; + + try { + Canonicalizer serializer = new Canonicalizer(out, false); + serializer.write(doc.query("/* | //child312 | /*/@* | //child312/@*")); + } + finally { + out.close(); + } + + String actual = new String(out.toByteArray(), "UTF-8"); + assertEquals(expected, actual); + + } + + + public void testXMLNamespaceAttributeNotOverridden() + throws IOException { + + Element root = new Element("root"); + Document doc = new Document(root); + Element child = new Element("child312"); + + root.addAttribute(new Attribute("xml:id", Namespace.XML_NAMESPACE, "p1")); + child.addAttribute(new Attribute("xml:id", Namespace.XML_NAMESPACE, "p2")); + + root.appendChild(child); + + String expected = ""; + + try { + Canonicalizer serializer = new Canonicalizer(out, false); + serializer.write(doc.query("/*/child312 | /*/*/@*")); + } + finally { + out.close(); + } + + String actual = new String(out.toByteArray(), "UTF-8"); + assertEquals(expected, actual); + + } + + + public void testXMLNamespaceAttributeNotOverridden2() + throws IOException { + + Element root = new Element("root"); + Document doc = new Document(root); + Element child = new Element("child312"); + + root.addAttribute(new Attribute("xml:id", Namespace.XML_NAMESPACE, "p1")); + child.addAttribute(new Attribute("xml:id", Namespace.XML_NAMESPACE, "p2")); + + root.appendChild(child); + + String expected = ""; + + try { + Canonicalizer serializer = new Canonicalizer(out, false); + serializer.write(doc.query("/*/child312 ")); + } + finally { + out.close(); + } + + String actual = new String(out.toByteArray(), "UTF-8"); + assertEquals(expected, actual); + + } + + + public void testXMLNamespaceAttributeNotInheritedWithExclusiveCanonicalization() + throws IOException { + + Element root = new Element("root"); + Document doc = new Document(root); + root.addAttribute(new Attribute("xml:id", Namespace.XML_NAMESPACE, "p1")); + root.appendChild(new Element("child312")); + + String expected = ""; + + try { + Canonicalizer serializer = new Canonicalizer(out, + Canonicalizer.EXCLUSIVE_XML_CANONICALIZATION); + serializer.write(doc.query("/*/child312")); + } + finally { + out.close(); + } + + String actual = new String(out.toByteArray(), "UTF-8"); + assertEquals(expected, actual); + + } + + + public void testXMLNSAttributeNotInheritedWithExclusiveCanonicalization() + throws IOException { + + Element root = new Element("root", "http://www.example.org/"); + Document doc = new Document(root); + root.appendChild(new Element("child312", "http://www.example.org/")); + + String expected = ""; + + try { + Canonicalizer serializer = new Canonicalizer(out, + Canonicalizer.EXCLUSIVE_XML_CANONICALIZATION); + XPathContext context = new XPathContext("pre", "http://www.example.org/"); + serializer.write(doc.query("/*/pre:child312 | /*/pre:child312/namespace::node()", + context)); + } + finally { + out.close(); + } + + String actual = new String(out.toByteArray(), "UTF-8"); + assertEquals(expected, actual); + + } + + + public void testXMLNSPrefixAttributeInheritedWithExclusiveCanonicalization() + throws IOException { + + Element root = new Element("pre:root", "http://www.example.org/"); + Document doc = new Document(root); + root.appendChild(new Element("pre:child312", "http://www.example.org/")); + + String expected = ""; + + try { + XPathContext context = new XPathContext("pre", "http://www.example.org/"); + Canonicalizer serializer = new Canonicalizer(out, + Canonicalizer.EXCLUSIVE_XML_CANONICALIZATION); + serializer.write(doc.query("/*/pre:child312 | /*/pre:child312/namespace::node()", context)); + } + finally { + out.close(); + } + + String actual = new String(out.toByteArray(), "UTF-8"); + assertEquals(expected, actual); + + } + + + public void testXMLNSEqualsEmptyString() + throws IOException { + + Element root = new Element("root", "http://www.ietf.org"); + Document doc = new Document(root); + root.appendChild(new Element("child")); + + String expected = ""; + + try { + Canonicalizer serializer = new Canonicalizer(out, false); + serializer.write(doc); + } + finally { + out.close(); + } + + String actual = new String(out.toByteArray(), "UTF-8"); + assertEquals(expected, actual); + + } + + + // from section 3.7 of spec + public void testDocumentSubsetCanonicalization() + throws ParsingException, IOException { + + String input = "\n" + + "\n" + + "]>\n" + + "\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + ""; + + Document doc = builder.build(input, null); + XPathContext context = new XPathContext("ietf", "http://www.ietf.org"); + String xpath = "(//. | //@* | //namespace::*)\n" + + "[\n" + + "self::ietf:e1 or (parent::ietf:e1 and not(self::text() or self::e2))" + + " or\n" + + " count(id(\"E3\")|ancestor-or-self::node()) = count(ancestor-or-self::node())\n" + + "]"; + + String expected = ""; + + try { + Canonicalizer serializer = new Canonicalizer(out, false); + serializer.write(doc.query(xpath, context)); + } + finally { + out.close(); + } + + String actual = new String(out.toByteArray(), "UTF-8"); + assertEquals(expected, actual); + + } + + + public void testCanonicalizeEmptyDocumentSubset() + throws ParsingException, IOException { + + String input = "\n" + + "\n" + + "]>\n" + + "\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + ""; + + Document doc = builder.build(input, null); + XPathContext context = new XPathContext("ietf", "http://www.ietf.org"); + String xpath = "//aaa"; + try { + Canonicalizer serializer = new Canonicalizer(out, false); + serializer.write(doc.query(xpath, context)); + } + finally { + out.close(); + } + + String actual = new String(out.toByteArray(), "UTF-8"); + assertEquals("", actual); + + } + + + public void testDocumentSubsetCanonicalizationSkippingProcessingInstructions() + throws ParsingException, IOException { + + String input = "\n" + + "]>\n" + + "\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + ""; + + String expected = "\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + ""; + + Document doc = builder.build(input, null); + XPathContext context = new XPathContext("ietf", "http://www.ietf.org"); + String xpath = "//* | //text() | //@* | //namespace::*"; + + try { + Canonicalizer serializer = new Canonicalizer(out, false); + serializer.write(doc.query(xpath, context)); + } + finally { + out.close(); + } + + String actual = new String(out.toByteArray(), "UTF-8"); + assertEquals(expected, actual); + + } + + + public void testDocumentSubsetCanonicalizationWithoutSelectingPrologAndEpilog() + throws ParsingException, IOException { + + String input = "\n" + + "\n" + + "\n" + + "]>\n" + + "\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + ""; + + Document doc = builder.build(input, null); + XPathContext context = new XPathContext("ietf", "http://www.ietf.org"); + String xpath = "(/*//. | //@* | //namespace::*)\n" + + "[\n" + + "self::ietf:e1 or (parent::ietf:e1 and not(self::text() or self::e2))" + + " or\n" + + " count(id(\"E3\")|ancestor-or-self::node()) = count(ancestor-or-self::node())\n" + + "]"; + + String expected = ""; + + try { + Canonicalizer serializer = new Canonicalizer(out, Canonicalizer.CANONICAL_XML_WITH_COMMENTS); + serializer.write(doc.query(xpath, context)); + } + finally { + out.close(); + } + + String actual = new String(out.toByteArray(), "UTF-8"); + assertEquals(expected, actual); + + } + + + public void testEmptyDefaultNamespace() + throws ParsingException, IOException { + + String input = "" + + "" + + ""; + + Document doc = builder.build(input, null); + String xpath = "(//* | //namespace::*)"; + + String expected = input; + + try { + Canonicalizer serializer = new Canonicalizer(out, false); + serializer.write(doc.query(xpath)); + } + finally { + out.close(); + } + + String actual = new String(out.toByteArray(), "UTF-8"); + assertEquals(expected, actual); + + } + + + + public void testDocumentSubsetCanonicalizationSimple() + throws ParsingException, IOException { + + String input = "\n" + + "\n" + + "]>\n" + + "\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + ""; + + Document doc = builder.build(input, null); + XPathContext context = new XPathContext("ietf", "http://www.ietf.org"); + String xpath = "(/* | /*/namespace::*)\n"; + + String expected = ""; + + try { + Canonicalizer serializer = new Canonicalizer(out, false); + serializer.write(doc.query(xpath, context)); + } + finally { + out.close(); + } + + String actual = new String(out.toByteArray(), "UTF-8"); + assertEquals(expected, actual); + + } + + + public void testCanonicalizeDocumentSubsetIncludingRoot() + throws ParsingException, IOException { + + String input = ""; + + Document doc = builder.build(input, null); + + String expected = ""; + + try { + Canonicalizer serializer = new Canonicalizer(out, false); + Nodes subset = doc.query("//."); + serializer.write(subset); + } + finally { + out.close(); + } + + String actual = new String(out.toByteArray(), "UTF-8"); + assertEquals(expected, actual); + + } + + + public void testCanonicalizeDocumentSubsetThatOnlyContainsRoot() + throws IOException { + + Document doc = new Document(new Element("root")); + + try { + Canonicalizer serializer = new Canonicalizer(out, false); + Nodes subset = doc.query("/"); + serializer.write(subset); + } + finally { + out.close(); + } + + String actual = new String(out.toByteArray(), "UTF-8"); + assertEquals("", actual); + + } + + + public void testDocumentSubsetCanonicalizationNamespaceInheritance() + throws ParsingException, IOException { + + String input = "\n" + + "\n" + + "]>\n" + + "\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + ""; + + Document doc = builder.build(input, null); + XPathContext context = new XPathContext("ietf", "http://www.ietf.org"); + String xpath = "(/*/* | /*/*/namespace::*)\n"; + + String expected = ""; + + try { + Canonicalizer serializer = new Canonicalizer(out, false); + serializer.write(doc.query(xpath, context)); + } + finally { + out.close(); + } + + String actual = new String(out.toByteArray(), "UTF-8"); + assertEquals(expected, actual); + + } + + + public void testRelativeNamespaceURIsForbidden() + throws ParsingException, IOException { + + try { + String data = "data"; + Document doc = builder.build(data, "http://www.ex.org/"); + Canonicalizer serializer = new Canonicalizer(out, false); + serializer.write(doc); + fail("Canonicalized document with relative namespace URI"); + } + catch (ParsingException success) { + assertNotNull(success.getMessage()); + } + + } + + + private static class XMLFilter implements FilenameFilter { + + public boolean accept(File directory, String name) { + if (name.endsWith(".xml")) return true; + return false; + } + + } + + + public void testNFCFromISO88591() + throws ParsingException, IOException { + isoNormalizationTest("ISO-8859-1"); + } + + + public void testNFCFromISO88592() + throws ParsingException, IOException { + isoNormalizationTest("ISO-8859-2"); + } + + + public void testNFCFromISO88593() + throws ParsingException, IOException { + isoNormalizationTest("ISO-8859-3"); + } + + + public void testNFCFromISO88594() + throws ParsingException, IOException { + isoNormalizationTest("ISO-8859-4"); + } + + + public void testNFCFromISO88595() + throws ParsingException, IOException { + isoNormalizationTest("ISO-8859-5"); + } + + + public void testNFCFromISO88596() + throws ParsingException, IOException { + + // This test fails in 1.2.2 due to an apparent bug in the + // conversion of the characters '1' and '0' to bytes in + // ISO-8859-6 + isoNormalizationTest("ISO-8859-6"); + + } + + + public void testNFCFromISO88597() + throws ParsingException, IOException { + isoNormalizationTest("ISO-8859-7"); + } + + + public void testNFCFromISO88598() + throws ParsingException, IOException { + isoNormalizationTest("ISO-8859-8"); + } + + + public void testNFCFromISO88599() + throws ParsingException, IOException { + isoNormalizationTest("ISO-8859-9"); + } + + + public void testNFCFromISO885913() + throws ParsingException, IOException { + + if (version >= 1.3) { + // iSO-8859-6 not supported in Java 1.2 + isoNormalizationTest("ISO-8859-13"); + } + + } + + + public void testNFCFromISO885915() + throws ParsingException, IOException { + isoNormalizationTest("ISO-8859-15"); + } + + + // 14 and 16 aren't tested because Java doesn't support them yet + private void isoNormalizationTest(String encoding) + throws ParsingException, IOException { + + String prolog = "\r\n"; + + byte[] prologData = prolog.getBytes(encoding); + + String epilog = ""; + byte[] epilogData = epilog.getBytes(encoding); + byte[] data = new byte[prologData.length + epilogData.length + 255 - 160 + 1]; + System.arraycopy(prologData, 0, data, 0, prologData.length); + System.arraycopy(epilogData, 0, data, + data.length - epilogData.length, epilogData.length); + for (int i = 160; i <= 255; i++) { + data[prologData.length + (i-160)] = (byte) i; + } + InputStream in = new ByteArrayInputStream(data); + Document doc = builder.build(in); + + // make a Unicode normalized version of the same document + Serializer serializer = new Serializer(out); + serializer.setUnicodeNormalizationFormC(true); + serializer.write(doc); + byte[] temp = out.toByteArray(); + in = new ByteArrayInputStream(temp); + Document nfcDoc = builder.build(in); + + assertEquals("Parser doesn't use NFC when converting from " + encoding, + doc, nfcDoc); + + } + + + public void testEBCDIC() + throws ParsingException, IOException { + + String encoding = "IBM037"; + String prolog = "\r\n"; + byte[] prologData = prolog.getBytes(encoding); + String epilog = ""; + byte[] epilogData = epilog.getBytes(encoding); + byte[] data = new byte[prologData.length + epilogData.length + 255 - 160 + 1]; + System.arraycopy(prologData, 0, data, 0, prologData.length); + System.arraycopy(epilogData, 0, data, + data.length - epilogData.length, epilogData.length); + StringBuffer buffer = new StringBuffer(255 - 160 + 1); + for (int i = 160; i <= 255; i++) { + buffer.append((char) i); + } + byte[] temp = buffer.toString().getBytes(encoding); + System.arraycopy(temp, 0, data, prologData.length, temp.length); + InputStream in = new ByteArrayInputStream(data); + Document doc = builder.build(in); + + // make a Unicode normalized version of the same document + Serializer serializer = new Serializer(out); + serializer.setUnicodeNormalizationFormC(true); + serializer.write(doc); + temp = out.toByteArray(); + in = new ByteArrayInputStream(temp); + Document nfcDoc = builder.build(in); + + // String normalizedResult = Normalizer.normalize(rawResult, Normalizer.NFC); + assertEquals("Parser doesn't use NFC when converting from " + encoding, + doc, nfcDoc); + + } + + + // make sure null pointer exception doesn't cause any output + public void testNullDocument() + throws IOException { + + try { + canonicalizer.write((Document) null); + fail("Wrote null document"); + } + catch (NullPointerException success) { + // success + } + byte[] result = out.toByteArray(); + assertEquals(0, result.length); + + } + + + public void testWhiteSpaceTrimmingInNonCDATAAttribute() + throws IOException { + + Attribute attribute = new Attribute("name", " value1 value2 "); + attribute.setType(Attribute.Type.NMTOKENS); + Element root = new Element("root"); + root.addAttribute(attribute); + Document doc = new Document(root); + canonicalizer.write(doc); + out.close(); + String result = new String(out.toByteArray(), "UTF8"); + assertEquals("", result); + + } + + + // compare to output generated by Apache XML Security code + public void testXMLConformanceTestSuiteDocuments() + throws ParsingException, IOException { + + File masterList = new File(canonical, "xmlconf"); + masterList = new File(masterList, "xmlconf.xml"); + if (masterList.exists()) { + Document xmlconf = builder.build(masterList); + Elements testcases = xmlconf.getRootElement().getChildElements("TESTCASES"); + processTestCases(testcases); + } + + } + + + // xmlconf/xmltest/valid/sa/097.xml appears to be screwed up by a lot + // of parsers + private void processTestCases(Elements testcases) + throws ParsingException, IOException { + + for (int i = 0; i < testcases.size(); i++) { + Element testcase = testcases.get(i); + Elements tests = testcase.getChildElements("TEST"); + processTests(tests); + Elements level2 = testcase.getChildElements("TESTCASES"); + // need to be recursive to handle recursive IBM test cases + processTestCases(level2); + } + + } + + + private void processTests(Elements tests) + throws ParsingException, IOException { + + Element parent = new Element("e"); + Element child = new Element("a"); + parent.appendChild(child); + + int size = tests.size(); + for (int i = 0; i < size; i++) { + Element test = tests.get(i); + String namespace = test.getAttributeValue("NAMESPACE"); + if ("no".equals(namespace)) continue; + String type = test.getAttributeValue("TYPE"); + if ("not-wf".equals(type)) continue; + String uri = test.getAttributeValue("URI"); + String base = test.getBaseURI(); + // Hack because URIUtil isn't public; and I don't want to + // depend on 1.4 only java.net.URI + parent.setBaseURI(base); + + child.addAttribute(new Attribute("xml:base", + "http://www.w3.org/XML/1998/namespace", uri)); + String resolvedURI = child.getBaseURI(); + + Document doc = builder.build(resolvedURI); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + try { + Canonicalizer serializer = new Canonicalizer(out); + serializer.write(doc); + } + finally { + out.close(); + } + byte[] actual = out.toByteArray(); + + File input = new File(resolvedURI.substring(5) + ".can"); + assertEquals(resolvedURI, input.length(), actual.length); + byte[] expected = new byte[actual.length]; + DataInputStream in = new DataInputStream( + new BufferedInputStream(new FileInputStream(input))); + try { + in.readFully(expected); + } + finally { + in.close(); + } + assertEquals(expected, actual); + + } + + } + + + public void testExclusive() throws IOException { + + Element pdu = new Element("n0:pdu", "http://a.example"); + Element elem1 = new Element("n1:elem1", "http://b.example"); + elem1.appendChild("content"); + pdu.appendChild(elem1); + + String expected = "content"; + Canonicalizer canonicalizer = new Canonicalizer(out, + Canonicalizer.EXCLUSIVE_XML_CANONICALIZATION_WITH_COMMENTS); + + XPathContext context = new XPathContext("n1", "http://b.example"); + Document doc = new Document(pdu); + canonicalizer.write(doc.query("(//. | //@* | //namespace::*)[ancestor-or-self::n1:elem1]", context)); + + out.close(); + String s = new String(out.toByteArray(), "UTF8"); + assertEquals(expected, s); + + } + + public void testAustB() throws IOException { + + Element e1 = new Element("a:a", "urn:a"); + Element e2 = new Element("b"); + e1.appendChild(e2); + + Canonicalizer c = new Canonicalizer(out, + Canonicalizer.EXCLUSIVE_XML_CANONICALIZATION); + c.write(e1); + String s = out.toString("UTF8"); + assertEquals("", s); + + } + + + public void testAustB2() throws IOException { + + Element e1 = new Element("a:a", "urn:a"); + Element e2 = new Element("b"); + e1.appendChild(e2); + + Canonicalizer c = new Canonicalizer(out); + c.write(e1); + String s = out.toString("UTF8"); + assertEquals("", s); + + } + + + public void testAust() throws Exception { + + Element e1 = new Element("a:a", "urn:a"); + Element e2 = new Element("a:b", "urn:a"); + e1.appendChild(e2); + Canonicalizer c = new Canonicalizer(out, + Canonicalizer.EXCLUSIVE_XML_CANONICALIZATION); + c.write(e2); + String s = out.toString("UTF8"); + assertEquals("", s); + + } + + + public void testAust4() throws Exception { + + Element e1 = new Element("a:a", "urn:a"); + Element e2 = new Element("a:b", "urn:a"); + e1.appendChild(e2); + Canonicalizer canonicalizer = new Canonicalizer(out, + Canonicalizer.EXCLUSIVE_XML_CANONICALIZATION_WITH_COMMENTS); + XPathContext context = new XPathContext("a", "urn:a"); + Document doc = new Document(e1); + canonicalizer.write(doc.query("(//. | //@* | //namespace::*)[ancestor-or-self::a:b]", context)); + + String s = out.toString("UTF8"); + assertEquals("", s); + + } + + + public void testAust5() throws Exception { + + Element e1 = new Element("a:a", "urn:a"); + Element e2 = new Element("a:b", "urn:a"); + e1.appendChild(e2); + Canonicalizer canonicalizer = new Canonicalizer(out, + Canonicalizer.EXCLUSIVE_XML_CANONICALIZATION_WITH_COMMENTS); + new Document(e1); + Nodes set = new Nodes(e2); + canonicalizer.write(set); + + String s = out.toString("UTF8"); + // The namespace was not explicitly included in + // the set so it should not be output. + assertEquals("", s); + + } + + + public void testAust3() throws Exception { + + Element e2 = new Element("a:b", "urn:a"); + new Canonicalizer(out, + Canonicalizer.EXCLUSIVE_XML_CANONICALIZATION).write(e2); + String s = out.toString("UTF8"); + assertEquals("", s); + + } + + + public void testAust2() throws Exception { + + Element e1 = new Element("a:a", "urn:a"); + Element e2 = new Element("a:b", "urn:a"); + e1.appendChild(e2); + new Canonicalizer(out, + Canonicalizer.CANONICAL_XML).write(e2); + String s = out.toString("UTF8"); + assertEquals("", s); + + } + + + public void testExclusiveWithNamespacedAttributes() throws IOException { + + Element pdu = new Element("n0:pdu", "http://a.example"); + Element elem1 = new Element("n1:elem1", "http://b.example"); + elem1.appendChild("content"); + pdu.appendChild(elem1); + elem1.addAttribute(new Attribute("pre:foo", "http://www.example.org/", "value")); + + String expected = "content"; + Canonicalizer canonicalizer = new Canonicalizer(out, + Canonicalizer.EXCLUSIVE_XML_CANONICALIZATION_WITH_COMMENTS); + + XPathContext context = new XPathContext("n1", "http://b.example"); + Document doc = new Document(pdu); + canonicalizer.write(doc.query("(//. | //@* | //namespace::*)[ancestor-or-self::n1:elem1]", context)); + + out.close(); + String s = new String(out.toByteArray(), "UTF8"); + assertEquals(expected, s); + + } + + + /*
    test
    + +Choose the document subset selected by /root//node() + +and expect to see + +test */ + public void testInheritanceOfXMLLang() throws IOException { + + Element root = new Element("root"); + root.addAttribute(new Attribute("xml:lang", Namespace.XML_NAMESPACE, "en")); + Element a = new Element("a"); + Element b = new Element("b"); + b.appendChild("test"); + a.appendChild(b); + root.appendChild(a); + + String expected = "test"; + Canonicalizer canonicalizer = new Canonicalizer(out, Canonicalizer.CANONICAL_XML); + + Document doc = new Document(root); + canonicalizer.write(doc.query("/root//node()")); + + out.close(); + String s = new String(out.toByteArray(), "UTF8"); + assertEquals(expected, s); + + } + + + public void testExclusiveWithInclusiveNamespaces() + throws IOException { + + Element pdu = new Element("n0:pdu", "http://a.example"); + Element elem1 = new Element("n1:elem1", "http://b.example"); + elem1.appendChild("content"); + pdu.appendChild(elem1); + + String expected = "content"; + Canonicalizer canonicalizer = new Canonicalizer(out, + Canonicalizer.EXCLUSIVE_XML_CANONICALIZATION_WITH_COMMENTS); + + XPathContext context = new XPathContext("n1", "http://b.example"); + Document doc = new Document(pdu); + canonicalizer.setInclusiveNamespacePrefixList("n0"); + Nodes subset = doc.query( + "(//. | //@* | //namespace::*)[ancestor-or-self::n1:elem1]", + context); + canonicalizer.write(subset); + + out.close(); + String s = new String(out.toByteArray(), "UTF8"); + assertEquals(expected, s); + + } + + + public void testClearInclusiveNamespacePrefixes() + throws IOException { + + Element pdu = new Element("n0:pdu", "http://a.example"); + Element elem1 = new Element("n1:elem1", "http://b.example"); + elem1.appendChild("content"); + pdu.appendChild(elem1); + + String expected = "content"; + Canonicalizer canonicalizer = new Canonicalizer(out, + Canonicalizer.EXCLUSIVE_XML_CANONICALIZATION_WITH_COMMENTS); + + XPathContext context = new XPathContext("n1", "http://b.example"); + Document doc = new Document(pdu); + canonicalizer.setInclusiveNamespacePrefixList("n0"); + canonicalizer.setInclusiveNamespacePrefixList(null); + Nodes subset = doc.query( + "(//. | //@* | //namespace::*)[ancestor-or-self::n1:elem1]", + context); + canonicalizer.write(subset); + + out.close(); + String s = new String(out.toByteArray(), "UTF8"); + assertEquals(expected, s); + + } + + + public void testExclusive22a() throws ParsingException, IOException { + + Builder builder = new Builder(); + String input = "" + + "" + + ""; + Document doc = builder.build(input, null); + + String expected = "" + + ""; + Canonicalizer canonicalizer = new Canonicalizer(out, + Canonicalizer.EXCLUSIVE_XML_CANONICALIZATION_WITH_COMMENTS); + + XPathContext context = new XPathContext("n1", "http://example.net"); + canonicalizer.write(doc.query( + " (//. | //@* | //namespace::*)[ancestor-or-self::n1:elem2]", + context)); + + out.close(); + String s = new String(out.toByteArray(), "UTF8"); + assertEquals(expected, s); + + } + + + public void testExclusive22b() throws ParsingException, IOException { + + Builder builder = new Builder(); + String input = "" + + "" + + ""; + Document doc = builder.build(input, null); + + String expected = "" + + ""; + Canonicalizer canonicalizer = new Canonicalizer(out, + Canonicalizer.EXCLUSIVE_XML_CANONICALIZATION_WITH_COMMENTS); + + XPathContext context = new XPathContext("n1", "http://example.net"); + canonicalizer.write(doc.query(" (//. | //@* | //namespace::*)[ancestor-or-self::n1:elem2]", context)); + + out.close(); + String s = new String(out.toByteArray(), "UTF8"); + assertEquals(expected, s); + + } + + + // compare to output generated by Apache XML Security code + public void testExclusiveXMLConformanceTestSuiteDocuments() + throws ParsingException, IOException { + + File masterList = new File(canonical, "xmlconf"); + masterList = new File(masterList, "xmlconf.xml"); + if (masterList.exists()) { + Document xmlconf = builder.build(masterList); + Elements testcases = xmlconf.getRootElement().getChildElements("TESTCASES"); + processExclusiveTestCases(testcases); + } + + } + + + // xmlconf/xmltest/valid/sa/097.xml appears to be screwed up by a lot + // of parsers + private void processExclusiveTestCases(Elements testcases) + throws ParsingException, IOException { + + for (int i = 0; i < testcases.size(); i++) { + Element testcase = testcases.get(i); + Elements tests = testcase.getChildElements("TEST"); + processExclusiveTests(tests); + Elements level2 = testcase.getChildElements("TESTCASES"); + // need to be recursive to handle recursive IBM test cases + processExclusiveTestCases(level2); + } + + } + + + private void processExclusiveTests(Elements tests) + throws ParsingException, IOException { + + for (int i = 0; i < tests.size(); i++) { + Element test = tests.get(i); + String namespace = test.getAttributeValue("NAMESPACE"); + if ("no".equals(namespace)) continue; + String type = test.getAttributeValue("TYPE"); + if ("not-wf".equals(type)) continue; + String uri = test.getAttributeValue("URI"); + String base = test.getBaseURI(); + // Hack because URIUtil isn't public; and I don't want to + // depend on 1.4 only java.net.URI + Element parent = new Element("e"); + parent.setBaseURI(base); + Element child = new Element("a"); + child.addAttribute(new Attribute("xml:base", + "http://www.w3.org/XML/1998/namespace", uri)); + parent.appendChild(child); + String resolvedURI = child.getBaseURI(); + + Document doc = builder.build(resolvedURI); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + try { + Canonicalizer serializer = new Canonicalizer( + out, + Canonicalizer.EXCLUSIVE_XML_CANONICALIZATION_WITH_COMMENTS + ); + serializer.write(doc); + } + finally { + out.close(); + } + byte[] actual = out.toByteArray(); + + File input = new File(resolvedURI.substring(5) + ".exc"); + byte[] expected = new byte[(int) input.length()]; + DataInputStream in = new DataInputStream( + new BufferedInputStream(new FileInputStream(input))); + try { + in.readFully(expected); + } + finally { + in.close(); + } + + assertEquals(resolvedURI, new String(expected, "UTF-8"), new String(actual, "UTF-8")); + + } + + } + + + public void testCanonicalizeAttribute() throws IOException { + + Attribute att = new Attribute("pre:foo", "http://www.example.org", "value"); + try { + Canonicalizer serializer = new Canonicalizer(out); + serializer.write(att); + } + finally { + out.close(); + } + byte[] actual = out.toByteArray(); + byte[] expected = " pre:foo=\"value\"".getBytes("UTF-8"); + assertEquals(expected, actual); + + } + + + public void testCanonicalizeNamespace() throws IOException { + + Element element = new Element("pre:foo", "http://www.example.org"); + Nodes namespaces = element.query("namespace::pre"); + Namespace ns = (Namespace) namespaces.get(0); + try { + Canonicalizer serializer = new Canonicalizer(out); + serializer.write(ns); + } + finally { + out.close(); + } + byte[] actual = out.toByteArray(); + byte[] expected = " xmlns:pre=\"http://www.example.org\"".getBytes("UTF-8"); + assertEquals(expected, actual); + + + } + + + public void testCanonicalizeDefaultNamespace() throws IOException { + + Element element = new Element("foo", "http://www.example.org"); + Nodes namespaces = element.query("namespace::*"); + Namespace ns = (Namespace) namespaces.get(0); + if (ns.getPrefix().equals("xml")) ns = (Namespace) namespaces.get(1); + try { + Canonicalizer serializer = new Canonicalizer(out); + serializer.write(ns); + } + finally { + out.close(); + } + byte[] actual = out.toByteArray(); + byte[] expected = " xmlns=\"http://www.example.org\"".getBytes("UTF-8"); + assertEquals(expected, actual); + + } + + + public void testCanonicalizeXMLNamespace() throws IOException { + + Element element = new Element("foo"); + Nodes namespaces = element.query("namespace::*"); + Namespace ns = (Namespace) namespaces.get(0); + try { + Canonicalizer serializer = new Canonicalizer(out); + serializer.write(ns); + } + finally { + out.close(); + } + byte[] actual = out.toByteArray(); + byte[] expected + = " xmlns:xml=\"http://www.w3.org/XML/1998/namespace\"".getBytes("UTF-8"); + assertEquals(expected, actual); + + } + + + public void testCanonicalizeElement() throws IOException { + + Element element = new Element("pre:foo", "http://www.example.org"); + element.appendChild(" value \n value"); + try { + Canonicalizer serializer = new Canonicalizer(out); + serializer.write(element); + } + finally { + out.close(); + } + byte[] actual = out.toByteArray(); + byte[] expected = + " value \n value" + .getBytes("UTF-8"); + assertEquals(expected, actual); + + } + + + public void testDontPutElementInDocument() throws IOException { + + Element element = new Element("pre:foo", "http://www.example.org"); + assertNull(element.getDocument()); + try { + Canonicalizer serializer = new Canonicalizer(out); + serializer.write(element); + assertNull(element.getDocument()); + } + finally { + out.close(); + } + + } + + + public void testCanonicalizeElementInDocument() throws IOException { + + Element root = new Element("root"); + new Document(root); + Element element = new Element("pre:foo", "http://www.example.org"); + root.appendChild(element); + element.appendChild(" value \n value"); + try { + Canonicalizer serializer = new Canonicalizer(out); + serializer.write(element); + } + finally { + out.close(); + } + byte[] actual = out.toByteArray(); + byte[] expected = + " value \n value" + .getBytes("UTF-8"); + assertEquals(expected, actual); + + } + + + public void testNoInfiniteLoopWhenWritingADocumentlessElement() throws IOException { + + Element root = new Element("root"); + Element a = new Element("a"); + root.appendChild(a); + Element b = new Element("b"); + a.appendChild(b); + + try { + Canonicalizer serializer = new Canonicalizer(out); + serializer.write(b); + } + finally { + out.close(); + } + byte[] actual = out.toByteArray(); + byte[] expected = + "" + .getBytes("UTF-8"); + assertEquals(expected, actual); + + } + + + public void testExclusiveCanonicalizeElementInDocument() throws IOException { + + Element root = new Element("root"); + new Document(root); + Element element = new Element("pre:foo", "http://www.example.org"); + root.appendChild(element); + element.appendChild(" value \n value"); + try { + Canonicalizer serializer = new Canonicalizer(out, Canonicalizer.EXCLUSIVE_XML_CANONICALIZATION); + serializer.write(element); + } + finally { + out.close(); + } + byte[] actual = out.toByteArray(); + byte[] expected = + " value \n value" + .getBytes("UTF-8"); + assertEquals(expected, actual); + + } + + + public void testCanonicalizeDocumentTypeDeclaration() throws IOException { + + DocType doctype = new DocType("root", "http://www.example.org"); + try { + Canonicalizer serializer = new Canonicalizer(out); + serializer.write(doctype); + } + finally { + out.close(); + } + byte[] actual = out.toByteArray(); + assertEquals(0, actual.length); + + } + + + public void testCanonicalizeProcessingInstruction() throws IOException { + + ProcessingInstruction pi = new ProcessingInstruction("target", "value \n value"); + try { + Canonicalizer serializer = new Canonicalizer(out); + serializer.write(pi); + } + finally { + out.close(); + } + byte[] actual = out.toByteArray(); + byte[] expected = "".getBytes("UTF-8"); + assertEquals(expected, actual); + + } + + + public void testCanonicalizeText() throws IOException { + + Text c = new Text(" pre:foo \n "); + try { + Canonicalizer serializer = new Canonicalizer(out); + serializer.write(c); + } + finally { + out.close(); + } + byte[] actual = out.toByteArray(); + byte[] expected = " pre:foo \n ".getBytes("UTF-8"); + assertEquals(expected, actual); + + + } + + + public void testCanonicalizeComment() throws IOException { + + Comment c = new Comment("pre:foo"); + try { + Canonicalizer serializer = new Canonicalizer(out); + serializer.write(c); + } + finally { + out.close(); + } + byte[] actual = out.toByteArray(); + byte[] expected = "".getBytes("UTF-8"); + assertEquals(expected, actual); + + } + + + public void testUnsupportedAlgorithm() throws IOException { + + try { + new Canonicalizer(out, "http://www.example.org/canonical"); + fail("Allowed unrecognized algorithm"); + } + catch (CanonicalizationException success) { + assertNotNull(success.getMessage()); + } + finally { + out.close(); + } + + } + + + public void testCanonicalizeDetachedNodes() throws IOException { + + Element e = new Element("test"); + Nodes nodes = new Nodes(e); + Canonicalizer serializer = new Canonicalizer(out); + try { + serializer.write(nodes); + fail("Canonicalized detached node"); + } + catch (CanonicalizationException success) { + assertNotNull(success.getMessage()); + } + finally { + out.close(); + } + + } + + + public void testCanonicalizeNodesFromTwoDocuments() throws IOException { + + Element e1 = new Element("test"); + new Document(e1); + Element e2 = new Element("test"); + new Document(e2); + Nodes nodes = new Nodes(e1); + nodes.append(e2); + Canonicalizer serializer = new Canonicalizer(out); + try { + serializer.write(nodes); + fail("Canonicalized multiple document nodes"); + } + catch (CanonicalizationException success) { + assertNotNull(success.getMessage()); + } + finally { + out.close(); + } + + } + + + /** + *

    + * Asserts that two byte arrays are equal. If the two arrays are + * not equal a ComparisonFailure is thrown. Two + * arrays are equal if and only if they have the same length, + * and each item in the expected array is equal to the + * corresponding item in the actual array. + *

    + * + * @param expected the byte array the test should produce + * @param actual the byte array the test does produce + */ + private void assertEquals(byte[] expected, byte[] actual) { + + if (expected == null && actual == null) { + return; + } + // what if one is null and the other isn't???? + assertEquals(expected.length, actual.length); + for (int i = 0; i < actual.length; i++) { + assertEquals(expected[i], actual[i]); + } + + } + + + // This class forces IOExceptions when writing + private static class UnwriteableOutputStream extends OutputStream { + + public void write(int b) throws IOException { + throw new IOException(); + } + + } + + + public void testDetachElementWhenExceptionIsThrown() { + + Element e = new Element("a"); + Canonicalizer canonicalizer = new Canonicalizer(new UnwriteableOutputStream()); + try { + canonicalizer.write(e); + } + catch (IOException ex) { + } + assertNull(e.getParent()); + + } + + +} diff --git a/src/nu/xom/tests/CommentTest.java b/src/nu/xom/tests/CommentTest.java new file mode 100644 index 0000000..f9ee7dd --- /dev/null +++ b/src/nu/xom/tests/CommentTest.java @@ -0,0 +1,322 @@ +/* Copyright 2002-2004 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.tests; + +import nu.xom.Comment; +import nu.xom.Document; +import nu.xom.Element; +import nu.xom.IllegalDataException; +import nu.xom.IllegalCharacterDataException; + + +/** + *

    + * Various basic unit tests for the Comment class. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.3.0 + * + */ +public class CommentTest extends XOMTestCase { + + + public CommentTest(String name) { + super(name); + } + + + public void testConstructor() { + Comment c1 = new Comment("test"); + assertEquals("test", c1.getValue()); + Comment c2 = new Comment(""); + assertEquals("", c2.getValue()); + } + + + public void testCopyConstructor() { + Comment c1 = new Comment("test"); + Comment c2 = new Comment(c1); + assertEquals("test", c2.getValue()); + assertEquals(c1.getValue(), c2.getValue()); + assertTrue(c1 != c2); + } + + + public void testToString() { + + Comment c1 = new Comment("content"); + assertEquals("[nu.xom.Comment: content]", c1.toString()); + + c1.setValue("012345678901234567890123456789012345678901234567890123456789"); + assertEquals( + "[nu.xom.Comment: 01234567890123456789012345678901234...]", + c1.toString() + ); + + } + + + public void testToStringWithLineFeed() { + + Comment c = new Comment("content\ncontent"); + assertEquals("[nu.xom.Comment: content\\ncontent]", c.toString()); + + } + + + public void testToStringWithLotsOfData() { + + Comment c + = new Comment("content content 012345678901234567890123456789012345678901234567890123456789"); + String s = c.toString(); + assertTrue(s.length() < 72); + + } + + + public void testToXML() { + + Comment c1 = new Comment("content"); + assertEquals("", c1.toXML()); + + c1.setValue(" 012345678901234567890123456789012345678901234567890123456789 "); + assertEquals( + "", + c1.toXML() + ); + + } + + + // This is a problem because it cannot be serialized + // since character and entity references aren't + // recognized in comment data + public void testCarriageReturnInCommentData() { + try { + new Comment("data\rdata"); + fail("Allowed carriage return in comment"); + } + catch (IllegalDataException success) { + assertEquals("data\rdata", success.getData()); + assertNotNull(success.getMessage()); + } + } + + + public void testSetter() { + + Comment c1 = new Comment("test"); + c1.setValue("legal"); + assertEquals("legal", c1.getValue()); + + try { + c1.setValue("test -- test"); + fail("Should raise an IllegalDataException"); + } + catch (IllegalDataException success) { + assertEquals("test -- test", success.getData()); + assertNotNull(success.getMessage()); + } + + try { + c1.setValue("test-"); + fail("Should raise an IllegalDataException"); + } + catch (IllegalDataException success) { + assertEquals("test-", success.getData()); + assertNotNull(success.getMessage()); + } + + c1.setValue(null); + assertEquals("", c1.getValue()); + + } + + + public void testEquals() { + + Comment c1 = new Comment("test"); + Comment c2 = new Comment("test"); + Comment c3 = new Comment("skjlchsakdjh"); + + assertEquals(c1, c1); + assertEquals(c1.hashCode(), c1.hashCode()); + assertTrue(!c1.equals(c2)); + assertTrue(!c1.equals(c3)); + + } + + + public void testCopy() { + + Comment c1 = new Comment("test"); + Comment c2 = c1.copy(); + + assertEquals(c1.getValue(), c2.getValue()); + assertTrue(!c1.equals(c2)); + assertNull(c2.getParent()); + + } + + + // Check passing in a string with broken surrogate pairs + // and with correct surrogate pairs + public void testSurrogates() { + + String goodString = "test: \uD8F5\uDF80 "; + Comment c = new Comment(goodString); + assertEquals(goodString, c.getValue()); + + // Two high-halves + try { + new Comment("test: \uD8F5\uDBF0 "); + fail("Should raise an IllegalCharacterDataException"); + } + catch (IllegalCharacterDataException success) { + assertEquals("test: \uD8F5\uDBF0 ", success.getData()); + assertNotNull(success.getMessage()); + } + + // Two high-halves + try { + new Comment("test: \uD8F5\uD8F5 "); + fail("Should raise an IllegalCharacterDataException"); + } + catch (IllegalCharacterDataException success) { + assertEquals("test: \uD8F5\uD8F5 ", success.getData()); + assertNotNull(success.getMessage()); + } + + // One high-half + try { + new Comment("test: \uD8F5 "); + fail("Should raise an IllegalCharacterDataException"); + } + catch (IllegalCharacterDataException success) { + assertEquals("test: \uD8F5 ", success.getData()); + assertNotNull(success.getMessage()); + } + + // One low half + try { + new Comment("test: \uDF80 "); + fail("Should raise an IllegalCharacterDataException"); + } + catch (IllegalCharacterDataException success) { + assertEquals("test: \uDF80 ", success.getData()); + assertNotNull(success.getMessage()); + } + + // Low half before high half + try { + new Comment("test: \uDCF5\uD8F5 "); + fail("Should raise an IllegalCharacterDataException"); + } + catch (IllegalCharacterDataException success) { + assertEquals("test: \uDCF5\uD8F5 ", success.getData()); + assertNotNull(success.getMessage()); + } + + + } + + + public void testLeafNode() { + + Comment c1 = new Comment("data"); + assertEquals(0, c1.getChildCount()); + try { + c1.getChild(0); + fail("Didn't throw IndexOutofBoundsException"); + } + catch (IndexOutOfBoundsException success) { + assertNotNull(success.getMessage()); + } + + assertNull(c1.getParent()); + + Element element = new Element("test"); + element.appendChild(c1); + assertEquals(element, c1.getParent()); + assertEquals(element.getChild(0), c1); + + element.removeChild(c1); + assertTrue(element.getChildCount() == 0); + + } + + + public void testGetDocument() { + + Comment c1 = new Comment("data"); + assertNull(c1.getDocument()); + Element root = new Element("root"); + root.appendChild(c1); + assertNull(c1.getDocument()); + Document doc = new Document(root); + assertEquals(doc, c1.getDocument()); + + } + + + public void testAllowReservedCharactersInData() { + Comment comment = new Comment("&&greater;"); + String xml = comment.toXML(); + assertEquals("", xml); + } + + + public void testForbidIllegalCharactersInComments() { + + try { + new Comment(" \u0001 "); + fail("Allowed C0 control in comment"); + } + catch (IllegalCharacterDataException success) { + assertEquals(" \u0001 ", success.getData()); + assertNotNull(success.getMessage()); + } + + } + + + public void testForbidUnmatchedSurrogatesInComments() { + + try { + new Comment(" \uD800 "); + fail("Allowed unmatched high surrogate in comment"); + } + catch (IllegalCharacterDataException success) { + assertEquals(" \uD800 ", success.getData()); + assertNotNull(success.getMessage()); + } + + } + + + public void testCommentDataCanStartWithAHyphen() { + Comment c = new Comment("- - "); + assertEquals("- - ", c.getValue()); + } + +} diff --git a/src/nu/xom/tests/DOMConverterTest.java b/src/nu/xom/tests/DOMConverterTest.java new file mode 100644 index 0000000..edaf0aa --- /dev/null +++ b/src/nu/xom/tests/DOMConverterTest.java @@ -0,0 +1,737 @@ +/* Copyright 2002-2006, 2019 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.tests; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.IOException; +import java.io.Reader; +import java.io.StringReader; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; + +import nu.xom.Attribute; +import nu.xom.Builder; +import nu.xom.Comment; +import nu.xom.DocType; +import nu.xom.Document; +import nu.xom.Element; +import nu.xom.Elements; +import nu.xom.Namespace; +import nu.xom.Node; +import nu.xom.NodeFactory; +import nu.xom.Nodes; +import nu.xom.ParsingException; +import nu.xom.ProcessingInstruction; +import nu.xom.Text; +import nu.xom.XMLException; +import nu.xom.converters.DOMConverter; + +import org.w3c.dom.CDATASection; +import org.w3c.dom.DOMImplementation; +import org.w3c.dom.DocumentFragment; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; + + +/** + *

    + * Basic tests for conversion from DOM trees to XOM trees + * and back again. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.3.1 + * + */ +public class DOMConverterTest extends XOMTestCase { + + + public DOMConverterTest(String name) { + super(name); + } + + + private String source = "\r\n" + + "" + + "" + + "Hello dear" + + "\r\nvery important" + + "here's the link\r\n" + + "" + + "text in a namespace\r\n" + + "text in a " + + "namespace" + + "\r\n" + + ""; + + + private Document xomDocument; + private org.w3c.dom.Document domDocument; + private DOMImplementation impl; + private DocumentBuilder builder; + + + protected void setUp() throws ParserConfigurationException { + + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + factory.setNamespaceAware(true); + builder = factory.newDocumentBuilder(); + impl = builder.getDOMImplementation(); + + DocType type = new DocType("test"); + Element root = new Element("test"); + xomDocument = new Document(root); + xomDocument.insertChild(type, 0); + xomDocument.insertChild(new ProcessingInstruction( + "xml-stylesheet", "href=\"file.css\" type=\"text/css\""), 1); + xomDocument.insertChild(new Comment(" test "), 2); + xomDocument.appendChild(new Comment("epilog")); + root.addNamespaceDeclaration("xlink", + "http://www.w3.org/TR/1999/xlink"); + root.appendChild("Hello dear\r\n"); + Element em = new Element("em"); + root.appendChild(em); + em.addAttribute(new Attribute("id", "p1")); + em.addNamespaceDeclaration("none", "http://www.example.com"); + em.appendChild("very important"); + Element span = new Element("span"); + root.appendChild(span); + span.addAttribute(new Attribute("xlink:type", + "http://www.w3.org/TR/1999/xlink", "simple")); + span.appendChild("here's the link"); + root.appendChild("\r\n"); + + Element empty = new Element("empty"); + root.appendChild(empty); + empty.addAttribute(new Attribute("xom:temp", + "http://xom.nu/", + "Just to see if this can handle namespaced attributes")); + + root.appendChild("\r\n"); + Element svg = new Element("svg:svg", + "http://www.w3.org/TR/2000/svg"); + root.appendChild(svg); + Element text = new Element("svg:text", "http://www.w3.org/TR/2000/svg"); + svg.appendChild(text); + text.appendChild("text in a namespace"); + root.appendChild("\r\n"); + + svg = new Element("svg", "http://www.w3.org/TR/2000/svg"); + root.appendChild(svg); + text = new Element("text", "http://www.w3.org/TR/2000/svg"); + svg.appendChild(text); + text.appendChild("text in a namespace"); + + Reader reader = new StringReader(source); + InputSource inso = new InputSource(reader); + + try { + domDocument = builder.parse(inso); + } + catch (Exception ex) { + // shouldn't happen from known good doc + throw new RuntimeException("Oops!"); + } + + } + + + public void testToDOM() { + + org.w3c.dom.Document domDoc = DOMConverter.convert(xomDocument, impl); + org.w3c.dom.DocumentType doctype = domDoc.getDoctype(); + + assertEquals("test", domDoc.getDocumentElement().getNodeName()); + assertTrue(doctype != null); + assertEquals(org.w3c.dom.Node.DOCUMENT_TYPE_NODE, + domDoc.getFirstChild().getNodeType()); + assertEquals(org.w3c.dom.Node.COMMENT_NODE, + domDoc.getLastChild().getNodeType()); + + } + + + public void testToXOM() { + + Document xomDoc = DOMConverter.convert(domDocument); + DocType doctype = xomDoc.getDocType(); + Element root = xomDoc.getRootElement(); + + assertEquals("test", root.getQualifiedName()); + assertEquals("test", root.getLocalName()); + assertEquals("", root.getNamespaceURI()); + + assertNotNull(doctype); + assertTrue(xomDoc.getChild(0) instanceof DocType); + assertTrue(xomDoc.getChild(4) instanceof nu.xom.Comment); + assertTrue(xomDoc.getChild(2) instanceof nu.xom.Comment); + assertEquals(" test ", xomDoc.getChild(2).getValue()); + assertEquals("epilog", xomDoc.getChild(4).getValue()); + assertTrue( + xomDoc.getChild(1) instanceof nu.xom.ProcessingInstruction); + assertEquals("test", doctype.getRootElementName()); + assertNull(doctype.getPublicID()); + assertNull(doctype.getSystemID()); + + } + + + public void testDefaultNamespacedElement() + throws SAXException, IOException, ParserConfigurationException { + + byte[] data = "".getBytes(); + org.w3c.dom.Document doc = builder.parse(new ByteArrayInputStream(data)); + Document xomDoc = DOMConverter.convert(doc); + + Element root = xomDoc.getRootElement(); + assertEquals("root", root.getQualifiedName()); + assertEquals("http://www.example.com", root.getNamespaceURI()); + + } + + + public void testPrefixedElement() + throws SAXException, IOException, ParserConfigurationException { + + byte[] data = "".getBytes(); + org.w3c.dom.Document doc = builder.parse(new ByteArrayInputStream(data)); + Document xomDoc = DOMConverter.convert(doc); + + Element root = xomDoc.getRootElement(); + assertEquals("pre:root", root.getQualifiedName()); + assertEquals("http://www.example.com", root.getNamespaceURI()); + + } + + + public void testConvertAttr() + throws SAXException, IOException, ParserConfigurationException { + + byte[] data = ("").getBytes(); + org.w3c.dom.Document doc = builder.parse(new ByteArrayInputStream(data)); + + org.w3c.dom.Element root = doc.getDocumentElement(); + Attribute attribute = DOMConverter.convert(root.getAttributeNode("name")); + assertEquals("name", attribute.getQualifiedName()); + assertEquals("", attribute.getNamespacePrefix()); + assertEquals("", attribute.getNamespaceURI()); + assertEquals("value", attribute.getValue()); + + try { + DOMConverter.convert(root.getAttributeNode("xmlns")); + fail("Converted xmlns attribute"); + } + catch (XMLException ex) { + assertNotNull(ex.getMessage()); + } + try { + DOMConverter.convert(root.getAttributeNode("xmlns:pre")); + fail("Converted xmlns:pre attribute"); + } + catch (XMLException ex) { + assertNotNull(ex.getMessage()); + } + + } + + + public void testConvertElement() + throws SAXException, IOException, ParserConfigurationException { + + byte[] data = ("").getBytes(); + org.w3c.dom.Document doc = builder.parse(new ByteArrayInputStream(data)); + + org.w3c.dom.Element root = doc.getDocumentElement(); + Element xomRoot = DOMConverter.convert(root); + assertEquals("name", xomRoot.getAttribute("name").getQualifiedName()); + assertEquals("", xomRoot.getAttribute("name").getNamespacePrefix()); + assertEquals("", xomRoot.getAttribute("name").getNamespaceURI()); + assertEquals("value", xomRoot.getAttribute("name").getValue()); + assertEquals("element", xomRoot.getQualifiedName()); + assertEquals("", xomRoot.getValue()); + assertEquals(0, xomRoot.getChildCount()); + assertEquals("http://example.com/", xomRoot.getNamespaceURI()); + assertEquals("http://example.net", xomRoot.getNamespaceURI("pre")); + + } + + + public void testConvertComment() + throws SAXException, IOException, ParserConfigurationException { + + byte[] data = "".getBytes(); + org.w3c.dom.Document doc = builder.parse(new ByteArrayInputStream(data)); + + org.w3c.dom.Element root = doc.getDocumentElement(); + org.w3c.dom.Comment comment = (org.w3c.dom.Comment) (root.getChildNodes().item(0)); + Comment xomComment = DOMConverter.convert(comment); + assertEquals(comment.getNodeValue(), xomComment.getValue()); + + } + + + public void testConvertText() + throws SAXException, IOException, ParserConfigurationException { + + byte[] data = " here's the text ".getBytes(); + org.w3c.dom.Document doc = builder.parse(new ByteArrayInputStream(data)); + + org.w3c.dom.Element root = doc.getDocumentElement(); + org.w3c.dom.Text node = (org.w3c.dom.Text) (root.getChildNodes().item(0)); + Text text = DOMConverter.convert(node); + assertEquals(node.getNodeValue(), text.getValue()); + + } + + + public void testConvertCDATASection() + throws SAXException, IOException, ParserConfigurationException { + + byte[] data = "".getBytes(); + org.w3c.dom.Document doc = builder.parse(new ByteArrayInputStream(data)); + + org.w3c.dom.Element root = doc.getDocumentElement(); + CDATASection node = (CDATASection) (root.getChildNodes().item(0)); + Text text = DOMConverter.convert(node); + assertEquals(node.getNodeValue(), text.getValue()); + + // Now test indirect conversion + Document xomDoc = DOMConverter.convert(doc); + assertEquals(node.getNodeValue(), xomDoc.getValue()); + + } + + + public void testConvertProcessingInstruction() + throws SAXException, IOException, ParserConfigurationException { + + byte[] data = "".getBytes(); + org.w3c.dom.Document doc = builder.parse(new ByteArrayInputStream(data)); + + org.w3c.dom.Element root = doc.getDocumentElement(); + org.w3c.dom.ProcessingInstruction node + = (org.w3c.dom.ProcessingInstruction) (root.getChildNodes().item(0)); + ProcessingInstruction pi = DOMConverter.convert(node); + assertEquals(node.getNodeValue(), pi.getValue()); + assertEquals(node.getTarget(), pi.getTarget()); + + } + + + public void testConvertDocType() + throws SAXException, IOException, ParserConfigurationException { + + byte[] data = "".getBytes(); + org.w3c.dom.Document doc = builder.parse(new ByteArrayInputStream(data)); + + org.w3c.dom.DocumentType type = doc.getDoctype(); + DocType xomType = DOMConverter.convert(type); + assertEquals(type.getName(), xomType.getRootElementName()); + + } + + + public void testConvertInternalDTDSubset() + throws SAXException, IOException, ParserConfigurationException { + + byte[] data = "]>".getBytes(); + org.w3c.dom.Document doc = builder.parse(new ByteArrayInputStream(data)); + + org.w3c.dom.DocumentType type = doc.getDoctype(); + DocType xomType = DOMConverter.convert(type); + assertEquals(type.getName(), xomType.getRootElementName()); + assertEquals(type.getInternalSubset(), xomType.getInternalDTDSubset()); + + } + + + public void testConvertDocTypePublicAndSystemIdentifier() + throws SAXException, IOException, ParserConfigurationException { + + File inputDir = new File("data"); + File f = new File(inputDir, "dtdtest.xhtml"); + org.w3c.dom.Document doc = builder.parse(f); + + org.w3c.dom.DocumentType type = doc.getDoctype(); + DocType xomType = DOMConverter.convert(type); + assertEquals(type.getName(), xomType.getRootElementName()); + assertEquals(type.getSystemId(), xomType.getSystemID()); + assertEquals(type.getPublicId(), xomType.getPublicID()); + + } + + + public void testChildElementAddsNamespace() { + + Element root = new Element("root"); + Element child = new Element("pre:child", + "http://www.example.org/"); + child.addAttribute(new Attribute("xlink:type", + "http://www.w3.org/1999/xlink", "simple")); + root.appendChild(child); + Document doc = new Document(root); + + assertEquals( + doc, + DOMConverter.convert(DOMConverter.convert(doc, impl)) + ); + + } + + + public void testChildElementUsesSameNamespace() { + + Element root = new Element("pre:root", "http://www.example.org/"); + Element child = new Element("pre:child", "http://www.example.org/"); + root.appendChild(child); + Document doc = new Document(root); + assertEquals(doc, DOMConverter.convert(DOMConverter.convert(doc, impl))); + + } + + + public void testPrefixMappingChanges() { + + Element root = new Element("pre:root", "http://www.example.org/"); + Element child = new Element("pre:child", "http://www.example.net/"); + root.appendChild(child); + Document doc = new Document(root); + assertEquals(doc, DOMConverter.convert(DOMConverter.convert(doc, impl))); + + } + + + public void testConvertDocumentFragment() { + + DocumentFragment fragment = domDocument.createDocumentFragment(); + org.w3c.dom.Element element = domDocument.createElement("name"); + element.setAttribute("name", "value"); + fragment.appendChild(element); + fragment.appendChild(domDocument.createComment("data")); + fragment.appendChild(domDocument.createTextNode("text")); + + Nodes result = DOMConverter.convert(fragment); + + Element first = (Element) result.get(0); + assertEquals("name", first.getQualifiedName()); + assertEquals("name", first.getAttribute("name").getQualifiedName()); + assertEquals("value", first.getAttribute("name").getValue()); + + Comment second = (Comment) result.get(1); + assertEquals("data", second.getValue()); + + Text third = (Text) result.get(2); + assertEquals("text", third.getValue()); + + } + + + // regression found by Wolfgang Hoschek + public void testWolfgang() throws ParsingException, IOException { + + String data = "" + + "" + + "http://www.example.com" + + "Some data" + + "StockAccess" + + "" + + ""; + + Document doc = new Builder().build(data, null); + Element article = doc.getRootElement().getChildElements().get(0); + Document temp = new Document(new Element(article)); + + org.w3c.dom.Element domArticle + = DOMConverter.convert(temp, impl).getDocumentElement(); + assertEquals(3, domArticle.getChildNodes().getLength()); + Element out = DOMConverter.convert(domArticle); + assertEquals(article, out); + + } + + + public void testConvertNullXOMDocument() { + + try { + DOMConverter.convert((Document) null, impl); + fail("Converted null document"); + } + catch (NullPointerException success) { + } + + + } + + + public void testConvertNullDOMElement() { + + try { + DOMConverter.convert((org.w3c.dom.Element) null); + fail("Converted null element"); + } + catch (NullPointerException success) { + } + + + } + + + public void testConvertNullDOMDocument() { + + try { + DOMConverter.convert((org.w3c.dom.Document) null); + fail("Converted null element"); + } + catch (NullPointerException success) { + } + + + } + + + /** + * Test a bug in a document provided by Wolfgang Hoschek. + */ + public void testFullHoschek() throws ParsingException, IOException { + + File f = new File("data"); + f = new File(f, "hoschektest.xml"); + Builder builder = new Builder(); + Document xomDocIn = builder.build(f); + org.w3c.dom.Document domDoc = DOMConverter.convert(xomDocIn, impl); + Document xomDocOut = DOMConverter.convert(domDoc); + assertEquals(xomDocIn, xomDocOut); + + } + + + /** + * Test a bug in identified by Wolfgang Hoschek. + */ + public void testSimplifiedHoschekDOMToXOM() + throws SAXException, IOException { + + File f = new File("data"); + f = new File(f, "simplehoschektest.xml"); + org.w3c.dom.Document domDocIn = builder.parse(f); + int domRootNumber = domDocIn.getDocumentElement().getChildNodes().getLength(); + Document xomDoc = DOMConverter.convert(domDocIn); + Element xomRoot = xomDoc.getRootElement(); + assertEquals(domRootNumber, xomRoot.getChildCount()); + + } + + + public void testChildNodesRemainChildNodes() + throws ParsingException, IOException { + + String data = "12"; + Builder builder = new Builder(); + Document xomDocIn = builder.build(data, null); + int xomRootNumber = xomDocIn.getRootElement().getChildCount(); + org.w3c.dom.Document domDoc = DOMConverter.convert(xomDocIn, impl); + org.w3c.dom.Element domRoot = domDoc.getDocumentElement(); + assertEquals(xomRootNumber, domRoot.getChildNodes().getLength()); + Document xomDocOut = DOMConverter.convert(domDoc); + assertEquals(xomDocIn, xomDocOut); + + } + + + public void testLastChildNodeIsLeafNode() + throws ParsingException, IOException { + + String data = "ABCD"; + Builder builder = new Builder(); + Document xomDocIn = builder.build(data, null); + org.w3c.dom.Document domDoc = DOMConverter.convert(xomDocIn, impl); + org.w3c.dom.Element domRoot = domDoc.getDocumentElement(); + org.w3c.dom.Node domTop = domRoot.getFirstChild(); + assertEquals(2, domTop.getChildNodes().getLength()); + + } + + + public void testNamespaceAttributes() { + + Element root = new Element("root", "http://www.example.org/"); + root.appendChild("text"); + Document xomDocIn = new Document(root); + org.w3c.dom.Document domDoc = DOMConverter.convert(xomDocIn, impl); + org.w3c.dom.Element domRoot = domDoc.getDocumentElement(); + assertTrue(domRoot.hasAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns")); + + } + + + public void testConvertSingleElementDocumentFromXOMToDOM() { + + Element root = new Element("root"); + Document xomDocIn = new Document(root); + org.w3c.dom.Document domDoc = DOMConverter.convert(xomDocIn, impl); + org.w3c.dom.Element domRoot = domDoc.getDocumentElement(); + assertEquals(0, domRoot.getChildNodes().getLength()); + assertEquals("root", domRoot.getNodeName()); + + } + + + public void testConvertSingleElementDocumentFromDOMToXOM() + throws SAXException, IOException { + + byte[] data = "".getBytes(); + org.w3c.dom.Document doc = builder.parse(new ByteArrayInputStream(data)); + Document xomDoc = DOMConverter.convert(doc); + Element root = xomDoc.getRootElement(); + assertEquals(0, root.getChildCount()); + assertEquals("element", root.getLocalName()); + + } + + + public void testConvertXMLSpaceAttributeFromXOMToDOM() + throws SAXException, IOException { + + Element root = new Element("element"); + Document doc = new Document(root); + root.addAttribute(new Attribute("xml:space", + "http://www.w3.org/XML/1998/namespace", "preserve")); + org.w3c.dom.Document domDoc = DOMConverter.convert(doc, impl); + org.w3c.dom.Element domRoot = domDoc.getDocumentElement(); + assertEquals(1, domRoot.getAttributes().getLength()); + assertEquals("preserve", domRoot.getAttributeNS("http://www.w3.org/XML/1998/namespace", "space")); + + } + + + public void testConvertXMLSpaceAttributeFromDOMToXOM() + throws SAXException, IOException, ParserConfigurationException { + + DocumentBuilderFactory df = DocumentBuilderFactory.newInstance(); + df.setNamespaceAware(true); + org.w3c.dom.Document doc = df.newDocumentBuilder().parse(new InputSource(new StringReader(""))); + Document xom = DOMConverter.convert (doc); + assertEquals("preserve", xom.getRootElement().getAttributeValue("space", Namespace.XML_NAMESPACE)); + + } + + + public void testConvertXMLPrefixedElementFromXOMToDOM() + throws SAXException, IOException { + + Element root = new Element( + "xml:element", "http://www.w3.org/XML/1998/namespace"); + Document doc = new Document(root); + org.w3c.dom.Document domDoc = DOMConverter.convert(doc, impl); + org.w3c.dom.Element domRoot = domDoc.getDocumentElement(); + assertEquals(0, domRoot.getAttributes().getLength()); + + } + + + public void testDeepConvert() { + + Element top = new Element("e"); + Element parent = top; + for (int i = 0; i < 100; i++) { + Element child = new Element("e" + i); + parent.appendChild(child); + parent = child; + } + Document xomDocIn = new Document(top); + org.w3c.dom.Document domDoc = DOMConverter.convert(xomDocIn, impl); + Document xomDocOut = DOMConverter.convert(domDoc); + + assertEquals(xomDocIn, xomDocOut); + + } + + public void testUseFactory() { + + Document xomDocOut = DOMConverter.convert(domDocument, new ANodeFactory()); + assertTrue(xomDocOut instanceof ADocument); + Element root = xomDocOut.getRootElement(); + assertTrue(root instanceof AElement); + Node doctype = xomDocOut.getChild(0); + assertTrue(doctype instanceof ADocType); + Node pi = xomDocOut.getChild(1); + assertTrue(pi instanceof AProcessingInstruction); + Node comment = xomDocOut.getChild(2); + assertTrue(comment instanceof AComment); + assertTrue(root.getChild(0) instanceof AText); + Elements children = root.getChildElements(); + for (int i = 0; i < children.size(); i++) { + Element child = children.get(i); + assertTrue(child instanceof AElement); + for (int j = 0; j < child.getAttributeCount(); j++) { + assertTrue(child.getAttribute(j) instanceof AnAttribute); + } + } + + } + + public void testUseFactoryWithPrologAndEpilog() { + + org.w3c.dom.Comment before = domDocument.createComment("before"); + org.w3c.dom.Comment after = domDocument.createComment("after"); + domDocument.appendChild(after); + domDocument.insertBefore(before, domDocument.getFirstChild()); + Document xomDocOut = DOMConverter.convert(domDocument, new ANodeFactory()); + assertTrue(xomDocOut instanceof ADocument); + Comment bc = (Comment) xomDocOut.getChild(0); + Comment ac = (Comment) xomDocOut.getChild(xomDocOut.getChildCount()-1); + assertTrue(bc instanceof AComment); + assertTrue(ac instanceof AComment); + + } + + public void testUseMinimizingFactory() { + Document xomDocOut = DOMConverter.convert( + domDocument, new NodeFactoryTest.MinimizingFactory()); + assertEquals(0, xomDocOut.getRootElement().getChildCount()); + } + + public void testUseStrippingFactory() { + Document xomDocOut = DOMConverter.convert( + domDocument, new StrippingFactory()); + assertEquals("Hello dear\nvery importanthere's the link\n" + + "text in a namespace\ntext in a namespace", xomDocOut.getRootElement().getValue()); + } + + private static class StrippingFactory extends NodeFactory { + + public Element startMakingElement(String name, String namespaceURI) { + if (! "test".equals(name)) return null; + return super.startMakingElement(name, namespaceURI); + } + + } + +} diff --git a/src/nu/xom/tests/DocTypeTest.java b/src/nu/xom/tests/DocTypeTest.java new file mode 100644 index 0000000..ed5d7fa --- /dev/null +++ b/src/nu/xom/tests/DocTypeTest.java @@ -0,0 +1,922 @@ +/* Copyright 2002-2005 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.tests; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.PrintStream; + +import org.xml.sax.SAXException; +import org.xml.sax.XMLFilter; +import org.xml.sax.XMLReader; +import org.xml.sax.helpers.XMLFilterImpl; +import org.xml.sax.helpers.XMLReaderFactory; + +import nu.xom.Builder; +import nu.xom.DocType; +import nu.xom.Document; +import nu.xom.IllegalDataException; +import nu.xom.IllegalNameException; +import nu.xom.ParsingException; +import nu.xom.ValidityException; +import nu.xom.WellformednessException; + +/** + *

    + * Various tests for the DocType class. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.2b3 + * + */ +public class DocTypeTest extends XOMTestCase { + + + public DocTypeTest(String name) { + super(name); + } + + + private String name = "MyName"; + private String systemID = "http://www.w3.org/TR/some.dtd"; + private String publicID = "-//Me//some public ID"; + + + // This class tests error conditions, which Xerces + // annoyingly logs to System.err. This hides System.err + // before each test and restores it after each test. + private PrintStream systemErr = System.err; + private DocType doctypePublicID; + private DocType doctypeSystemID; + private DocType doctypeRootOnly; + + + protected void setUp() { + doctypePublicID = new DocType(name, publicID, systemID); + doctypeSystemID = new DocType(name, systemID); + doctypeRootOnly = new DocType(name); + System.setErr(new PrintStream(new ByteArrayOutputStream())); + } + + + protected void tearDown() { + System.setErr(systemErr); + } + + + public void testToXML() { + + String expected + = ""; + assertEquals(expected, doctypePublicID.toXML()); + assertEquals( + "", + doctypeSystemID.toXML() + ); + assertEquals( + "", + doctypeRootOnly.toXML() + ); + + } + + + public void testToXMLWithInternalDTDSubset() + throws ValidityException, ParsingException, IOException { + + String data = "\n" + + "\n]>" + + "\n\n"; + Document doc = (new Builder()).build(data, null); + String result = doc.toXML(); + assertEquals(data, result); + + } + + + public void testToXMLWithCommentsInInternalDTDSubset() + throws ValidityException, ParsingException, IOException { + + String data = "\n" + + "\n " + + "\n \n]>" + + "\n\n"; + Document doc = (new Builder()).build(data, null); + String result = doc.toXML(); + assertEquals(data, result); + + } + + + public void testToXMLWithCommentsInInternalDTDSubsetAndVerifyingBuilder() + throws ValidityException, ParsingException, IOException, SAXException { + + String data = "\n" + + "\n " + + "\n \n]>" + + "\n\n"; + XMLFilter filter = new XMLFilterImpl(); + filter.setParent(XMLReaderFactory.createXMLReader("org.apache.xerces.parsers.SAXParser")); + Document doc = (new Builder(filter)).build(data, null); + String result = doc.toXML(); + assertEquals(data, result); + + } + + + public void testToXMLWithProcessingInstructionsInInternalDTDSubset() + throws ValidityException, ParsingException, IOException { + + String data = "\n" + + "\n " + + "\n \n]>" + + "\n\n"; + Document doc = (new Builder()).build(data, null); + String result = doc.toXML(); + assertEquals(data, result); + + } + + + public void testToXMLWithProcessingInstructionsInInternalDTDSubsetAndNonverifyingBuilder() + throws ValidityException, ParsingException, IOException, SAXException { + + String data = "\n" + + "\n " + + "\n \n]>" + + "\n\n"; + XMLFilter filter = new XMLFilterImpl(); + filter.setParent(XMLReaderFactory.createXMLReader("org.apache.xerces.parsers.SAXParser")); + Document doc = (new Builder(filter)).build(data, null); + String result = doc.toXML(); + assertEquals(data, result); + + } + + + public void testInternalDTDSubset() + throws ParsingException, IOException { + + String data = " ]>"; + Builder builder = new Builder(); + Document doc = builder.build(data, "http://www.example.com"); + DocType doctype = doc.getDocType(); + assertEquals("root", doctype.getRootElementName()); + String internalSubset = doctype.getInternalDTDSubset(); + assertEquals(" \n", internalSubset); + assertTrue(doctype.toXML().indexOf("[") > 0); + assertTrue(doctype.toXML().indexOf("]") > 0); + assertTrue(doctype.toXML().indexOf("") > 0); + + } + + + public void testToString() { + + String expected + = "[nu.xom.DocType: " + name + "]"; + assertEquals(expected, doctypePublicID.toString()); + + } + + + public void testConstructor1Arg() { + + String name = "MyName"; + DocType doctype = new DocType(name); + assertEquals(name, doctype.getRootElementName()); + assertEquals("", doctype.getInternalDTDSubset()); + assertNull(doctype.getSystemID()); + assertNull(doctype.getPublicID()); + + // legal to have a colon here + name = "try:MyName"; + doctype = new DocType(name); + assertEquals("", doctype.getInternalDTDSubset()); + assertEquals(name, doctype.getRootElementName()); + assertNull(doctype.getSystemID()); + assertNull(doctype.getPublicID()); + + // illegal name + try { + name = "try MyName"; + new DocType(name); + fail("allowed root element name to contain spaces"); + } + catch (IllegalNameException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testNullRootElementName() { + + try { + new DocType((String) null); + fail("Allowed null root element name"); + } + catch (IllegalNameException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testRootElementNameBeginsWithDigit() { + + try { + new DocType("1Data"); + fail("Allowed non-namestart character in root element name"); + } + catch (IllegalNameException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testRootElementNameBeginsWithColon() { + + try { + new DocType(":Data"); + fail("Allowed colon to begin root element name"); + } + catch (IllegalNameException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testSetRootElementName() { + + DocType doctype = new DocType("root"); + doctype.setRootElementName("newname"); + assertEquals("newname", doctype.getRootElementName()); + doctype.setRootElementName("new:name"); + assertEquals("new:name", doctype.getRootElementName()); + try { + doctype.setRootElementName(":Data"); + fail("Allowed colon to begin root element name"); + } + catch (IllegalNameException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testAllowEmptyInternalDTDSubset() { + + DocType doctype = new DocType("root"); + doctype.setInternalDTDSubset(""); + assertEquals("", doctype.getInternalDTDSubset()); + + } + + + public void testAllowNullInternalDTDSubset() { + + DocType doctype = new DocType("root"); + doctype.setInternalDTDSubset(null); + assertEquals("", doctype.getInternalDTDSubset()); + + } + + + public void testSetInternalDTDSubset() { + + DocType doctype = new DocType("root"); + doctype.setInternalDTDSubset(""); + assertEquals("", doctype.getInternalDTDSubset()); + + } + + + public void testSetInternalDTDSubsetWithEntityThatPointsToNonExistentURL() { + + String subset = + "\n" + + "%test;\n"; + DocType doctype = new DocType("root"); + doctype.setInternalDTDSubset(subset); + assertEquals(subset, doctype.getInternalDTDSubset()); + + } + + + public void testSetInternalDTDSubsetWithRelativeURL() + throws ParsingException, IOException { + + Builder builder = new Builder(); + Document doc = builder.build("data/outer21.xml"); + String subset = doc.getDocType().getInternalDTDSubset(); + assertEquals(subset, subset.indexOf("file:/"), -1); + + } + + + public void testSetInternalDTDSubsetWithRelativeURLAndCrimson() + throws ParsingException, IOException { + + try { + XMLReader crimson = XMLReaderFactory.createXMLReader( + "org.apache.crimson.parser.XMLReaderImpl"); + Builder builder = new Builder(crimson); + Document doc = builder.build("data/outer21.xml"); + String subset = doc.getDocType().getInternalDTDSubset(); + assertEquals(subset, subset.indexOf("file:/"), -1); + } + catch (SAXException ex) { + // can't test Crimson if you can't load it + return; + } + + } + + + public void testSetMalformedInternalDTDSubset() { + + DocType doctype = new DocType("root"); + try { + doctype.setInternalDTDSubset(""); + fail("Allowed malformed internal DTD subset"); + } + catch (WellformednessException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testEmptyRootElementName() { + + try { + new DocType(""); + fail("Allowed empty string to be root element name"); + } + catch (IllegalNameException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testNoChildren() { + + assertEquals(0, doctypePublicID.getChildCount()); + assertEquals(0, doctypeSystemID.getChildCount()); + assertEquals(0, doctypeRootOnly.getChildCount()); + + try { + doctypePublicID.getChild(0); + fail("Got zeroth child"); + } + catch (IndexOutOfBoundsException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testConstructor2Arg() { + + String name = "MyName"; + String systemID = "http://www.w3.org/TR/some.dtd"; + DocType doctype = new DocType(name, systemID); + assertEquals(name, doctype.getRootElementName()); + assertEquals("", doctype.getInternalDTDSubset()); + assertEquals(systemID, doctype.getSystemID()); + assertNull(doctype.getPublicID()); + + // empty system ID + name = "try:MyName"; + systemID = ""; + doctype = new DocType(name, systemID); + assertEquals(name, doctype.getRootElementName()); + assertEquals("", doctype.getInternalDTDSubset()); + assertEquals(systemID, doctype.getSystemID()); + assertNull(doctype.getPublicID()); + + } + + + public void testConstructor3Arg() { + + String name = "MyName"; + String systemID = "http://www.w3.org/TR/some.dtd"; + String publicID = "-//Me//some public ID"; + DocType doctype = new DocType(name, publicID, systemID); + assertEquals(name, doctype.getRootElementName()); + assertEquals("", doctype.getInternalDTDSubset()); + assertEquals(systemID, doctype.getSystemID()); + assertEquals(publicID, doctype.getPublicID()); + + } + + + public void testEmptyStringForPublicID() { + + String name = "MyName"; + String systemID = "http://www.w3.org/TR/some.dtd"; + String publicID = ""; + DocType doctype = new DocType(name, publicID, systemID); + assertEquals(name, doctype.getRootElementName()); + assertEquals("", doctype.getInternalDTDSubset()); + assertEquals(systemID, doctype.getSystemID()); + assertEquals(publicID, doctype.getPublicID()); + + } + + + public void testEmptyStringForSystemID() { + + String name = "MyName"; + String systemID = ""; + String publicID = "-//Me//some public ID"; + DocType doctype = new DocType(name, publicID, systemID); + assertEquals(name, doctype.getRootElementName()); + assertEquals("", doctype.getInternalDTDSubset()); + assertEquals(systemID, doctype.getSystemID()); + assertEquals(publicID, doctype.getPublicID()); + + } + + + public void testIllegalPublicIDs() { + + // PubidChar ::= #x20 | #xD | #xA | [a-zA-Z0-9] | [-'()+,./:=?;!*#@$_%] + for (char c = 0; c <= 0x9; c++) { + try { + checkPublicIDCharacter(c + ""); + fail("Allowed bad public ID character " + + Integer.toHexString(c)); + } + catch (WellformednessException success) { + // successfully detected bad public ID + assertNotNull(success.getMessage()); + } + } + for (char c = 0xB; c < 0xD; c++) { + try { + checkPublicIDCharacter(c + ""); + fail("Allowed bad public ID character " + + Integer.toHexString(c)); + } + catch (WellformednessException success) { + // successfully detected bad public ID + assertNotNull(success.getMessage()); + } + } + for (char c = 0xE; c < 0x20; c++) { + try { + checkPublicIDCharacter(c + ""); + fail("Allowed bad public ID character " + + Integer.toHexString(c)); + } + catch (WellformednessException success) { + // successfully detected bad public ID + assertNotNull(success.getMessage()); + } + } + for (char c = '~'; c < 1000; c++) { + try { + checkPublicIDCharacter(c + ""); + fail("Allowed bad public ID character " + + Integer.toHexString(c)); + } + catch (WellformednessException success) { + // successfully detected bad public ID + assertNotNull(success.getMessage()); + } + } + + char[] illegalPunctuationMarks = "<>`^&\"[]{}|\\~".toCharArray(); + for (int i = 0; i < illegalPunctuationMarks.length; i++) { + char c = illegalPunctuationMarks[i]; + try { + checkPublicIDCharacter(c + ""); + fail("Allowed bad public ID character " + + Integer.toHexString(c)); + } + catch (WellformednessException success) { + // successfully detected bad public ID + assertNotNull(success.getMessage()); + } + } + + } + + + public void testLegalPublicIDs() { + + // PubidChar ::= #x20 | #xD | #xA | [a-zA-Z0-9] | [-'()+,./:=?;!*#@$_%] + // should public IDs allow initial and trailing space? No. + // These are normalized like attribute values according to + // section 4.2.2 of the XML spec. + // This also means charriage returns and linefeeds are + // not roundtrippable. They can appear in the document but not + // the infoset so XOM forbids them. + checkPublicIDCharacter("-'()+,./:=?;!*#@$_%"); + for (char c = 'a'; c <= 'z'; c++) checkPublicIDCharacter(c + ""); + for (char c = 'A'; c <= 'Z'; c++) checkPublicIDCharacter(c + ""); + for (char c = '0'; c <= '9'; c++) checkPublicIDCharacter(c + ""); + + } + + + public void testSpaceContainingPublicIDs() { + + // According to section 4.2.2 of the XML spec, public IDs are + // normalized like attribute values of non-CDATA type + try { + new DocType("root", " test", "http://www.example.org"); + fail("allowed initial space in public ID"); + } + catch (WellformednessException success) { + assertNotNull(success.getMessage()); + } + + try { + new DocType("root", "test ", "http://www.example.org"); + fail("allowed trailing space in public ID"); + } + catch (WellformednessException success) { + assertNotNull(success.getMessage()); + } + + try { + new DocType("root", "test\ntest", "http://www.example.org"); + fail("allowed linefeed public ID"); + } + catch (WellformednessException success) { + assertNotNull(success.getMessage()); + } + + try { + new DocType("root", "test\rtest", "http://www.example.org"); + fail("allowed carriage return in public ID"); + } + catch (WellformednessException success) { + assertNotNull(success.getMessage()); + } + + try { + new DocType("root", "test\r\ntest", "http://www.example.org"); + fail("allowed carriage return linefeed pair public ID"); + } + catch (WellformednessException success) { + assertNotNull(success.getMessage()); + } + + try { + new DocType("root", "test test", "http://www.example.org"); + fail("allowed multiple consecutive spaces in public ID"); + } + catch (WellformednessException success) { + assertNotNull(success.getMessage()); + } + + // one space is legal + DocType test = new DocType("root", "test test", "http://www.example.org"); + assertEquals(test.getPublicID(), "test test"); + + } + + + public void testSystemIDWithDollarSignAndComma() { + + String systemID = "http://www.example.com/test$red/limit,data.xml"; + DocType doctype = new DocType("root", systemID); + assertEquals(systemID, doctype.getSystemID()); + + } + + + public void testSystemIDWithSemicolon() { + + String systemID = "smb://domain;user:pass@server/share/path/to/file"; + DocType doctype = new DocType("root", systemID); + assertEquals(systemID, doctype.getSystemID()); + + } + + + public void testIllegalSystemIDs() { + + // "It is an error for a fragment identifier + // (beginning with a # character) + // to be part of a system identifier." + try { + new DocType("test", "http://www.example.com/index.html#test"); + fail("Allowed system ID with fragment identifier"); + } + catch (IllegalDataException success) { + // successfully detected bad system ID + assertNotNull(success.getMessage()); + } + + try { + new DocType("test", "http://www.example.com/index.html#"); + fail("Allowed # in system ID"); + } + catch (IllegalDataException success) { + // successfully detected bad system ID + assertNotNull(success.getMessage()); + } + + try { + new DocType("test", "http://www.example.com/\u00A9.html#"); + fail("Allowed non-ASCII character in system ID"); + } + catch (IllegalDataException success) { + // successfully detected bad system ID + assertNotNull(success.getMessage()); + } + + try { + new DocType("test", "http://www.example.com/\u0007.html#"); + fail("Allowed C0 control character in system ID"); + } + catch (IllegalDataException success) { + // successfully detected bad system ID + assertNotNull(success.getMessage()); + } + + try { + new DocType("test", "test\" and ' in the same ID"); + fail("Allowed both \" and ' in system ID"); + } + catch (IllegalDataException success) { + // successfully detected bad system ID + assertNotNull(success.getMessage()); + } + + } + + + void checkPublicIDCharacter(String publicID) { + String name = "MyName"; + String systemID = "http://www.w3.org/TR/some.dtd"; + DocType doctype = new DocType(name, publicID, systemID); + assertEquals(publicID, doctype.getPublicID()); + } + + + public void testClone() { + + String name = "MyName"; + String systemID = "http://www.w3.org/TR/some.dtd"; + String publicID = "-//Me//some public ID"; + DocType doctype = new DocType(name, publicID, systemID); + + DocType other = (DocType) doctype.copy(); + + assertEquals( + doctype.getRootElementName(), + other.getRootElementName()); + assertEquals( + doctype.getInternalDTDSubset(), + other.getInternalDTDSubset() + ); + assertEquals(doctype.getSystemID(), other.getSystemID()); + assertEquals(doctype.getPublicID(), other.getPublicID()); + assertTrue(!other.equals(doctype)); + + } + + + public void testGetters() { + + String name = "MyName"; + String systemID = "http://www.w3.org/TR/some.dtd"; + String publicID = "-//Me//some public ID"; + DocType doctype = new DocType(name, publicID, systemID); + + assertEquals("", doctype.getValue()); + + } + + + public void testSystemIDRequiredForPublicID() { + + String name = "MyName"; + DocType doctype = new DocType(name); + + try { + doctype.setPublicID("-//Me//some public ID"); + fail("created a doctype with a public ID and no system ID"); + } + catch (WellformednessException ex) { + // success + assertNotNull(ex.getMessage()); + } + + } + + + public void testRemove() { + + String name = "MyName"; + String publicID = "-//Me//some public ID"; + DocType doctype = + new DocType(name, publicID, "http://www.example.com"); + + doctype.setPublicID(null); + assertNull(doctype.getPublicID()); + doctype.setPublicID(publicID); + assertEquals(publicID, doctype.getPublicID()); + + try { + doctype.setSystemID(null); + fail("removed system ID before removing public ID"); + } + catch (WellformednessException success) { + assertNotNull(success.getMessage()); + } + + doctype.setPublicID(null); + assertNull(doctype.getPublicID()); + doctype.setSystemID(null); + assertNull(doctype.getSystemID()); + + } + + + public void testCarriageReturnInEntityReplacementTextInInternalDTDSubset() + throws ParsingException, IOException { + + Builder builder = new Builder(); + String data = "" + + "]>"; + Document doc = builder.build(data, null); + String internalDTDSubset = doc.getDocType().getInternalDTDSubset(); + assertTrue(internalDTDSubset.indexOf("") > 1); + + } + + + public void testCarriageReturnInAttributeDefaultValueInInternalDTDSubset() + throws ParsingException, IOException { + + Builder builder = new Builder(); + String data = "" + + "]>"; + Document doc = builder.build(data, null); + String internalDTDSubset = doc.getDocType().getInternalDTDSubset(); + assertTrue(internalDTDSubset.indexOf("") > 1); + + } + + + public void testQuotationMarksInNotationDeclarationInInternalDTDSubset() + throws ParsingException, IOException { + + Builder builder = new Builder(); + String data = "\n" + + "]>"; + Document doc = builder.build(data, null); + String internalDTDSubset = doc.getDocType().getInternalDTDSubset(); + assertEquals(" \n", internalDTDSubset); + + } + + + public void testAmpersandInEntityReplacementTextInInternalDTDSubset() + throws ParsingException, IOException { + + Builder builder = new Builder(); + String data = "" + + "]>"; + Document doc = builder.build(data, null); + String internalDTDSubset = doc.getDocType().getInternalDTDSubset(); + assertTrue(internalDTDSubset.indexOf("") > 1); + + } + + + public void testAmpersandInAttributeDefaultValueInInternalDTDSubset() + throws ParsingException, IOException { + + Builder builder = new Builder(); + String data = "" + + "]>"; + Document doc = builder.build(data, null); + String internalDTDSubset = doc.getDocType().getInternalDTDSubset(); + assertTrue(internalDTDSubset.indexOf( + "") > 1 + ); + + } + + + public void testDoubleQuoteInAttributeDefaultValueInInternalDTDSubset() + throws ParsingException, IOException { + + Builder builder = new Builder(); + String data = "" + + "]>"; + Document doc = builder.build(data, null); + String internalDTDSubset = doc.getDocType().getInternalDTDSubset(); + assertTrue(internalDTDSubset.indexOf( + "") > 1 + ); + + } + + + public void testSingleQuoteInAttributeDefaultValueInInternalDTDSubset() + throws ParsingException, IOException { + + Builder builder = new Builder(); + String data = "" + + "]>"; + Document doc = builder.build(data, null); + String internalDTDSubset = doc.getDocType().getInternalDTDSubset(); + assertTrue(internalDTDSubset.indexOf( + "") > 1 + ); + + } + + + public void testLessThanSignInAttributeDefaultValueInInternalDTDSubset() + throws ParsingException, IOException { + + Builder builder = new Builder(); + String data = "" + + "]>"; + Document doc = builder.build(data, null); + String internalDTDSubset = doc.getDocType().getInternalDTDSubset(); + assertTrue(internalDTDSubset.indexOf( + "") > 1 + ); + + } + + + public void testGreaterThanSignInAttributeDefaultValueInInternalDTDSubset() + throws ParsingException, IOException { + + Builder builder = new Builder(); + String data = "'>" + + "]>"; + Document doc = builder.build(data, null); + String internalDTDSubset = doc.getDocType().getInternalDTDSubset(); + assertTrue(internalDTDSubset.indexOf( + "\">") > 1 + ); + + } + + + public void testDoubleQuoteInEntityReplacementTextInInternalDTDSubset() + throws ParsingException, IOException { + + Builder builder = new Builder(); + String data = "" + + "]>"; + Document doc = builder.build(data, null); + String internalDTDSubset = doc.getDocType().getInternalDTDSubset(); + assertTrue(internalDTDSubset.indexOf("") > 1); + + } + + +} \ No newline at end of file diff --git a/src/nu/xom/tests/DocumentTest.java b/src/nu/xom/tests/DocumentTest.java new file mode 100644 index 0000000..b576e1a --- /dev/null +++ b/src/nu/xom/tests/DocumentTest.java @@ -0,0 +1,585 @@ +/* Copyright 2002-2004, 2011 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.tests; + +import java.io.File; +import java.io.IOException; + +import nu.xom.Builder; +import nu.xom.Comment; +import nu.xom.DocType; +import nu.xom.Document; +import nu.xom.Element; +import nu.xom.IllegalAddException; +import nu.xom.MultipleParentException; +import nu.xom.NoSuchChildException; +import nu.xom.ParsingException; +import nu.xom.ProcessingInstruction; +import nu.xom.Text; +import nu.xom.WellformednessException; + + +/** + *

    + * Various basic tests for the Document class. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.3.0 + * + */ +public class DocumentTest extends XOMTestCase { + + public DocumentTest(String name) { + super(name); + } + + private Element root; + private Document doc; + + protected void setUp() { + root = new Element("root"); + doc = new Document(root); + } + + + public void testToString() { + assertEquals("[nu.xom.Document: root]", doc.toString()); + } + + + public void testDocTypeInsertion() { + + DocType type1 = new DocType("root"); + + try { + doc.insertChild(type1, 1); + fail("inserted doctype after root element"); + } + catch (IllegalAddException success) { + assertNotNull(success.getMessage()); + } + + doc.insertChild(type1, 0); + assertEquals(type1, doc.getDocType()); + + DocType type2 = new DocType("test"); + try { + doc.insertChild(type2, 1); + fail("Inserted 2nd DocType"); + } + catch (IllegalAddException success) { + assertNotNull(success.getMessage()); + } + assertEquals(type1, doc.getDocType()); + assertNull(type2.getParent()); + assertEquals(type1, doc.getChild(0)); + + doc.setDocType(type2); + assertEquals(doc.getDocType(), type2); + assertNull(type1.getParent()); + assertEquals(type2, doc.getChild(0)); + + } + + + public void testSetDocType() { + + DocType type1 = new DocType("root"); + doc.setDocType(type1); + assertEquals(type1, doc.getDocType()); + + doc.insertChild(new Comment("test"), 0); + + DocType type2 = new DocType("root", "http://www.example.com/"); + doc.setDocType(type2); + assertEquals(type2, doc.getDocType()); + assertEquals(1, doc.indexOf(type2)); + assertNull(type1.getParent()); + assertEquals(doc, type2.getParent()); + + // set same doctype + doc.setDocType(type2); + assertEquals(type2, doc.getDocType()); + assertEquals(1, doc.indexOf(type2)); + assertEquals(doc, type2.getParent()); + + try { + doc.setDocType(null); + fail("Allowed null doctype"); + } + catch (NullPointerException success) { + assertNotNull(success.getMessage()); + assertEquals(type2, doc.getDocType()); + } + + try { + Document doc2 = new Document(new Element("root")); + doc2.setDocType(type2); + fail("Allowed multiple parents for doctype"); + } + catch (MultipleParentException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testBaseURI() { + + assertEquals("", doc.getBaseURI()); + doc.setBaseURI("http://www.example.com/index.xml"); + assertEquals( + "http://www.example.com/index.xml", + doc.getBaseURI() + ); + doc.setBaseURI("file:///home/elharo/XOM/data/test.xml"); + assertEquals( + "file:///home/elharo/XOM/data/test.xml", + doc.getBaseURI() + ); + doc.setBaseURI("file:///home/elharo/XO%4D/data/test.xml"); + assertEquals( + "file:///home/elharo/XO%4D/data/test.xml", + doc.getBaseURI() + ); + + } + + + public void testSecondRoot() { + + try { + doc.insertChild(new Element("test"), 0); + fail("Added second root element"); + } + catch (IllegalAddException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testSetRoot() { + + Element newRoot = new Element("newroot"); + doc.setRootElement(newRoot); + + assertEquals(newRoot, doc.getRootElement()); + assertNull(root.getParent()); + + try { + doc.setRootElement(null); + } + catch (NullPointerException success) { + assertNotNull(success.getMessage()); + } + + Element top = new Element("top"); + Element child = new Element("child"); + top.appendChild(child); + try { + doc.setRootElement(child); + fail("Allowed element with two parents"); + } + catch (MultipleParentException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testReplaceRootWithItself() { + Element root = doc.getRootElement(); + doc.setRootElement(root); + assertEquals(root, doc.getRootElement()); + } + + + public void testReplaceRootElementWithDifferentElementUsingReplaceChild() { + + Element newRoot = new Element("newRoot"); + Element oldRoot = doc.getRootElement(); + doc.replaceChild(oldRoot, newRoot); + assertEquals(newRoot, doc.getRootElement()); + assertNull(oldRoot.getParent()); + assertEquals(doc, newRoot.getParent()); + + } + + + public void testInsertionAllowed() { + + Document doc = new Document(new Element("root")); + Comment original = new Comment("original"); + doc.insertChild(original, 0); + + Element temp = new Element("temp"); + Comment c2 = new Comment("new comment"); + temp.appendChild(c2); + + try { + doc.replaceChild(original, c2); + fail("Missed multiple parent exception"); + } + catch (MultipleParentException success) { + assertEquals(2, doc.getChildCount()); + } + + } + + + public void testReplaceDocTypeWithDifferentDocTypeUsingReplaceChild() { + + DocType newDocType = new DocType("new"); + DocType oldDocType = new DocType("old"); + doc.setDocType(oldDocType); + doc.replaceChild(oldDocType, newDocType); + assertEquals(newDocType, doc.getDocType()); + assertNull(oldDocType.getParent()); + assertEquals(doc, newDocType.getParent()); + + } + + + public void testReplaceDocTypeWithParentedDocTypeUsingReplaceChild() { + + DocType newDocType = new DocType("new"); + DocType oldDocType = new DocType("old"); + Document temp = new Document(new Element("root")); + temp.setDocType(newDocType); + + doc.setDocType(oldDocType); + try { + doc.replaceChild(oldDocType, newDocType); + fail("Missed MultipleParentException"); + } + catch (MultipleParentException success) { + assertEquals(2, doc.getChildCount()); + assertEquals(2, temp.getChildCount()); + } + + assertEquals(oldDocType, doc.getDocType()); + assertNotNull(oldDocType.getParent()); + assertEquals(doc, oldDocType.getParent()); + assertEquals(newDocType, temp.getDocType()); + assertNotNull(oldDocType.getParent()); + assertEquals(temp, newDocType.getParent()); + + } + + + public void testReplaceRootElementWithParentedElementUsingReplaceChild() { + + Element oldRoot = new Element("oldRoot"); + Element newRoot = new Element("newRoot"); + Document doc = new Document(oldRoot); + Element temp = new Element("temp"); + temp.appendChild(newRoot); + + try { + doc.replaceChild(oldRoot, newRoot); + fail("Missed MultipleParentException"); + } + catch (MultipleParentException success) { + assertEquals(1, doc.getChildCount()); + assertEquals(1, temp.getChildCount()); + } + + assertEquals(oldRoot, doc.getRootElement()); + assertEquals(newRoot, temp.getChild(0)); + assertNotNull(oldRoot.getParent()); + assertEquals(temp, newRoot.getParent()); + + } + + + public void testReplaceRootElementWithComment() { + + Element oldRoot = new Element("oldRoot"); + Document doc = new Document(oldRoot); + + try { + doc.replaceChild(oldRoot, new Comment("c")); + fail("Replaced root with comment"); + } + catch (WellformednessException success) { + assertNotNull(success.getMessage()); + } + + assertEquals(oldRoot, doc.getRootElement()); + assertEquals(doc, oldRoot.getParent()); + + } + + + public void testReplaceNonDocTypeWithDocTypeUsingReplaceChild() { + + Comment c = new Comment("Not a doctype"); + DocType newDocType = new DocType("new"); + doc.insertChild(c, 0); + doc.replaceChild(c, newDocType); + assertEquals(newDocType, doc.getDocType()); + assertNull(c.getParent()); + assertEquals(doc, newDocType.getParent()); + + } + + + public void testDetach() { + Comment comment + = new Comment("This will be attached then detached"); + doc.appendChild(comment); + assertEquals(doc, comment.getParent()); + comment.detach(); + assertNull(comment.getParent()); + } + + + public void testGetDocument() { + assertEquals(doc, doc.getDocument()); + } + + + public void testConstructor() { + + assertEquals(root, doc.getRootElement()); + assertEquals(1, doc.getChildCount()); + + Element newRoot = new Element("newRoot"); + doc.setRootElement(newRoot); + assertEquals(newRoot, doc.getRootElement()); + assertEquals(1, doc.getChildCount()); + + doc.appendChild(new Comment("test")); + assertEquals(2, doc.getChildCount()); + + doc.insertChild(new Comment("prolog comment"), 0); + assertEquals(3, doc.getChildCount()); + assertTrue(doc.getChild(0) instanceof Comment); + assertTrue(doc.getChild(1) instanceof Element); + assertTrue(doc.getChild(2) instanceof Comment); + + doc.insertChild(new ProcessingInstruction("target", "data"),1); + assertTrue(doc.getChild(0) instanceof Comment); + assertTrue(doc.getChild(1) instanceof ProcessingInstruction); + assertTrue(doc.getChild(2) instanceof Element); + assertTrue(doc.getChild(3) instanceof Comment); + + doc.insertChild(new ProcessingInstruction("epilog", "data"),3); + assertTrue(doc.getChild(0) instanceof Comment); + assertTrue(doc.getChild(1) instanceof ProcessingInstruction); + assertTrue(doc.getChild(2) instanceof Element); + assertTrue(doc.getChild(3) instanceof ProcessingInstruction); + assertTrue(doc.getChild(4) instanceof Comment); + + + try { + Element nullRoot = null; + new Document(nullRoot); + fail("allowed null root!"); + } + catch (NullPointerException success) { + } + + try { + Document nullDoc = null; + new Document(nullDoc); + fail("allowed null doc!"); + } + catch (NullPointerException success) { + // success + } + + } + + + public void testCopyConstructor() { + + doc.insertChild(new Comment("text"), 0); + doc.insertChild(new ProcessingInstruction("text", "data"), 1); + doc.insertChild(new DocType("text"), 2); + root.appendChild("some data"); + doc.appendChild(new Comment("after")); + doc.appendChild(new ProcessingInstruction("text", "after")); + + Document doc2 = new Document(doc); + assertEquals(doc, doc2); + + } + + + public void testCopyConstructorBaseURI() { + + doc.setBaseURI("http://www.example.com/"); + + Document doc2 = new Document(doc); + assertEquals(doc.getBaseURI(), doc2.getBaseURI()); + assertEquals("http://www.example.com/", doc2.getBaseURI()); + assertEquals( + doc.getRootElement().getBaseURI(), + doc2.getRootElement().getBaseURI() + ); + assertEquals( + "http://www.example.com/", + doc2.getRootElement().getBaseURI() + ); + + } + + + public void testCopy() { + + doc.insertChild(new Comment("text"), 0); + doc.insertChild(new ProcessingInstruction("text", "data"), 1); + doc.insertChild(new DocType("text"), 2); + root.appendChild("some data"); + doc.appendChild(new Comment("after")); + doc.appendChild(new ProcessingInstruction("text", "after")); + + Document doc2 = doc.copy(); + assertEquals(doc, doc2); + + } + + + public void testAppend() { + + Element root = new Element("root"); + Document doc = new Document(root); + + try { + doc.appendChild(new Text("test")); + fail("appended string"); + } + catch (IllegalAddException success) { + assertNotNull(success.getMessage()); + } + + try { + doc.appendChild(new Text(" ")); + fail("appended white space"); + } + catch (IllegalAddException success) { + assertNotNull(success.getMessage()); + } + + try { + doc.appendChild(new Text("test")); + fail("appended Text"); + } + catch (IllegalAddException success) { + assertNotNull(success.getMessage()); + } + + try { + doc.appendChild(new Comment("test")); + doc.appendChild(new Element("test")); + fail("appended element"); + } + catch (IllegalAddException success) { + assertNotNull(success.getMessage()); + } + + try { + doc.insertChild(new Element("test"), 0); + fail("inserted element"); + } + catch (IllegalAddException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testRemoval() { + + Element root = new Element("root"); + Document doc = new Document(root); + + try { + root.detach(); + fail("detached root element"); + } + catch (WellformednessException success) { + assertNotNull(success.getMessage()); + } + + try { + doc.removeChild(root); + fail("removed root element"); + } + catch (WellformednessException success) { + assertNotNull(success.getMessage()); + } + + try { + doc.removeChild(0); + fail("removed root element"); + } + catch (WellformednessException success) { + assertNotNull(success.getMessage()); + } + + doc.appendChild(new Comment("test")); + doc.removeChild(1); + assertEquals(1, doc.getChildCount()); + + Comment test = new Comment("test"); + doc.appendChild(test); + doc.removeChild(test); + assertEquals(1, doc.getChildCount()); + + try { + Comment something = new Comment("sd"); + doc.removeChild(something); + fail("Removed nonchild"); + } + catch (NoSuchChildException success) { + assertNotNull(success.getMessage()); + } + + try { + doc.removeChild(20); + fail("removed overly sized element"); + } + catch (IndexOutOfBoundsException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testToXML() throws ParsingException, IOException { + + Builder builder = new Builder(); + File f = new File("data"); + f = new File(f, "test.xml"); + Document input = builder.build(f); + String s = input.toXML(); + Document output = builder.build(s, f.toURI().toString()); + assertEquals(input, output); + + } + + +} diff --git a/src/nu/xom/tests/EBCDICTest.java b/src/nu/xom/tests/EBCDICTest.java new file mode 100644 index 0000000..80a7338 --- /dev/null +++ b/src/nu/xom/tests/EBCDICTest.java @@ -0,0 +1,107 @@ +/* Copyright 2002-2004 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.tests; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; + +import nu.xom.Builder; +import nu.xom.Document; +import nu.xom.Element; +import nu.xom.ParsingException; +import nu.xom.Serializer; + +/** + *

    + * Tests support for the typical U.S. EBCDIC encoding. + * Unfortunately this test exposes a bug + * in the handling of NEL, character 0x85, in Sun's JDK. Specifically + * InputStreamReader maps 0x85 to a line feed rather than + * NEL. I've reported the bug to the Java Developer Connection, + * but until it's fixed this test fails. I don't have an easy + * workaround. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.0 + * + */ +public class EBCDICTest extends XOMTestCase { + + public final static char NEL = 0x85; + + public EBCDICTest(String name) { + super(name); + } + + private Document doc; + private String data; + + protected void setUp() { + Element root = new Element("r"); + doc = new Document(root); + data = "\u0085"; + root.appendChild(data); + } + + + // This test will only pass if Java's NEL handling is fixed + public void testEBCDIC037() + throws ParsingException, UnsupportedEncodingException { + + Builder builder = new Builder(); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + try { + // Write data into a byte array using encoding + Serializer serializer = new Serializer(out, "Cp037"); + serializer.write(doc); + serializer.flush(); + out.flush(); + out.close(); + byte[] result = out.toByteArray(); + + // We have to look directly rather than converting to + // a String because Java gets the conversion of NEL to + // Unicode wrong + for (int i = 0; i < result.length; i++) { + if (result[i] == 0x15) fail("Bad NEL output"); + } + + InputStream in = new ByteArrayInputStream(result); + Document reparsed = builder.build(in); + assertEquals(doc, reparsed); + } + catch (UnsupportedEncodingException ex) { + throw ex; + } + catch (IOException ex) { + ex.printStackTrace(); + } + + } + + +} diff --git a/src/nu/xom/tests/ElementTest.java b/src/nu/xom/tests/ElementTest.java new file mode 100644 index 0000000..c2c61d5 --- /dev/null +++ b/src/nu/xom/tests/ElementTest.java @@ -0,0 +1,1752 @@ +/* Copyright 2002-2004, 2011, 2018 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@mibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.tests; + +import java.io.File; +import java.io.IOException; + +import junit.framework.Assert; +import nu.xom.Attribute; +import nu.xom.Builder; +import nu.xom.Comment; +import nu.xom.DocType; +import nu.xom.Document; +import nu.xom.Element; +import nu.xom.Elements; +import nu.xom.IllegalAddException; +import nu.xom.IllegalNameException; +import nu.xom.MalformedURIException; +import nu.xom.MultipleParentException; +import nu.xom.NamespaceConflictException; +import nu.xom.NoSuchAttributeException; +import nu.xom.Node; +import nu.xom.Nodes; +import nu.xom.ParsingException; +import nu.xom.ProcessingInstruction; +import nu.xom.Text; + + +/** + *

    + * Tests for the Element class. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.3.0 + * + */ +public class ElementTest extends XOMTestCase { + + private Element element; + private Element child1; + private Text child2; + private Comment child3; + private Element child4; + private Element child5; + private String[] legal = { + "http://www.is.edu/sakdsk#sjadh", + "http://www.is.edu/sakdsk?name=value&name=head", + "uri:isbn:0832473864", + "http://www.examples.com:80", + "http://www.examples.com:80/", + "http://www.is.edu/%20sakdsk#sjadh" + }; + + private String[] illegal = { + "http://www.is.edu/%sakdsk#sjadh", + "http://www.is.edu/k\u0245kakdsk#sjadh", + "!@#$%^&*()", + "fred", + "#fred", + "/fred" + }; + + + protected void setUp() { + + child1 = new Element("test"); + child2 = new Text("test2"); + child3 = new Comment("test3"); + child4 = new Element("pre:test", "http://www.example.com"); + child5 = new Element("test", "http://www.example.com"); + element = new Element("name"); + + element.appendChild(child1); + element.appendChild(child2); + element.appendChild(child3); + element.appendChild(child4); + element.appendChild(child5); + element.appendChild(" \r\n"); + + } + + + public ElementTest(String name) { + super(name); + } + + + public void testGetChildElementsNull() { + + Elements elements = element.getChildElements( + "", "http://www.example.com"); + assertEquals(2, elements.size()); + elements = element.getChildElements("", ""); + assertEquals(1, elements.size()); + elements = element.getChildElements(null, + "http://www.example.com"); + assertEquals(2, elements.size()); + elements = element.getChildElements("", null); + assertEquals(1, elements.size()); + + } + + private static class TestElement extends Element { + + public TestElement(String name) { + super(name); + } + + public String toString() { + return "foo"; + } + + } + + + public void testOverrideToString() { + + Element element = new TestElement("bar"); + assertEquals("foo", element.toString()); + + } + + + public void testGetFirstChildElement() { + + Element first = element.getFirstChildElement("test"); + assertEquals(child1, first); + + first = element.getFirstChildElement( + "test", "http://www.example.com"); + assertEquals(child4, first); + + assertNull(element.getFirstChildElement("nonesuch")); + assertNull(element.getFirstChildElement("pre:test")); + assertNull( + element.getFirstChildElement( + "nonesuch", "http://www.example.com") + ); + + } + + + public void testElementNamedXMLNS() { + + String name = "xmlns"; + Element e = new Element(name); + + assertEquals(name, e.getLocalName()); + assertEquals(name, e.getQualifiedName()); + assertEquals("", e.getNamespacePrefix()); + assertEquals("", e.getNamespaceURI()); + + } + + + public void testElementWithPrefixXMLNS() { + + try { + new Element("xmlns:foo", "http://www.example.org/"); + fail("Allowed xmlns prefix on element"); + } + catch (NamespaceConflictException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testConstructor1() { + + String name = "Jethro"; + Element e = new Element(name); + + assertEquals(name, e.getLocalName()); + assertEquals(name, e.getQualifiedName()); + assertEquals("", e.getNamespacePrefix()); + assertEquals("", e.getNamespaceURI()); + } + + + public void testConstructor2() { + String name = "sakjdhjhd"; + String uri = "http://www.something.com/"; + Element e = new Element(name, uri); + + assertEquals(name, e.getLocalName()); + assertEquals(name, e.getQualifiedName()); + assertEquals("", e.getNamespacePrefix()); + assertEquals(uri, e.getNamespaceURI()); + + } + + + public void testConstructor3() { + + String name = "red:sakjdhjhd"; + String uri = "http://www.something.com/"; + Element e = new Element(name, uri); + + assertEquals("sakjdhjhd", e.getLocalName()); + assertEquals(name, e.getQualifiedName()); + assertEquals("red", e.getNamespacePrefix()); + assertEquals(uri, e.getNamespaceURI()); + + } + + + public void testCopyConstructorWithAdditionalNamespaces() { + + Element original = new Element("red"); + original.addNamespaceDeclaration("pre", "http://www.example.org"); + Element copy = new Element(original); + assertEquals("http://www.example.org", copy.getNamespaceURI("pre")); + + } + + + public void testAllowEmptyNamespace() { + + String name = "sakjdhjhd"; + String uri = "http://www.something.com/"; + Element e = new Element(name, uri); + + e.setNamespaceURI(""); + + assertEquals("", e.getNamespaceURI()); + + } + + + public void testToString() { + + String name = "sakjdhjhd"; + String uri = "http://www.something.com/"; + Element e = new Element(name, uri); + + String s = e.toString(); + assertTrue(s.startsWith("[nu.xom.Element: ")); + assertTrue(s.endsWith("]")); + assertTrue(s.indexOf(name) != -1); + + } + + + public void testToXML() { + + String name = "sakjdhjhd"; + String uri = "http://www.something.com/"; + Element e = new Element(name, uri); + + String s = e.toXML(); + assertTrue(s.endsWith("/>")); + assertTrue(s.startsWith("<" + name)); + assertTrue(s.indexOf(uri) != -1); + assertTrue(s.indexOf("xmlns=") != -1); + + } + + + public void testToXML2() throws ParsingException, IOException { + + Builder builder = new Builder(); + File f = new File("data"); + f = new File(f, "soapresponse.xml"); + Document doc = builder.build(f); + Element root = doc.getRootElement(); + String form = root.toXML(); + Document doc2 + = builder.build(form, f.toURI().toString()); + Element root2 = doc2.getRootElement(); + assertEquals(root, root2); + + } + + + public void testPhilip() { + Element element = new Element("x", "x:&"); + assertEquals("", element.toXML()); + } + + + public void testToXMLWithXMLLangAttribute() { + Element e = new Element("e"); + e.addAttribute(new Attribute("xml:lang", + "http://www.w3.org/XML/1998/namespace", "en")); + assertEquals("", e.toXML()); + } + + + public void testAllowNullNamespace() { + String name = "sakjdhjhd"; + String uri = "http://www.something.com/"; + Element e = new Element(name, uri); + + e.setNamespaceURI(null); + + assertEquals("", e.getNamespaceURI()); + + } + + + public void testInsertChildAfterEnd() { + + Element parent = new Element("p"); + for (int i = 0; i < 3; i++) { + Element c = new Element("c"); + parent.appendChild(c); + } + assertEquals(3, parent.getChildCount()); + Element d = new Element("d"); + + try { + parent.insertChild(d, 5); + fail("Inserted child too far"); + } + catch (IndexOutOfBoundsException ex) { + assertNotNull(ex.getMessage()); + } + + } + + + public void testInsertChildAtEnd() { + + Element parent = new Element("p"); + for (int i = 0; i < 3; i++) { + Element c = new Element("c"); + parent.appendChild(c); + } + assertEquals(3, parent.getChildCount()); + Element d = new Element("d"); + + parent.insertChild(d, 3); + assertEquals(4, parent.getChildCount()); + + } + + + public void testInsertChildNegative() { + + Element parent = new Element("p"); + for (int i = 0; i < 3; i++) { + Element c = new Element("c"); + parent.appendChild(c); + } + assertEquals(3, parent.getChildCount()); + Element d = new Element("d"); + + try { + parent.insertChild(d, -1); + fail("Inserted child with negative index"); + } + catch (IndexOutOfBoundsException ex) { + assertNotNull(ex); + } + + } + + + public void testCantInsertDoctype() { + String name = "sakjdhjhd"; + String uri = "http://www.something.com/"; + Element e = new Element(name, uri); + + try { + e.appendChild(new DocType(name)); + fail("Appended a document type declaration to an element"); + } + catch (IllegalAddException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testXMLNamespace() { + + String name = "red:sakjdhjhd"; + String uri = "http://www.red.com/"; + Element e = new Element(name, uri); + + String xmlNamespace = "http://www.w3.org/XML/1998/namespace"; + + assertEquals(xmlNamespace, e.getNamespaceURI("xml")); + + try { + e.addNamespaceDeclaration("xml", "http://www.yahoo.com/"); + fail("remapped xml prefix!"); + } + catch (NamespaceConflictException success) { + assertNotNull(success.getMessage()); + } + assertEquals(e.getNamespaceURI("xml"), xmlNamespace); + + // This should succeed + e.addNamespaceDeclaration("xml", xmlNamespace); + assertEquals(xmlNamespace, e.getNamespaceURI("xml")); + + + } + + + public void testUndeclareDefaultNamespace() { + + String name = "red:sakjdhjhd"; + String uri = "http://www.red.com/"; + Element child = new Element(name, uri); + Element parent = new Element( + "parent", "http://www.example.com"); + + assertEquals("http://www.example.com", + parent.getNamespaceURI("")); + + parent.appendChild(child); + child.addNamespaceDeclaration("", ""); + + assertEquals("", child.getNamespaceURI("")); + assertEquals( + "http://www.example.com", + parent.getNamespaceURI("")); + + Element child2 = new Element("name", "http://www.default.com"); + parent.appendChild(child2); + + // should not be able to undeclare default namespace on child2 + try { + child2.addNamespaceDeclaration("", ""); + fail("Illegally undeclared default namespace"); + } + catch (NamespaceConflictException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testUnsetDefaultNamespaceWithAttribute() { + + String name = "sakjdhjhd"; + String uri = "http://www.red.com/"; + Element element = new Element(name, uri); + element.addAttribute(new Attribute("test", "test")); + element.setNamespaceURI(""); + assertEquals("", element.getNamespaceURI("")); + + } + + + public void testSetNamespaceWithAttribute() { + + String name = "red:sakjdhjhd"; + String uri = "http://www.red.com/"; + Element element = new Element(name, uri); + element.addAttribute( + new Attribute("red:attribute", uri, "test")); + element.setNamespaceURI(uri); + assertEquals(uri, element.getNamespaceURI()); + assertEquals(uri, element.getNamespaceURI("red")); + + } + + + public void testAddNamespaceToElementWithAttribute() { + + String name = "a"; + String uri = "http://www.w3.org/1999/xhtml"; + Element element = new Element(name); + element.addAttribute( + new Attribute("href", "http://www.elharo.com")); + element.setNamespaceURI(uri); + assertEquals(uri, element.getNamespaceURI()); + + } + + + public void testSameNamespaceForElementAndAttribute() { + + String name = "a"; + String uri = "http://www.w3.org/1999/xhtml"; + Element element = new Element(name); + element.addAttribute( + new Attribute("html:href", uri, "http://www.elharo.com")); + element.setNamespaceURI("http://www.example.com"); + element.setNamespacePrefix("pre"); + element.setNamespaceURI(uri); + element.setNamespacePrefix("html"); + assertEquals(uri, element.getNamespaceURI()); + assertEquals("html", element.getNamespacePrefix()); + + } + + + public void testToXMLWithXMLAttributes() { + Element e = new Element("test"); + e.addAttribute( + new Attribute("xml:space", + "http://www.w3.org/XML/1998/namespace", + "preserve")); + e.addAttribute( + new Attribute("zzz:zzz", "http://www.example.org", "preserve")); + String result = e.toXML(); + assertEquals("", result); + } + + + public void testGetNamespacePrefixInt() { + Element e = new Element("test"); + e.addAttribute( + new Attribute("xml:space", + "http://www.w3.org/XML/1998/namespace", + "preserve")); + e.addAttribute( + new Attribute("zzz:zzz", "http://www.example.org", "preserve")); + assertEquals(2, e.getNamespaceDeclarationCount()); + try { + e.getNamespacePrefix(2); + fail("Got prefix beyond bounds"); + } + catch (IndexOutOfBoundsException success) { + assertNotNull(success.getMessage()); + } + } + + + // a very weird case but legal + public void testXMLPrefixOnElement() { + Element e = new Element("xml:test", "http://www.w3.org/XML/1998/namespace"); + assertEquals(0, e.getNamespaceDeclarationCount()); + assertEquals("", e.toXML()); + try { + e.getNamespacePrefix(0); + fail("Got prefix beyond bounds"); + } + catch (IndexOutOfBoundsException success) { + assertNotNull(success.getMessage()); + } + } + + + public void testConflictingDefaultNamespace() { + + Element e = new Element("foo", "http://www.foo.com/"); + try { + e.addNamespaceDeclaration("", "http://www.bar.com/"); + fail("Added conflicting default namespace"); + } + catch (NamespaceConflictException success) { + String message = success.getMessage(); + assertTrue(message.indexOf("default namespace") > 0); + assertTrue(message.indexOf("http://www.foo.com") > 0); + assertTrue(message.indexOf("http://www.bar.com/") > 0); + } + + } + + + public void testNamespaceMappings() { + + String name = "red:sakjdhjhd"; + String uri = "http://www.red.com/"; + Element e = new Element(name, uri); + e.addNamespaceDeclaration("blue", "http://www.blue.com/"); + e.addNamespaceDeclaration("green", "http://www.green.com/"); + Attribute a1 = new Attribute("test", "test"); + Attribute a2 = new Attribute( + "pre1:green", "http://www.green.com/", "data"); + Attribute a3 = new Attribute( + "yellow:sfsdadf", "http://www.yellow.com/", "data"); + e.addAttribute(a1); + e.addAttribute(a2); + e.addAttribute(a3); + + assertEquals("http://www.red.com/", e.getNamespaceURI("red")); + assertEquals( + "http://www.green.com/", + e.getNamespaceURI("green")); + assertEquals( + "http://www.blue.com/", + e.getNamespaceURI("blue")); + assertEquals( + "http://www.green.com/", + e.getNamespaceURI("pre1")); + assertEquals( + "http://www.yellow.com/", + e.getNamespaceURI("yellow")); + + + Element e2 = new Element( + "mauve:child", "http://www.mauve.com"); + e.appendChild(e2); + assertEquals( + "http://www.red.com/", + e2.getNamespaceURI("red")); + assertEquals( + "http://www.blue.com/", + e2.getNamespaceURI("blue")); + assertEquals( + "http://www.green.com/", + e2.getNamespaceURI("green")); + assertEquals( + "http://www.green.com/", + e2.getNamespaceURI("pre1")); + assertEquals( + "http://www.yellow.com/", + e2.getNamespaceURI("yellow")); + assertNull(e2.getNamespaceURI("head")); + + + try { + e.addNamespaceDeclaration("pre1", "http://www.blue2.com"); + fail("Added conflicting namespace"); + } + catch (NamespaceConflictException success) { + String message = success.getMessage(); + assertTrue(message.indexOf("http://www.blue2.com") > 0); + assertTrue(message.indexOf("pre1") > 0); + } + + try { + Attribute a4 = new Attribute( + "pre1:mauve", "http://www.sadas.com/", "data"); + e.addAttribute(a4); + fail("Added conflicting namespace"); + } + catch (NamespaceConflictException success) { + assertNotNull(success.getMessage()); + } + + // can't add conflicting attribute from + // different namespace even with identical QName + try { + Attribute a4 = new Attribute( + "pre1:green", "http://www.example.com/", "data"); + e.addAttribute(a4); + } + catch (NamespaceConflictException success) { + assertNotNull(success.getMessage()); + } + + e.removeNamespaceDeclaration("green"); + assertNull(e.getNamespaceURI("green")); + e.addNamespaceDeclaration("green", "http://www.green2.com/"); + assertEquals( + "http://www.green2.com/", + e.getNamespaceURI("green")); + + } + + + public void testAttributes() { + + String name = "red:sakjdhjhd"; + String uri = "http://www.red.com/"; + Element e = new Element(name, uri); + + Attribute a1 = new Attribute("name", "simple"); + Attribute a2 = new Attribute( + "pre1:green", "http://www.green.com/", "data"); + + e.addAttribute(a1); + e.addAttribute(a2); + + assertEquals( + a2, + e.getAttribute("green", "http://www.green.com/")); + assertEquals(a1, e.getAttribute("name")); + assertEquals(a1, e.getAttribute("name", "")); + assertEquals(e, a1.getParent()); + assertEquals("simple", e.getAttribute("name").getValue()); + + a1.detach(); + assertNull(a1.getParent()); + assertNull(e.getAttribute("name")); + + Attribute removed = e.removeAttribute(a2); + assertNull(a2.getParent()); + assertEquals(a2, removed); + assertNull( e.getAttribute("green", "http://www.green.com/")); + + } + + + public void testRemoveNullAttribute() { + + String name = "red:sakjdhjhd"; + String uri = "http://www.red.com/"; + Element e = new Element(name, uri); + + Attribute a1 = new Attribute("name", "simple"); + Attribute a2 = new Attribute( + "pre1:green", "http://www.green.com/", "data"); + + e.addAttribute(a1); + e.addAttribute(a2); + + try { + e.removeAttribute(null); + fail("Removed Null Attribute"); + } + catch (NullPointerException success) { + assertNotNull(success.getMessage()); + } + + } + + public void testRemoveAttributeFromElementWithNoAttributes() { + + String name = "red:sakjdhjhd"; + String uri = "http://www.red.com/"; + Element e = new Element(name, uri); + + Attribute a1 = new Attribute("name", "simple"); + + try { + e.removeAttribute(a1); + fail("Removed Attribute that didn't belong"); + } + catch (NoSuchAttributeException success) { + assertTrue(success.getMessage().indexOf(a1.getQualifiedName()) > 0); + } + + } + + + public void testRemoveAttributeFromElementWithDifferentAttributes() { + + String name = "red:sakjdhjhd"; + String uri = "http://www.red.com/"; + Element e = new Element(name, uri); + e.addAttribute(new Attribute("name", "value")); + Attribute a1 = new Attribute("name", "simple"); + + try { + e.removeAttribute(a1); + fail("Removed Attribute that didn't belong"); + } + catch (NoSuchAttributeException success) { + assertTrue(success.getMessage().indexOf(a1.getQualifiedName()) > 0); + } + + } + + + public void testGetValue() { + + String name = "red:sakjdhjhd"; + String uri = "http://www.red.com/"; + Element e = new Element(name, uri); + + assertEquals(e.getValue(), ""); + + e.appendChild(new Text("data")); + assertEquals(e.getValue(), "data"); + e.appendChild(new Text(" moredata")); + assertEquals(e.getValue(), "data moredata"); + e.appendChild(new Comment(" moredata")); + assertEquals(e.getValue(), "data moredata"); + e.appendChild( + new ProcessingInstruction("target", "moredata")); + assertEquals(e.getValue(), "data moredata"); + + Element e2 = new Element("child"); + e.appendChild(e2); + assertEquals("data moredata", e.getValue()); + e2.appendChild(new Text("something")); + assertEquals("data moredatasomething", e.getValue()); + + } + + + public void testSetLocalName() { + + String name = "red:sakjdhjhd"; + String uri = "http://www.red.com/"; + Element e = new Element(name, uri); + + assertEquals("sakjdhjhd", e.getLocalName()); + + e.setLocalName("dude"); + assertEquals("dude", e.getLocalName()); + e.setLocalName("digits__"); + assertEquals("digits__", e.getLocalName()); + e.setLocalName("digits1234"); + assertEquals("digits1234", e.getLocalName()); + e.setLocalName("digits-z"); + assertEquals("digits-z", e.getLocalName()); + + try { + e.setLocalName("spaces "); + fail("Local name allowed to contain spaces"); + } + catch (IllegalNameException success) { + assertNotNull(success.getMessage()); + assertEquals("spaces ", success.getData()); + } + + try { + e.setLocalName("digits:test"); + fail("Local name allowed to contain colon"); + } + catch (IllegalNameException success) { + assertNotNull(success.getMessage()); + } + + try { + e.setLocalName("digits!test"); + fail("Local name allowed to contain bang"); + } + catch (IllegalNameException success) { + assertNotNull(success.getMessage()); + assertEquals("digits!test", success.getData()); + } + + try { + e.setLocalName("digits\u0000test"); + fail("Local name allowed to contain null"); + } + catch (IllegalNameException success) { + assertNotNull(success.getMessage()); + assertEquals("digits\u0000test", success.getData()); + } + + } + + + public void testSetNamespacePrefix() { + + String name = "red:sakjdhjhd"; + String uri = "http://www.red.com/"; + String prefix = "red"; + Element e = new Element(name, uri); + + assertEquals(prefix, e.getNamespacePrefix()); + + e.setNamespacePrefix("dude"); + assertEquals("dude", e.getNamespacePrefix()); + e.setNamespacePrefix("digits__"); + assertEquals("digits__", e.getNamespacePrefix()); + e.setNamespacePrefix("digits1234"); + assertEquals("digits1234", e.getNamespacePrefix()); + e.setNamespacePrefix("digits-z"); + assertEquals("digits-z", e.getNamespacePrefix()); + e.setNamespacePrefix(""); + assertEquals("", e.getNamespacePrefix()); + e.setNamespacePrefix(null); + assertEquals("", e.getNamespacePrefix()); + + try { + e.setNamespacePrefix("spaces "); + fail("namespace prefix allowed to contain spaces"); + } + catch (IllegalNameException success) { + assertNotNull(success.getMessage()); + assertEquals("spaces ", success.getData()); + } + + try { + e.setNamespacePrefix("digits:test"); + fail("namespace prefix allowed to contain colon"); + } + catch (IllegalNameException success) { + assertNotNull(success.getMessage()); + } + + try { + e.setNamespacePrefix("digits!test"); + fail("namespace prefix allowed to contain bang"); + } + catch (IllegalNameException success) { + assertNotNull(success.getMessage()); + assertEquals("digits!test", success.getData()); + } + + try { + e.setNamespacePrefix("digits\u0000test"); + fail("namespace prefix allowed to contain null"); + } + catch (IllegalNameException success) { + assertNotNull(success.getMessage()); + assertEquals("digits\u0000test", success.getData()); + } + + } + + + public void testSetNamespaceURI() { + + String name = "red:sakjdhjhd"; + String uri = "http://www.red.com/"; + Element e = new Element(name, uri); + + assertEquals(e.getNamespaceURI(), uri); + + for (int i = 0; i < legal.length; i++) { + e.setNamespaceURI(legal[i]); + assertEquals(legal[i], e.getNamespaceURI()); + } + + for (int i = 0; i < illegal.length; i++) { + try { + e.setNamespaceURI(illegal[i]); + fail("illegal namespace URI allowed"); + } + catch (MalformedURIException success) { + assertNotNull(success.getMessage()); + } + } + + } + + public void testRFC2396LegalRFC3986IllegalNamespaceURI() { + + String name = "red:green"; + String uri = "dcp.tcp.pft://192.168.0.1:1002:3002?fec=1&crc=0'"; + try { + new Element(name, uri); + fail("Allowed RFC 2396 legal but RFC 3986 illegal URI"); + } + catch (MalformedURIException success) { + assertNotNull(success.getMessage()); + } + + } + + public void + testSetNamespaceURIConflictsWithAdditionalNamespaceDeclaration() + { + + String name = "red:sakjdhjhd"; + String uri = "http://www.red.com/"; + Element e = new Element(name, uri); + e.addNamespaceDeclaration("red", "http://www.red.com/"); + + try { + e.setNamespaceURI("http://www.example.com"); + fail("illegal namespace conflict"); + } + catch (NamespaceConflictException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testSetNamespaceURIConflictsWithAttributeNamespace() { + + String name = "red:sakjdhjhd"; + String uri = "http://www.red.com/"; + Element e = new Element(name, uri); + e.addAttribute( + new Attribute("red:test", "http://www.red.com/", "value")); + + try { + e.setNamespaceURI("http://www.example.com"); + fail("illegal attribute namespace conflict"); + } + catch (NamespaceConflictException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testChangePrefix() { + + String name = "red:sakjdhjhd"; + String uri = "http://www.red.com/"; + Element element = new Element(name, uri); + element.addNamespaceDeclaration("blue", "http://www.foo.com/"); + element.addAttribute(new Attribute("green:money", + "http://example.com/", "value")); + element.addNamespaceDeclaration("purple", uri); + element.addAttribute( + new Attribute("mauve:money", uri, "value")); + + try { + element.setNamespacePrefix("blue"); + fail("Conflicting prefix allowed against additional " + + "namespace declaration"); + } + catch (NamespaceConflictException success) { + assertNotNull(success.getMessage()); + } + + try { + element.setNamespacePrefix("green"); + fail("Conflicting prefix allowed against attribute"); + } + catch (NamespaceConflictException success) { + assertNotNull(success.getMessage()); + } + + element.setNamespacePrefix("purple"); + assertEquals("purple", element.getNamespacePrefix()); + element.setNamespacePrefix("mauve"); + assertEquals("mauve", element.getNamespacePrefix()); + + } + + + public void testDeclareNamespacePrefix() { + + String name = "red:sakjdhjhd"; + String uri = "http://www.red.com/"; + Element e = new Element(name, uri); + + for (int i = 0; i < legal.length; i++) { + e.removeNamespaceDeclaration("prefix"); + e.addNamespaceDeclaration("prefix", legal[i]); + assertEquals(legal[i], e.getNamespaceURI("prefix")); + } + + for (int i = 0; i < illegal.length; i++) { + try { + e.addNamespaceDeclaration("prefix", illegal[i]); + fail("illegal namespace URI allowed " + illegal[i]); + } + catch (MalformedURIException ex) { + // success + assertNotNull(ex.getMessage()); + } + } + + } + + + public void testInsertChildUsingString() { + + String name = "red:sakjdhjhd"; + String uri = "http://www.red.com/"; + Element e = new Element(name, uri); + + Element e2 = new Element( + "mauve:child", "http://www.mauve.com"); + e.insertChild(e2, 0); + Element e3 = new Element( + "mauve:child", "http://www.mauve.com"); + e.insertChild(e3, 0); + + e.insertChild("Hello", 0); + assertEquals("Hello", e.getChild(0).getValue()); + + } + + + public void testInsertNull() { + + String name = "red:sakjdhjhd"; + String uri = "http://www.red.com/"; + Element e = new Element(name, uri); + String data = null; + try { + e.insertChild(data, 0); + fail("Inserted null"); + } + catch (NullPointerException success) { + // success; + } + } + + + public void appendNullChild() { + + String name = "red:sakjdhjhd"; + String uri = "http://www.red.com/"; + Element e = new Element(name, uri); + String data = null; + try { + e.appendChild(data); + fail("Appended null"); + } + catch (NullPointerException success) { + // success; + } + } + + + public void testInsertChild() { + + String name = "red:sakjdhjhd"; + String uri = "http://www.red.com/"; + Element e = new Element(name, uri); + + Element e2 = new Element("mv:child", "http://www.mauve.com"); + e.insertChild(e2, 0); + Element e3 = new Element("mv:child", "http://www.mauve.com"); + e.insertChild(e3, 0); + Element e4 = new Element("mv:child", "http://www.mauve.com"); + e3.insertChild(e4, 0); + + assertEquals(e3, e.getChild(0)); + + try { + Element root = new Element("root"); + Document doc = new Document(root); + e.insertChild(doc, 0); + fail("Added document as child"); + } + catch (IllegalAddException ex) { + // success + assertNotNull(ex.getMessage()); + } + try { + e.insertChild(e2, 0); + fail("Added child twice"); + } + catch (MultipleParentException ex) { + // success + assertNotNull(ex.getMessage()); + } + try { + e.insertChild(e4, 0); + fail("Added child twice"); + } + catch (MultipleParentException ex) { + // success + assertNotNull(ex.getMessage()); + } + try { + e.insertChild((Node) null, 0); + fail("Added null child"); + } + catch (NullPointerException success) { + // success + } + try { + e.insertChild(new Comment("test"), 20); + fail("Added comment after end"); + } + catch (IndexOutOfBoundsException success) { + // success + assertNotNull(success.getMessage()); + } + try { + e.insertChild(new Comment("test"), -20); + fail("Added comment before start"); + } + catch (IndexOutOfBoundsException success) { + // success + } + + } + + + public void testInsertString() { + + Element element = new Element("test"); + element.insertChild("test", 0); + + String s = null; + try { + element.insertChild(s, 0); + fail("Inserted string as null"); + } + catch (NullPointerException success) { + // success + } + + element.insertChild("" , 0); + // empty node should be created + assertEquals(2, element.getChildCount()); + + } + + + public void testUnsetNamespaceWhenPrefixed() { + + Element element + = new Element("prefix:name", "http://www.foo.com/"); + + try { + element.setNamespaceURI(""); + fail("Unset namespace"); + } + catch (NamespaceConflictException success) { + assertNotNull(success.getMessage()); + } + try { + element.setNamespaceURI(null); + fail("Unset namespace"); + } + catch (NamespaceConflictException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testGetChildElements() { + + Elements children = element.getChildElements(); + assertEquals(3, children.size()); + assertEquals(child1, children.get(0)); + assertEquals(child4, children.get(1)); + assertEquals(child5, children.get(2)); + + children = element.getChildElements("nonesuch"); + assertEquals(0, children.size()); + + children = element.getChildElements("test"); + assertEquals(1, children.size()); + assertEquals(child1, children.get(0)); + + children = element.getChildElements( + "test", "http://www.example.com"); + assertEquals(2, children.size()); + assertEquals(child4, children.get(0)); + assertEquals(child5, children.get(1)); + + } + + + public void testForEach() { + + Elements children = element.getChildElements(); + for (Element element : children) { + Assert.assertNotNull(element); + } + + } + + public void testIteratorIsReadOnly() { + + Elements children = element.getChildElements(); + try { + children.iterator().remove(); + Assert.fail("removed element"); + } + catch (UnsupportedOperationException ex) { + } + } + + + public void testAddAttribute() { + + Element element = new Element("name"); + Attribute a1 = new Attribute("name", "value"); + Attribute a2 = new Attribute("xlink:type", + "http://www.w3.org/TR/1999/xlink", "simple"); + + element.addAttribute(a1); + element.addAttribute(a2); + + assertEquals(2, element.getAttributeCount()); + + + Element element2 = new Element("name"); + try { + element2.addAttribute(a1); + fail("added attribute with existing parent"); + } + catch (MultipleParentException success) { + assertNotNull(success.getMessage()); + } + + a1.detach(); + element2.addAttribute(a1); + + a2.detach(); + Element funky + = new Element("xlink:funky", "http://www.funky.org"); + try { + funky.addAttribute(a2); + fail("added conflicting namespace"); + } + catch (NamespaceConflictException success) { + assertNotNull(success.getMessage()); + } + + a2.detach(); + Element notasfunky = new Element( + "prefix:funky", "http://www.w3.org/TR/1999/xlink"); + notasfunky.addAttribute(a2); + + + Attribute a3 = new Attribute( + "xlink:type", "http://www.w3.org/TR/1999/xlink", "simple"); + Attribute a4 = new Attribute( + "xlink:href", "http://www.w3.org/1998/xlink", "simple"); + Element test = new Element("test"); + try { + test.addAttribute(a3); + test.addAttribute(a4); + fail("added conflicting attributes"); + } + catch (NamespaceConflictException success) { + assertNotNull(success.getMessage()); + } + + Attribute a5 = new Attribute( + "xlink:type", "http://www.w3.org/TR/1999/xlink", "simple"); + Attribute a6 = new Attribute( + "xlink:type", "http://www.w3.org/1998/xlink", "simple"); + Element test2 = new Element("test"); + try { + test2.addAttribute(a5); + test2.addAttribute(a6); + fail("added conflicting attributes"); + } + catch (NamespaceConflictException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testAddAttributesWithAdditionalNamespaces() { + + Element element = new Element("name"); + element.addNamespaceDeclaration( + "xlink", "http://www.w3.org/TR/1999/xlink"); + element.addNamespaceDeclaration( + "pre", "http://www.example.com"); + + + Attribute a1 = new Attribute("name", "value"); + Attribute a2 = new Attribute("xlink:type", + "http://www.w3.org/TR/1999/xlink", "simple"); + + element.addAttribute(a1); + element.addAttribute(a2); + + assertEquals(2, element.getAttributeCount()); + + try { + element.addAttribute( + new Attribute("pre:att", "ftp://example.com/", "value") + ); + fail("added attribute that conflicts with " + + "additional namespace declaration"); + } + catch (NamespaceConflictException success) { + assertNotNull(success.getMessage()); + } + + element.addAttribute( + new Attribute("ok:att", "ftp://example.com/", "value") + ); + assertEquals(3, element.getAttributeCount()); + + try { + element.addNamespaceDeclaration( + "ok", "http://www.example.net"); + fail("added namespace declaration that " + + "conflicts with attribute"); + } + catch (NamespaceConflictException ex) { + assertNotNull(ex.getMessage()); + } + + assertEquals( + "ftp://example.com/", + element.getNamespaceURI("ok")); + assertEquals( + "http://www.w3.org/TR/1999/xlink", + element.getNamespaceURI("xlink")); + assertEquals( + "http://www.example.com", + element.getNamespaceURI("pre")); + + } + + + public void testTriple() + throws IOException, ParsingException { + String data = ""; + Builder builder = new Builder(); + Document doc = builder.build(data, "http://www.example.org/"); + Element root = doc.getRootElement(); + Element rootcopy = root.copy(); + assertEquals(data, rootcopy.toXML()); + } + + + public void testCopyChildElementWithNoChildren() { + + Element parent = new Element("parent"); + Element child = new Element("child"); + parent.appendChild(child); + Element copy = new Element(child); + assertEquals(child, copy); + + } + + + public void testSimpleCopy() { + + Element parent = new Element("parent"); + Element child = new Element("child"); + parent.appendChild(child); + Element copy = new Element(parent); + assertEquals(parent, copy); + + } + + + public void testCopyElementWithAttributes() { + + Element parent = new Element("parent"); + Attribute a = new Attribute("name", "value"); + parent.addAttribute(a); + Element copy = new Element(parent); + assertEquals(parent, copy); + Attribute copied = copy.getAttribute(0); + assertEquals(a, copied); + assertEquals(copy, copied.getParent()); + + } + + + public void testCopyEmptyElement() { + + Element parent = new Element("parent"); + Element copy = new Element(parent); + assertEquals(parent, copy); + + } + + + public void testEmptyElementAsRootElementCopy() { + + Element root = new Element("root"); + Document doc = new Document(root); + Node copy = doc.copy(); + assertEquals(doc, copy); + } + + + public void testCopy() { + + String name = "red:sakjdhjhd"; + String uri = "http://www.red.com/"; + String baseURI = "http://www.example.com/"; + Element e = new Element(name, uri); + + e.addNamespaceDeclaration("blue", "http://www.blue.com"); + e.addNamespaceDeclaration("green", "http://www.green.com"); + Attribute a1 = new Attribute("test", "test"); + Attribute a2 = new Attribute("pre1:green", + "http://www.green.com/", "data"); + Attribute a3 = new Attribute("yellow:sfsdadf", + "http://www.yellow.com/", "data"); + e.addAttribute(a1); + e.addAttribute(a2); + e.addAttribute(a3); + + Element e2 = new Element("mv:child", "http://www.mauve.com"); + e.appendChild(e2); + + Element e3 = new Element("mv:child", "http://www.mauve.com"); + e.insertChild(e3, 0); + Element e4 = new Element("mv:child", "http://www.mauve.com"); + e3.insertChild(e4, 0); + e.setBaseURI(baseURI); + + Element copy = e.copy(); + + assertEquals( + e.getNamespaceURI("red"), + copy.getNamespaceURI("red")); + assertEquals( + e.getNamespaceURI("blue"), + copy.getNamespaceURI("blue")); + assertEquals(e.getValue(), copy.getValue()); + + Attribute ea = e.getAttribute("test"); + Attribute ca = copy.getAttribute("test"); + assertEquals(ea.getValue(), ca.getValue()); + assertEquals(e.getBaseURI(), copy.getBaseURI()); + + } + + + public void testDeepCopy() { + + Element top = new Element("e"); + Element parent = top; + for (int i = 0; i < 100; i++) { + Element child = new Element("e" + i); + parent.appendChild(child); + parent = child; + } + Element copy = (Element) top.copy(); + assertEquals(top, copy); + + } + + + public void testRemoveChildren() { + + String name = "red:sakjdhjhd"; + String uri = "http://www.red.com/"; + Element parent = new Element(name, uri); + + Attribute a1 = new Attribute("test", "test"); + parent.addAttribute(a1); + + Element child1 = new Element("mv:child", "http://www.mauve.com"); + parent.appendChild(child1); + Element child2 = new Element("mv:child", "http://www.mauve.com"); + parent.appendChild(child2); + Element grandchild = new Element("mv:child", "http://www.mauve.com"); + child2.insertChild(grandchild, 0); + + assertEquals(child2, grandchild.getParent()); + assertEquals(parent, child1.getParent()); + assertEquals(parent, child2.getParent()); + + Nodes result = parent.removeChildren(); + + assertEquals(0, parent.getChildCount()); + assertNull(child1.getParent()); + assertNull(child2.getParent()); + assertEquals(child2, grandchild.getParent()); + assertEquals(parent, a1.getParent()); + + assertEquals(2, result.size()); + assertEquals(child1, result.get(0)); + assertEquals(child2, result.get(1)); + assertEquals(grandchild, child2.getChild(0)); + assertEquals(child2, grandchild.getParent()); + + } + + + public void testRemovedChildrenInheritBaseURI() { + + String base = "http://www.example.com/"; + Element parent = new Element("parent"); + Element child = new Element("child"); + parent.setBaseURI(base); + parent.appendChild(child); + parent.removeChildren(); + assertEquals(base, child.getBaseURI()); + + } + + + public void testRemoveNonElementChildren() { + + String name = "red:sakjdhjhd"; + String uri = "http://www.red.com/"; + Element parent = new Element(name, uri); + + Attribute a1 = new Attribute("test", "test"); + parent.addAttribute(a1); + + Node child1 = new Text("http://www.mauve.com"); + parent.appendChild(child1); + Node child2 = new ProcessingInstruction( + "child", "http://www.mauve.com"); + parent.appendChild(child2); + Node child3 = new Comment("http://www.mauve.com"); + parent.appendChild(child3); + + assertEquals(parent, child3.getParent()); + assertEquals(parent, child1.getParent()); + assertEquals(parent, child2.getParent()); + + Nodes result = parent.removeChildren(); + + assertEquals(0, parent.getChildCount()); + assertNull(child1.getParent()); + assertNull(child2.getParent()); + assertNull(child3.getParent()); + assertEquals(parent, a1.getParent()); + + assertEquals(3, result.size()); + assertEquals(child1, result.get(0)); + assertEquals(child2, result.get(1)); + assertEquals(child3, result.get(2)); + + } + + + public void testGetAttributeValue() { + + String name = "sakjdhjhd"; + Element e = new Element(name); + + assertNull(e.getAttributeValue("test")); + assertNull(e.getAttributeValue("base", + "http://www.w3.org/XML/1998/namespace")); + + e.addAttribute(new Attribute("test", "value")); + e.addAttribute(new Attribute("xml:base", + "http://www.w3.org/XML/1998/namespace", + "http://www.example.com/")); + + assertEquals("value", e.getAttributeValue("test")); + assertEquals( + "http://www.example.com/", + e.getAttributeValue("base", + "http://www.w3.org/XML/1998/namespace")); + assertNull(e.getAttributeValue("xml:base")); + assertNull(e.getAttributeValue("base")); + assertNull(e.getAttributeValue("test", + "http://www.w3.org/XML/1998/namespace")); + + } + + + public void testGetAttribute() { + + String name = "sakjdhjhd"; + Element e = new Element(name); + + assertNull(e.getAttribute("test")); + assertNull(e.getAttribute("base", + "http://www.w3.org/XML/1998/namespace")); + + try { + e.getAttribute(0); + } + catch (IndexOutOfBoundsException success) { + // success + } + + Attribute a1 = new Attribute("test", "value"); + Attribute a2 = new Attribute("xml:base", + "http://www.w3.org/XML/1998/namespace", + "http://www.example.com/"); + + e.addAttribute(a1); + e.addAttribute(a2); + + assertEquals(a1, e.getAttribute("test")); + assertEquals(a2, e.getAttribute("base", + "http://www.w3.org/XML/1998/namespace")); + + try { + e.getAttribute(2); + fail("Got attribute beyond bounds"); + } + catch (IndexOutOfBoundsException success) { + assertNotNull(success.getMessage()); + } + + try { + e.getAttribute(-1); + fail("Got attribute with negative index"); + } + catch (IndexOutOfBoundsException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testGetNamespacePrefix() { + Element html = new Element("html"); + assertEquals("", html.getNamespacePrefix()); + } + + + public void testXMLPrefixAllowed() { + Element test = new Element("xml:base", + "http://www.w3.org/XML/1998/namespace"); + assertEquals("xml", test.getNamespacePrefix()); + assertEquals("http://www.w3.org/XML/1998/namespace", test.getNamespaceURI()); + assertEquals("xml:base", test.getQualifiedName()); + } + + + public void testXMLPrefixNotAllowedWithWrongURI() { + try { + new Element("xml:base", "http://www.example.org/"); + fail("Allowed wrong namespace for xml prefix"); + } + catch (NamespaceConflictException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testWrongPrefixNotAllowedWithXMLURI() { + + try { + new Element("test:base", "http://www.w3.org/XML/1998/namespace"); + fail("Allowed XML namespace to be associated with non-xml prefix"); + } + catch (NamespaceConflictException success) { + assertNotNull(success.getMessage()); + } + + + } + + + public void testEmptyName() { + + try { + new Element(""); + fail("Allowed empty string for element name"); + } + catch (IllegalNameException success) { + assertNotNull(success.getMessage()); + assertEquals("", success.getData()); + } + + } + + + public void testBadNameStartCharacter() { + + try { + new Element("1Kelvin"); + fail("Allowed element name to begin with digit"); + } + catch (IllegalNameException success) { + assertNotNull(success.getMessage()); + assertEquals("1Kelvin", success.getData()); + } + + } + + + public void testNullName() { + + try { + new Element((String) null); + fail("Allowed null for element name"); + } + catch (NullPointerException success) { + // success + } + + } + + +} diff --git a/src/nu/xom/tests/EncodingTest.java b/src/nu/xom/tests/EncodingTest.java new file mode 100644 index 0000000..09bdc58 --- /dev/null +++ b/src/nu/xom/tests/EncodingTest.java @@ -0,0 +1,318 @@ +/* Copyright 2002-2005, 2018 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.tests; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.Charset; +import java.nio.charset.UnsupportedCharsetException; + +import nu.xom.Attribute; +import nu.xom.Builder; +import nu.xom.Document; +import nu.xom.Element; +import nu.xom.ParsingException; +import nu.xom.Serializer; + +/** + *

    + * Check serialization of almost all of Unicode + * in a variety of encodings. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.2.11 + * + */ +public class EncodingTest extends XOMTestCase { + + + public EncodingTest(String name) { + super(name); + } + + + private Document doc; + + private static int SURROGATE_OFFSET = 0x10000 - (0xD800 << 10) - 0xDC00; + + protected void setUp() { + + Element root = new Element("root"); + doc = new Document(root); + + Element prototype = new Element("d"); + for (int i = 0x20; i <= 0xD7FF; i++) { + Element data = (Element) prototype.copy(); + data.appendChild(String.valueOf(((char) i))); + data.addAttribute(new Attribute("c", String.valueOf(i))); + root.appendChild(data); + } + + // skip surrogates between 0xD800 and 0xDFFF + for (int i = 0xE000; i <= 0xFFFD; i++) { + Element data = (Element) prototype.copy(); + data.appendChild(String.valueOf(((char) i))); + data.addAttribute(new Attribute("c", String.valueOf(i))); + root.appendChild(data); + } + + // Test Plane-1 characters. These are tricky because Java + // strings encode them as surrogate pairs. We'll test with + // the characters from 1D100 to 1D1FF (the musical symbols) + StringBuffer sb = new StringBuffer(2); + char high = 0xD834; + sb.append(high); + for (int i = 0; i < 256; i++) { + char low = (char) (0xDD00+i); + sb.setLength(1); + sb.append(low); + String s = sb.toString(); + Element data = (Element) prototype.copy(); + data.appendChild( s ); + data.addAttribute(new Attribute("c", String.valueOf(0x1D100 + i))); + root.appendChild(data); + } + + } + + + protected void tearDown() { + doc = null; + System.gc(); + } + + + public void testEUCJP() throws ParsingException, IOException { + checkAll("EUC-JP"); + } + + + public void testShift_JIS() throws ParsingException, IOException { + checkAll("Shift_JIS"); + } + + + public void testISO2022JP() throws ParsingException, IOException { + if (charsetAvailable("ISO-2022-JP")) checkAll("ISO-2022-JP"); + } + + + public void testGeneric() throws ParsingException, IOException { + checkAll("Cp1252"); + } + + + // Main purpose here is to test a character set whose name is + // case dependent + public void testMacRoman() throws ParsingException, IOException { + checkAll("MacRoman"); + } + + + public void testBig5() throws ParsingException, IOException { + checkAll("Big5"); + } + + public void testUSASCII() throws ParsingException, IOException { + checkAll("US-ASCII"); + } + + public void testASCII() throws ParsingException, IOException { + checkAll("ASCII"); + } + + public void testLatin1() throws ParsingException, IOException { + checkAll("ISO-8859-1"); + } + + public void testLatin2() throws ParsingException, IOException { + checkAll("ISO-8859-2"); + } + + public void testLatin3() throws ParsingException, IOException { + checkAll("ISO-8859-3"); + } + + public void testLatin4() throws ParsingException, IOException { + checkAll("ISO-8859-4"); + } + + public void testCyrillic() throws ParsingException, IOException { + checkAll("ISO-8859-5"); + } + + public void testArabic() throws ParsingException, IOException { + checkAll("ISO-8859-6"); + } + + public void testGreek() throws ParsingException, IOException { + // This test seems to fail in Java 1.5, at least on Mac OS X + // It passes in 1.4. The problem is the delete character 127 + checkAll("ISO-8859-7"); + } + + public void testThai() throws ParsingException, IOException { + checkAll("TIS-620"); + } + + public void testHebrew() throws ParsingException, IOException { + checkAll("ISO-8859-8"); + } + + public void testLatin5() throws ParsingException, IOException { + checkAll("ISO-8859-9"); + } + + public void testUTF8() throws ParsingException, IOException { + checkAll("UTF-8"); + } + + public void testUTF16() throws ParsingException, IOException { + checkAll("UTF-16"); + } + + public void testUCS2() throws ParsingException, IOException { + checkAll("ISO-10646-UCS-2"); + } + + public void testEBCDIC() throws ParsingException, IOException { + checkAll("Cp037"); + } + + // These encodings are only available after Java 1.3 + private static boolean java14OrLater = false; + + static { + String version = System.getProperty("java.version"); + String majorVersion = version.substring(0, 3); + double versionNumber = Double.parseDouble(majorVersion); + if (versionNumber >= 1.4) java14OrLater = true; + } + + public void testLatin7() throws ParsingException, IOException { + if (java14OrLater) checkAll("ISO-8859-13"); + } + + public void testLatin9() throws ParsingException, IOException { + if (java14OrLater) checkAll("ISO-8859-15"); + } + + public void testGB18030() throws ParsingException, IOException { + if (java14OrLater) checkAll("GB18030"); + } + + // These encodings are not installed in all distributions by + // default. They are only found currently in IBM's Java 1.4.1 VM. + // They don't seem to be supported in the 1.5 alpha + // either. + public void testUCS4() throws ParsingException, IOException { + if (charsetAvailable("ISO-10646-UCS-4")) checkAll("ISO-10646-UCS-4"); + } + + public void testLatin6() throws ParsingException, IOException { + if (charsetAvailable("ISO-8859-10")) checkAll("ISO-8859-10"); + } + + public void testLatin8() throws ParsingException, IOException { + if (charsetAvailable("ISO-8859-14")) checkAll("ISO-8859-14"); + } + + public void testLatin10() throws ParsingException, IOException { + if (charsetAvailable("ISO-8859-16")) checkAll("ISO-8859-16"); + } + + + // Test that with an encoding XOM does not specifically support + // but the VM does, everything still works. + public void testUnsupportedEncoding() + throws ParsingException, IOException { + checkAll("Cp1252"); + } + + + private static boolean charsetAvailable(String name) { + try { + Charset.forName(name); + return true; + } catch (UnsupportedCharsetException ex) { + return false; + } + } + + + private void checkAll(String encoding) + throws ParsingException, IOException { + + Builder builder = new Builder(); + System.gc(); + ByteArrayOutputStream out = new ByteArrayOutputStream(100000); + // Write data into a byte array using encoding + Serializer serializer = new Serializer(out, encoding); + serializer.write(doc); + serializer.flush(); + out.flush(); + out.close(); + byte[] data = out.toByteArray(); + InputStream in = new ByteArrayInputStream(data); + Document reparsed = builder.build(in); + in.close(); + serializer = null; + + Element reparsedRoot = reparsed.getRootElement(); + int childCount = reparsedRoot.getChildCount(); + for (int i = 0; i < childCount; i++) { + Element test = (Element) reparsedRoot.getChild(i); + String value = test.getValue(); + int expected + = Integer.parseInt(test.getAttributeValue("c")); + // workaround for EBCDIC bugs + if (expected == 133 && encoding.equalsIgnoreCase("Cp037")) { + continue; + } + int actual = value.charAt(0); + if (value.length() > 1) { + int low = value.charAt(1); + actual = (actual << 10) + low + SURROGATE_OFFSET; + } + // This doesn't work for all encodings, because there are + // a few cases where you write a Unicode compatibility + // character such as an Arabic presentation form, + // but read back what is essentially a different version + // of the same character. That is the mapping from some + // legacy character sets to Unicode is not always 1-1. + /* + assertEquals("Expected 0x" + + Integer.toHexString(expected).toUpperCase() + + " but was 0x" + + Integer.toHexString(actual).toUpperCase(), expected, actual); */ + assertEquals(expected, actual); + } + + in = null; + + } + + +} diff --git a/src/nu/xom/tests/FastTests.java b/src/nu/xom/tests/FastTests.java new file mode 100644 index 0000000..bbb9810 --- /dev/null +++ b/src/nu/xom/tests/FastTests.java @@ -0,0 +1,87 @@ +/* Copyright 2002-2004 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.tests; + +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestSuite; + +/** + *

    + * Collect the faster of XOM's test cases into a single class. + * This is a faster first check on sanity. Alone this is not enough to + * guarantee that the implementation is correct, but may quickly prove + * that it's incorrect. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.0 + * + */ +public class FastTests extends TestCase { + + public FastTests() { + super("XOM Tests that run fairly quickly"); + } + + public static Test suite() { + TestSuite result = new TestSuite(); + result.addTest(new TestSuite(VerifierTest.class)); + result.addTest(new TestSuite(SubclassTest.class)); + result.addTest(new TestSuite(NodeFactoryTest.class)); + result.addTest(new TestSuite(ParentNodeTest.class)); + result.addTest(new TestSuite(LeafNodeTest.class)); + result.addTest(new TestSuite(AttributeTest.class)); + result.addTest(new TestSuite(AttributeTypeTest.class)); + result.addTest(new TestSuite(TextTest.class)); + result.addTest(new TestSuite(ElementTest.class)); + result.addTest(new TestSuite(CommentTest.class)); + result.addTest(new TestSuite(ProcessingInstructionTest.class)); + result.addTest(new TestSuite(DocumentTest.class)); + result.addTest(new TestSuite(DocTypeTest.class)); + result.addTest(new TestSuite(AttributesTest.class)); + result.addTest(new TestSuite(NamespacesTest.class)); + // Too slow to run routinely + // result.addTest(new TestSuite(MegaTest.class)); + result.addTest(new TestSuite(XMLExceptionTest.class)); + result.addTest(new TestSuite(ParsingExceptionTest.class)); + result.addTest(new TestSuite(XSLExceptionTest.class)); + result.addTest(new TestSuite(XIncludeExceptionTest.class)); + result.addTest(new TestSuite(CDATASectionTest.class)); + result.addTest(new TestSuite(NodesTest.class)); + // EBCDIC test fails due to bugs in the Java class library + // result.addTest(new TestSuite(EBCDICTest.class)); + result.addTest(new TestSuite(RoundTripTest.class)); + result.addTest(new TestSuite(DOMConverterTest.class)); + // result.addTest(new TestSuite(SAXConverterTest.class)); + result.addTest(new TestSuite(XSLTransformTest.class)); + result.addTest(new TestSuite(SerializerTest.class)); + result.addTest(new TestSuite(CanonicalizerTest.class)); + result.addTest(new TestSuite(BuilderTest.class)); + /* result.addTest(new TestSuite(XIncludeTest.class)); + result.addTest(new TestSuite(BaseURITest.class)); + result.addTest(new TestSuite(EncodingTest.class)); */ + return result; + + } + +} diff --git a/src/nu/xom/tests/IDTest.java b/src/nu/xom/tests/IDTest.java new file mode 100644 index 0000000..6ed5ab2 --- /dev/null +++ b/src/nu/xom/tests/IDTest.java @@ -0,0 +1,355 @@ +/* Copyright 2005, 2006 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ +package nu.xom.tests; + +import java.io.File; +import java.io.IOException; +import java.net.URL; + +import nu.xom.Attribute; +import nu.xom.Builder; +import nu.xom.Document; +import nu.xom.Element; +import nu.xom.Elements; +import nu.xom.IllegalDataException; +import nu.xom.Node; +import nu.xom.Nodes; +import nu.xom.ParsingException; + +/** + *

    + * Tests for xml:id attributes. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.2b2 + * + */ +public class IDTest extends XOMTestCase { + + + public IDTest(String name) { + super(name); + } + + + public void testBuilderAllowsNonNCNameXmlIdAttributes() + throws ParsingException, IOException { + + Builder builder = new Builder(); + String data = ""; + Document doc = builder.build(data, null); + Element root = doc.getRootElement(); + Attribute id = root.getAttribute(0); + assertEquals("p 2", id.getValue()); + + } + + + public void testIDCanBeNonNCName() { + + Attribute id = new Attribute("xml:id", + "http://www.w3.org/XML/1998/namespace", "name"); + assertEquals("name", id.getValue()); + id.setValue("not a name"); + assertEquals(id.getValue(), "not a name"); + + } + + + public void testAttributeConstructorNormalizesValue() { + + Attribute id = new Attribute("xml:id", + "http://www.w3.org/XML/1998/namespace", " name "); + assertEquals("name", id.getValue()); + + } + + + public void testNameSetIDNeedNotBeNCName() { + + Attribute id = new Attribute("id", "not a name"); + id.setNamespace("xml", + "http://www.w3.org/XML/1998/namespace"); + assertEquals(Attribute.Type.ID, id.getType()); + + } + + + public void testBuilderNormalizesXmlIdAttributes() + throws ParsingException, IOException { + + Builder builder = new Builder(); + String data = ""; + Document doc = builder.build(data, null); + Element root = doc.getRootElement(); + String value = root.getAttributeValue("id", + "http://www.w3.org/XML/1998/namespace"); + assertEquals("p2", value); + + } + + + public void testBuilderDoesNotOverNormalizeXmlIdAttributesWithCarriageReturns() + throws ParsingException, IOException { + + Builder builder = new Builder(); + String data = ""; + Document doc = builder.build(data, null); + Element root = doc.getRootElement(); + Attribute id = root.getAttribute(0); + assertEquals("\r\u0020p2", id.getValue()); + + } + + + public void testBuilderDoesNotOverNormalizeXmlIdAttributesWithLineFeeds() + throws ParsingException, IOException { + + Builder builder = new Builder(); + String data = ""; + Document doc = builder.build(data, null); + Element root = doc.getRootElement(); + Attribute id = root.getAttribute(0); + assertEquals("\n\u0020p2", id.getValue()); + + } + + + public void testBuiltXmlIdAttributeHasTypeId() + throws ParsingException, IOException { + + Builder builder = new Builder(); + String data = ""; + Document doc = builder.build(data, null); + Element root = doc.getRootElement(); + Attribute id = root.getAttribute("id", + "http://www.w3.org/XML/1998/namespace"); + assertEquals(Attribute.Type.ID, id.getType()); + + } + + + public void testConstructedXmlIdAttributeHasTypeId() + throws ParsingException, IOException { + + Attribute id = new Attribute("xml:id", + "http://www.w3.org/XML/1998/namespace", "p2"); + assertEquals(Attribute.Type.ID, id.getType()); + + } + + + public void testNamespaceSetXmlIdAttributeHasTypeId() { + + Attribute id = new Attribute("id", "p2"); + id.setNamespace("xml", "http://www.w3.org/XML/1998/namespace"); + assertEquals(Attribute.Type.ID, id.getType()); + + } + + + public void testNameSetXmlIdAttributeHasTypeId() { + + Attribute id = new Attribute("xml:space", + "http://www.w3.org/XML/1998/namespace", "preserve"); + id.setLocalName("id"); + assertEquals(Attribute.Type.ID, id.getType()); + + } + + + public void testNameSetXmlIdAttributeMustBeNCName() { + + Attribute id = new Attribute("xml:space", + "http://www.w3.org/XML/1998/namespace", "not a name"); + try { + id.setLocalName("id"); + fail("Allowed non-NCNAME ID"); + } + catch (IllegalDataException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testValueSetXmlIdAttributeIsNormalized() { + + Attribute id = new Attribute("xml:id", + "http://www.w3.org/XML/1998/namespace", "value"); + id.setValue(" a3 "); + assertEquals("a3", id.getValue()); + + } + + + public void testCantChangeTypeOfXMLIDAttribute() { + + Attribute id = new Attribute("xml:id", + "http://www.w3.org/XML/1998/namespace", "p2"); + + try { + id.setType(Attribute.Type.CDATA); + fail("changed xml:id attribute to CDATA"); + } + catch (IllegalDataException success) { + assertNotNull(success.getMessage()); + } + assertEquals(Attribute.Type.ID, id.getType()); + + } + + + public void testXPathRecognizesXmlIDAttributes() + throws ParsingException, IOException { + + Element root = new Element("root"); + Document doc = new Document(root); + Element child = new Element("child"); + root.appendChild(child); + Attribute id = new Attribute("id", "p2"); + child.addAttribute(id); + id.setNamespace("xml", "http://www.w3.org/XML/1998/namespace"); + Nodes result = doc.query("id('p2')"); + assertEquals(1, result.size()); + assertEquals(child, result.get(0)); + + } + + + public void testXMLIDTestSuiteFromW3CServer() + throws ParsingException, IOException { + + URL base = new URL("http://www.w3.org/XML/2005/01/xml-id/test-suite.xml"); + Builder builder = new Builder(); + Document catalog = builder.build(base.openStream()); + Element testsuite = catalog.getRootElement(); + Elements testCatalogs = testsuite.getChildElements("test-catalog"); + for (int i = 0; i < testCatalogs.size(); i++) { + Elements testcases = testCatalogs.get(i).getChildElements("test-case"); + for (int j = 0; j < testcases.size(); j++) { + Element testcase = testcases.get(j); + String features = testcase.getAttributeValue("feature"); + if (features != null && features.indexOf("xml11") >= 0) { + continue; // skip test + } + URL testURL = new URL(base, testcase.getFirstChildElement("file-path").getValue() + "/"); + Element scenario = testcase.getFirstChildElement("scenario"); + boolean errorExpected = scenario.getAttribute("operation").getValue().equals("error"); + Element input = scenario.getFirstChildElement("input-file"); + URL inputFile = new URL(testURL, input.getValue()); + Elements expectedIDs = scenario.getChildElements("id"); + try { + Document inputDoc = builder.build(inputFile.openStream()); + Nodes recognizedIDs = getIDs(inputDoc); + assertEquals(expectedIDs.size(), recognizedIDs.size()); + for (int k = 0; k < expectedIDs.size(); k++) { + assertEquals(expectedIDs.get(i).getValue(), recognizedIDs.get(i).getValue()); + } + if (errorExpected) fail("Did not detect xml:id error"); + } + catch (ParsingException ex) { + if (!errorExpected) throw ex; + } + } // end for + } + + } + + + public void testXMLIDTestSuite() + throws ParsingException, IOException { + + Builder builder = new Builder(); + File base = new File("data"); + base = new File(base, "xmlid"); + File catalog = new File(base, "catalog.xml"); + + // The test suite needs to be installed separately. If we can't + // find the catalog, we just don't run these tests. + if (catalog.exists()) { + Document doc = builder.build(catalog); + Element testsuite = doc.getRootElement(); + Elements testCatalogs = testsuite.getChildElements("test-catalog"); + for (int i = 0; i < testCatalogs.size(); i++) { + Elements testcases = testCatalogs.get(i).getChildElements("test-case"); + for (int j = 0; j < testcases.size(); j++) { + Element testcase = testcases.get(j); + String features = testcase.getAttributeValue("features"); + if (features != null && features.indexOf("xml11") >= 0) { + continue; // skip test + } + File root = new File(base, testcase.getFirstChildElement("file-path").getValue()); + File inputFile = null; + Element scenario = testcase.getFirstChildElement("scenario"); + Element input = scenario.getFirstChildElement("input"); + inputFile = new File(root, input.getValue()); + Elements expectedIDs = scenario.getChildElements("id"); + try { + Document inputDoc = builder.build(inputFile); + Nodes recognizedIDs = getIDs(inputDoc); + assertEquals(expectedIDs.size(), recognizedIDs.size()); + for (int k = 0; k < expectedIDs.size(); k++) { + assertEquals(expectedIDs.get(i).getValue(), recognizedIDs.get(i).getValue()); + } + } + catch (ParsingException ex) { + System.err.println(inputFile); + ex.printStackTrace(); + } + } // end for + } + + } // end if + + } + + + private Nodes getIDs(Document doc) { + + Element root = doc.getRootElement(); + Nodes list = new Nodes(); + getIDs(root, list); + return list; + } + + + private void getIDs(Element element, Nodes list) { + + for (int i = 0; i < element.getAttributeCount(); i++) { + Attribute a = element.getAttribute(i); + if (a.getType() == Attribute.Type.ID) { + // need to sort these into specific order + list.append(a); + } + } + for (int i = 0; i < element.getChildCount(); i++) { + Node child = element.getChild(i); + if (child instanceof Element) { + getIDs((Element) child, list); + } + } + + } + + +} diff --git a/src/nu/xom/tests/InfoTest.java b/src/nu/xom/tests/InfoTest.java new file mode 100644 index 0000000..1be483f --- /dev/null +++ b/src/nu/xom/tests/InfoTest.java @@ -0,0 +1,75 @@ +/* Copyright 2004, 2018 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.tests; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +/** + *

    + * Test the main() method for the JAR file. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.3.1 + * + */ +public class InfoTest extends XOMTestCase { + + + public InfoTest(String name) { + super(name); + } + + + // Note the subversion of access protection + public void testInfo() throws ClassNotFoundException, NoSuchMethodException, + IllegalAccessException, InvocationTargetException { + + PrintStream systemOut = System.out; + ByteArrayOutputStream out = new ByteArrayOutputStream(); + System.setOut(new PrintStream(out)); + + Class info = Class.forName("nu.xom.Info"); + Class[] args = {String[].class}; + Method main = info.getMethod("main", args); + main.setAccessible(true); + Object[] wrappedArgs = new Object[1]; + wrappedArgs[0] = new String[0]; + main.invoke(null, wrappedArgs); + System.out.flush(); + System.setOut(systemOut); + String output = new String(out.toByteArray()); + assertTrue(output.indexOf("Copyright 2002") > 0); + assertEquals(19, output.indexOf(" Elliotte Rusty Harold") - output.indexOf("Copyright 2002")); + assertTrue(output.indexOf("http://www.xom.nu") > 0); + assertTrue(output.indexOf("General Public License") > 0); + assertTrue(output.indexOf("GNU") > 0); + assertTrue(output.indexOf("WITHOUT ANY WARRANTY") > 0); + assertTrue(output.indexOf("without even the implied warranty") > 0); + + } + + +} \ No newline at end of file diff --git a/src/nu/xom/tests/LeafNodeTest.java b/src/nu/xom/tests/LeafNodeTest.java new file mode 100644 index 0000000..b72b246 --- /dev/null +++ b/src/nu/xom/tests/LeafNodeTest.java @@ -0,0 +1,74 @@ +/* Copyright 2002-2004 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.tests; + +import nu.xom.Text; + + +/** + *

    + * + *

    + * + * @author Elliotte Rusty Harold + * @version 1.0 + * + */ +public class LeafNodeTest extends XOMTestCase { + + + public LeafNodeTest(String name) { + super(name); + } + + + private Text leaf; + + + protected void setUp() { + leaf = new Text("parent"); + } + + + public void testHasChildren() { + assertEquals(0, leaf.getChildCount()); + } + + + public void testGetChild() { + + try { + leaf.getChild(0); + fail("No index exception"); + } + catch (IndexOutOfBoundsException success) { + // success + } + + } + + + public void testGetChildCount() { + assertEquals(0, leaf.getChildCount()); + } + +} diff --git a/src/nu/xom/tests/LocatorFilter.java b/src/nu/xom/tests/LocatorFilter.java new file mode 100644 index 0000000..062b35f --- /dev/null +++ b/src/nu/xom/tests/LocatorFilter.java @@ -0,0 +1,53 @@ +package nu.xom.tests; + +import org.xml.sax.Locator; +import org.xml.sax.XMLReader; +import org.xml.sax.helpers.XMLFilterImpl; + +/** + *

    + * Makes sure SAX does not have a locator. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.2b2 + * + */ +class LocatorFilter extends XMLFilterImpl { + + public LocatorFilter(XMLReader reader) { + super(reader); + } + + public void setDocumentLocator(Locator loc) { + super.setDocumentLocator(new NullLocator(loc)); + } + +} + + +class NullLocator implements Locator { + + private Locator loc; + + public NullLocator(Locator loc) { + this.loc = loc; + } + + public String getSystemId() { + return null; + } + + public String getPublicId() { + return loc.getPublicId(); + } + + public int getLineNumber() { + return loc.getLineNumber(); + } + + public int getColumnNumber() { + return loc.getColumnNumber(); + } + +} diff --git a/src/nu/xom/tests/MegaTest.java b/src/nu/xom/tests/MegaTest.java new file mode 100644 index 0000000..9bedc20 --- /dev/null +++ b/src/nu/xom/tests/MegaTest.java @@ -0,0 +1,152 @@ +/* Copyright 2003, 2004 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ +package nu.xom.tests; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.PipedReader; +import java.io.PipedWriter; +import java.io.Reader; +import java.io.Writer; + +import nu.xom.Attribute; +import nu.xom.Builder; +import nu.xom.Element; +import nu.xom.NodeFactory; +import nu.xom.Nodes; +import nu.xom.ParsingException; + + +/** + *

    + * Test that XOM can handle really big files. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.0 + * + */ +public class MegaTest extends XOMTestCase { + + private Reader in; + private Writer out; + private Builder builder; + private final static int expectedResult = 200000000; + private int actualResult = 0; + private Thread generator; + + public MegaTest(String name) { + super(name); + } + + + public static void main(String[] args) throws Exception { + MegaTest test = new MegaTest("MegaTest"); + test.setUp(); + test.testMegaFile(); + } + + + protected void setUp() throws IOException { + PipedReader pin = new PipedReader(); + out = new PipedWriter(pin); + in = new BufferedReader(pin); + actualResult = 0; + builder = new Builder(new MinimizingFactory()); + generator = new Generator(); + generator.start(); + } + + + class Generator extends Thread { + + public void run() { + try { + out.write("\n"); + out.write("\n"); + for (int i = 0; i < expectedResult; i++) { + out.write(" 1\n"); + // out.flush(); + if (i % 10000 == 0) { + System.out.println(i / 10000); + } + } + out.write("\n"); + out.close(); + } + catch (IOException ex) { + fail("threw IOException " + ex); + } + + } + + } + + + public void testMegaFile() + throws IOException, ParsingException { + + builder.build(in); + assertEquals(expectedResult, actualResult); + + } + + + private class MinimizingFactory extends NodeFactory { + + private Nodes empty = new Nodes(); + + public Nodes makeComment(String data) { + return empty; + } + + public Nodes finishMakingElement(Element element) { + if (element.getQualifiedName().equals("data")) { + actualResult += Integer.parseInt(element.getValue()); + return empty; + } + return new Nodes(element); + } + + public Nodes makeAttribute(String name, String URI, + String value, Attribute.Type type) { + return empty; + } + + public Nodes makeDocType(String rootElementName, + String publicID, String systemID) { + return empty; + } + + public Nodes makeText(String data) { + data = data.trim(); + if ("".equals(data)) return empty; + return super.makeText(data); + } + + public Nodes makeProcessingInstruction( + String target, String data) { + return empty; + } + + } + +} diff --git a/src/nu/xom/tests/NamespaceNodeTest.java b/src/nu/xom/tests/NamespaceNodeTest.java new file mode 100644 index 0000000..0131b92 --- /dev/null +++ b/src/nu/xom/tests/NamespaceNodeTest.java @@ -0,0 +1,287 @@ +/* Copyright 2005 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ +package nu.xom.tests; + +import nu.xom.Element; +import nu.xom.IllegalNameException; +import nu.xom.MalformedURIException; +import nu.xom.Namespace; +import nu.xom.NamespaceConflictException; +import nu.xom.NoSuchChildException; +import nu.xom.Nodes; + +/** + *

    + * Tests for namespace nodes used in XPath + *

    + * + * @author Elliotte Rusty Harold + * @version 1.3.0 + * + */ +public class NamespaceNodeTest extends XOMTestCase { + + + public NamespaceNodeTest(String name) { + super(name); + } + + + public void testGetters() { + + Element root = new Element("pre:root", "http://www.example.org/"); + Nodes result = root.query("namespace::pre"); + assertEquals(1, result.size()); + Namespace namespace = (Namespace) result.get(0); + assertEquals("pre", namespace.getPrefix()); + assertEquals("http://www.example.org/", namespace.getValue()); + assertEquals(root, namespace.getParent()); + + } + + + public void testCopy() { + + Element root = new Element("pre:root", "http://www.example.org/"); + Nodes result = root.query("namespace::pre"); + assertEquals(1, result.size()); + Namespace namespace = (Namespace) result.get(0); + + Namespace copy = namespace.copy(); + assertEquals(namespace, copy); + assertEquals("pre", copy.getPrefix()); + assertEquals("http://www.example.org/", copy.getValue()); + assertEquals(null, copy.getParent()); + + } + + + public void testToXML() { + + Element root = new Element("pre:root", "http://www.example.org/"); + Nodes result = root.query("namespace::pre"); + Namespace namespace = (Namespace) result.get(0); + assertEquals("xmlns:pre=\"http://www.example.org/\"", namespace.toXML()); + + } + + + public void testGetChildCount() { + + Element root = new Element("pre:root", "http://www.example.org/"); + Nodes result = root.query("namespace::pre"); + Namespace namespace = (Namespace) result.get(0); + assertEquals(0, namespace.getChildCount()); + + } + + + public void testGetChild() { + + Element root = new Element("pre:root", "http://www.example.org/"); + Nodes result = root.query("namespace::pre"); + Namespace namespace = (Namespace) result.get(0); + try { + namespace.getChild(0); + fail("Got namespace child"); + } + catch (IndexOutOfBoundsException success) { + assertEquals("Namespaces do not have children", success.getMessage()); + } + + } + + + public void testToXMLOnDefaultNamespace() { + + Element root = new Element("root", "http://www.example.org/"); + Nodes result = root.query("namespace::*[name() != 'xml']"); + Namespace namespace = (Namespace) result.get(0); + assertEquals("xmlns=\"http://www.example.org/\"", namespace.toXML()); + + } + + + public void testDetachNamespaceNode() { + + Element root = new Element("pre:root", "http://www.example.org/"); + Nodes result = root.query("namespace::pre"); + Namespace namespace = (Namespace) result.get(0); + namespace.detach(); + assertNull(namespace.getParent()); + + } + + + public void testRemoveNamespaceNode() { + + Element root = new Element("pre:root", "http://www.example.org/"); + Nodes result = root.query("namespace::pre"); + Namespace namespace = (Namespace) result.get(0); + try { + root.removeChild(namespace); + fail("Namespaces are not children"); + } + catch (NoSuchChildException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testGetParent() { + + Element root = new Element("pre:root", "http://www.example.org/"); + Nodes result = root.query("namespace::pre"); + Namespace namespace = (Namespace) result.get(0); + assertEquals(root, namespace.getParent()); + + } + + + public void testToString() { + + Element root = new Element("pre:root", "http://www.example.org/"); + Nodes result = root.query("namespace::pre"); + Namespace namespace = (Namespace) result.get(0); + assertEquals("[Namespace: xmlns:pre=\"http://www.example.org/\"]", namespace.toString()); + + } + + + public void testIllegalPrefix() { + + try { + new Namespace("white space", "http://www.example.org", null); + fail("Allowed prefix containing white space"); + } + catch (IllegalNameException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testEmptyStringPrefix() { + Namespace ns = new Namespace("", "http://www.example.org", null); + assertEquals("", ns.getPrefix()); + } + + + public void testNullPrefix() { + Namespace ns = new Namespace(null, "http://www.example.org", null); + assertEquals("", ns.getPrefix()); + } + + + public void testIllegalURI() { + + try { + new Namespace("pre", "http:// www.example.org", null); + fail("Allowed URI containing white space"); + } + catch (MalformedURIException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testNullURI() { + Namespace ns = new Namespace("", null, null); + assertEquals("", ns.getValue()); + } + + + public void testCantBindPrefixToEmptyURI() { + + try { + new Namespace("pre", "", null); + fail("Bound prefix to no namespace"); + } + catch (NamespaceConflictException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testCantBindXMLNS() { + + try { + new Namespace("xmlns", "", null); + fail("Bound xmlns prefix to no namespace"); + } + catch (IllegalNameException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testCantBindXMLNSToDOMURI() { + + try { + new Namespace("xmlns", "http://www.w3.org/2000/xmlns/", null); + fail("Bound xmlns prefix to DOM namespace"); + } + catch (IllegalNameException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testCantBindXMLPrefixToWrongURI() { + + try { + new Namespace("xml", "http://www.w3.org/2000/xmlns/", null); + fail("Bound xml prefix to DOM namespace"); + } + catch (NamespaceConflictException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testCanBindXMLPrefixToCorrectURI() { + + Namespace ns = new Namespace("xml", Namespace.XML_NAMESPACE, null); + assertEquals(Namespace.XML_NAMESPACE, ns.getValue()); + + } + + + public void testCanBindNonXMLPrefixToXMLURI() { + + try { + new Namespace("pre", Namespace.XML_NAMESPACE, null); + fail("Bound non-xml prefix to XML namespace"); + } + catch (NamespaceConflictException success) { + assertNotNull(success.getMessage()); + } + + } + + +} diff --git a/src/nu/xom/tests/NamespacesTest.java b/src/nu/xom/tests/NamespacesTest.java new file mode 100644 index 0000000..e9e207f --- /dev/null +++ b/src/nu/xom/tests/NamespacesTest.java @@ -0,0 +1,910 @@ +/* Copyright 2002-2006 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.tests; + +import nu.xom.Element; +import nu.xom.Attribute; +import nu.xom.MalformedURIException; +import nu.xom.NamespaceConflictException; + +/** + *

    + * Tests that namespace well-formedness is maintained. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.2d1 + * + */ +public class NamespacesTest extends XOMTestCase { + + public NamespacesTest() { + super("Namespaces tests"); + } + + private Element someNamespaces; + private Element noNamespaces; + private Element severalNamespaces; + + + protected void setUp() { + noNamespaces = new Element("test"); + + someNamespaces = new Element("test"); + someNamespaces.addNamespaceDeclaration("xlink", + "http://www.w3.org/2001/xlink"); + someNamespaces.addNamespaceDeclaration("xsl", + "http://www.w3.org/1999/xslt"); + + severalNamespaces + = new Element("test", "http://www.example.com/"); + severalNamespaces.addAttribute( + new Attribute("xlink:type", + "http://www.w3.org/2001/xlink", "simple")); + severalNamespaces.addNamespaceDeclaration("xsl", + "http://www.w3.org/1999/xslt"); + severalNamespaces.addNamespaceDeclaration("", + "http://www.example.com/"); + severalNamespaces.addNamespaceDeclaration("xlink", + "http://www.w3.org/2001/xlink"); + + } + + + public void testSetNamespacePrefixInConflictWithAdditionalNamespaceDeclaration() { + + someNamespaces.setNamespaceURI("http://www.example.net"); + try { + someNamespaces.setNamespacePrefix("xsl"); + fail("changed prefix to conflict with additional namespace declaration"); + } + catch (NamespaceConflictException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testSetNamespaceURIInConflictWithAdditionalNamespaceDeclaration() { + + someNamespaces.setNamespaceURI("http://www.w3.org/2001/xlink"); + someNamespaces.setNamespacePrefix("xlink"); + try { + someNamespaces.setNamespaceURI("http://www.example.net"); + fail("changed namespace URI to conflict with additional namespace declaration"); + } + catch (NamespaceConflictException ex) { + assertNotNull(ex.getMessage()); + } + + } + + + public void testAdditionalNamespaceDuplicatesElementNamespace() { + + Element element = new Element("pre:element", "http://www.example.org"); + element.addNamespaceDeclaration("pre", "http://www.example.org"); + element.setNamespacePrefix("foo"); + assertEquals("http://www.example.org", element.getNamespaceURI("pre")); + + } + + + public void testXMLNamespace() { + + assertEquals( + "http://www.w3.org/XML/1998/namespace", + noNamespaces.getNamespaceURI("xml") + ); + assertEquals( + "http://www.w3.org/XML/1998/namespace", + severalNamespaces.getNamespaceURI("xml") + ); + + } + + + public void testWrongPrefixNotAllowedWithXMLURI() { + + try { + noNamespaces.addNamespaceDeclaration("pre", + "http://www.w3.org/XML/1998/namespace"); + fail("Allowed XML namespace to be associated with non-xml prefix"); + } + catch (NamespaceConflictException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testUnmappingPrefix() { + + try { + noNamespaces.addNamespaceDeclaration("pre", ""); + } + catch (MalformedURIException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testAllowCapitalSchemes() { + noNamespaces.addNamespaceDeclaration("pre", "HTTP://WWW.EXAMPLE.COM/"); + assertEquals(noNamespaces.getNamespaceURI("pre"), "HTTP://WWW.EXAMPLE.COM/"); + } + + + public void testBadSchemes() { + + try { + noNamespaces.addNamespaceDeclaration("pre", "uri!urn:somedata"); + fail("Allowed illegal characters in scheme"); + } + catch (MalformedURIException success) { + assertNotNull(success.getMessage()); + } + } + + + public void testXMLPrefixNotAllowedWithWrongURI() { + + try { + noNamespaces.addNamespaceDeclaration("xml", "http://www.example.org/"); + fail("Allowed xml prefix to be associated with wrong URI"); + } + catch (NamespaceConflictException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testXMLNSNamespace() { + assertEquals("", noNamespaces.getNamespaceURI("xmlns")); + assertEquals("", severalNamespaces.getNamespaceURI("xmlns")); + } + + + public void testCantUseXMLNSPrefix() { + + try { + noNamespaces.addNamespaceDeclaration( + "xmlns", "http://www.w3.org/2000/xmlns/"); + fail("added xmlns prefix"); + } + catch (NamespaceConflictException success) { + assertNotNull(success.getMessage()); + } + + try { + noNamespaces.addNamespaceDeclaration("xmlns", + "http://www.example.com"); + fail("added xmlns prefix"); + } + catch (NamespaceConflictException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testCantUseXMLPrefix() { + + try { + noNamespaces.addNamespaceDeclaration( + "xml", "http://www.example.com"); + fail("added xmlns prefix"); + } + catch (NamespaceConflictException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testCanUseXMLPrefix() { + noNamespaces.addNamespaceDeclaration( + "xml", "http://www.w3.org/XML/1998/namespace"); + assertEquals(1, noNamespaces.getNamespaceDeclarationCount()); + } + + + public void testIndexedAccess() { + + assertEquals("", noNamespaces.getNamespacePrefix(0)); + + assertNotNull(someNamespaces.getNamespacePrefix(0)); + assertNotNull(someNamespaces.getNamespacePrefix(1)); + assertNotNull(someNamespaces.getNamespacePrefix(2)); + + assertNotNull(severalNamespaces.getNamespacePrefix(0)); + assertNotNull(severalNamespaces.getNamespacePrefix(1)); + assertNotNull(severalNamespaces.getNamespacePrefix(2)); + try { + severalNamespaces.getNamespacePrefix(3); + fail("Got a namespace 3"); + } + catch (IndexOutOfBoundsException ex) { + // success; + } + + + } + + + public void testSize() { + assertEquals(1, noNamespaces.getNamespaceDeclarationCount()); + assertEquals(3, someNamespaces.getNamespaceDeclarationCount()); + assertEquals(3, severalNamespaces.getNamespaceDeclarationCount()); + } + + + public void testDefaultNamespace() { + Element html = new Element("html", "http://www.w3.org/1999/xhtml"); + assertEquals(1, html.getNamespaceDeclarationCount()); + assertEquals("", html.getNamespacePrefix(0)); + assertEquals("http://www.w3.org/1999/xhtml", html.getNamespaceURI("")); + } + + + public void testGetByPrefix() { + + assertEquals("http://www.w3.org/2001/xlink", + someNamespaces.getNamespaceURI("xlink")); + assertEquals("http://www.w3.org/1999/xslt", + someNamespaces.getNamespaceURI("xsl")); + assertNull(someNamespaces.getNamespaceURI("fo")); + assertNull(noNamespaces.getNamespaceURI("xsl")); + assertEquals("", someNamespaces.getNamespaceURI("")); + assertEquals("", noNamespaces.getNamespaceURI("")); + + } + + + public void testGetNamespaceDeclarationCount() { + Element test = new Element("test"); + assertEquals(1, test.getNamespaceDeclarationCount()); + test.setNamespaceURI("http://www.example.com"); + assertEquals(1, test.getNamespaceDeclarationCount()); + test.addAttribute(new Attribute("test", "test")); + assertEquals(1, test.getNamespaceDeclarationCount()); + test.addAttribute(new Attribute("xlink:type", + "http://www.w3.org/2001/xlink", "value")); + assertEquals(2, test.getNamespaceDeclarationCount()); + test.addAttribute(new Attribute("xlink:href", + "http://www.w3.org/2001/xlink", "value")); + assertEquals(2, test.getNamespaceDeclarationCount()); + test.addNamespaceDeclaration("xlink", "http://www.w3.org/2001/xlink"); + assertEquals(2, test.getNamespaceDeclarationCount()); + test.addNamespaceDeclaration("xsi", "http://www.w3.org/2001/xmlschema-instance"); + assertEquals(3, test.getNamespaceDeclarationCount()); + + } + + + public void testGetNegativeNamespacePrefix() { + + Element test = new Element("pre:test", "http://www.example.com/"); + try { + test.getNamespacePrefix(-1); + fail("Got negative prefix"); + } + catch (IndexOutOfBoundsException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testRemoving() { + + assertEquals("http://www.w3.org/2001/xlink", + severalNamespaces.getNamespaceURI("xlink")); + assertEquals("http://www.w3.org/1999/xslt", + severalNamespaces.getNamespaceURI("xsl")); + assertEquals("http://www.example.com/", + severalNamespaces.getNamespaceURI("")); + + severalNamespaces.removeNamespaceDeclaration("xlink"); + severalNamespaces.removeNamespaceDeclaration("xsl"); + severalNamespaces.removeNamespaceDeclaration(""); + severalNamespaces.removeNamespaceDeclaration("nosuchdeclaration"); + + assertEquals("http://www.w3.org/2001/xlink", + severalNamespaces.getNamespaceURI("xlink")); + assertNull(severalNamespaces.getNamespaceURI("xsl")); + assertEquals("http://www.example.com/", + severalNamespaces.getNamespaceURI("")); + + } + + + public void testAddSameNamespaceDeclaration() { + + Element e = new Element("test", "http://www.example.com"); + + try { + e.addNamespaceDeclaration("", "http://www.red.com"); + fail("added conflicting default namespace"); + } + catch (NamespaceConflictException success) { + assertNotNull(success.getMessage()); + } + + e.addNamespaceDeclaration("", "http://www.example.com"); + assertEquals("http://www.example.com", e.getNamespaceURI("")); + assertEquals(1, e.getNamespaceDeclarationCount()); + + } + + + public void testAddEmptyNamespaceDeclaration() { + + Element e = new Element("test"); + + try { + e.addNamespaceDeclaration("", "http://www.example.com"); + fail("added conflicting default namespace"); + } + catch (NamespaceConflictException success) { + assertNotNull(success.getMessage()); + } + + e.setNamespaceURI("http://www.example.com"); + e.setNamespacePrefix("pre"); + e.addNamespaceDeclaration("", "http://www.example.net"); + + assertEquals("http://www.example.net", e.getNamespaceURI("")); + + } + + + public void testAddNullPrefix() { + + Element e = new Element("test"); + + try { + e.addNamespaceDeclaration(null, + "http://www.example.com"); + fail("added conflicting empty prefix to element in no namespace"); + } + catch (NamespaceConflictException success) { + assertNotNull(success.getMessage()); + } + + e.setNamespaceURI("http://www.example.com"); + e.setNamespacePrefix("pre"); + e.addNamespaceDeclaration(null, "http://www.example.net"); + + assertEquals("http://www.example.net", e.getNamespaceURI("")); + + } + + + public void testAddNullURI() { + Element parent = new Element("parent", "http://www.example.org/"); + Element e = new Element("pre:test", "http://www.example.com/"); + parent.appendChild(e); + e.addNamespaceDeclaration("", null); + assertEquals("", e.getNamespaceURI("")); + } + + + public void testRemoveNullPrefix() { + Element e = new Element("pre:test", "http://www.example.com/"); + e.addNamespaceDeclaration("", "http://www.example.net"); + e.removeNamespaceDeclaration(null); + assertEquals("", e.getNamespaceURI("")); + } + + + public void testBindXMLNSPrefix() { + + Element e = new Element("pre:test", "http://www.example.com/"); + try { + e.addNamespaceDeclaration("xmlns", "http://www.example.net"); + fail("Bound xmlns prefix to http://www.example.net"); + } + catch (NamespaceConflictException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testBindXMLNSPrefixToEmptyString() { + + Element e = new Element("pre:test", "http://www.example.com/"); + assertEquals("", e.getNamespaceURI("xmlns")); + e.addNamespaceDeclaration("xmlns", ""); + assertEquals("", e.getNamespaceURI("xmlns")); + + } + + + public void testUndeclareDefaultNamespace() { + Element parent = new Element("parent", "http://www.example.org/"); + Element e2 = new Element("pre:test", "http://www.example.net"); + parent.appendChild(e2); + e2.addNamespaceDeclaration("", ""); + assertEquals("", e2.getNamespaceURI("")); + } + + + public void testForConflictWithDefaultNamespace() { + Element e = new Element("test", "http://www.example.net"); + try { + e.addNamespaceDeclaration("", "http://www.example.com"); + fail("Conflicting default namespace"); + } + catch (NamespaceConflictException success) { + assertNotNull(success.getMessage()); + } + } + + + public void testConflictingUndeclarationOfDefaultNamespace() { + Element e = new Element("test", "http://www.example.net"); + try { + e.addNamespaceDeclaration("", ""); + fail("Conflicting undeclaration of default namespace"); + } + catch (NamespaceConflictException success) { + assertNotNull(success.getMessage()); + } + } + + + public void testAdding() { + + try { + noNamespaces.addNamespaceDeclaration("", + "http://www.example.com/"); + fail("added conflicting default namespace"); + } + catch (NamespaceConflictException success) { + assertNotNull(success.getMessage()); + } + + try { + severalNamespaces.addNamespaceDeclaration( + "xlink", "http://www.example.com/"); + fail("added conflicting attribute prefix namespace"); + } + catch (NamespaceConflictException success) { + assertNotNull(success.getMessage()); + } + + try { + someNamespaces.addNamespaceDeclaration("xsl", + "http://www.example.com/"); + fail("added conflicting additional prefix namespace"); + } + catch (NamespaceConflictException success) { + assertNotNull(success.getMessage()); + } + + someNamespaces.addNamespaceDeclaration("foo", + "http://www.example.com/"); + assertEquals("http://www.example.com/", + someNamespaces.getNamespaceURI("foo")); + + } + + + public void testReplacingNamespaceDeclaration() { + + assertEquals("http://www.w3.org/2001/xlink", + someNamespaces.getNamespaceURI("xlink")); + try { + someNamespaces.addNamespaceDeclaration("xlink", + "http://www.example.com/"); + fail("Redeclared without removal"); + } + catch (NamespaceConflictException success) { + assertNotNull(success.getMessage()); + } + someNamespaces.removeNamespaceDeclaration("xlink"); + assertNull(someNamespaces.getNamespaceURI("xlink")); + someNamespaces.addNamespaceDeclaration("xlink", + "http://www.example.com/"); + assertEquals("http://www.example.com/", + someNamespaces.getNamespaceURI("xlink")); + + + } + + + public void testSetPrefix() { + + try { + Attribute a = severalNamespaces.getAttribute(0); + a.setNamespace("xsl", "http://www.example.com/"); + fail("added conflicting attribute prefix"); + } + catch (NamespaceConflictException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testElementConflict() { + + Element element = new Element("pre:test", + "http://www.example.com/"); + element.addNamespaceDeclaration("pre", + "http://www.example.com/"); + try { + element.setNamespaceURI("http://www.yahoo.com"); + fail("changed to conflicting element namespace"); + } + catch (NamespaceConflictException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testAttributeConflict() { + + Element element = new Element("test"); + element.addNamespaceDeclaration("pre", + "http://www.example.com/"); + Attribute a = new Attribute("pre:test", + "http://www.example.com/", "value"); + element.addAttribute(a); + try { + a.setNamespace("pre", "http://www.yahoo.com/"); + fail("changed to conflicting attribute namespace"); + } + catch (NamespaceConflictException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testSetAttributePrefix() { + + try { + severalNamespaces.setNamespacePrefix("xlink"); + fail("added conflicting element prefix"); + } + catch (NamespaceConflictException success) { + assertNotNull(success.getMessage()); + } + + try { + severalNamespaces.setNamespacePrefix("xsl"); + fail("added conflicting element prefix"); + } + catch (NamespaceConflictException success) { + assertNotNull(success.getMessage()); + } + + } + + + /* + * Test document submitted by Laurent Bihanic + + + + */ + public void testLaurent() { + + Element b = new Element("b", "urn:x-xom:b"); + b.addAttribute(new Attribute("id", "b")); + + Element c = new Element("c:c", "urn:x-xom:c"); + c.addAttribute(new Attribute("id", "c")); + b.appendChild(c); + + assertEquals("urn:x-xom:b", b.getNamespaceURI()); + assertEquals("urn:x-xom:c", c.getNamespaceURI()); + assertEquals("urn:x-xom:b", b.getNamespaceURI("")); + assertEquals("urn:x-xom:b", c.getNamespaceURI("")); + assertEquals("urn:x-xom:c", c.getNamespaceURI("c")); + + } + + + public void testCountNamespaces() { + + Element html = new Element("html"); + assertEquals(1, html.getNamespaceDeclarationCount()); + html.setNamespaceURI("http://www.w3.org/1999/xhtml"); + assertEquals(1, html.getNamespaceDeclarationCount()); + html.addAttribute(new Attribute("pre:test", + "http://www.examnple.org/", "value")); + assertEquals(2, html.getNamespaceDeclarationCount()); + html.addNamespaceDeclaration("rddl", "http://www.rddl.org"); + assertEquals(3, html.getNamespaceDeclarationCount()); + html.addNamespaceDeclaration("xlink", + "http://www.w3.org/2001/xlink"); + assertEquals(4, html.getNamespaceDeclarationCount()); + html.addAttribute(new Attribute("xml:space", + "http://www.w3.org/XML/1998/namespace", "default")); + assertEquals(4, html.getNamespaceDeclarationCount()); + + } + + + public void testGetNamespaceURIByPrefix() { + + Element a = new Element("a"); + a.addNamespaceDeclaration("foo", "urn:foo"); + Element b = new Element("b"); + Element c = new Element("c"); + Element d = new Element("foo:d", "urn:foo"); + a.appendChild(b); + b.appendChild(c); + c.appendChild(d); + + assertEquals("urn:foo", a.getNamespaceURI("foo")); + assertEquals("urn:foo", b.getNamespaceURI("foo")); + assertEquals("urn:foo", c.getNamespaceURI("foo")); + assertEquals("urn:foo", d.getNamespaceURI("foo")); + } + + + public void testNumbersAllowedInSchemes() { + + String namespace = "u0123456789:schemespecificdata"; + Element e = new Element("test", namespace); + assertEquals(namespace, e.getNamespaceURI()); + + } + + + public void testPunctuationMarksAllowedInSchemes() { + String namespace = "u+-.:schemespecificdata"; + Element e = new Element("test", namespace); + assertEquals(namespace, e.getNamespaceURI()); + } + + + public void testPunctuationMarksCantStartSchemes() { + String namespace = "+:schemespecificdata"; + try { + new Element("test", namespace); + fail("Allowed scheme name to start with +"); + } + catch (MalformedURIException success) { + assertNotNull(success.getMessage()); + } + } + + + public void testNumbersCantStartSchemes() { + + String namespace = "8uri:schemespecificdata"; + try { + new Element("test", namespace); + fail("Allowed scheme name to start with digit"); + } + catch (MalformedURIException success) { + assertNotNull(success.getMessage()); + } + + } + + + /** This is a very funny test case. RFC 1738 allows URIs like + * prospero://host.dom//pros/name but RFC 2396 and + * RFC2396bis forbid them. + */ + public void testPathCantStartWithDoubleSlash() { + + String namespace = "prospero://host.dom//pros/name"; + try { + new Element("test", namespace); + fail("Allowed URI with path containing double slash"); + } + catch (MalformedURIException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testURIReferenceCantHaveTwoFragmentIDs() { + String namespace = "uri:schemespecificdata#test#id"; + try { + new Element("test", namespace); + fail("Allowed URI reference to contain multiple fragment IDs"); + } + catch (MalformedURIException success) { + assertNotNull(success.getMessage()); + } + } + + + public void testHalfPercentEscape() { + String namespace = "http://www.example.org/%ce%a"; + try { + new Element("test", namespace); + fail("Allowed path to contain only half a percent escape"); + } + catch (MalformedURIException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testUnescapedPercentSign() { + + String namespace = "http://www.example.org/%ce%"; + try { + new Element("test", namespace); + fail("Allowed path to contain only percent"); + } + catch (MalformedURIException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testPercentSignFollowedByNonHexDigits() { + + for (char c = 'G'; c <= '`'; c++) { + String namespace = "http://www.example.org/%1" + c + "test/"; + try { + new Element("test", namespace); + fail("Allowed malformed namespace URI " + namespace); + } + catch (MalformedURIException success) { + assertNotNull(success.getMessage()); + } + } + + for (char c = ':'; c <= '@'; c++) { + String namespace = "http://www.example.org/%1" + c + "test/"; + try { + new Element("test", namespace); + fail("Allowed malformed namespace URI " + namespace); + } + catch (MalformedURIException success) { + assertNotNull(success.getMessage()); + } + } + + } + + + public void testVariousSchemes() { + + String[] urls = { + "ftp://example.com", "FTP://example.com/pub/", + "MAILTO:elharo@ibiblio.org?Subject=XOM%20Namespace", + "mailto:elharo@ibiblio.org?Subject=XOM%20Namespace", + "telnet:namespaces.ibiblio.org", "TELNET:namespaces.ibiblio.org", + "gopher://gopher.uminn.edu/", "GOPHER://gopher.uminn.edu/", + "uri:urn:nwalsh:namespaces", "URI:urn:nwalsh:namespaces", + "news:comp.lang.xml", "NEWS:comp.lang.xml", + "wais://wais.example.com:78/database", "WAIS://wais.example.com:78/database", + "file://vms.host.edu/disk$user/my/notes/note12345.txt", + "FILE://vms.host.edu/disk$user/my/notes/note12345.txt", + "z39.50s://melvyl.ucop.edu/cat", "Z39.50S://melvyl.ucop.edu/cat", + "z39.50r://melvyl.ucop.edu/mags?elecworld.v30.n19", + "Z39.50R://melvyl.ucop.edu/mags?elecworld.v30.n19", + "z39.50r://cnidr.org:2100/tmf?bkirch_rules__a1;esn=f;rs=marc", + "Z39.50R://cnidr.org:2100/tmf?bkirch_rules__a1;esn=f;rs=marc", + "mid:960830.1639@XIson.com/partA.960830.1639@XIson.com", + "MID:960830.1639@XIson.com/partA.960830.1639@XIson.com", + "cid:foo4*foo1@bar.net", "CID:foo4*foo1@bar.net", + "vemmi://zeus.mctel.fr/demo", "VEMMI://zeus.mctel.fr/demo", + "vemmi://mctel.fr/demo;$USERDATA=smith;account=1234", + "opaquelocktoken:f81d4fae-7dec-11d0-a765-00a0c91e6bf6", + "OPAQUELOCKTOKEN:f81d4fae-7dec-11d0-a765-00a0c91e6bf6", + "fax:+358.555.1234567", "FAX:+358.555.1234567", + "modem:+3585551234567;type=v32b?7e1;type=v110", + "tel:0w003585551234567;phone-context=+3585551234", + "tel:+1234567890;phone-context=+1234;vnd.company.option=foo", + "xmlrpc.beeps://stateserver.example.com/NumberToName", + "XMLRPC.BEEPS://stateserver.example.com/NumberToName", + "h323:user@h323.example.com", "H323:user@h323.example.com", + "tn3270://login.example.com" + }; + for (int i = 0; i < urls.length; i++) { + Element e = new Element("pre:test", urls[i]); + assertEquals(e.getNamespaceURI("pre"), urls[i]); + } + + } + + + public void testPercentEscapes() { + + // Namespace URIs are compared for direct string equality; + // no de-escaping is performed + for (char c = ' '; c <= '~'; c++) { + String url = "http://www.example.com/%" + Integer.toHexString(c) + "test/"; + Element e = new Element("pre:test", url); + assertEquals(url, e.getNamespaceURI()); + assertEquals(url, e.getNamespaceURI("pre")); + } + // repeat in upper case + for (char c = ' '; c <= '~'; c++) { + String url = "http://www.example.com/%" + Integer.toHexString(c).toUpperCase() + "test/"; + Element e = new Element("pre:test", url); + assertEquals(url, e.getNamespaceURI()); + assertEquals(url, e.getNamespaceURI("pre")); + } + + } + + + public void testDelims() { + + String[] delims = {"<", ">", "\""}; + for (int i = 0; i < delims.length; i++) { + String url = "http://www.example.com/" + delims[i] + "/"; + try { + new Element("test", url); + fail("Allowed " + url + " as namespace URI"); + } + catch (MalformedURIException success) { + assertNotNull(success.getMessage()); + } + } + + } + + + public void testUnwise() { + + char[] unwise = {'{', '}', '|', '\\', '^', '`'}; + for (int i = 0; i < unwise.length; i++) { + String url = "http://www.example.com/" + unwise[i] + "/"; + try { + new Element("test", url); + fail("Allowed " + url + " as namespace URI"); + } + catch (MalformedURIException success) { + assertNotNull(success.getMessage()); + } + } + + } + + + public void testSetPrefixOnElementInNoNamespace() { + + Element e = new Element("Seq"); + try { + e.setNamespacePrefix("rdf"); + fail("Set prefix on element in no namespace"); + } + catch (NamespaceConflictException success) { + assertNotNull(success.getMessage()); + } + + } + + +} \ No newline at end of file diff --git a/src/nu/xom/tests/NodeFactoryTest.java b/src/nu/xom/tests/NodeFactoryTest.java new file mode 100644 index 0000000..7aacdf8 --- /dev/null +++ b/src/nu/xom/tests/NodeFactoryTest.java @@ -0,0 +1,1145 @@ +/* Copyright 2002-2004 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.tests; + +import java.io.File; +import java.io.IOException; + +import nu.xom.ParsingException; + +import nu.xom.Attribute; +import nu.xom.Builder; +import nu.xom.Comment; +import nu.xom.DocType; +import nu.xom.Document; +import nu.xom.Element; +import nu.xom.Node; +import nu.xom.NodeFactory; +import nu.xom.Nodes; +import nu.xom.ProcessingInstruction; +import nu.xom.Text; +import nu.xom.ValidityException; +import nu.xom.XMLException; + +/** + *

    + * Tests that subclasses of NodeFactory can filter + * on building in various ways. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.1b2 + * + */ +public class NodeFactoryTest extends XOMTestCase { + + + private File data = new File("data"); + + + public NodeFactoryTest(String name) { + super(name); + } + + + protected void setUp() { + numNodesInExternalDTDSubset = 0; + } + + + public void testSkippingComment() + throws IOException, ParsingException { + + String data = "
    18"; + Builder builder = new Builder(new CommentFilter()); + Document doc = builder.build(data, "http://www.example.org/"); + Element root = doc.getRootElement(); + assertEquals("Skipped comment interrupted text node", + 2, root.getChildCount()); + assertEquals("18", doc.getValue()); + Node first = root.getChild(0); + assertEquals(first.getValue(), "1"); + Node second = root.getChild(1); + assertEquals(second.getValue(), "8"); + + } + + + static class CommentFilter extends NodeFactory { + + public Nodes makeComment(String data) { + return new Nodes(); + } + + } + + + public void testCantAddOneElementMultipleTimes() + throws IOException, ParsingException { + + String data = "18"; + Builder builder = new Builder(new SingleElementFactory()); + try { + builder.build(data, "http://www.example.org/"); + fail("Allowed one element in several places"); + } + catch (ParsingException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testCantAddOneAttributeMultipleTimes() + throws IOException, ParsingException { + + String data = ""; + Builder builder = new Builder(new SingleAttributeFactory()); + try { + builder.build(data, "http://www.example.org/"); + fail("Allowed one attribute twice"); + } + catch (ParsingException success) { + assertNotNull(success.getMessage()); + } + + } + + + private static class SingleElementFactory extends NodeFactory { + + private Element test = new Element("test"); + + public Element startMakingElement(String name, String namespace) { + return test; + } + + } + + + private static class SingleAttributeFactory extends NodeFactory { + + private Attribute test = new Attribute("limit", "none"); + + public Nodes makeAttribute(String name, String URI, + String value, Attribute.Type type) { + return new Nodes(test); + } + + } + + + public void testChangingElementName() + throws IOException, ParsingException { + + String data + = "1234innermost5678"; + Builder builder = new Builder(new CFactory()); + Document doc = builder.build(data, "http://www.example.org/"); + Element root = doc.getRootElement(); + assertEquals("1234innermost5678", doc.getValue()); + assertEquals("c", root.getQualifiedName()); + assertEquals(3, root.getChildCount()); + Element b = (Element) root.getChild(1); + assertEquals("c", b.getQualifiedName()); + + } + + + static class CFactory extends NodeFactory { + + public Element startMakingElement( + String name, String namespace) { + return new Element("c"); + } + + } + + + public void testMakeRoot() throws IOException, ParsingException { + + String data = "18"; + Builder builder = new Builder(new CallsMakeRoot()); + Document doc = builder.build(data, "http://www.example.org/"); + Element root = doc.getRootElement(); + assertEquals("rootSubstitute", root.getQualifiedName()); + + // make sure the other elements aren't changed + assertNotNull(root.getFirstChildElement("b")); + + } + + + static class CallsMakeRoot extends NodeFactory { + + public Element makeRootElement( + String name, String namepaceURI) { + return new Element("rootSubstitute"); + } + + } + + + public void testSkippingProcessingInstruction() + throws IOException, ParsingException { + + String data = "18"; + Builder builder = new Builder(new ProcessingInstructionFilter()); + Document doc = builder.build(data, "http://www.example.org/"); + Element root = doc.getRootElement(); + assertEquals( + "Skipped processing instruction interrupted text node", + 2, root.getChildCount() + ); + assertEquals("18", doc.getValue()); + Node first = root.getChild(0); + assertEquals(first.getValue(), "1"); + Node second = root.getChild(1); + assertEquals(second.getValue(), "8"); + + } + + + static class ProcessingInstructionFilter extends NodeFactory { + + public Nodes makeProcessingInstruction( + String target, String data) { + return new Nodes(); + } + + } + + + public void testSkipping2() throws IOException, ParsingException { + + String data + = "1234innermost5678"; + Builder builder = new Builder(new BFilter()); + Document doc = builder.build(data, "http://www.example.org/"); + Element root = doc.getRootElement(); + assertEquals("1234innermost5678", doc.getValue()); + assertEquals(5, root.getChildCount()); + Node first = root.getChild(0); + assertEquals("1", first.getValue()); + Node middle = root.getChild(2); + assertEquals("34innermost56", middle.getValue()); + Node last = root.getChild(4); + assertEquals("8", last.getValue()); + + Node innermost = middle.getChild(2); + assertEquals("innermost", innermost.getValue()); + Node inner1 = middle.getChild(0); + assertEquals("3", inner1.getValue()); + Node inner3 = middle.getChild(4); + assertEquals("6", inner3.getValue()); + + } + + + static class BFilter extends NodeFactory { + + public Element startMakingElement( + String name, String namespaceURI) { + if (name.equals("b")) return null; + return super.startMakingElement(name, namespaceURI); + } + + } + + + public void testMinimalizedDocument() + throws IOException, ParsingException { + + File input = new File(data, "entitytest.xml"); + Builder builder = new Builder(new MinimizingFactory()); + Document doc = builder.build(input); + assertEquals(1, doc.getChildCount()); + Element root = doc.getRootElement(); + assertEquals("root", root.getQualifiedName()); + assertEquals("", root.getNamespaceURI()); + assertEquals(0, root.getChildCount()); + assertEquals(0, root.getAttributeCount()); + + } + + + public void testValidateWithFactory() + throws ParsingException, IOException { + Builder validator = new Builder(true, new MinimizingFactory()); + Document doc = validator.build("" + + "]>" + + "", + "http://www.example.org/"); + assertEquals(1, doc.getChildCount()); + } + + + // Throws away everything except the document and the + // root element + static class MinimizingFactory extends NodeFactory { + + private Nodes empty = new Nodes(); + + public Nodes makeComment(String data) { + return empty; + } + + public Nodes makeText(String data) { + return empty; + } + + public Nodes finishMakingElement(Element element) { + if (element.getParent() instanceof Document) { + return new Nodes(element); + } + return empty; + } + + public Nodes makeAttribute(String name, String URI, + String value, Attribute.Type type) { + return empty; + } + + public Nodes makeDocType(String rootElementName, + String publicID, String systemID) { + return empty; + } + + public Nodes makeProcessingInstruction( + String target, String data) { + return empty; + } + + } + + + public void testCDATASectionsCanBeOverridden() + throws ValidityException, ParsingException, IOException { + + String data =""; + Builder builder = new Builder(new MinimizingFactory()); + Document doc = builder.build(data, "http://www.example.com"); + assertEquals("", doc.getValue()); + + } + + + public void testNullRootNotAllowed() + throws IOException, ParsingException { + + File input = new File(data, "entitytest.xml"); + Builder builder = new Builder(new NullElementFactory()); + try { + builder.build(input); + fail("Allowed null root"); + } + catch (ParsingException success) { + assertNotNull(success.getMessage()); + } + + } + + + // Returns null for all elements including the root. + // This should cause an exception. + static class NullElementFactory extends NodeFactory { + + public Element startMakingElement( + String name, String namespaceURI) { + return null; + } + + } + + + public void testNullDocumentNotAllowed() + throws IOException, ParsingException { + + File input = new File(data, "entitytest.xml"); + Builder builder = new Builder(new NullDocumentFactory()); + try { + builder.build(input); + fail("Allowed null document"); + } + catch (ParsingException success) { + assertTrue(success.getCause() instanceof NullPointerException); + } + + } + + + // Returns null for all elements including the root. + // This should cause an exception. + static class NullDocumentFactory extends NodeFactory { + + public Document startMakingDocument() { + return null; + } + + } + + + public void testSkipping() throws IOException, ParsingException { + + String data = "datadatadatadatadata"; + Builder builder = new Builder(new BFilter()); + Document doc = builder.build(data, "http://www.example.org/"); + Element root = doc.getRootElement(); + assertEquals(5, root.getChildCount()); + assertEquals("datadatadatadatadata", root.getValue()); + Element middle = (Element) root.getChild(2); + assertEquals("data", middle.getValue()); + Node start = root.getChild(0); + Node end = root.getChild(4); + assertEquals("data", start.getValue()); + assertEquals("data", end.getValue()); + + } + + + int numNodesInExternalDTDSubset = 0; + + public void testDontReportCommentsAndProcessingInstructionsInExternalDTDSubset() + throws IOException, ParsingException { + + File input = new File(data, "contentindtd.xml"); + Builder builder = new Builder(new Counter()); + builder.build(input); + assertEquals(0, numNodesInExternalDTDSubset); + + } + + + private class Counter extends NodeFactory { + + public Nodes makeComment(String data) { + numNodesInExternalDTDSubset++; + return super.makeComment(data); + } + + public Nodes makeProcessingInstruction(String target, String data) { + numNodesInExternalDTDSubset++; + return super.makeProcessingInstruction(target, data); + } + + } + + + public void testDontCoalesceTextNodes() + throws IOException, ParsingException { + + String data = "data data"; + Builder builder = new Builder(new CommentFilter()); + Document doc = builder.build(data, "http://www.example.org/"); + Element root = doc.getRootElement(); + assertEquals(2, root.getChildCount()); + assertEquals("data data", root.getValue()); + Text text = (Text) root.getChild(0); + assertEquals("data", text.getValue()); + + } + + + static class TripleElementFilter extends NodeFactory { + + public Nodes finishMakingElement(Element element) { + Nodes result = new Nodes(element); + if (!(element.getParent() instanceof Document)) { + result.append(element.copy()); + result.append(element.copy()); + } + return result; + } + + } + + + public void testTriple() + throws IOException, ParsingException { + + String data = ""; + Builder builder = new Builder(new TripleElementFilter()); + Document doc = builder.build(data, "http://www.example.org/"); + Element root = doc.getRootElement(); + assertEquals(3, root.getChildCount()); + assertEquals("", root.getValue()); + Element b = (Element) root.getChild(0); + assertEquals("b", b.getLocalName()); + assertEquals( + "", + root.toXML()); + + } + + + static class UncommentFilter extends NodeFactory { + + public Nodes makeComment(String data) { + Nodes result = new Nodes(new Text(data)); + return result; + } + + } + + + public void testUncomment() + throws ParsingException, IOException { + + String data = ""; + Builder builder = new Builder(new UncommentFilter()); + try { + builder.build(data, "http://www.example.org/"); + fail("built Text into prolog"); + } + catch (ParsingException success) { + assertNotNull(success.getMessage()); + } + + } + + + static class DocumentFilter extends NodeFactory { + + public Nodes makeComment(String data) { + Element root = new Element("root"); + Nodes result = new Nodes(new Document(root)); + return result; + } + + } + + + public void testCantAddDocument() + throws ParsingException, IOException { + + String data = ""; + Builder builder = new Builder(new DocumentFilter()); + try { + builder.build(data, "http://www.example.org/"); + fail("built document into document"); + } + catch (ParsingException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testCantAddTwoDoctypes() + throws ParsingException, IOException { + + String data = ""; + Builder builder = new Builder(new NodeFactory() { + + public Nodes makeDocType(String name, String publicID, String systemID) { + Nodes result = new Nodes(); + result.append(new DocType(name, publicID, systemID)); + result.append(new Comment("sajdha")); + result.append(new DocType(name, publicID, systemID)); + return result; + } + + }); + try { + builder.build(data, "http://www.example.org/"); + fail("built two doctypes"); + } + catch (ParsingException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testChangeAttributesToElements() + throws ParsingException, IOException { + + String data = ""; + Builder builder = new Builder(new NodeFactory() { + + public Nodes makeAttribute(String name, String URI, + String value, Attribute.Type type) { + Nodes result = new Nodes(); + Element element = new Element(name, URI); + element.appendChild(value); + result.append(element); + return result; + } + + }); + Document doc = builder.build(data, "http://www.example.org/"); + Element root = doc.getRootElement(); + assertEquals(2, root.getChildCount()); + Element name = root.getFirstChildElement("name"); + Element value = root.getFirstChildElement("value"); + assertEquals("test", name.getValue()); + assertEquals("data", value.getValue()); + assertEquals("name", name.getLocalName()); + assertEquals("value", value.getQualifiedName()); + + } + + + public void testInsertElementsInInternalDTDSubsetViaProcessingInstruction() + throws ParsingException, IOException { + + String data = "]>data1text"; + Builder builder = new Builder(new NodeFactory() { + + public Nodes makeProcessingInstruction(String target, String data) { + Nodes result = new Nodes(); + Element e = new Element(target); + e.appendChild(data); + result.append(e); + return result; + } + + }); + try { + builder.build(data, "http://www.example.org/"); + fail("Allowed element in internal DTD subset via processing instruction"); + } + catch (ParsingException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testInsertElementsInInternalDTDSubsetViaComment() + throws ParsingException, IOException { + + String data = "]>data1text"; + Builder builder = new Builder(new NodeFactory() { + + public Nodes makeComment(String data) { + Nodes result = new Nodes(); + Element e = new Element("comment"); + e.appendChild(data); + result.append(e); + return result; + } + + }); + try { + builder.build(data, "http://www.example.org/"); + fail("Allowed element in internal DTD subset via comment"); + } + catch (ParsingException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testChangeElementsToAttributes() + throws ParsingException, IOException { + + String data = "data1text"; + Builder builder = new Builder(new NodeFactory() { + + public Nodes finishMakingElement(Element element) { + Nodes result = new Nodes(); + if (element.getParent() instanceof Document) { + result.append(element); + } + else { + result.append(new Attribute(element.getLocalName(), element.getValue())); + } + return result; + } + + }); + Document doc = builder.build(data, "http://www.example.org/"); + Element root = doc.getRootElement(); + assertEquals(0, root.getChildCount()); + assertEquals(2, root.getAttributeCount()); + assertEquals("data1", root.getAttribute("b").getValue()); + assertEquals("text", root.getAttribute("c").getValue()); + + } + + + public void testChangeDefaultNamespaceFromEnd() + throws ParsingException, IOException { + + String data = ""; + Builder builder = new Builder(new NodeFactory() { + + public Nodes finishMakingElement(Element element) { + Nodes result = new Nodes(element); + element.setNamespaceURI("http://www.b.org/"); + return result; + } + + }); + Document doc = builder.build(data, "http://www.example.org/"); + Element root = doc.getRootElement(); + Element child = (Element) root.getChild(0); + assertEquals("http://www.b.org/", child.getNamespaceURI()); + + } + + + // XXX need to test changing namespaces of attributes too + public void testChangePrefixedNamespaceFromEnd() + throws ParsingException, IOException { + + String data = ""; + Builder builder = new Builder(new NodeFactory() { + + public Nodes finishMakingElement(Element element) { + Nodes result = new Nodes(element); + element.setNamespaceURI("http://www.b.org/"); + return result; + } + + }); + Document doc = builder.build(data, "http://www.example.org/"); + Element root = doc.getRootElement(); + Element child = (Element) root.getChild(0); + assertEquals("http://www.b.org/", child.getNamespaceURI()); + + } + + + public void testChangeDefaultNamespaceFromBeginning() + throws ParsingException, IOException { + + String data = ""; + Builder builder = new Builder(new NodeFactory() { + + public Element startMakingElement(String name, String namespaceURI) { + return new Element(name, "http://www.b.org/"); + } + + }); + Document doc = builder.build(data, "http://www.example.org/"); + Element root = doc.getRootElement(); + Element child = (Element) root.getChild(0); + assertEquals("http://www.b.org/", child.getNamespaceURI()); + + } + + + public void testChangePrefixedNamespaceFromBeginning() + throws ParsingException, IOException { + + String data = ""; + Builder builder = new Builder(new NodeFactory() { + + public Element startMakingElement(String name, String namespaceURI) { + return new Element(name, "http://www.b.org/"); + } + + }); + Document doc = builder.build(data, "http://www.example.org/"); + Element root = doc.getRootElement(); + Element child = (Element) root.getChild(0); + assertEquals("http://www.b.org/", child.getNamespaceURI()); + + } + + + public void testChangeTextToAttributes() + throws ParsingException, IOException { + + String data = "data1text"; + Builder builder = new Builder(new NodeFactory() { + + public Nodes makeText(String text) { + Nodes result = new Nodes(); + result.append(new Attribute("name", text)); + return result; + } + + }); + Document doc = builder.build(data, "http://www.example.org/"); + Element root = doc.getRootElement(); + assertEquals(2, root.getChildCount()); + assertEquals(0, root.getAttributeCount()); + assertEquals("", root.getValue()); + Element b = root.getFirstChildElement("b"); + Element c = root.getFirstChildElement("c"); + assertEquals("data1", b.getAttribute("name").getValue()); + assertEquals("text", c.getAttribute("name").getValue()); + + } + + + public void testChangeRootElementsToAttribute() + throws ParsingException, IOException { + + String data = "data1text"; + Builder builder = new Builder(new NodeFactory() { + + public Nodes finishMakingElement(Element element) { + Nodes result = new Nodes(); + result.append(new Attribute(element.getLocalName(), element.getValue())); + return result; + } + + }); + try { + builder.build(data, "http://www.example.org/"); + fail("replaced root element with attribute"); + } + catch (ParsingException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testCantBypassMultipleParentChecks() + throws ParsingException, IOException { + + String doc = ""; + Builder builder = new Builder(new NodeFactory() { + + private Element a = new Element("a"); + + public Element startMakingElement(String name, String namespace) { + if (name.equals("a")) return a; + return new Element(name, namespace); + } + + }); + try { + builder.build(doc, "http://www.example.org/"); + fail("built with multiple parents"); + } + catch (ParsingException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testCantBypassMultipleParentChecksFromFinishMakingElement() + throws ParsingException, IOException { + + String doc = ""; + Builder builder = new Builder(new NodeFactory() { + + private Element a = new Element("a"); + + public Nodes finishMakingElement(Element element) { + if (element.getLocalName().equals("a")) return new Nodes(a); + else return new Nodes(element); + } + + }); + try { + builder.build(doc, "http://www.example.org/"); + fail("built with multiple parents"); + } + catch (ParsingException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testFinishMakingElementIsCalledForRootElement() + throws ParsingException, IOException { + + String doc = ""; + Builder builder = new Builder(new NodeFactory() { + + public Nodes finishMakingElement(Element element) { + throw new XMLException("Method was called"); + } + + }); + try { + builder.build(doc, "http://www.example.org/"); + fail("Did not call finishMakingElement for root"); + } + catch (ParsingException success) { + assertEquals("Method was called", success.getMessage()); + } + + } + + + public void testCanReplaceRootElementFromFinishMakingElement() + throws ParsingException, IOException { + + String data = ""; + Builder builder = new Builder(new NodeFactory() { + + public Nodes finishMakingElement(Element element) { + Nodes result = new Nodes(); + result.append(new Comment("test")); + result.append(new Element("newroot")); + result.append(new ProcessingInstruction("test", "test")); + return result; + } + + }); + Document doc = builder.build(data, "http://www.example.org/"); + assertEquals("newroot", doc.getRootElement().getQualifiedName()); + assertEquals(3, doc.getChildCount()); + + } + + + public void testCanAddAroundExistingRootElementFromFinishMakingElement() + throws ParsingException, IOException { + + String data = ""; + Builder builder = new Builder(new NodeFactory() { + + public Nodes finishMakingElement(Element element) { + Nodes result = new Nodes(); + result.append(new Comment("test")); + result.append(element); + result.append(new ProcessingInstruction("test", "test")); + return result; + } + + }); + Document doc = builder.build(data, "http://www.example.org/"); + assertEquals("root", doc.getRootElement().getQualifiedName()); + assertEquals(3, doc.getChildCount()); + + } + + + public void testCantReplaceRootElementWithNoElement() + throws ParsingException, IOException { + + String data = ""; + Builder builder = new Builder(new NodeFactory() { + + public Nodes finishMakingElement(Element element) { + Nodes result = new Nodes(); + result.append(new Comment("test")); + result.append(new ProcessingInstruction("test", "test")); + return result; + } + + }); + try { + builder.build(data, "http://www.example.org/"); + fail("Built document without root element"); + } + catch (ParsingException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testCantReplaceRootElementWithNothing() + throws ParsingException, IOException { + + String data = ""; + Builder builder = new Builder(new NodeFactory() { + + public Nodes finishMakingElement(Element element) { + return new Nodes(); + } + + }); + try { + builder.build(data, "http://www.example.org/"); + fail("Built document without root element"); + } + catch (ParsingException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testReplaceCommentWithAttribute() + throws ParsingException, IOException { + + String data = ""; + Builder builder = new Builder(new NodeFactory() { + + public Nodes makeComment(String data) { + return new Nodes(new Attribute("name", "value")); + } + + }); + Document doc = builder.build(data, "http://www.example.org/"); + Element root = doc.getRootElement(); + assertEquals(0, root.getChildCount()); + assertEquals(1, root.getAttributeCount()); + assertEquals("value", root.getAttribute("name").getValue()); + + } + + + public void testReplaceProcessingInstructionWithAttribute() + throws ParsingException, IOException { + + String data = ""; + Builder builder = new Builder(new NodeFactory() { + + public Nodes makeProcessingInstruction(String target, String data) { + return new Nodes(new Attribute("name", "value")); + } + + }); + Document doc = builder.build(data, "http://www.example.org/"); + Element root = doc.getRootElement(); + assertEquals(0, root.getChildCount()); + assertEquals(1, root.getAttributeCount()); + assertEquals("value", root.getAttribute("name").getValue()); + + } + + + public void testReplaceProcessingInstructionWithText() + throws ParsingException, IOException { + + String data = ""; + Builder builder = new Builder(new NodeFactory() { + + public Nodes makeProcessingInstruction(String target, String data) { + return new Nodes(new Text(data)); + } + + }); + Document doc = builder.build(data, "http://www.example.org/"); + Element root = doc.getRootElement(); + assertEquals(1, root.getChildCount()); + assertEquals(0, root.getAttributeCount()); + assertEquals("data", root.getValue()); + + } + + + public void testCantReplaceRootElementWithTwoElements() + throws ParsingException, IOException { + + String data = ""; + Builder builder = new Builder(new NodeFactory() { + + public Nodes finishMakingElement(Element element) { + Nodes result = new Nodes(); + result.append(new Element("first")); + result.append(new Element("second")); + return result; + } + + }); + try { + builder.build(data, "http://www.example.org/"); + fail("Built document without root element"); + } + catch (ParsingException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testOrderOfCalls() + throws ParsingException, IOException { + + String data = "123"; + Builder builder = new Builder(new NodeFactory() { + + String s = ""; + + public Nodes makeText(String text) { + s += text; + return super.makeText(text); + } + + public Element startMakingElement(String name, String namespace) { + + if (name.equals("child")) assertEquals("1", s); + return super.startMakingElement(name, namespace); + } + + public Nodes finishMakingElement(Element element) { + if (element.getLocalName().equals("child")) assertEquals("12", s); + if (element.getLocalName().equals("root")) assertEquals("123", s); + return super.finishMakingElement(element); + } + + }); + builder.build(data, null); + } + + + public void testOrderOfCallsWithPI() + throws ParsingException, IOException { + + String data = "12"; + Builder builder = new Builder(new NodeFactory() { + + String s = ""; + + public Nodes makeText(String text) { + s += text; + return super.makeText(text); + } + + public Nodes makeProcessingInstruction(String target, String data) { + + assertEquals("1", s); + return new Nodes(); + } + + }); + Document doc = builder.build(data, null); + assertEquals(2, doc.getRootElement().getChildCount()); + + } + + + public void testFinishMakingElementDetachesItsArgument() + throws ParsingException, IOException { + + String data = "
    "; + Builder builder = new Builder(new NodeFactory() { + + public Nodes finishMakingElement(Element element) { + + if (element.getLocalName().equals("b")) { + element.detach(); + return new Nodes(); + } + return new Nodes(element); + } + + }); + + try { + builder.build(data, null); + fail("Allowed finishmakingElement to detach its argument"); + } + catch (ParsingException success) { + assertNotNull(success.getMessage()); + } + + } + +} diff --git a/src/nu/xom/tests/NodesTest.java b/src/nu/xom/tests/NodesTest.java new file mode 100644 index 0000000..1b96c09 --- /dev/null +++ b/src/nu/xom/tests/NodesTest.java @@ -0,0 +1,272 @@ +/* Copyright 2003-2005, 2018 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.tests; + +import nu.xom.Comment; +import nu.xom.Element; +import nu.xom.Node; +import nu.xom.Nodes; +import nu.xom.Text; + +/** + *

    + * Various basic tests for the Nodes class. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.3.0 + * + */ +public class NodesTest extends XOMTestCase { + + + public NodesTest(String name) { + super(name); + } + + + public void testNoArgsConstructor() { + Nodes nodes = new Nodes(); + assertEquals(0, nodes.size()); + } + + + public void testOneArgConstructor() { + Element test = new Element("test"); + Nodes nodes = new Nodes(test); + assertEquals(1, nodes.size()); + Element stored = (Element) nodes.get(0); + assertEquals(test, stored); + } + + + public void testConstructWithNull() { + + try { + new Nodes(null); + fail("constructed with null"); + } + catch (NullPointerException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testAppendNull() { + + Nodes nodes = new Nodes(); + try { + nodes.append(null); + fail("appended null"); + } + catch (NullPointerException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testInsertNull() { + + Nodes nodes = new Nodes(); + try { + nodes.insert(null, 0); + fail("inserted null"); + } + catch (NullPointerException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testIndexOutofBoundsException() { + Nodes nodes = new Nodes(); + try { + nodes.get(0); + fail("Didn't throw IndexOutOfBoundsException for empty list"); + } + catch (IndexOutOfBoundsException success) { + assertNotNull(success.getMessage()); + } + + nodes.append(new Comment("data")); + try { + nodes.get(-1); + fail("Didn't throw IndexOutOfBoundsException for -1"); + } + catch (IndexOutOfBoundsException success) { + assertNotNull(success.getMessage()); + } + try { + nodes.get(1); + fail("Didn't throw IndexOutOfBoundsException for fence post"); + } + catch (IndexOutOfBoundsException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testAppendAndGet() { + + Nodes nodes = new Nodes(); + int length = 10; + for (int i = 0; i < length; i++) { + nodes.append(new Text(String.valueOf(i))); + } + assertEquals(length, nodes.size()); + for (int i = 0; i < length; i++) { + assertEquals(String.valueOf(i), nodes.get(i).getValue()); + } + + } + + + public void testForEach() { + Nodes nodes = new Nodes(); + int length = 10; + for (int i = 0; i < length; i++) { + nodes.append(new Text(String.valueOf(i))); + } + assertEquals(length, nodes.size()); + int i = 0; + for (Node node : nodes) { + assertEquals(String.valueOf(i), node.getValue()); + i++; + } + + } + + + public void testInsertAtEnd() { + + Nodes nodes = new Nodes(); + nodes.insert(new Text("test"), 0); + assertEquals("test", nodes.get(0).getValue()); + nodes.insert(new Text("test2"), 1); + assertEquals("test2", nodes.get(1).getValue()); + + } + + + public void testInsert() { + + Nodes nodes = new Nodes(); + int length = 10; + for (int i = 0; i < length; i++) { + nodes.append(new Text(String.valueOf(i))); + } + nodes.insert(new Comment("dTA"), 3); + nodes.insert(new Comment("dTA"), 5); + nodes.insert(new Comment("dTA"), 12); + assertEquals(length+3, nodes.size()); + for (int i = 0; i < 3; i++) { + assertEquals(String.valueOf(i), nodes.get(i).getValue()); + } + assertEquals("dTA", nodes.get(3).getValue()); + assertEquals("dTA", nodes.get(5).getValue()); + assertEquals("dTA", nodes.get(12).getValue()); + for (int i = 6; i < length+2; i++) { + assertEquals(String.valueOf(i-2), nodes.get(i).getValue()); + } + + try { + nodes.insert(new Text("data"), 14); + } + catch (IndexOutOfBoundsException ex) { + assertNotNull(ex.getMessage()); + } + + try { + nodes.insert(new Text("data"), 140); + } + catch (IndexOutOfBoundsException ex) { + assertNotNull(ex.getMessage()); + } + + try { + nodes.insert(new Text("data"), -14); + } + catch (IndexOutOfBoundsException ex) { + assertNotNull(ex.getMessage()); + } + + } + + + public void testDelete() { + + Nodes nodes = new Nodes(); + int length = 10; + for (int i = 0; i < length; i++) { + nodes.append(new Text(String.valueOf(i))); + } + + Node result = nodes.remove(0); + assertEquals(length-1, nodes.size()); + assertEquals("0", result.getValue()); + + for (int i = 0; i < nodes.size(); i++) { + assertEquals(String.valueOf(i+1), nodes.get(i).getValue()); + } + nodes.remove(nodes.size()-1); + assertEquals(length-2, nodes.size()); + for (int i = 0; i < nodes.size(); i++) { + assertEquals(String.valueOf(i+1), nodes.get(i).getValue()); + } + nodes.remove(2); + for (int i = 0; i < 2; i++) { + assertEquals(String.valueOf(i+1), nodes.get(i).getValue()); + } + for (int i = 2; i < nodes.size(); i++) { + assertEquals(String.valueOf(i+2), nodes.get(i).getValue()); + } + assertEquals(length-3, nodes.size()); + + try { + nodes.remove(14); + } + catch (IndexOutOfBoundsException ex) { + assertNotNull(ex.getMessage()); + } + + try { + nodes.remove(nodes.size()); + } + catch (IndexOutOfBoundsException ex) { + assertNotNull(ex.getMessage()); + } + + try { + nodes.remove(-14); + } + catch (IndexOutOfBoundsException ex) { + assertNotNull(ex.getMessage()); + } + + } + + +} diff --git a/src/nu/xom/tests/ParentNodeTest.java b/src/nu/xom/tests/ParentNodeTest.java new file mode 100644 index 0000000..10f92d1 --- /dev/null +++ b/src/nu/xom/tests/ParentNodeTest.java @@ -0,0 +1,559 @@ +/* Copyright 2002-2004, 2006 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.tests; + +import nu.xom.Comment; +import nu.xom.CycleException; +import nu.xom.DocType; +import nu.xom.Document; +import nu.xom.Element; +import nu.xom.IllegalAddException; +import nu.xom.MultipleParentException; +import nu.xom.NoSuchChildException; +import nu.xom.Node; +import nu.xom.ProcessingInstruction; +import nu.xom.Text; + + +/** + *

    + * Tests adding, removing, and counting children from parent nodes. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.3.0 + * + */ +public class ParentNodeTest extends XOMTestCase { + + public ParentNodeTest(String name) { + super(name); + } + + + private Element empty; + private Element notEmpty; + private Text child; + + + protected void setUp() { + empty = new Element("Empty"); + notEmpty = new Element("NotEmpty"); + child = new Text("Hello"); + notEmpty.appendChild(child); + } + + + public void testDetach() { + + Text text = new Text("This will be attached then detached"); + empty.appendChild(text); + assertEquals(empty, text.getParent()); + text.detach(); + assertNull(text.getParent()); + + } + + + public void testAppendChild() { + + Element child = new Element("test"); + empty.appendChild(child); + assertEquals(1, empty.getChildCount()); + assertEquals(empty.getChild(0), child); + child.detach(); + + notEmpty.appendChild(child); + assertFalse(notEmpty.getChild(0).equals(child)); + assertTrue(notEmpty.getChild(1).equals(child)); + + } + + + public void testAppendNull() { + + Element child = null; + try { + empty.appendChild(child); + fail("Inserted null"); + } + catch (NullPointerException ex) { + assertNotNull(ex.getMessage()); + } + + } + + + public void testAppendChildToItself() { + + Element child = new Element("test"); + try { + child.appendChild(child); + fail("Appended node to itself"); + } + catch (CycleException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testCycle() { + + Element a = new Element("test"); + Element b = new Element("test"); + try { + a.appendChild(b); + b.appendChild(a); + fail("Allowed cycle"); + } + catch (CycleException success) { + assertNotNull(success.getMessage()); + } + + } + + public void testInsertChild() { + + Element parent = new Element("parent"); + + // Test insert into empty element + Element child1 = new Element("child"); + parent.insertChild(child1, 0); + assertTrue(parent.getChildCount() > 0); + assertEquals(0, parent.indexOf(child1)); + + // Test insert at beginning + Element child2 = new Element("child2"); + parent.insertChild(child2, 0); + assertEquals(0, parent.indexOf(child2)); + assertEquals(1, parent.indexOf(child1)); + + // Test insert in middle + Element child3 = new Element("child3"); + parent.insertChild(child3, 1); + assertEquals(0, parent.indexOf(child2)); + assertEquals(1, parent.indexOf(child3)); + assertEquals(2, parent.indexOf(child1)); + + // Test insert at beginning with children + Element child4 = new Element("child4"); + parent.insertChild(child4, 0); + assertEquals(0, parent.indexOf(child4)); + assertEquals(1, parent.indexOf(child2)); + assertEquals(2, parent.indexOf(child3)); + assertEquals(3, parent.indexOf(child1)); + + // Test insert at end with children + Element child5 = new Element("child5"); + parent.insertChild(child5, 4); + assertEquals(0, parent.indexOf(child4)); + assertEquals(1, parent.indexOf(child2)); + assertEquals(2, parent.indexOf(child3)); + assertEquals(3, parent.indexOf(child1)); + assertEquals(4, parent.indexOf(child5)); + + try { + parent.insertChild((Element) null, 0); + fail("Inserted null"); + } + catch (NullPointerException success) { + assertNotNull(success.getMessage()); + } + + try { + parent.insertChild((Text) null, 0); + fail("Inserted null"); + } + catch (NullPointerException success) { + assertNotNull(success.getMessage()); + } + + try { + parent.insertChild((Comment) null, 0); + fail("Inserted null"); + } + catch (NullPointerException success) { + assertNotNull(success.getMessage()); + } + + try { + parent.insertChild((ProcessingInstruction) null, 0); + fail("Inserted null"); + } + catch (NullPointerException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testAppendChild2() { + + try { + empty.appendChild(new Document(notEmpty)); + fail("appended a document to an element"); + } + catch (IllegalAddException success) { + assertNotNull(success.getMessage()); + } + + try { + empty.appendChild(child); + fail("appended a child twice"); + } + catch (MultipleParentException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testReplaceChild() { + + Element old1 = new Element("old1"); + Element old2 = new Element("old2"); + Element old3 = new Element("old3"); + Element new1 = new Element("new1"); + Element new2 = new Element("new2"); + Element new3 = new Element("new3"); + + empty.appendChild(old1); + empty.appendChild(old2); + empty.appendChild(old3); + + empty.replaceChild(old1, new1); + empty.replaceChild(old3, new3); + empty.replaceChild(old2, new2); + + Node current1 = empty.getChild(0); + Node current2 = empty.getChild(1); + Node current3 = empty.getChild(2); + + assertEquals(new1, current1); + assertEquals(new2, current2); + assertEquals(new3, current3); + + try { + empty.replaceChild(new1, null); + } + catch (NullPointerException success) { + assertNotNull(success.getMessage()); + } + + try { + empty.replaceChild(null, old1); + } + catch (NullPointerException success) { + assertNotNull(success.getMessage()); + } + + Element new4 = new Element("new4"); + + try { + empty.replaceChild(new4, new Element("test")); + fail("Replaced Nonexistent element"); + } + catch (NoSuchChildException success) { + assertNotNull(success.getMessage()); + } + + // Test replacing node with itself + empty.replaceChild(new1, new1); + assertEquals(new1, empty.getChild(0)); + assertEquals(empty, new1.getParent()); + + // Test replacing node with a sibling + try { + empty.replaceChild(new1, new2); + fail("replaced a node with its sibling"); + } + catch (MultipleParentException success) { + assertNotNull(success.getMessage()); + } + + + } + + + public void testIndexOf() { + + Element child1 = new Element("old1"); + Text child2 = new Text("old2"); + Comment child3 = new Comment("old3"); + + assertEquals(-1, empty.indexOf(child1)); + + empty.appendChild(child1); + empty.appendChild(child2); + empty.appendChild(child3); + + assertEquals(0, empty.indexOf(child1)); + assertEquals(1, empty.indexOf(child2)); + assertEquals(2, empty.indexOf(child3)); + assertEquals(-1, empty.indexOf(empty)); + assertEquals(-1, empty.indexOf(new Text("test"))); + + } + + + public void testGetChild() { + + Element old1 = new Element("old1"); + Element old2 = new Element("old2"); + Element old3 = new Element("old3"); + + try { + empty.getChild(0); + fail("No index exception"); + } + catch (IndexOutOfBoundsException success) { + // success + assertNotNull(success.getMessage()); + } + + empty.appendChild(old1); + empty.appendChild(old2); + empty.appendChild(old3); + + assertEquals(old1, empty.getChild(0)); + assertEquals(old3, empty.getChild(2)); + assertEquals(old2, empty.getChild(1)); + + try { + empty.getChild(5); + fail("No index exception"); + } + catch (IndexOutOfBoundsException success) { + // success + assertNotNull(success.getMessage()); + } + + } + + + public void testRemoveChild() { + + try { + empty.removeChild(0); + fail("Removed child from empty element"); + } + catch (IndexOutOfBoundsException success) { + assertNotNull(success.getMessage()); + } + + Element old1 = new Element("old1"); + Element old2 = new Element("old2"); + Element old3 = new Element("old3"); + + try { + empty.removeChild(old1); + fail("Removed non-existent child from empty element"); + } + catch (NoSuchChildException success) { + assertNotNull(success.getMessage()); + } + + empty.appendChild(old1); + empty.appendChild(old2); + empty.appendChild(old3); + + empty.removeChild(1); + assertEquals(old1, empty.getChild(0)); + assertEquals(old3, empty.getChild(1)); + + try { + empty.removeChild(5); + fail("No IndexOutOfBoundsException"); + } + catch (IndexOutOfBoundsException success) { + assertNotNull(success.getMessage()); + } + + empty.removeChild(1); + empty.removeChild(0); + assertNull(old2.getParent()); + + assertEquals(0, empty.getChildCount()); + + empty.appendChild(old1); + empty.appendChild(old2); + empty.appendChild(old3); + + assertEquals(3, empty.getChildCount()); + + empty.removeChild(old3); + empty.removeChild(old1); + empty.removeChild(old2); + + assertEquals(0, empty.getChildCount()); + assertNull(old1.getParent()); + + } + + + public void testReplaceChildFailures() { + + Element old1 = new Element("old1"); + Element old2 = new Element("old2"); + Element old3 = new Element("old3"); + Element new1 = new Element("new1"); + Element new3 = new Element("new3"); + + empty.appendChild(old1); + empty.appendChild(old2); + + try { + empty.replaceChild(old3, new3); + fail("Replaced non-existent child"); + } + catch (NoSuchChildException success) { + assertNotNull(success.getMessage()); + } + + try { + empty.replaceChild(old1, null); + fail("Replaced child with null"); + } + catch (NullPointerException success) { + assertNotNull(success.getMessage()); + } + + try { + empty.replaceChild(null, new1); + fail("Replaced null"); + } + catch (NullPointerException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testReplaceChildInEmptyParent() { + + Element test1 = new Element("test"); + Element test2 = new Element("test"); + try { + empty.replaceChild(test1, test2); + fail("Replaced element in empty parent"); + } + catch (NoSuchChildException success) { + assertNotNull(success.getMessage()); + } + + } + + + // Document that this behavior is intentional + // An element cannot be replaced by its sibling unless + // the sibling is first detached. + public void testReplaceSibling() { + + Element parent = new Element("parent"); + Element test1 = new Element("test"); + Element test2 = new Element("test"); + parent.appendChild(test1); + parent.appendChild(test2); + try { + parent.replaceChild(test1, test2); + fail("Replaced element without detaching first"); + } + catch (IllegalAddException success) { + assertNotNull(success.getMessage()); + } + + assertEquals(2, parent.getChildCount()); + assertEquals(parent, test1.getParent()); + assertEquals(parent, test2.getParent()); + + } + + + // Similarly, this test documents the conscious decision + // that you cannot insert an existing child into its own parent, + // even at the same position + public void testCantInsertExisitngChild() { + + Element parent = new Element("parent"); + Element test1 = new Element("test"); + Element test2 = new Element("test"); + parent.appendChild(test1); + parent.appendChild(test2); + try { + parent.insertChild(test2, 0); + fail("Inserted element without detaching first"); + } + catch (MultipleParentException success) { + assertNotNull(success.getMessage()); + } + + try { + parent.insertChild(test2, 1); + fail("Inserted element without detaching first"); + } + catch (MultipleParentException success) { + assertNotNull(success.getMessage()); + } + + assertEquals(2, parent.getChildCount()); + assertEquals(parent, test1.getParent()); + assertEquals(parent, test2.getParent()); + + } + + + // can't remove when insertion is legal; + // succeed or fail as unit + public void testReplaceChildAtomicity() { + + Element parent = new Element("parent"); + Text child = new Text("child"); + parent.appendChild(child); + + try { + parent.replaceChild(child, new DocType("root")); + fail("allowed doctype child of element"); + } + catch (IllegalAddException success) { + assertEquals(parent, child.getParent()); + assertEquals(1, parent.getChildCount()); + } + + Element e = new Element("e"); + Text child2 = new Text("child2"); + e.appendChild(child2); + try { + parent.replaceChild(child, child2); + fail("allowed replacement with existing parent"); + } + catch (MultipleParentException success) { + assertEquals(e, child2.getParent()); + assertEquals(parent, child.getParent()); + assertEquals(1, parent.getChildCount()); + assertEquals(1, e.getChildCount()); + } + + } + + +} diff --git a/src/nu/xom/tests/ParsingExceptionTest.java b/src/nu/xom/tests/ParsingExceptionTest.java new file mode 100644 index 0000000..1a4d9d1 --- /dev/null +++ b/src/nu/xom/tests/ParsingExceptionTest.java @@ -0,0 +1,183 @@ +/* Copyright 2003, 2004 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.tests; + +import nu.xom.ParsingException; + +/** + *

    + * Unit tests for the ParsingException class. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.1b2 + * + */ +public class ParsingExceptionTest extends XOMTestCase { + + + private ParsingException ex; + private Exception cause; + private String message = "testing 1-2-3"; + + + public ParsingExceptionTest(String name) { + super(name); + } + + + protected void setUp() { + ex = new ParsingException("message"); + cause = new Exception(); + } + + + public void testConstructor() { + ParsingException ex = new ParsingException(message, cause); + assertEquals(message, ex.getMessage()); + assertEquals(cause, ex.getCause()); + } + + + public void testFourArgumentConstructor() { + + ParsingException ex = new ParsingException(message, 100000, 400000, cause); + assertEquals(message, ex.getMessage()); + assertEquals(cause, ex.getCause()); + assertEquals(100000, ex.getLineNumber()); + assertEquals(400000, ex.getColumnNumber()); + + } + + + public void testLineAndColumnNumbers() { + ParsingException ex = new ParsingException(message, 10, 20); + assertEquals(message, ex.getMessage()); + assertNull(ex.getCause()); + assertEquals(10, ex.getLineNumber()); + assertEquals(20, ex.getColumnNumber()); + } + + + public void testLineAndColumnNumbersInToString() { + ParsingException ex = new ParsingException(message, -1, -1); + String result = ex.toString(); + assertEquals(-1, result.indexOf("-1")); + } + + + public void testToString() { + ParsingException ex = new ParsingException(message, 10, 20); + assertTrue(ex.toString().endsWith(" at line 10, column 20")); + } + + + public void testInitCause() { + + assertNull(ex.getCause()); + ex.initCause(cause); + assertEquals(cause, ex.getCause()); + + try { + ex.initCause(null); + fail("Reinitialized cause over null"); + } + catch (IllegalStateException result) { + // success + } + + try { + ex.initCause(new Exception()); + fail("Reinitialized cause over null"); + } + catch (IllegalStateException result) { + // success + } + + } + + + public void testNullInitCause() { + + ParsingException ex = new ParsingException(null, null); + assertNull(ex.getCause()); + + try { + ex.initCause(new Exception()); + fail("Reinitialized cause over null"); + } + catch (IllegalStateException result) { + // success + } + + try { + ex.initCause(null); + fail("Reinitialized cause over null"); + } + catch (IllegalStateException result) { + // success + } + + } + + + public void testSelfCause() { + + try { + ex.initCause(ex); + fail("Allowed self-causation"); + } + catch (IllegalArgumentException result) { + // success + } + + } + + + public void testGetMessage() { + Exception ex = new ParsingException("testing"); + assertEquals("testing", ex.getMessage()); + } + + + public void testGetURI() { + + ParsingException ex = new ParsingException("testing", "http://www.example.org/", 32, 24); + assertEquals("http://www.example.org/", ex.getURI()); + + Exception cause = new Exception("test"); + ex = new ParsingException("testing", "http://www.example.org/", 32, 24, cause); + assertEquals("http://www.example.org/", ex.getURI()); + assertEquals(cause, ex.getCause()); + + } + + + public void testURIInToString() { + + ParsingException ex = new ParsingException("testing", "http://www.example.org/", 32, 24); + assertTrue(ex.toString().indexOf("http://www.example.org/") > 1); + + } + + +} diff --git a/src/nu/xom/tests/ProcessingInstructionTest.java b/src/nu/xom/tests/ProcessingInstructionTest.java new file mode 100644 index 0000000..d0a7ae9 --- /dev/null +++ b/src/nu/xom/tests/ProcessingInstructionTest.java @@ -0,0 +1,494 @@ +/* Copyright 2002-2004 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.tests; + +import nu.xom.Element; +import nu.xom.IllegalDataException; +import nu.xom.IllegalTargetException; +import nu.xom.ProcessingInstruction; + +/** + *

    + * Unit tests for the ProcessingInstruction class. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.3.0 + * + */ +public class ProcessingInstructionTest extends XOMTestCase { + + + public ProcessingInstructionTest(String name) { + super(name); + } + + + private ProcessingInstruction pi; + + + protected void setUp() { + pi = new ProcessingInstruction("test", "test"); + } + + + public void testToXML() { + assertEquals("", pi.toXML()); + } + + + public void testToString() { + assertEquals( + "[nu.xom.ProcessingInstruction: target=\"test\"; data=\"test\"]", + pi.toString()); + } + + + public void testToStringWithLineFeed() { + + ProcessingInstruction p + = new ProcessingInstruction("test", "content\ncontent"); + assertEquals( + "[nu.xom.ProcessingInstruction: target=\"test\"; data=\"content\\ncontent\"]", + p.toString() + ); + + } + + + public void testToStringWithLotsOfData() { + + ProcessingInstruction p + = new ProcessingInstruction("target", + "content content 012345678901234567890123456789012345678901234567890123456789"); + String s = p.toString(); + assertTrue(s.endsWith("...\"]")); + + } + + + public void testConstructor() { + + assertEquals("test", pi.getValue()); + assertEquals("test", pi.getTarget()); + + try { + new ProcessingInstruction("test:test", "test"); + fail("Processing instruction targets cannot contain colons"); + } + catch (IllegalTargetException success) { + assertNotNull(success.getMessage()); + assertEquals("test:test", success.getData()); + } + + try { + new ProcessingInstruction("", "test"); + fail("Processing instruction targets cannot be empty"); + } + catch (IllegalTargetException success) { + assertNotNull(success.getMessage()); + assertEquals("", success.getData()); + } + + try { + new ProcessingInstruction(null, "test"); + fail("Processing instruction targets cannot be empty"); + } + catch (IllegalTargetException success) { + assertNotNull(success.getMessage()); + assertNull(success.getData()); + } + + try { + new ProcessingInstruction("12345", "test"); + fail("Processing instruction targets must be NCNames"); + } + catch (IllegalTargetException success) { + assertEquals("12345", success.getData()); + } + + // test empty data allowed + pi = new ProcessingInstruction("test", ""); + assertEquals("", pi.getValue()); + assertEquals("", pi.toXML()); + + } + + + public void testSetTarget() { + + try { + pi.setTarget("test:test"); + fail("Processing instruction targets cannot contain colons"); + } + catch (IllegalTargetException success) { + assertNotNull(success.getMessage()); + assertEquals("test:test", success.getData()); + } + + try { + pi.setTarget(""); + fail("Processing instruction targets cannot be empty"); + } + catch (IllegalTargetException success) { + assertNotNull(success.getMessage()); + assertEquals("", success.getData()); + } + + try { + pi.setTarget(null); + fail("Processing instruction targets cannot be empty"); + } + catch (IllegalTargetException success) { + assertNotNull(success.getMessage()); + assertNull(success.getData()); + } + + try { + pi.setTarget("12345"); + fail("Processing instruction targets must be NCNames"); + } + catch (IllegalTargetException success) { + assertEquals("12345", success.getData()); + } + + pi.setTarget("testing123"); + assertEquals("testing123", pi.getTarget()); + + } + + + public void testCopyConstructor() { + + ProcessingInstruction instruction1 = new ProcessingInstruction("target", "data"); + ProcessingInstruction instruction2 = new ProcessingInstruction(instruction1); + + assertEquals(instruction1.getTarget(), instruction2.getTarget()); + assertEquals(instruction1.getValue(), instruction2.getValue()); + assertEquals(instruction1.toXML(), instruction2.toXML()); + + } + + + public void testSetValue() { + + try { + pi.setValue("kjsahdj ?>"); + fail("Should raise an IllegalDataException"); + } + catch (IllegalDataException success) { + assertEquals("kjsahdj ?>", success.getData()); + assertNotNull(success.getMessage()); + } + + try { + pi.setValue("?>"); + fail("Should raise an IllegalDataException"); + } + catch (IllegalDataException success) { + assertEquals("?>", success.getData()); + assertNotNull(success.getMessage()); + } + + try { + pi.setValue("kjsahdj ?> skhskjlhd"); + fail("Should raise an IllegalDataException"); + } + catch (IllegalDataException success) { + assertEquals("kjsahdj ?> skhskjlhd", success.getData()); + assertNotNull(success.getMessage()); + } + + try { + pi.setValue(null); + fail("Allowed null data"); + } + catch (IllegalDataException success) { + assertNull(success.getData()); + assertNotNull(success.getMessage()); + } + + + // These should all work + String[] testData = {"", + "name=value", + "name='value'", + "name=\"value\"", + "salkdhsalkjhdkjsadhkj sadhsajkdh", + "", "--" + }; + for (int i = 0; i < testData.length; i++) { + pi.setValue(testData[i]); + assertEquals(testData[i], pi.getValue()); + } + + } + + + public void testNames() { + assertEquals("test", pi.getTarget()); + } + + + public void testEquals() { + + ProcessingInstruction pi1 + = new ProcessingInstruction("test", "afaf"); + ProcessingInstruction pi2 + = new ProcessingInstruction("test", "afaf"); + ProcessingInstruction pi3 + = new ProcessingInstruction("tegggst", "afaf"); + ProcessingInstruction pi4 + = new ProcessingInstruction("test", "1234"); + + assertEquals(pi1, pi1); + assertEquals(pi1.hashCode(), pi1.hashCode()); + assertTrue(!pi1.equals(pi2)); + assertTrue(!pi1.equals(pi3)); + assertTrue(!pi3.equals(pi4)); + assertTrue(!pi2.equals(pi4)); + assertTrue(!pi2.equals(pi3)); + + } + + + public void testCopy() { + + Element test = new Element("test"); + test.appendChild(pi); + ProcessingInstruction c2 = pi.copy(); + + assertEquals(pi, c2); + assertEquals(pi.getValue(), c2.getValue()); + assertTrue(!pi.equals(c2)); + assertNull(c2.getParent()); + + } + + + // Check passing in a string with correct surrogate pairs + public void testCorrectSurrogates() { + + String goodString = "test: \uD8F5\uDF80 "; + pi.setValue(goodString); + assertEquals(goodString, pi.getValue()); + + } + + + // Check passing in a string with broken surrogate pairs + public void testSurrogates() { + + try { + pi.setValue("test \uD8F5\uD8F5 test"); + fail("Allowed two high halves"); + } + catch (IllegalDataException success) { + assertEquals("test \uD8F5\uD8F5 test", success.getData()); + assertNotNull(success.getMessage()); + } + + try { + pi.setValue("test \uDF80\uDF80 test"); + fail("Allowed two low halves"); + } + catch (IllegalDataException success) { + assertEquals("test \uDF80\uDF80 test", success.getData()); + assertNotNull(success.getMessage()); + } + + try { + pi.setValue("test \uD8F5 \uDF80 test"); + fail("Allowed two halves split by space"); + } + catch (IllegalDataException success) { + assertEquals("test \uD8F5 \uDF80 test", success.getData()); + assertNotNull(success.getMessage()); + } + + try { + pi.setValue("test \uDF80\uD8F5 test"); + fail("Allowed reversed pair"); + } + catch (IllegalDataException success) { + assertEquals("test \uDF80\uD8F5 test", success.getData()); + assertNotNull(success.getMessage()); + } + + } + + + public void testLeafNode() { + + assertEquals(0, pi.getChildCount()); + try { + pi.getChild(0); + fail("Didn't throw IndexOutofBoundsException"); + } + catch (IndexOutOfBoundsException success) { + assertNotNull(success.getMessage()); + } + + assertNull(pi.getParent()); + + Element element = new Element("test"); + element.appendChild(pi); + assertEquals(element, pi.getParent()); + assertEquals(pi, element.getChild(0)); + + element.removeChild(pi); + assertEquals(0, element.getChildCount()); + + } + + + // This is a problem becuase it cannot be serialized + // since character and entity references aren't + // recognized in comment data + public void testCarriageReturnInProcessingInstructionData() { + + try { + new ProcessingInstruction("target", "data\rdata"); + fail("Allowed carriage return in processing instruction data"); + } + catch (IllegalDataException success) { + assertEquals("data\rdata", success.getData()); + assertNotNull(success.getMessage()); + } + + } + + + public void testAllowReservedCharactersInData() { + ProcessingInstruction pi = new ProcessingInstruction("target", "&&greater;"); + String xml = pi.toXML(); + assertEquals("&&greater;?>", xml); + } + + + // This can't be round-tripped + public void testNoInitialWhiteSpace() { + + try { + new ProcessingInstruction("target", " initial spaces"); + fail("allowed processing instruction data with leading space"); + } + catch (IllegalDataException success) { + assertEquals(" initial spaces", success.getData()); + assertNotNull(success.getMessage()); + } + + try { + new ProcessingInstruction("target", "\tinitial tab"); + fail("allowed processing instruction data with leading space"); + } + catch (IllegalDataException success) { + assertEquals("\tinitial tab", success.getData()); + assertNotNull(success.getMessage()); + } + + try { + new ProcessingInstruction("target", "\ninitial linefeed"); + fail("allowed processing instruction data with leading space"); + } + catch (IllegalDataException success) { + assertEquals("\ninitial linefeed", success.getData()); + assertNotNull(success.getMessage()); + } + + try { + new ProcessingInstruction("target", "\r initial carriage return"); + fail("allowed processing instruction data with leading space"); + } + catch (IllegalDataException success) { + assertEquals("\r initial carriage return", success.getData()); + assertNotNull(success.getMessage()); + } + + } + + + public void testNoXMLTargets() { + + try { + new ProcessingInstruction("xml", "data"); + fail("allowed processing instruction with target xml"); + } + catch (IllegalTargetException success) { + assertEquals("xml", success.getData()); + assertNotNull(success.getMessage()); + } + + try { + new ProcessingInstruction("XML", "data"); + fail("allowed processing instruction with target XML"); + } + catch (IllegalTargetException success) { + assertEquals("XML", success.getData()); + assertNotNull(success.getMessage()); + } + + try { + new ProcessingInstruction("Xml", "data"); + fail("allowed processing instruction with target Xml"); + } + catch (IllegalTargetException success) { + assertEquals("Xml", success.getData()); + assertNotNull(success.getMessage()); + } + + } + + + public void testColonsNotAllowedInTargets() { + + try { + new ProcessingInstruction("pre:target", "data"); + fail("allowed processing instruction with target that uses a prefixed name"); + } + catch (IllegalTargetException success) { + assertEquals("pre:target", success.getData()); + assertNotNull(success.getMessage()); + } + + try { + new ProcessingInstruction("pre:", "data"); + fail("allowed processing instruction with trailing colon in target"); + } + catch (IllegalTargetException success) { + assertEquals("pre:", success.getData()); + assertNotNull(success.getMessage()); + } + + try { + new ProcessingInstruction(":target", "data"); + fail("allowed processing instruction with initial colon in target"); + } + catch (IllegalTargetException success) { + assertEquals(":target", success.getData()); + assertNotNull(success.getMessage()); + } + + } + + +} diff --git a/src/nu/xom/tests/RoundTripTest.java b/src/nu/xom/tests/RoundTripTest.java new file mode 100644 index 0000000..4877da4 --- /dev/null +++ b/src/nu/xom/tests/RoundTripTest.java @@ -0,0 +1,146 @@ +/* Copyright 2002, 2003 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.tests; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +import nu.xom.Attribute; +import nu.xom.Builder; +import nu.xom.Document; +import nu.xom.Element; +import nu.xom.ParsingException; +import nu.xom.Serializer; + +/** + *

    + * This mostly verifies that white space + * is properly escaped on output. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.0 + * + */ +public class RoundTripTest extends XOMTestCase { + + private Builder builder; + private Serializer serializer; + private ByteArrayOutputStream out; + + public RoundTripTest(String name) { + super(name); + } + + protected void setUp() { + builder = new Builder(); + out = new ByteArrayOutputStream(); + serializer = new Serializer(out); + } + + public void testTabInAttributeValue() + throws IOException, ParsingException { + Element test = new Element("test"); + test.addAttribute(new Attribute("tab", "\t")); + Document doc = new Document(test); + serializer.write(doc); + byte[] input = out.toByteArray(); + Document reparsed = builder.build(new ByteArrayInputStream(input)); + Element root = reparsed.getRootElement(); + Attribute tab = root.getAttribute("tab"); + assertEquals( + "Round trip did not preserve tab in attribute value", + "\t", tab.getValue() + ); + assertEquals("Unexpected error on round trip", doc, reparsed); + } + + public void testCarriageReturnInAttributeValue() + throws IOException, ParsingException { + Element test = new Element("test"); + test.addAttribute(new Attribute("cr", "\r")); + Document doc = new Document(test); + serializer.write(doc); + byte[] input = out.toByteArray(); + Document reparsed = builder.build(new ByteArrayInputStream(input)); + Element root = reparsed.getRootElement(); + Attribute cr = root.getAttribute("cr"); + assertEquals( + "Round trip did not preserve carriage return in attribute value", + "\r", cr.getValue() + ); + assertEquals("Unexpected error on round trip", doc, reparsed); + } + + public void testCarriageReturnInText() + throws IOException, ParsingException { + Element test = new Element("test"); + test.appendChild("\r"); + Document doc = new Document(test); + serializer.write(doc); + byte[] input = out.toByteArray(); + Document reparsed = builder.build(new ByteArrayInputStream(input)); + Element root = reparsed.getRootElement(); + String value = root.getValue(); + assertEquals( + "Round trip did not preserve carriage return in text", + "\r", value + ); + assertEquals("Unexpected error on round trip", doc, reparsed); + } + + public void testLineFeedInAttributeValue() + throws IOException, ParsingException { + Element test = new Element("test"); + test.addAttribute(new Attribute("lf", "\n")); + Document doc = new Document(test); + serializer.write(doc); + byte[] input = out.toByteArray(); + Document reparsed = builder.build(new ByteArrayInputStream(input)); + Element root = reparsed.getRootElement(); + Attribute lf = root.getAttribute("lf"); + assertEquals( + "Round trip did not preserve carriage return in attribute value", + "\n", lf.getValue() + ); + assertEquals("Unexpected error on round trip", doc, reparsed); + } + + public void testSpacesInAttributeValue() + throws IOException, ParsingException { + Element test = new Element("test"); + test.addAttribute(new Attribute("spaces", " ")); + Document doc = new Document(test); + serializer.write(doc); + byte[] input = out.toByteArray(); + Document reparsed = builder.build(new ByteArrayInputStream(input)); + Element root = reparsed.getRootElement(); + Attribute spaces = root.getAttribute("spaces"); + assertEquals( + "Round trip did not preserve spaces in attribute value", + " ", spaces.getValue() + ); + assertEquals("Unexpected error on round trip", doc, reparsed); + } + +} diff --git a/src/nu/xom/tests/SAXConverterTest.java b/src/nu/xom/tests/SAXConverterTest.java new file mode 100644 index 0000000..91f987d --- /dev/null +++ b/src/nu/xom/tests/SAXConverterTest.java @@ -0,0 +1,468 @@ +/* Copyright 2003-2005 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + + +package nu.xom.tests; + +import java.io.File; +import java.io.IOException; +import java.io.StringWriter; + +import nu.xom.Attribute; +import nu.xom.Builder; +import nu.xom.Comment; +import nu.xom.DocType; +import nu.xom.Document; +import nu.xom.Element; +import nu.xom.ParsingException; +import nu.xom.ProcessingInstruction; +import nu.xom.converters.SAXConverter; + +import org.xml.sax.Attributes; +import org.xml.sax.ContentHandler; +import org.xml.sax.Locator; +import org.xml.sax.SAXException; +import org.xml.sax.ext.LexicalHandler; +import org.xml.sax.helpers.DefaultHandler; + +/** + *

    + * Basic tests for conversion from XOM trees + * to SAX ContentHandlers. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.1b4 + * + */ +public class SAXConverterTest extends XOMTestCase { + + + public SAXConverterTest(String name) { + super(name); + } + + + private DefaultHandler handler; + private SAXConverter converter; + private Builder builder = new Builder(); + + + protected void setUp() { + handler = new DefaultHandler(); + converter = new SAXConverter(handler); + } + + + public void testGetContentHandler() { + assertEquals(handler, converter.getContentHandler()); + } + + + public void testSetContentHandler() { + + handler = new DefaultHandler(); + converter.setContentHandler(handler); + assertEquals(handler, converter.getContentHandler()); + + try { + converter.setContentHandler(null); + fail("Allowed null ContentHandler"); + } + catch (NullPointerException success) { + // success + } + + } + + + public void testSetAndGetLexicalHandler() { + + LexicalHandler handler = new XMLWriter(); + converter.setLexicalHandler(handler); + assertEquals(handler, converter.getLexicalHandler()); + + converter.setLexicalHandler(null); + assertNull(converter.getLexicalHandler()); + + } + + + private void convertAndCompare(Document doc) + throws IOException, SAXException, ParsingException { + + StringWriter result = new StringWriter(); + XMLWriter handler = new XMLWriter(result); + converter.setContentHandler(handler); + converter.setLexicalHandler(handler); + converter.convert(doc); + result.flush(); + result.close(); + String convertedDoc = result.toString(); + Document rebuiltDoc = builder.build(convertedDoc, doc.getBaseURI()); + assertEquals(doc, rebuiltDoc); + + } + + + public void testSimplestDoc() + throws IOException, SAXException, ParsingException { + Document doc = new Document(new Element("a")); + convertAndCompare(doc); + } + + + public void testXMLBaseAttributesAreThrownAway() + throws SAXException { + + Element root = new Element("root"); + Element child = new Element("child"); + Attribute base = new Attribute("xml:base", + "http://www.w3.org/XML/1998/namespace", "data"); + child.addAttribute(base); + root.appendChild(child); + Document doc = new Document(root); + doc.setBaseURI("http://www.example.com/"); + converter.setContentHandler(new BaseChecker()); + converter.convert(doc); + + } + + + private static class BaseChecker extends DefaultHandler { + + private Locator locator; + + public void setDocumentLocator(Locator locator) { + this.locator = locator; + } + + public void startElement(String localName, String qualifiedName, + String namespaceURI, Attributes attributes) + throws SAXException { + + if (localName.equals("root")) { + assertEquals("http://www.example.com/", locator.getSystemId()); + } + else if (localName.equals("child")) { + assertEquals("http://www.example.com/data", locator.getSystemId()); + } + + for (int i=0; i < attributes.getLength(); i++) { + String name = attributes.getLocalName(i); + String uri = attributes.getURI(i); + if ("base".equals(name) + && "http://www.w3.org/XML/1998/namespace".equals(uri)) { + fail("Passed xml:base attribute into SAXConverter"); + } + } + + } + + } + + + + public void testDocType() + throws IOException, SAXException, ParsingException { + Document doc = new Document(new Element("a")); + doc.setDocType(new DocType("root")); + convertAndCompare(doc); + } + + + public void testProcessingInstruction() + throws IOException, SAXException, ParsingException { + + Document doc = new Document(new Element("a")); + doc.insertChild(new ProcessingInstruction( + "xml-stylesheet", "type=\"application/xml\" href=\"stylesheet.xsl\""), 0); + convertAndCompare(doc); + + } + + + public void testComment() + throws IOException, SAXException, ParsingException { + + Element root = new Element("root"); + root.appendChild(" Lots of random text\n\n\n "); + Document doc = new Document(root); + doc.insertChild(new Comment("some comment data"), 0); + root.insertChild(new Comment("some comment data"), 0); + doc.appendChild(new Comment("some comment data")); + convertAndCompare(doc); + + } + + + public void testDefaultNamespace() + throws IOException, SAXException, ParsingException { + Document doc = new Document(new Element("a", "http://www.a.com/")); + convertAndCompare(doc); + } + + + public void testTextContent() + throws IOException, SAXException, ParsingException { + Element root = new Element("root"); + root.appendChild(" Lots of random text\n\n\n "); + Document doc = new Document(root); + convertAndCompare(doc); + } + + + public void testPrefixedNamespace() + throws IOException, SAXException, ParsingException { + Document doc = new Document(new Element("a:a", "http://www.a.com/")); + convertAndCompare(doc); + } + + + public void testAdditionalNamespace() + throws IOException, SAXException, ParsingException { + + Element root = new Element("root"); + root.addNamespaceDeclaration("xsl", "http://www.w3.org/1999/XSL/Transform"); + Document doc = new Document(root); + convertAndCompare(doc); + + } + + + public void testPrefixAndAdditionalNamespace() + throws IOException, SAXException, ParsingException { + + Element root = new Element("xsl:root", "http://www.w3.org/1999/XSL/Transform"); + root.addNamespaceDeclaration("xsl", "http://www.w3.org/1999/XSL/Transform"); + Document doc = new Document(root); + convertAndCompare(doc); + + } + + + public void testPrefixAndAdditionalNamespaceFromParser() + throws IOException, SAXException, ParsingException { + Document doc = builder.build( + "", + null); + convertAndCompare(doc); + } + + + public void testChildElementAddsNamespace() + throws IOException, SAXException, ParsingException { + + Element root = new Element("root"); + Element child = new Element("pre:child", "http://www.example.org/"); + child.addAttribute(new Attribute("xlink:type", "http://www.w3.org/1999/xlink", "simple")); + root.appendChild(child); + Document doc = new Document(root); + convertAndCompare(doc); + + } + + + public void testAttributesTypes() + throws IOException, SAXException, ParsingException { + + Element root = new Element("root"); + root.addAttribute(new Attribute("CDATA", "CDATA", Attribute.Type.CDATA)); + root.addAttribute(new Attribute("ID", "ID", Attribute.Type.ID)); + root.addAttribute(new Attribute("IDREF", "IDREF", Attribute.Type.IDREF)); + root.addAttribute(new Attribute("IDRES", "IDREFS", Attribute.Type.IDREFS)); + root.addAttribute(new Attribute("NMTOKEN", "NMTOKEN", Attribute.Type.NMTOKEN)); + root.addAttribute(new Attribute("NMTOKENS", "NMTOKENS", Attribute.Type.NMTOKENS)); + root.addAttribute(new Attribute("UNDECLARED", "UNDECLARED", Attribute.Type.UNDECLARED)); + root.addAttribute(new Attribute("ENTITY", "ENTITY", Attribute.Type.ENTITY)); + root.addAttribute(new Attribute("ENTITIES", "ENTITIES", Attribute.Type.ENTITIES)); + root.addAttribute(new Attribute("NOTATION", "NOTATION", Attribute.Type.NOTATION)); + root.addAttribute(new Attribute("ENUMERATION", "ENUMERATION", Attribute.Type.ENUMERATION)); + Document doc = new Document(root); + convertAndCompare(doc); + + } + + + public void testAttributes() + throws IOException, SAXException, ParsingException { + + Element root = new Element("root"); + root.addAttribute(new Attribute("a", "test")); + root.addAttribute(new Attribute("xlink:type", + "http://www.w3.org/1999/xlink", "simple")); + Document doc = new Document(root); + convertAndCompare(doc); + + } + + + public void testExternalDTDSubset() + throws IOException, SAXException, ParsingException { + + File input = new File("data"); + input = new File(input, "externalDTDtest.xml"); + Document doc = builder.build(input); + convertAndCompare(doc); + + } + + + public void testBigDoc() + throws IOException, SAXException, ParsingException { + Document doc = builder.build("http://www.cafeconleche.org/"); + convertAndCompare(doc); + } + + + public void testNoPrefixMappingEventsForDefaultEmptyNamespace() + throws ParsingException, IOException, SAXException { + + String data = ""; + Document doc = builder.build(data, null); + ContentHandler handler = new XMLPrefixTester2(); + SAXConverter converter = new SAXConverter(handler); + converter.convert(doc); + + } + + + public void testNoPrefixMappingEventsForXMLPrefix() + throws ParsingException, IOException, SAXException { + + String data = ""; + Document doc = builder.build(data, null); + ContentHandler handler = new XMLPrefixTester(); + SAXConverter converter = new SAXConverter(handler); + converter.convert(doc); + + } + + + public void testNoPrefixMappingEventsForXMLPrefixOnElement() + throws ParsingException, IOException, SAXException { + + String data = ""; + Document doc = builder.build(data, null); + ContentHandler handler = new XMLPrefixTester(); + SAXConverter converter = new SAXConverter(handler); + converter.convert(doc); + + } + + + private static class XMLPrefixTester extends DefaultHandler { + + public void startPrefixMapping(String prefix, String uri) { + if ("xml".equals(prefix)) { + fail("start mapped prefix xml"); + } + } + + public void endPrefixMapping(String prefix) { + if ("xml".equals(prefix)) { + fail("end mapped prefix xml"); + } + } + + } + + + private static class XMLPrefixTester2 extends DefaultHandler { + + public void startPrefixMapping(String prefix, String uri) { + fail("start mapped prefix " + prefix); + } + + public void endPrefixMapping(String prefix) { + fail("end mapped prefix " + prefix); + } + + } + + + public void testNoRedundantPrefixMappingEventsForDefaultNamespace() + throws ParsingException, IOException, SAXException { + + String data = " "; + Document doc = builder.build(data, null); + XMLPrefixMapCounter handler = new XMLPrefixMapCounter(); + SAXConverter converter = new SAXConverter(handler); + converter.convert(doc); + assertEquals(1, handler.getStarts()); + assertEquals(1, handler.getEnds()); + + } + + + public void testNoRedundantPrefixMappingEventsForPrefixedNamespace() + throws ParsingException, IOException, SAXException { + + String data = ""; + Document doc = builder.build(data, null); + XMLPrefixMapCounter handler = new XMLPrefixMapCounter(); + SAXConverter converter = new SAXConverter(handler); + converter.convert(doc); + assertEquals(1, handler.getStarts()); + assertEquals(1, handler.getEnds()); + + } + + + public void testChildNamespace() + throws ParsingException, IOException, SAXException { + + String data = ""; + Document doc = builder.build(data, null); + XMLPrefixMapCounter handler = new XMLPrefixMapCounter(); + SAXConverter converter = new SAXConverter(handler); + converter.convert(doc); + assertEquals(1, handler.getStarts()); + assertEquals(1, handler.getEnds()); + + } + + + private static class XMLPrefixMapCounter extends DefaultHandler { + + private int starts = 0; + private int ends = 0; + + public void startPrefixMapping(String prefix, String uri) { + starts++; + } + + public void endPrefixMapping(String prefix) { + ends++; + } + + int getStarts() { + return starts; + } + + int getEnds() { + return ends; + } + + } + + +} diff --git a/src/nu/xom/tests/SerializerTest.java b/src/nu/xom/tests/SerializerTest.java new file mode 100644 index 0000000..057a09a --- /dev/null +++ b/src/nu/xom/tests/SerializerTest.java @@ -0,0 +1,2799 @@ +/* Copyright 2002-2006, 2009 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.tests; + +import nu.xom.Elements; +import nu.xom.Namespace; +import nu.xom.Node; +import nu.xom.Serializer; +import nu.xom.Element; +import nu.xom.DocType; +import nu.xom.Document; +import nu.xom.Builder; +import nu.xom.Comment; +import nu.xom.ParsingException; +import nu.xom.ProcessingInstruction; +import nu.xom.Attribute; +import nu.xom.Text; +import nu.xom.UnavailableCharacterException; +import nu.xom.ValidityException; +import nu.xom.XMLException; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.BufferedReader; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.StringReader; +import java.io.UnsupportedEncodingException; + +/** + *

    + * Tests for Serializer functionality. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.2 + * + */ +public class SerializerTest extends XOMTestCase { + + + private Builder parser; + private final static double version = Double.parseDouble( + System.getProperty("java.version").substring(0,3) + ); + Element root = new Element("root"); + Document doc = new Document(root); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + + + public SerializerTest(String name) { + super(name); + } + + + protected void setUp() { + parser = new Builder(); + } + + + public void testNFC88592() throws IOException, ParsingException { + + String data = " Ą˘Ł¤ĽŚ§¨ŠŞŤŹ­ŽŻ°ą˛ł´ľśˇ¸šşťź˝žżŔÁÂĂÄĹĆÇČÉĘËĚÍÎĎĐŃŇÓÔŐÖ×ŘŮÚŰÜÝŢßŕáâăäĺćçčéęëěíîďđńňóôőö÷řůúűüýţ˙"; + Element root = new Element("a"); + root.appendChild(data); + Document doc = new Document(root); + + // try with 8859-2 encoding on serializer + Serializer serializer = new Serializer(out); + serializer.setUnicodeNormalizationFormC(true); + serializer.write(doc); + serializer.flush(); + out.close(); + byte[] temp = out.toByteArray(); + Document roundTrip = parser.build(new ByteArrayInputStream(temp)); + + assertEquals(data, roundTrip.getValue()); + + } + + + public void testMultipleCombiningCharactersWithNFC() + throws ParsingException, IOException { + + // LATIN CAPITAL LETTER D WITH DOT BELOW, COMBINING DOT ABOVE + String input = "Ḍ̇"; + // LATIN CAPITAL LETTER D WITH DOT ABOVE, COMBINING DOT BELOW + String output = "\r\n" + + "Ḍ̇\r\n"; + + Document doc = parser.build(input, null); + Serializer serializer = new Serializer(out, "US-ASCII"); + serializer.setUnicodeNormalizationFormC(true); + serializer.write(doc); + serializer.flush(); + String result = out.toString("US-ASCII"); + assertEquals(output, result); + + } + + + public void testNamespaceURIContainsAmpersand() + throws ParsingException, IOException { + + String input = "\r\n"; + String expected = "\r\n" + input; + Document doc = parser.build(input, null); + Serializer serializer = new Serializer(out, "US-ASCII"); + serializer.write(doc); + serializer.flush(); + String result = out.toString("US-ASCII"); + assertEquals(expected, result); + + } + + + public void testNamespaceURIContainsAmpersandPhilip() + throws ParsingException, IOException { + + Element root = new Element("x", "x:&"); + Document doc = new Document(root); + String expected = "\r\n\r\n"; + Serializer serializer = new Serializer(out, "US-ASCII"); + serializer.write(doc); + serializer.flush(); + String result = out.toString("US-ASCII"); + assertEquals(expected, result); + + } + + + public void testMultipleCombiningCharactersWithDifferentCombiningClassesNFC() + throws ParsingException, IOException { + + // LATIN SMALL LETTER A, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, + // MUSICAL SYMBOL COMBINING AUGMENTATION DOT, HEBREW ACCENT ZINOR, + // LATIN SMALL LETTER B + String input = "à𝅭֮֮b"; + // There was a bug where 1D16D was not listed as a combining character + String output = "\r\n" + + "à𝅭֮֮b\r\n"; + + Document doc = parser.build(input, null); + Serializer serializer = new Serializer(out, "US-ASCII"); + serializer.setUnicodeNormalizationFormC(true); + serializer.write(doc); + serializer.flush(); + String result = out.toString("US-ASCII"); + assertEquals(output, result); + + } + + + public void testEWithCombiningMacron() + throws ParsingException, IOException { + + // LATIN CAPITAL LETTER E WITH COMBINING MACRON + String input = ""; + String output = "\r\nĒ\r\n"; + + Document doc = parser.build(input, null); + Serializer serializer = new Serializer(out, "US-ASCII"); + serializer.setUnicodeNormalizationFormC(true); + serializer.write(doc); + serializer.flush(); + String result = out.toString("US-ASCII"); + assertEquals(output, result); + + } + + + public void testEWithCombiningMacronAndGrave() + throws ParsingException, IOException { + + // LATIN CAPITAL LETTER E WITH COMBINING MACRON and grave + String input = "Ḕ"; + String output = "\r\n\r\n"; + + Document doc = parser.build(input, null); + Serializer serializer = new Serializer(out, "US-ASCII"); + serializer.setUnicodeNormalizationFormC(true); + serializer.write(doc); + serializer.flush(); + String result = out.toString("US-ASCII"); + assertEquals(output, result); + + } + + + public void testParenthesizedOjeon() + throws ParsingException, IOException { + + // (Initial consonant Medial Vowel Initial consonant Medial Vowel Final Consonant) + String input = "(오전)"; + String output = "\r\n(오전)\r\n"; + + Document doc = parser.build(input, null); + Serializer serializer = new Serializer(out, "US-ASCII"); + serializer.setUnicodeNormalizationFormC(true); + serializer.write(doc); + serializer.flush(); + String result = out.toString("US-ASCII"); + assertEquals(output, result); + + } + + + public void testNonParenthesizedOjeon() + throws ParsingException, IOException { + + // Initial consonant Medial Vowel Initial consonant Medial Vowel Final Consonant + String input = "오전"; + String output = "\r\n오전\r\n"; + + Document doc = parser.build(input, null); + Serializer serializer = new Serializer(out, "US-ASCII"); + serializer.setUnicodeNormalizationFormC(true); + serializer.write(doc); + serializer.flush(); + String result = out.toString("US-ASCII"); + assertEquals(output, result); + + } + + + public void testElementBeginsWithXML() + throws ParsingException, IOException { + + String input = ""; + String output = "\r\n\r\n"; + + Document doc = parser.build(input, null); + Serializer serializer = new Serializer(out, "US-ASCII"); + serializer.setUnicodeNormalizationFormC(true); + serializer.write(doc); + serializer.flush(); + String result = out.toString("US-ASCII"); + assertEquals(output, result); + + } + + + public void testElementHasAdditionalNamespaces() + throws ParsingException, IOException { + + String input = ""; + String output = "\r\n\r\n"; + + Document doc = parser.build(input, null); + Serializer serializer = new Serializer(out, "US-ASCII"); + serializer.setUnicodeNormalizationFormC(true); + serializer.write(doc); + serializer.flush(); + String result = out.toString("US-ASCII"); + assertEquals(output, result); + + } + + + public void testOjeon() + throws ParsingException, IOException { + + // (Initial consonant Medial Vowel Initial consonant Medial Vowel Final Consonant) + String input = "전"; + String output = "\r\n\r\n"; + + Document doc = parser.build(input, null); + Serializer serializer = new Serializer(out, "US-ASCII"); + serializer.setUnicodeNormalizationFormC(true); + serializer.write(doc); + serializer.flush(); + String result = out.toString("US-ASCII"); + assertEquals(output, result); + + } + + + public void testKannadaVowelSignOO() + throws ParsingException, IOException { + + String input = "ೋ"; + String output = "\r\n\r\n"; + + Document doc = parser.build(input, null); + Serializer serializer = new Serializer(out, "US-ASCII"); + serializer.setUnicodeNormalizationFormC(true); + serializer.write(doc); + serializer.flush(); + String result = out.toString("US-ASCII"); + assertEquals(output, result); + + } + + + public void testCDATASectionEndDelimiter() throws IOException { + + root.appendChild("]]>"); + Serializer serializer = new Serializer(out, "UTF-8"); + serializer.setMaxLength(20); + serializer.write(doc); + String result = out.toString("UTF-8"); + assertTrue(result.indexOf("]]>") > 0); + + } + + + public void testXMLSpacePreserve() throws IOException { + + root.addAttribute( + new Attribute( + "xml:space", + "http://www.w3.org/XML/1998/namespace", + "preserve")); + String value = + "This is a long sentence with plenty of opportunities for " + + "breaking from beginning to end."; + root.appendChild(value); + Serializer serializer = new Serializer(out, "UTF-8"); + serializer.setMaxLength(20); + serializer.write(doc); + String result = out.toString("UTF-8"); + assertTrue(result.indexOf(value) > 0); + + } + + + /** + *

    + * Check that the UTF-16LE encoding omits the byte-order mark. + *

    + * + * @throws IOException + */ + public void testUTF16LEBOM() throws IOException { + + if (version >= 1.3) { + // UTF-16LE only supported in 1.3 and later + Serializer serializer = new Serializer(out, "UTF-16LE"); + serializer.write(doc); + serializer.flush(); + out.flush(); + out.close(); + byte[] data = out.toByteArray(); + assertEquals('<', (char) data[0]); + assertEquals((byte) 0, data[1]); + } + + } + + + /** + *

    + * Check that the UTF-16 encoding outputs a byte-order mark. + *

    + * + * @throws IOException + */ + public void testUTF16BOM() throws IOException { + + Serializer serializer = new Serializer(out, "UTF-16"); + serializer.write(doc); + serializer.flush(); + out.flush(); + out.close(); + byte[] data = out.toByteArray(); + assertEquals((byte) 0xFE, data[0]); + assertEquals((byte) 0xFF, data[1]); + assertEquals((byte) 0, data[2]); + assertEquals('<', (char) data[3]); + + } + + + /** + *

    + * Check that the UTF-16BE encoding omits the byte-order mark. + *

    + * + * @throws IOException + */ + public void testUTF16BEBOM() throws IOException { + + if (version >= 1.3) { + // UTF-16LE only supported in 1.3 and later + Serializer serializer = new Serializer(out, "UTF-16BE"); + serializer.write(doc); + serializer.flush(); + out.flush(); + out.close(); + byte[] data = out.toByteArray(); + assertEquals((byte) 0, data[0]); + assertEquals('<', (char) data[1]); + } + + } + + + public void testXMLSpaceDefault() throws IOException { + + root.addAttribute( + new Attribute( + "xml:space", + "http://www.w3.org/XML/1998/namespace", + "preserve")); + Element child1 = new Element("preserve"); + String value = + "This is a long sentence with plenty of opportunities for " + + "breaking from beginning to end."; + child1.appendChild(value); + Element child2 = new Element("default"); + root.appendChild(child1); + root.appendChild(child2); + child2.addAttribute( + new Attribute( + "xml:space", + "http://www.w3.org/XML/1998/namespace", + "default")); + String value2 = + "This is another very long sentence with plenty" + + " of opportunities for breaking from beginning to end."; + child2.appendChild(value2); + + String value3 = + "This is still another very long sentence with plenty of " + + "opportunities for breaking from beginning to end."; + Element preserveAgain = new Element("test"); + preserveAgain.appendChild(value3); + child2.appendChild(preserveAgain); + preserveAgain.addAttribute( + new Attribute( + "xml:space", + "http://www.w3.org/XML/1998/namespace", + "preserve")); + + + Serializer serializer = new Serializer(out, "UTF-8"); + serializer.setMaxLength(20); + serializer.write(doc); + String result = out.toString("UTF-8"); + assertTrue(result.indexOf(value) > 0); + assertTrue(result.indexOf(value3) > 0); + assertEquals(-1, result.indexOf(value2)); + + } + + + public void testXMLSpacePreserveWithIndenting() + throws IOException { + + root.addAttribute( + new Attribute( + "xml:space", + "http://www.w3.org/XML/1998/namespace", + "preserve")); + root.appendChild(new Element("sameline")); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + Serializer serializer = new Serializer(out, "UTF-8"); + serializer.setIndent(4); + serializer.write(doc); + String result = out.toString("UTF-8"); + assertEquals("\r\n" + + "\r\n", + result); + + } + + + public void testXMLSpaceUnspecifiedValueWithIndenting() + throws IOException { + + root.addAttribute( + new Attribute( + "xml:space", + "http://www.w3.org/XML/1998/namespace", + "undefined")); + root.appendChild(new Element("sameline")); + Serializer serializer = new Serializer(out, "UTF-8"); + serializer.setIndent(4); + serializer.write(doc); + String result = out.toString("UTF-8"); + assertEquals("\r\n" + + "\r\n \r\n\r\n", + result); + + } + + + public void testXMLSpaceDefaultWithIndenting() throws IOException { + + root.addAttribute( + new Attribute( + "xml:space", + "http://www.w3.org/XML/1998/namespace", + "preserve")); + Element child = new Element("child"); + child.addAttribute( + new Attribute( + "xml:space", + "http://www.w3.org/XML/1998/namespace", + "default")); + root.appendChild(child); + Serializer serializer = new Serializer(out, "UTF-8"); + serializer.setIndent(4); + serializer.write(doc); + String result = out.toString("UTF-8"); + assertEquals("\r\n" + + "" + + "\r\n", + result); + + } + + + public void testXMLSpaceDefaultWithIndentingAndGrandchildren() + throws IOException { + + root.addAttribute( + new Attribute( + "xml:space", + "http://www.w3.org/XML/1998/namespace", + "preserve")); + Element child = new Element("child"); + child.addAttribute( + new Attribute( + "xml:space", + "http://www.w3.org/XML/1998/namespace", + "default")); + root.appendChild(child); + child.appendChild(new Element("differentLine")); + Serializer serializer = new Serializer(out, "UTF-8"); + serializer.setIndent(2); + serializer.write(doc); + String result = out.toString("UTF-8"); + assertEquals("\r\n" + + "" + + "\r\n \r\n" + + " \r\n", + result); + + } + + + public void testDontSerializeXMLNamespace() throws IOException { + + Element root + = new Element("html", "http://www.w3.org/1999/xhtml"); + root.addAttribute( + new Attribute( + "xml:lang", "http://www.w3.org/XML/1998/namespace", "en")); + Document doc = new Document(root); + Serializer serializer = new Serializer(out, "UTF-8"); + serializer.write(doc); + String result = out.toString("UTF-8"); + assertEquals(-1, result.indexOf("xmlns:xml")); + assertTrue(result.indexOf("xml:lang=") > 1); + + } + + public void testDontSerializeNoNamespace() throws IOException { + + Serializer serializer = new Serializer(out, "UTF-8"); + serializer.write(doc); + String result = out.toString("UTF-8"); + assertEquals(-1, result.indexOf("xmlns=")); + + } + + + public void testDefaultNamespace() throws IOException { + + Element root = new Element("root", "http://www.example.com"); + Document doc = new Document(root); + Serializer serializer = new Serializer(out, "UTF-8"); + serializer.write(doc); + String result = out.toString("UTF-8"); + assertTrue(result.indexOf("xmlns=") > 1); + assertTrue(result.indexOf("http://www.example.com") > 1); + + } + + + public void testEmptyElement() throws IOException { + + Serializer serializer = new Serializer(out, "UTF-8"); + serializer.write(doc); + String result = out.toString("UTF-8"); + assertEquals( + "\r\n\r\n", + result + ); + + } + + + public void testElementWithText() throws IOException { + + String data = " test \n\n \n \n hello again"; + root.appendChild(data); + Serializer serializer = new Serializer(out, "UTF-8"); + serializer.write(doc); + String result = out.toString("UTF-8"); + + assertEquals( + "\r\n" + + data + "\r\n", + result); + + } + + + public void testStaticElementWithText() + throws IOException { + + String data = " test \n\n \n \n hello again"; + root.appendChild(data); + Serializer serializer = new Serializer(out); + serializer.write(doc); + String result = out.toString("UTF-8"); + + assertEquals( + "\r\n" + + data + "\r\n", + result); + + } + + + public void testElementWithTextAndCarriageReturns() + throws IOException { + + String data = " test \r\n \n \r hello again"; + root.appendChild(data); + Serializer serializer = new Serializer(out, "UTF-8"); + serializer.write(doc); + String result = out.toString("UTF-8"); + + assertEquals( + "\r\n" + + " test \n \n hello again" + + "\r\n", + result); + + } + + + private void serializeParseAndCompare(Document doc) + throws IOException, ParsingException { + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + Serializer serializer = new Serializer(out, "UTF-8"); + serializer.write(doc); + String result = out.toString("UTF-8"); + + Document resultDoc = parser.build(result, null); + XOMTestCase.assertEquals(doc, resultDoc); + + setOutputStreamSerializeParseAndCompare(doc); + + } + + + private void setOutputStreamSerializeParseAndCompare(Document doc) + throws IOException, ParsingException { + + Serializer serializer = new Serializer(out); + serializer.write(doc); + ByteArrayOutputStream out2 = new ByteArrayOutputStream(); + serializer.setOutputStream(out2); + serializer.write(doc); + String result = out2.toString("UTF-8"); + + Document resultDoc = parser.build(result, null); + XOMTestCase.assertEquals(doc, resultDoc); + + } + + + public void testComment() + throws IOException, ParsingException { + + String data = " <>&&entity; test \n hello again"; + root.appendChild(new Comment(data)); + serializeParseAndCompare(doc); + + } + + + public void testProcessingInstruction() + throws IOException, ParsingException { + + String data = "<>&&entity; test \n hello again"; + root.appendChild(new ProcessingInstruction("target", data)); + serializeParseAndCompare(doc); + + } + + public void testBasicElementWithText() + throws IOException, ParsingException { + + String data = " test \n hello again"; + root.appendChild(data); + serializeParseAndCompare(doc); + + } + + + public void testAttributes() + throws IOException, ParsingException { + + root.addAttribute(new Attribute("test", "sadlkhasdk")); + String data = " test \n hello again"; + root.appendChild(data); + serializeParseAndCompare(doc); + + root.addAttribute(new Attribute("test2", "sadlkhasdk")); + serializeParseAndCompare(doc); + + root.addAttribute(new Attribute("test3", " s adl khasdk ")); + serializeParseAndCompare(doc); + + root.addAttribute(new Attribute("xlink:type", + "http://www.w3.org/2001/xlink", " s adl khasdk ")); + serializeParseAndCompare(doc); + + } + + + public void testChildElements() + throws IOException, ParsingException { + + serializeParseAndCompare(doc); + + Element child1 = new Element("child"); + Element child2 = new Element("child"); + Element child3 = new Element("child"); + serializeParseAndCompare(doc); + root.appendChild(child1); + serializeParseAndCompare(doc); + child1.appendChild(child2); + serializeParseAndCompare(doc); + root.appendChild(child3); + serializeParseAndCompare(doc); + child3.appendChild("some data"); + serializeParseAndCompare(doc); + child2.appendChild("\nsome data with \n line breaks\n"); + serializeParseAndCompare(doc); + root.insertChild("now let's have some mixed content", 0); + serializeParseAndCompare(doc); + + root.setNamespaceURI("http://www.example.org/"); + serializeParseAndCompare(doc); + child1.setNamespaceURI("http://www.example.org/"); + serializeParseAndCompare(doc); + child2.setNamespaceURI("http://www.example.org/"); + serializeParseAndCompare(doc); + child3.setNamespaceURI("http://www.example.org/"); + serializeParseAndCompare(doc); + child1.setNamespacePrefix("example"); + serializeParseAndCompare(doc); + child2.setNamespacePrefix("perverse"); + serializeParseAndCompare(doc); + + } + + + public void testChildElementInNoNamespaceParentInDefaultNamespace() + throws IOException, ParsingException { + + Element root = new Element("root", "http://www.example.org/"); + Element child1 = new Element("child"); + root.appendChild(child1); + Document doc = new Document(root); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + Serializer serializer = new Serializer(out); + serializer.write(doc); + serializer.flush(); + String s = out.toString("UTF-8"); + assertTrue(s.indexOf("xmlns=\"\"") > 0); + + } + + + public void testSimpleSerialize() + throws IOException, ParsingException { + + Element root = new Element("root"); + Document doc = new Document(root); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + Serializer serializer = new Serializer(out); + serializer.write(doc); + serializer.flush(); + String s = out.toString("UTF-8"); + assertEquals("\r\n\r\n", s); + + } + + + public void testPrologAndEpilog() + throws IOException, ParsingException { + + serializeParseAndCompare(doc); + + doc.insertChild(new Comment("Hello"), 0); + serializeParseAndCompare(doc); + doc.insertChild(new DocType("root"), 0); + serializeParseAndCompare(doc); + doc.insertChild(new ProcessingInstruction("test", "some data"), + 0); + serializeParseAndCompare(doc); + doc.insertChild(new Comment("Goodbye"), 0); + serializeParseAndCompare(doc); + doc.insertChild( + new ProcessingInstruction("goodbye", "some data"), 0); + serializeParseAndCompare(doc); + doc.appendChild(new Comment("Hello")); + serializeParseAndCompare(doc); + doc.appendChild( + new ProcessingInstruction("test", "some data")); + serializeParseAndCompare(doc); + + } + + + public void testChangeLineSeparator() throws IOException { + + String breaks = + "This\nstring\rcontains\r\nseveral\r\rweird line breaks."; + root.appendChild(breaks); + root.addAttribute(new Attribute("test", breaks)); + + Serializer serializer = new Serializer(out, "UTF-8"); + serializer.setLineSeparator("\n"); + serializer.write(doc); + String result = out.toString("UTF-8"); + assertTrue(result.indexOf('\n') > 0); + assertTrue(result + "**\n" + result.indexOf('\r'), + result.indexOf('\r') == -1); + + out = new ByteArrayOutputStream(); + serializer = new Serializer(out, "UTF-8"); + serializer.setLineSeparator("\r"); + serializer.write(doc); + result = out.toString("UTF-8"); + assertTrue(result.indexOf('\r') > 0); + assertTrue(result.indexOf('\n') == -1); + + out = new ByteArrayOutputStream(); + serializer = new Serializer(out, "UTF-8"); + serializer.setLineSeparator("\r\n"); + serializer.write(doc); + result = out.toString("UTF-8"); + assertTrue(result.indexOf("\r\n") > 0); + for (int i = 0; i < result.length(); i++) { + int c = result.charAt(i); + if (c == '\r') assertTrue(result.charAt(i+1) == '\n'); + } + + } + + + public void testSettingOutputStreamDoesNotSetLineSeparator() + throws IOException { + + root.appendChild("This\rstring"); + Serializer serializer = new Serializer(System.out, "UTF-8"); + + serializer.setOutputStream(out); + + serializer.write(doc); + String result = out.toString("UTF-8"); + assertTrue(result.indexOf("This string") > 0); + + } + + + public void testSettingOutputStreamDoesNotUnnecessarilyEscapeLineBreaks() + throws IOException { + + root.appendChild("This\rstring"); + Serializer serializer = new Serializer(System.out, "UTF-8"); + + serializer.setOutputStream(out); + serializer.setLineSeparator("\r\n"); + serializer.write(doc); + String result = out.toString("UTF-8"); + assertTrue(result.indexOf("This\r\nstring") > 0); + + } + + + public void testDontChangeLineSeparator() throws IOException { + + String breaks + = "This\nstring\rcontains\r\rseveral\n\nweird line breaks."; + String breaksHalfEscaped + = "This\nstring contains several" + + "\n\nweird line breaks."; + String breaksEscaped + = "This string contains several" + + " weird line breaks."; + root.appendChild(breaks); + + Serializer serializer = new Serializer(out, "UTF-8"); + serializer.write(doc); + String result = out.toString("UTF-8"); + assertTrue(result.indexOf(breaksHalfEscaped) > 0); + + root = new Element("root"); + doc = new Document(root); + root.addAttribute(new Attribute("test", breaks)); + + out = new ByteArrayOutputStream(); + serializer = new Serializer(out, "UTF-8"); + serializer.write(doc); + result = out.toString("UTF-8"); + assertTrue(result.indexOf(breaksEscaped) > 0); + + } + + + public void testPreserveBaseURI() throws IOException { + + Serializer serializer = new Serializer(out, "UTF-8"); + serializer.setPreserveBaseURI(true); + serializer.write(doc); + String result = out.toString("UTF-8"); + assertTrue(result.indexOf(" 1); + doc.setBaseURI("http://www.example.com/index.xml"); + serializer.write(doc); + result = out.toString("UTF-8"); + assertTrue(result.indexOf(" 1); + assertTrue(result.indexOf("xml:base=") > 1); + assertTrue( + result.indexOf("http://www.example.com/index.xml") > 1 + ); + + } + + + public void testPreserveBaseURIWithChildren() throws IOException { + + String base = "http://www.example.com/index.xml"; + root.setBaseURI(base); + Element child = new Element("child"); + child.setBaseURI(base); + root.appendChild(child); + Serializer serializer = new Serializer(out, "UTF-8"); + serializer.setPreserveBaseURI(true); + serializer.write(doc); + String result = out.toString("UTF-8"); + assertEquals("\r\n" + + "\r\n", result); + + } + + + public void testPreserveBaseURIDoesntOverrideXMLBase() + throws IOException { + + root.addAttribute(new Attribute("xml:base", + "http://www.w3.org/XML/1998/namespace", + "http://www.cafeconleche.org/")); + Serializer serializer = new Serializer(out, "UTF-8"); + serializer.setPreserveBaseURI(true); + serializer.write(doc); + String result = out.toString("UTF-8"); + assertTrue(result.indexOf(" 1); + doc.setBaseURI("http://www.example.com/index.xml"); + serializer.write(doc); + result = out.toString("UTF-8"); + assertTrue(result.indexOf(" 1); + assertTrue(result.indexOf("xml:base=") > 1); + assertTrue(result.indexOf("http://www.cafeconleche.org/") > 1); + assertEquals(-1, result.indexOf("http://www.example.com/index.xml")); + + } + + + public void testSetLineSeparator() { + + Serializer serializer = new Serializer(System.out); + + serializer.setLineSeparator("\r"); + assertEquals("\r", serializer.getLineSeparator()); + serializer.setLineSeparator("\n"); + assertEquals("\n", serializer.getLineSeparator()); + serializer.setLineSeparator("\r\n"); + assertEquals("\r\n", serializer.getLineSeparator()); + + try { + serializer.setLineSeparator("r"); + fail("Allowed illegal separator character"); + } + catch (IllegalArgumentException success) { + assertNotNull(success.getMessage()); + } + + try { + serializer.setLineSeparator("n"); + fail("Allowed illegal separator character"); + } + catch (IllegalArgumentException success) { + assertNotNull(success.getMessage()); + } + + try { + serializer.setLineSeparator(" "); + fail("Allowed illegal separator character"); + } + catch (IllegalArgumentException success) { + assertNotNull(success.getMessage()); + } + + try { + serializer.setLineSeparator("rn"); + fail("Allowed illegal separator character"); + } + catch (IllegalArgumentException success) { + assertNotNull(success.getMessage()); + } + + try { + serializer.setLineSeparator("<"); + fail("Allowed illegal separator character"); + } + catch (IllegalArgumentException success) { + assertNotNull(success.getMessage()); + } + + try { + serializer.setLineSeparator("\u0085"); + fail("Allowed NEL separator character"); + } + catch (IllegalArgumentException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testLowerLimitOfUnicodeInCharacterData() + throws IOException { + + root.appendChild("\uD800\uDC00"); + Serializer serializer = new Serializer(out, "ISO-8859-1"); + serializer.write(doc); + String result = out.toString("ISO-8859-1"); + assertTrue(result, result.indexOf("𐀀") > 12); + + } + + + public void testUpperLimitOfUnicodeInCharacterData() + throws IOException { + + root.appendChild("\uDBFF\uDFFD"); + Serializer serializer = new Serializer(out, "ISO-8859-1"); + serializer.write(doc); + String result = out.toString("ISO-8859-1"); + assertTrue(result, result.indexOf("􏿽") > 12); + + } + + + public void testSerializePlane1CharacterInAttributeValue() + throws IOException { + + root.addAttribute(new Attribute("test", "\uD834\uDD1E")); + Serializer serializer = new Serializer(out, "ISO-8859-1"); + serializer.write(doc); + String result = out.toString("ISO-8859-1"); + assertTrue(result, result.indexOf("𝄞") > 12); + + } + + public void testSerializePlane1CharacterInCharacterData() + throws IOException { + + root.appendChild("\uD834\uDD1E"); + Serializer serializer = new Serializer(out, "ISO-8859-1"); + serializer.write(doc); + String result = out.toString("ISO-8859-1"); + assertTrue(result, result.indexOf("𝄞") > 12); + + } + + + public void testSurrogatePairCountsAsOneCharacterForColumnCount() + throws IOException { + + Element root = new Element("r"); + root.appendChild("\uD834\uDD1E"); + Document doc = new Document(root); + Serializer serializer = new ColumnSerializer(out); + serializer.write(doc); + + } + + + private static class ColumnSerializer extends Serializer { + + + ColumnSerializer(OutputStream out) { + super(out); + } + + + public void write(Document doc) throws IOException { + + for (int i = 0; i < doc.getChildCount(); i++) { + writeChild(doc.getChild(i)); + } + super.flush(); + assertEquals(8, super.getColumnNumber()); + + } + + + } + + + public void testEscapeAttributeValue() throws IOException { + + root.addAttribute(new Attribute("test", "\u0110")); + Serializer serializer = new Serializer(out, "ISO-8859-1"); + serializer.write(doc); + String result = out.toString("ISO-8859-1"); + assertTrue(result, result.indexOf("Đ") > 5); + + } + + + public void testLineFeedInAttributeValueWithDefaultOptions() + throws IOException, ParsingException { + + root.addAttribute(new Attribute("test", "\n")); + Serializer serializer = new Serializer(out, "ISO-8859-1"); + serializer.write(doc); + out.close(); + InputStream in = new ByteArrayInputStream(out.toByteArray()); + Document reparsed = parser.build(in); + assertEquals(doc, reparsed); + + } + + + public void testCarriageReturnInAttributeValueWithDefaultOptions() + throws IOException, ParsingException { + + root.addAttribute(new Attribute("test", "\r")); + Serializer serializer = new Serializer(out, "ISO-8859-1"); + serializer.write(doc); + out.close(); + InputStream in = new ByteArrayInputStream(out.toByteArray()); + Document reparsed = parser.build(in); + assertEquals(doc, reparsed); + + } + + + public void testCarriageReturnInTextWithDefaultOptions() + throws IOException, ParsingException { + + root.appendChild("\r"); + Serializer serializer = new Serializer(out, "ISO-8859-1"); + serializer.write(doc); + out.close(); + InputStream in = new ByteArrayInputStream(out.toByteArray()); + Document reparsed = parser.build(in); + assertEquals(doc, reparsed); + + } + + + public void testTabInAttributeValueWithDefaultOptions() + throws IOException, ParsingException { + + root.addAttribute(new Attribute("test", "\t")); + Serializer serializer = new Serializer(out, "ISO-8859-1"); + serializer.write(doc); + out.close(); + InputStream in = new ByteArrayInputStream(out.toByteArray()); + Document reparsed = parser.build(in); + assertEquals(doc, reparsed); + + } + + + /** + *

    + * Test that tabs in attribute values are escaped even when + * a line separator is set. + *

    + */ + public void testTabInAttributeValueWithLineSeparator() + throws IOException, ParsingException { + + root.addAttribute(new Attribute("test", "\t")); + Serializer serializer = new Serializer(out, "ISO-8859-1"); + serializer.setLineSeparator("\r"); + serializer.write(doc); + out.close(); + InputStream in = new ByteArrayInputStream(out.toByteArray()); + Document reparsed = parser.build(in); + assertEquals(doc, reparsed); + + } + + + + /** + *

    + * Test that tabs in attribute values are not escaped when + * indenting. + *

    + */ + public void testTabInAttributeValueWithIndenting() + throws IOException, ParsingException { + + root.addAttribute(new Attribute("test", "\t")); + Serializer serializer = new Serializer(out, "ISO-8859-1"); + serializer.setIndent(2); + serializer.write(doc); + out.close(); + InputStream in = new ByteArrayInputStream(out.toByteArray()); + Document reparsed = parser.build(in); + String result = reparsed.getRootElement().getAttributeValue("test"); + assertEquals("Tab not normalized to space", " ", result); + + } + + + public void testCRLFInAttributeValueWithLineSeparatorCR() + throws IOException, ParsingException { + + root.addAttribute(new Attribute("test", "\r\n")); + Serializer serializer = new Serializer(out, "ISO-8859-1"); + serializer.setLineSeparator("\r"); + serializer.write(doc); + out.close(); + InputStream in = new ByteArrayInputStream(out.toByteArray()); + Document reparsed = parser.build(in); + String result = reparsed.getRootElement().getAttributeValue("test"); + assertEquals("\r", result); + + } + + + public void testCRLFInAttributeValueWithLineSeparatorLF() + throws IOException, ParsingException { + + root.addAttribute(new Attribute("test", "\r\n")); + Serializer serializer = new Serializer(out, "ISO-8859-1"); + serializer.setLineSeparator("\n"); + serializer.write(doc); + out.close(); + InputStream in = new ByteArrayInputStream(out.toByteArray()); + Document reparsed = parser.build(in); + String result = reparsed.getRootElement().getAttributeValue("test"); + assertEquals("\n", result); + + } + + public void testLFInAttributeValueWithLineSeparatorCRLF() + throws IOException, ParsingException { + + root.addAttribute(new Attribute("test", "\n")); + Serializer serializer = new Serializer(out, "ISO-8859-1"); + serializer.setLineSeparator("\r\n"); + serializer.write(doc); + out.close(); + InputStream in = new ByteArrayInputStream(out.toByteArray()); + Document reparsed = parser.build(in); + String result = reparsed.getRootElement().getAttributeValue("test"); + assertEquals("\r\n", result); + + } + + + public void testNotEscapeLinefeedInTextContent() + throws IOException { + + root.appendChild("\r\n"); + Serializer serializer = new Serializer(out, "ISO-8859-1"); + serializer.write(doc); + out.close(); + String result = new String(out.toByteArray(), "ISO-8859-1"); + assertEquals( + "\r\n \n\r\n", + result + ); + + } + + + public void testCRLFInAttributeValue() + throws IOException, ParsingException { + + root.addAttribute(new Attribute("test", "a\r\na")); + Serializer serializer = new Serializer(out, "ISO-8859-1"); + serializer.write(doc); + out.close(); + InputStream in = new ByteArrayInputStream(out.toByteArray()); + Document reparsed = parser.build(in); + String result = reparsed.getRootElement().getAttributeValue("test"); + assertEquals("CRLF not escaped", "a\r\na", result); + + } + + + public void testPunctuationInAttributeValueNonUnicode() + throws IOException, ParsingException { + + root.addAttribute(new Attribute("test", "$()*+,=")); + Serializer serializer = new Serializer(out, "ISO-8859-1"); + serializer.write(doc); + out.close(); + InputStream in = new ByteArrayInputStream(out.toByteArray()); + Document reparsed = parser.build(in); + String result = reparsed.getRootElement().getAttributeValue("test"); + assertEquals("$()*+,=", result); + + } + + + public void testCRLFInAttributeValueWithIndenting() + throws IOException, ParsingException { + + root.addAttribute(new Attribute("test", "\r\n")); + Serializer serializer = new Serializer(out, "ISO-8859-1"); + serializer.setIndent(2); + serializer.write(doc); + out.close(); + InputStream in = new ByteArrayInputStream(out.toByteArray()); + Document reparsed = parser.build(in); + String result = reparsed.getRootElement().getAttributeValue("test"); + assertEquals("CRLF unnecessarily escaped", -1, result.indexOf('\r')); + // Need to figure out the serializer should indent this + // and write a unit test for that too. + + } + + + public void testCRLFInAttributeValueWithMaxLength() + throws IOException, ParsingException { + + root.addAttribute(new Attribute("test", "\r\n")); + + Serializer serializer = new Serializer(out, "ISO-8859-1"); + serializer.setMaxLength(64); + serializer.write(doc); + out.close(); + InputStream in = new ByteArrayInputStream(out.toByteArray()); + Document reparsed = parser.build(in); + String result = reparsed.getRootElement().getAttributeValue("test"); + assertEquals("CRLF unnecessarily escaped", " ", result); + + } + + + public void testCRInTextValueWithLineSeparator() + throws IOException, ParsingException { + + root.appendChild("\r"); + Serializer serializer = new Serializer(out, "ISO-8859-1"); + serializer.setLineSeparator("\n"); + serializer.write(doc); + out.close(); + InputStream in = new ByteArrayInputStream(out.toByteArray()); + Document reparsed = parser.build(in); + String result = reparsed.getValue(); + assertEquals("\n", result); + + } + + + public void testCRLFInTextValueWithLineSeparator() + throws IOException, ParsingException { + + root.appendChild("test \r\n test"); + Serializer serializer = new Serializer(out, "ISO-8859-1"); + serializer.setLineSeparator("\n"); + serializer.write(doc); + out.close(); + InputStream in = new ByteArrayInputStream(out.toByteArray()); + Document reparsed = parser.build(in); + String result = reparsed.getValue(); + assertEquals("test \n test", result); + + } + + + public void testCRInTextWithIndenting() + throws IOException, ParsingException { + + root.appendChild("\r"); + Serializer serializer = new Serializer(out, "ISO-8859-1"); + serializer.setIndent(2); + serializer.write(doc); + out.close(); + InputStream in = new ByteArrayInputStream(out.toByteArray()); + Document reparsed = parser.build(in); + String result = reparsed.getValue(); + assertEquals("Carriage return unnecessarily escaped", + -1, result.indexOf('\r')); + + // really need to think about what the serializer should output here + // and write a test case for that; this is not ideal output + + } + + + public void testCRInTextWithMaxLength() + throws IOException, ParsingException { + + root.appendChild("\r"); + Serializer serializer = new Serializer(out, "ISO-8859-1"); + serializer.setMaxLength(64); + serializer.write(doc); + out.close(); + InputStream in = new ByteArrayInputStream(out.toByteArray()); + Document reparsed = parser.build(in); + String result = reparsed.getValue(); + assertEquals("Carriage return unnecessarily escaped", "\n", result); + + } + + + public void testTabInAttributeValueWithMaxLength() + throws IOException, ParsingException { + + root.addAttribute(new Attribute("test", "\t")); + Serializer serializer = new Serializer(out, "ISO-8859-1"); + serializer.setMaxLength(64); + serializer.write(doc); + out.close(); + InputStream in = new ByteArrayInputStream(out.toByteArray()); + Document reparsed = parser.build(in); + String result = reparsed.getRootElement().getAttributeValue("test"); + assertEquals("Tab not normalized to space", " ", result); + + } + + + /** + *

    + * Test that tabs in attribute values are escaped when + * max length is set to 0 + *

    + */ + public void testTabInAttributeValueWithZeroMaxLength() + throws IOException, ParsingException { + + root.addAttribute(new Attribute("test", "\t")); + Serializer serializer = new Serializer(out, "ISO-8859-1"); + serializer.setMaxLength(0); + serializer.write(doc); + out.close(); + InputStream in = new ByteArrayInputStream(out.toByteArray()); + Document reparsed = parser.build(in); + String result = reparsed.getRootElement().getAttributeValue("test"); + assertEquals("Tab not normalized to space", "\t", result); + + } + + + public void testSetMaxLength() { + Serializer serializer = new Serializer(System.out); + + serializer.setMaxLength(72); + assertEquals(72, serializer.getMaxLength()); + serializer.setMaxLength(720); + assertEquals(720, serializer.getMaxLength()); + serializer.setMaxLength(1); + assertEquals(1, serializer.getMaxLength()); + serializer.setMaxLength(0); + assertEquals(0, serializer.getMaxLength()); + serializer.setMaxLength(-1); + assertEquals(0, serializer.getMaxLength()); + + } + + + public void testSetIndent() { + + Serializer serializer = new Serializer(System.out); + serializer.setIndent(72); + assertEquals(72, serializer.getIndent()); + serializer.setIndent(720); + assertEquals(720, serializer.getIndent()); + serializer.setIndent(1); + assertEquals(1, serializer.getIndent()); + serializer.setIndent(0); + assertEquals(0, serializer.getIndent()); + try { + serializer.setIndent(-1); + fail("Allowed negative indent"); + } + catch (IllegalArgumentException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testReallyBigIndent() throws ValidityException, ParsingException, IOException { + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + Serializer serializer = new Serializer(out); + serializer.setIndent(100); + StringBuffer spaces = new StringBuffer(400); + for (int i = 0; i < 400; i++) spaces.append(' '); + String _400 = spaces.toString(); + String _300 = spaces.substring(0, 300); + String _200 = spaces.substring(0, 200); + String _100 = spaces.substring(0, 100); + + String original ="Hello"; + Document doc = parser.build(original, null); + serializer.write(doc); + String expected ="\r\n\r\n" + + _100 + "\r\n" + _200 + "\r\n" + _300 + "\r\n" + _400 + + "Hello\r\n" + _300 + "\r\n" + _200 + "\r\n" + + _100 + "\r\n\r\n"; + String result = new String(out.toByteArray(), "UTF-8"); + assertEquals(expected, result); + + } + + + public void testLineLength() throws IOException { + + int length = 40; + String data = "This is a really long string that does not " + + "contain any line breaks. However, there is lots of " + + "white space so there shouldn't be any trouble wrapping it" + + " into 40 characters or less per line. "; + root.appendChild(data); + Serializer serializer = new Serializer(out, "UTF-8"); + serializer.setMaxLength(length); + serializer.write(doc); + String result = out.toString("UTF-8"); + + BufferedReader reader + = new BufferedReader(new StringReader(result)); + for (String line = reader.readLine(); + line != null; + line = reader.readLine()) { + assertTrue(line.length() + ": " + line, + line.length() <= length); + } + + } + + + public void testLineLengthWithSetOutputStream() + throws IOException { + + int length = 40; + String data = "This is a really long string that does not " + + "contain any line breaks. However, there is lots of " + + "white space so there shouldn't be any trouble wrapping it" + + " into 40 characters or less per line. "; + root.appendChild(data); + Serializer serializer = new Serializer(new ByteArrayOutputStream(), "UTF-8"); + serializer.setMaxLength(length); + serializer.write(doc); + serializer.setOutputStream(out); + serializer.write(doc); + String result = out.toString("UTF-8"); + + BufferedReader reader + = new BufferedReader(new StringReader(result)); + for (String line = reader.readLine(); + line != null; + line = reader.readLine()) { + assertTrue(line.length() + ": " + line, + line.length() <= length); + } + + } + + + public void testPrettyXML() throws IOException { + + Element items = new Element("itemSet"); + items.appendChild(new Element("item1")); + items.appendChild(new Element("item2")); + Document doc = new Document(items); + Serializer serializer = new Serializer(out); + serializer.setIndent(4); + serializer.write(doc); + serializer.flush(); + out.close(); + String result = new String(out.toByteArray(), "UTF-8"); + assertEquals( + "\r\n" + + "\r\n \r\n \r\n" + + "\r\n", + result + ); + + } + + + public void testIndentingFromSubclassThatAvoidsWriteElement() + throws IOException { + + Element items = new Element("itemSet"); + items.appendChild(new Element("item1")); + items.appendChild(new Element("item2")); + Document doc = new Document(items); + Serializer serializer = new AvoidWriteElement(out); + serializer.setIndent(4); + serializer.write(doc); + serializer.flush(); + out.close(); + String result = new String(out.toByteArray(), "UTF-8"); + assertEquals( + "\r\n" + + "\r\n \r\n \r\n" + + "\r\n", + result + ); + + } + + + public void testIndentingFromSubclassThatAvoidsWriteElementWithXMLSpace() + throws IOException { + + Element items = new Element("itemSet"); + items.addAttribute(new Attribute("xml:space", Namespace.XML_NAMESPACE, "preserve")); + items.appendChild(new Element("item1")); + items.appendChild(new Element("item2")); + Document doc = new Document(items); + Serializer serializer = new AvoidWriteElement(out); + serializer.setIndent(4); + serializer.write(doc); + serializer.flush(); + out.close(); + String result = new String(out.toByteArray(), "UTF-8"); + assertEquals( + "\r\n" + + "" + + "\r\n", + result + ); + + } + + + private static class AvoidWriteElement extends Serializer { + + public AvoidWriteElement(OutputStream out) { + super(out); + } + + protected void write(Element element) throws IOException { + + writeStartTag(element); + for (int i = 0; i < element.getChildCount(); i++) { + Node child = element.getChild(i); + writeChild(child); + } + writeEndTag(element); + + } + + } + + + public void testIndentAndBreakBeforeComment() throws IOException { + + Element items = new Element("itemSet"); + items.appendChild(new Comment("item1")); + Document doc = new Document(items); + Serializer serializer = new Serializer(out); + serializer.setIndent(4); + serializer.write(doc); + serializer.flush(); + out.close(); + String result = new String(out.toByteArray(), "UTF-8"); + assertEquals( + "\r\n" + + "\r\n \r\n" + + "\r\n", + result + ); + + } + + + public void testOnlyXMLSpaceCountsAsBoundaryWhiteSpace() + throws IOException { + + String emSpace = "\u2003"; + Element items = new Element("itemSet"); + items.appendChild(emSpace); + items.appendChild(new Element("a")); + + Document doc = new Document(items); + Serializer serializer = new Serializer(out); + serializer.setIndent(4); + serializer.write(doc); + serializer.flush(); + out.close(); + String result = new String(out.toByteArray(), "UTF-8"); + assertEquals( + "\r\n" + + "\u2003\r\n \r\n\r\n", + result + ); + + } + + + public void testWhiteSpaceBetweenCommentsIsBoundaryWhiteSpace() + throws IOException { + + Element items = new Element("itemSet"); + items.appendChild(new Comment("item1")); + items.appendChild(" \r\n "); + items.appendChild(new Comment("item2")); + Document doc = new Document(items); + Serializer serializer = new Serializer(out); + serializer.setIndent(4); + serializer.write(doc); + serializer.flush(); + out.close(); + String result = new String(out.toByteArray(), "UTF-8"); + assertEquals( + "\r\n" + + "\r\n \r\n \r\n" + + "\r\n", + result + ); + + } + + + public void testWhiteSpaceBeforeCommentIsBoundaryWhiteSpace() + throws IOException { + + Element items = new Element("itemSet"); + items.appendChild(" \r\n "); + items.appendChild(new Comment("item1")); + items.appendChild(new Comment("item2")); + Document doc = new Document(items); + Serializer serializer = new Serializer(out); + serializer.setIndent(4); + serializer.write(doc); + serializer.flush(); + out.close(); + String result = new String(out.toByteArray(), "UTF-8"); + assertEquals( + "\r\n" + + "\r\n \r\n \r\n" + + "\r\n", + result + ); + + } + + + public void testWhiteSpaceAfterCommentsIsBoundaryWhiteSpace() + throws IOException { + + Element items = new Element("itemSet"); + items.appendChild(new Comment("item1")); + items.appendChild(new Comment("item2")); + items.appendChild(" \r\n "); + Document doc = new Document(items); + Serializer serializer = new Serializer(out); + serializer.setIndent(4); + serializer.write(doc); + serializer.flush(); + out.close(); + String result = new String(out.toByteArray(), "UTF-8"); + assertEquals( + "\r\n" + + "\r\n \r\n \r\n" + + "\r\n", + result + ); + + } + + + public void testIndentAndBreakBeforeProcessingInstruction() + throws IOException { + + Element items = new Element("itemSet"); + items.appendChild(new ProcessingInstruction("target", "value")); + Document doc = new Document(items); + Serializer serializer = new Serializer(out); + serializer.setIndent(4); + serializer.write(doc); + serializer.flush(); + out.close(); + String result = new String(out.toByteArray(), "UTF-8"); + assertEquals( + "\r\n" + + "\r\n \r\n" + + "\r\n", + result + ); + + } + + + public void testDontBreakLineInElementWithSimpleContent() + throws IOException { + + Element items = new Element("itemSet"); + Element item1 = new Element("item1"); + items.appendChild(item1); + item1.appendChild("content"); + Document doc = new Document(items); + Serializer serializer = new Serializer(out); + serializer.setIndent(4); + serializer.write(doc); + serializer.flush(); + out.close(); + String result = new String(out.toByteArray(), "UTF-8"); + assertEquals( + "\r\n" + + "\r\n content\r\n" + + "\r\n", + result + ); + + } + + + public void testPrettyXMLWithSetOutputStream() throws IOException { + + Element items = new Element("itemSet"); + items.appendChild(new Element("item1")); + items.appendChild(new Element("item2")); + Document doc = new Document(items); + Serializer serializer = new Serializer(new ByteArrayOutputStream()); + serializer.setIndent(4); + serializer.setLineSeparator("\n"); + serializer.write(doc); + serializer.setOutputStream(out); + serializer.write(doc); + serializer.flush(); + out.close(); + String result = new String(out.toByteArray(), "UTF-8"); + assertEquals( + "\n" + + "\n \n \n" + + "\n", + result + ); + + } + + + public void testAmpersandAndLessThanInText() throws IOException { + + root.appendChild("data\r\n" + + "data<data&data" + + "\r\n", + result + ); + + } + + + public void testAmpersandAndAngleBracketsInAttributeValue() + throws IOException { + + root.addAttribute(new Attribute("b", "datadata&")); + Serializer serializer = new Serializer(out); + serializer.write(doc); + serializer.flush(); + out.close(); + String result = new String(out.toByteArray(), "UTF-8"); + assertEquals( + "\r\n" + + "\r\n", + result + ); + + } + + + public void testSetNFC() { + + Serializer serializer = new Serializer(System.out); + assertFalse(serializer.getUnicodeNormalizationFormC()); + serializer.setUnicodeNormalizationFormC(true); + assertTrue(serializer.getUnicodeNormalizationFormC()); + + } + + + public void testNFCInElementContent() throws IOException { + + Element root = new Element("a"); + root.appendChild("c\u0327"); // c with combining cedilla + Document doc = new Document(root); + Serializer serializer = new Serializer(out); + serializer.setUnicodeNormalizationFormC(true); + serializer.write(doc); + serializer.flush(); + out.close(); + String result = new String(out.toByteArray(), "UTF-8"); + assertEquals( + "\r\n" + + "\u00E7\r\n", + result + ); + + } + + + public void testNFCInConsecutiveTextNodes() throws IOException { + + Element root = new Element("a"); + root.appendChild("c"); + root.appendChild("\u0327"); // combining cedilla + Document doc = new Document(root); + Serializer serializer = new Serializer(out); + serializer.setUnicodeNormalizationFormC(true); + serializer.write(doc); + serializer.flush(); + out.close(); + String result = new String(out.toByteArray(), "UTF-8"); + assertEquals( + "\r\n" + + "\u00E7\r\n", + result + ); + + } + + + public void testNoNFCByDefault() throws IOException { + + Element root = new Element("c\u0327"); + root.appendChild("c\u0327"); // c with combining cedilla + Document doc = new Document(root); + Serializer serializer = new Serializer(out); + serializer.write(doc); + serializer.flush(); + out.close(); + String result = new String(out.toByteArray(), "UTF-8"); + assertEquals( + "\r\n" + + "c\u0327\r\n", + result + ); + + } + + + + public void testNFCInAttribute() throws IOException { + + root.addAttribute(new Attribute("c\u0327", "c\u0327")); // c with combining cedilla + Serializer serializer = new Serializer(out); + serializer.setUnicodeNormalizationFormC(true); + serializer.write(doc); + serializer.flush(); + out.close(); + String result = new String(out.toByteArray(), "UTF-8"); + assertEquals( + "\r\n" + + "\r\n", + result + ); + + } + + + public void testNFCInElementName() throws IOException { + + Element root = new Element("c\u0327"); // c with combining cedilla + Document doc = new Document(root); + Serializer serializer = new Serializer(out); + serializer.setUnicodeNormalizationFormC(true); + serializer.write(doc); + serializer.flush(); + out.close(); + String result = new String(out.toByteArray(), "UTF-8"); + assertEquals( + "\r\n" + + "<\u00E7/>\r\n", + result + ); + + } + + + public void testNFCInComment() throws IOException { + + Element root = new Element("a"); + Document doc = new Document(root); + doc.insertChild(new Comment("c\u0327hat"), 0); // c with combining cedilla + Serializer serializer = new Serializer(out); + serializer.setUnicodeNormalizationFormC(true); + serializer.write(doc); + serializer.flush(); + out.close(); + String result = new String(out.toByteArray(), "UTF-8"); + assertEquals( + "\r\n" + + "\r\n" + + "\r\n", + result + ); + + } + + + public void testNFCInProcessingInstruction() throws IOException { + + doc.appendChild(new ProcessingInstruction("c\u0327hat", "c\u0327hat")); + Serializer serializer = new Serializer(out); + serializer.setUnicodeNormalizationFormC(true); + serializer.write(doc); + serializer.flush(); + out.close(); + String result = new String(out.toByteArray(), "UTF-8"); + assertEquals( + "\r\n" + + "\r\n" + + "\r\n", + result + ); + + } + + + public void testNFCInElementContentWithNonUnicodeEncoding() + throws IOException { + + root.appendChild("c\u0327"); // c with combining cedilla + Serializer serializer = new Serializer(out, "ISO-8859-5"); + serializer.setUnicodeNormalizationFormC(true); + serializer.write(doc); + serializer.flush(); + out.close(); + String result = new String(out.toByteArray(), "ISO-8859-5"); + assertEquals( + "\r\n" + + "ç\r\n", + result + ); + + } + + + public void testNFCFollowingEntityReference() + throws IOException { + + root.appendChild("<\u0338"); // < followed by not + Serializer serializer = new Serializer(out); + serializer.setUnicodeNormalizationFormC(true); + serializer.write(doc); + serializer.flush(); + out.close(); + String result = new String(out.toByteArray(), "UTF-8"); + assertEquals( + "\r\n" + + "\u226E\r\n", + result + ); + + } + + + public void testNFCWithSetOutputStream() + throws IOException { + + root.appendChild("c\u0327"); // c with combining cedilla + Serializer serializer = new Serializer(new ByteArrayOutputStream(), "ISO-8859-5"); + serializer.setUnicodeNormalizationFormC(true); + serializer.write(doc); + serializer.setOutputStream(out); + serializer.write(doc); + serializer.flush(); + out.close(); + String result = new String(out.toByteArray(), "ISO-8859-5"); + assertEquals( + "\r\n" + + "ç\r\n", + result + ); + + } + + + public void testNFCWithKoreanCharacter() + throws IOException { + + root.appendChild("\u1111\u1171\u11B6"); // see p. 88 of Unicode 4.0 book + Serializer serializer = new Serializer(new ByteArrayOutputStream()); + serializer.setUnicodeNormalizationFormC(true); + serializer.write(doc); + serializer.setOutputStream(out); + serializer.write(doc); + serializer.flush(); + out.close(); + String result = new String(out.toByteArray(), "UTF-8"); + assertEquals( + "\r\n" + + "\uD4DB\r\n", + result + ); + + } + + + public void testNullOutputStream() { + + try { + new Serializer(null); + fail("Allowed null output stream"); + } + catch (NullPointerException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testNullOutputStreamWithEncoding() + throws UnsupportedEncodingException { + + try { + new Serializer(null, "UTF-8"); + fail("Allowed null output stream"); + } + catch (NullPointerException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testNullEncoding() + throws UnsupportedEncodingException { + + try { + new Serializer(System.out, null); + fail("Allowed null encoding"); + } + catch (NullPointerException success) { + assertNotNull(success.getMessage()); + } + + } + + + // make sure null pointer exception doesn't cause any output + public void testNullDocument() throws IOException { + + Serializer serializer = new Serializer(out, "UTF-16"); + try { + serializer.write(null); + fail("Wrote null document"); + } + catch (NullPointerException success) { + // success + } + byte[] result = out.toByteArray(); + assertEquals(0, result.length); + + } + + + public void testGetEncoding() + throws UnsupportedEncodingException { + + Serializer serializer = new Serializer(System.out, "ISO-8859-1"); + assertEquals("ISO-8859-1", serializer.getEncoding()); + + } + + + public void testGetPreserveBaseURI() + throws UnsupportedEncodingException { + + Serializer serializer = new Serializer(System.out, "ISO-8859-1"); + assertFalse(serializer.getPreserveBaseURI()); + serializer.setPreserveBaseURI(true); + assertTrue(serializer.getPreserveBaseURI()); + serializer.setPreserveBaseURI(false); + assertFalse(serializer.getPreserveBaseURI()); + + } + + + public void testSerializeDocTypeWithSystemID() + throws IOException { + + Serializer serializer = new Serializer(out); + Document doc = new Document(new Element("a")); + doc.setDocType(new DocType("b", "example.dtd")); + serializer.write(doc); + String result = out.toString("UTF-8"); + assertTrue(result.endsWith("\r\n")); + assertTrue(result.indexOf("") > 0); + + } + + + public void testSerializeDocTypeWithPublicAndSystemID() + throws IOException { + + Serializer serializer = new Serializer(out); + Document doc = new Document(new Element("a")); + doc.setDocType(new DocType("b", + "-//W3C//DTD XHTML 1.0 Transitional//EN", "example.dtd")); + serializer.write(doc); + String result = out.toString("UTF-8"); + assertTrue(result.endsWith("\r\n")); + assertTrue(result.indexOf( + "") > 0); + + } + + + public void testSerializeXMLNamespaceDeclaration() + throws ParsingException, IOException { + + Serializer serializer = new Serializer(out); + Element root = new Element("root"); + root.addNamespaceDeclaration("xml", Namespace.XML_NAMESPACE); + Document doc = new Document(root); + serializer.write(doc); + String result = out.toString("UTF-8"); + assertTrue(result.endsWith("\r\n")); + + } + + + public void testSerializeDocTypeWithInternalDTDSubset() + throws ParsingException, IOException { + + Serializer serializer = new Serializer(out); + String data = " ]>"; + Builder builder = new Builder(); + Document doc = builder.build(data, "http://www.example.com"); + serializer.write(doc); + String result = out.toString("UTF-8"); + assertTrue(result.endsWith("\r\n")); + assertTrue(result.indexOf(" 0); + assertTrue(result.indexOf("\r\n]>\r\n") > 0); + assertTrue(result.indexOf("\r\n") > 0); + + } + + + public void testSerializeInternalDTDSubsetContainingUnavailableCharacter() + throws ParsingException, IOException { + + Serializer serializer = new Serializer(out, "US-ASCII"); + String data = " " + + " " + + "]>"; + Builder builder = new Builder(); + Document doc = builder.build(data, "http://www.example.com"); + try { + serializer.write(doc); + fail("How'd you serialize é in ASCII?"); + } + catch (UnavailableCharacterException success) { + assertTrue(success.getMessage().indexOf("é (é)") > 1); + } + + } + + + public void testLineBreaksInInternalDTDSubset() + throws ParsingException, IOException { + + Serializer serializer = new Serializer(out); + serializer.setLineSeparator("\r"); + String data = " ]>"; + Builder builder = new Builder(); + Document doc = builder.build(data, "http://www.example.com"); + serializer.write(doc); + String result = out.toString("UTF-8"); + assertTrue(result.endsWith("\r")); + assertTrue(result.indexOf(" 0); + assertTrue(result.indexOf("\r]>\r") > 0); + assertTrue(result.indexOf("\r") > 0); + assertTrue(result.indexOf("\r") > 0); + assertEquals(-1, result.indexOf("\r\n")); + assertEquals(-1, result.indexOf("\r\n")); + assertEquals(-1, result.indexOf("\r\r")); + assertEquals(-1, result.indexOf("\r\r")); + assertEquals(-1, result.indexOf('\n')); + + } + + + public void testSerializeQuoteInAttributeValue() + throws IOException { + + Serializer serializer = new Serializer(out); + root.addAttribute(new Attribute("name", "\"")); + serializer.write(doc); + String result = out.toString("UTF-8"); + assertTrue(result.endsWith("\r\n")); + + } + + + public void testSerializeUnavailableCharacterInMarkup() + throws IOException { + + Serializer serializer = new Serializer(out, "ISO-8859-1"); + Element root = new Element("\u0419"); + Document doc = new Document(root); + try { + serializer.write(doc); + fail("Wrote bad character: " + out.toString("ISO-8859-1")); + } + catch (UnavailableCharacterException success) { + assertNotNull(success.getMessage()); + assertEquals('\u0419', success.getCharacter()); + assertEquals("ISO-8859-1", success.getEncoding()); + } + + } + + + public void testTurnLineFeedInAttributeValueIntoSpaceWhenIndenting() + throws IOException { + + Element root = new Element("a"); + root.appendChild("c"); + root.addAttribute(new Attribute("name", "value1\nvalue2")); + Document doc = new Document(root); + Serializer serializer = new Serializer(out); + serializer.setMaxLength(245); + serializer.setIndent(4); + serializer.write(doc); + serializer.flush(); + out.close(); + String result = new String(out.toByteArray(), "UTF-8"); + assertEquals( + "\r\n" + + "c\r\n", + result + ); + + } + + + public void testConflictBetweenMaxLengthAndIndent() + throws IOException { + + Element root = new Element("a"); + Element b = new Element("b"); + Element c = new Element("c"); + Element d = new Element("d"); + b.appendChild(c); + root.appendChild(b); + c.appendChild(d); + d.appendChild("data"); + Document doc = new Document(root); + Serializer serializer = new Serializer(out); + serializer.setMaxLength(16); + serializer.setIndent(4); + serializer.write(doc); + serializer.flush(); + out.close(); + String result = new String(out.toByteArray(), "UTF-8"); + assertEquals( + "\r\n" + + "\r\n \r\n \r\n data\r\n \r\n \r\n\r\n", + result + ); + + } + + + public void testWriteChild() throws IOException { + + ExposingSerializer serializer = new ExposingSerializer(out, "UTF-8"); + try { + serializer.writeChild(doc); + fail("writeChild wrote a document"); + } + catch (XMLException success) { + assertNotNull(success.getMessage()); + } + try { + serializer.writeChild(new Attribute("name", "value")); + fail("writeChild wrote an attribute"); + } + catch (XMLException success) { + assertNotNull(success.getMessage()); + } + + } + + + // just so we can test protected methods + private static class ExposingSerializer extends Serializer { + + ExposingSerializer(OutputStream out, String encoding) + throws UnsupportedEncodingException { + super(out, encoding); + } + + public void writeChild(Node node) throws IOException { + super.writeChild(node); + } + + public void exposedWriteRaw(String text) throws IOException { + writeRaw(text); + } + + public void exposedWriteEscaped(String text) throws IOException { + writeEscaped(text); + } + + public void exposedWriteAttributeValue(String text) throws IOException { + writeAttributeValue(text); + } + + public int exposeGetColumnNumber() { + return super.getColumnNumber(); + } + + } + + + private static class ElementSerializer extends Serializer { + + ElementSerializer(OutputStream out, String encoding) + throws UnsupportedEncodingException { + super(out, encoding); + } + + protected void write(Element element) throws IOException { + super.write(element); + } + + } + + + private static class TextSerializer extends Serializer { + + TextSerializer(OutputStream out) { + super(out); + } + + protected void write(Text text) throws IOException { + super.write(text); + } + + } + + + public void testWriteParentlessTextNode() throws IOException { + + Text t = new Text("Hello"); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + TextSerializer serializer = new TextSerializer(out); + serializer.write(t); + serializer.flush(); + byte[] result = out.toByteArray(); + String s = new String(result, "UTF-8"); + assertEquals(s, t.getValue()); + + } + + + public void testWriteParentlessTextNodeWhileIndenting() throws IOException { + + Text t = new Text("Hello"); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + TextSerializer serializer = new TextSerializer(out); + serializer.setIndent(2); + serializer.write(t); + serializer.flush(); + byte[] result = out.toByteArray(); + String s = new String(result, "UTF-8"); + assertEquals(s, t.getValue()); + + } + + + public void testWriteWhiteSpaceOnlyTextNodeWhileIndenting() throws IOException { + + Text t = new Text(" \r\n "); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + TextSerializer serializer = new TextSerializer(out); + serializer.setIndent(2); + serializer.write(t); + serializer.flush(); + byte[] result = out.toByteArray(); + assertEquals(0, result.length); + + } + + + public void testWriteRaw() throws IOException { + + ExposingSerializer serializer = new ExposingSerializer(out, "UTF-8"); + serializer.exposedWriteRaw("<>&\"'"); + assertEquals(5, serializer.exposeGetColumnNumber()); + serializer.flush(); + String result = out.toString("UTF-8"); + assertEquals("<>&\"'", result); + + } + + + public void testWriteParentlessElementInANamespace() throws IOException { + + ElementSerializer serializer = new ElementSerializer(out, "UTF-8"); + Element element = new Element("a", "http://www.example.org"); + serializer.write(element); + serializer.flush(); + String result = out.toString("UTF-8"); + assertEquals("", result); + + } + + + public void testWriteParentedElementInANamespace() throws IOException { + + ElementSerializer serializer = new ElementSerializer(out, "UTF-8"); + Element a = new Element("a", "http://www.example.org"); + Element b = new Element("b", "http://www.example.org"); + b.appendChild(a); + serializer.write(a); + serializer.flush(); + String result = out.toString("UTF-8"); + assertEquals("", result); + + } + + + public void testWriteEscaped() throws IOException { + + ExposingSerializer serializer = new ExposingSerializer(out, "UTF-8"); + serializer.exposedWriteEscaped("<>&\"'"); + assertEquals(15, serializer.exposeGetColumnNumber()); + serializer.flush(); + byte[] data = out.toByteArray(); + String result = new String(data, "UTF-8"); + assertEquals("<>&\"'", result); + + } + + + public void testWriteAttributeValue() throws IOException { + + ExposingSerializer serializer = new ExposingSerializer(out, "UTF-8"); + serializer.exposedWriteAttributeValue("<>&\"'"); + assertEquals(20, serializer.exposeGetColumnNumber()); + serializer.flush(); + String result = out.toString("UTF-8"); + assertEquals("<>&"'", result); + + } + + + // This test case reproduces a bug that + // showed up while working on SAX conformance testing. + public void testElementsThatOnlyContainsAnEmptyTextNodeShouldBeOutputWithAnEmptyElementTag() + throws IOException { + + Element child = new Element("b"); + child.appendChild(""); + root.appendChild(child); + + Serializer serializer = new Serializer(out); + serializer.write(doc); + + serializer.flush(); + String result = out.toString("UTF-8"); + assertEquals("\r\n" + + "\r\n", result); + + } + + + // This test case reproduces a bug that + // showed up while working on SAX conformance testing. + public void testElementsThatOnlyContainEmptyTextNodesShouldBeOutputWithAnEmptyElementTag() + throws IOException { + + Element root = new Element("a"); + Element child = new Element("b"); + child.appendChild(""); + child.appendChild(""); + child.appendChild(""); + root.appendChild(child); + + Document doc = new Document(root); + + Serializer serializer = new Serializer(out); + serializer.write(doc); + + serializer.flush(); + String result = out.toString("UTF-8"); + assertEquals("\r\n" + + "\r\n", result); + + } + + + // This test case reproduces a bug that + // showed up while working on SAX conformance testing. + public void testElementsThatOnlyContainASingleSpaceShouldNotBeSplitWhileIndenting() + throws IOException { + + Element root = new Element("a"); + Element child = new Element("b"); + child.appendChild(" "); + root.appendChild(child); + + Document doc = new Document(root); + + Serializer serializer = new Serializer(out); + serializer.setIndent(4); + serializer.write(doc); + + serializer.flush(); + String result = out.toString("UTF-8"); + assertEquals("\r\n" + + "\r\n \r\n\r\n", result); + + } + + + public void testElementsThatOnlyContainTextNodesWithBoundaryWhiteSpaceShouldNotBeSplitWhileIndenting() + throws IOException { + + Element root = new Element("a"); + Element child = new Element("b"); + child.appendChild(" "); + child.appendChild(" "); + root.appendChild(child); + + Document doc = new Document(root); + + Serializer serializer = new Serializer(out); + serializer.setIndent(4); + serializer.write(doc); + + serializer.flush(); + String result = out.toString("UTF-8"); + assertEquals("\r\n" + + "\r\n \r\n\r\n", result); + + } + + + public void testElementsThatOnlyContainASingleLinefeedShouldNotBeSplitWhileIndenting() + throws IOException { + + Element root = new Element("a"); + Element child = new Element("b"); + child.appendChild("\n"); + root.appendChild(child); + + Document doc = new Document(root); + + Serializer serializer = new Serializer(out); + serializer.setIndent(4); + serializer.write(doc); + + serializer.flush(); + String result = out.toString("UTF-8"); + assertEquals("\r\n" + + "\r\n \r\n\r\n", result); + + } + + + public void testEndTagsOfElementsWithContentGoOnSeparateLine() + throws ParsingException, IOException { + + Serializer serializer = new Serializer(out, "ISO-8859-1"); + serializer.setIndent(4); + serializer.setPreserveBaseURI(true); + serializer.flush(); + + File f = new File("data"); + f = new File(f, "prettyxml.xml"); + Builder builder = new Builder(); + Document doc = builder.build(f); + serializer.write(doc); + String result = out.toString("UTF-8"); + assertTrue(result.endsWith("\r\n\r\n")); + + } + + + public void testDontDoubleBreak() + throws ParsingException, IOException { + + Serializer serializer = new Serializer(out, "ISO-8859-1"); + serializer.setIndent(4); + serializer.setMaxLength(64); + + File f = new File("data"); + f = new File(f, "prettytest.xml"); + Builder builder = new Builder(); + Document doc = builder.build(f); + serializer.write(doc); + String result = out.toString("UTF-8"); + assertEquals("\r\n" + + "\r\n" + + " \r\n" + + "\r\n", result); + + } + + + public void testUnicodeNFCTestSuite() + throws ParsingException, IOException { + + Builder builder = new Builder(); + Document doc = builder.build("data/nfctests.xml"); + Elements tests = doc.getRootElement().getChildElements("test"); + int size = tests.size(); + for (int i = 0; i < size; i++) { + // System.out.println(i); + Element test = tests.get(i); + test.detach(); + Document testdoc = new Document(test); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + Serializer serializer = new Serializer(out); + serializer.setUnicodeNormalizationFormC(true); + serializer.write(testdoc); + serializer.flush(); + String result = new String(out.toByteArray(), "UTF-8"); + Document resultDoc = builder.build(result, null); + Element root = resultDoc.getRootElement(); + String c1 = root.getAttributeValue("c1"); + String c2 = root.getAttributeValue("c2"); + String c3 = root.getAttributeValue("c3"); + String c4 = root.getAttributeValue("c4"); + String c5 = root.getAttributeValue("c5"); + /* String v1 = root.getAttributeValue("v1"); + String v2 = root.getAttributeValue("v2"); + String v3 = root.getAttributeValue("v3"); + String v4 = root.getAttributeValue("v4"); + String v5 = root.getAttributeValue("v5"); */ + + assertEquals(root.getValue(), c1, c2); + assertEquals(c2, c3); + // I'm not sure the v's are correct past the BMP + //assertEquals(root.getValue(), c1, v1); + // assertEquals(c1, v2); + // assertEquals(c1, v3); + + assertEquals(c4, c5); + // assertEquals(c4, v4); + // assertEquals(c4, v5); + + } + + } + +} diff --git a/src/nu/xom/tests/SubclassTest.java b/src/nu/xom/tests/SubclassTest.java new file mode 100644 index 0000000..2a88287 --- /dev/null +++ b/src/nu/xom/tests/SubclassTest.java @@ -0,0 +1,207 @@ +/* Copyright 2003, 2004, 2019 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.tests; + +import nu.xom.Attribute; +import nu.xom.Comment; +import nu.xom.DocType; +import nu.xom.Document; +import nu.xom.Element; +import nu.xom.ProcessingInstruction; +import nu.xom.Text; + + +/** + *

    + * Tests for subclasses of XOM classes. + * This makes sure XOM is sufficiently polymorphic. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.3.0 + * + */ +public class SubclassTest extends XOMTestCase { + + private Element root; + private Document doc; + + + public SubclassTest(String name) { + super(name); + } + + + protected void setUp() { + root = new Element("root"); + doc = new Document(new ElementSubclass("root")); + } + + + public void testAttributeClassInCopy() { + root.addAttribute(new AttributeSubclass("name", "value")); + assertTrue(root.getAttribute(0) instanceof AttributeSubclass); + Element copy = (Element) root.copy(); + assertTrue(copy.getAttribute(0) instanceof AttributeSubclass); + } + + + private class AttributeSubclass extends Attribute { + + AttributeSubclass(String name, String value) { + super(name, value); + } + + public Attribute copy() { + return new AttributeSubclass(this.getQualifiedName(), this.getValue()); + } + + } + + + public void testTextClassInCopy() { + root.appendChild(new TextSubclass("value")); + assertTrue(root.getChild(0) instanceof TextSubclass); + Element copy = (Element) root.copy(); + assertTrue(copy.getChild(0) instanceof TextSubclass); + } + + + private class TextSubclass extends Text { + + TextSubclass(String value) { + super(value); + } + + public Text copy() { + return new TextSubclass(this.getValue()); + } + } + + + public void testElementClassInCopy() { + root.appendChild(new ElementSubclass("child")); + assertTrue(root.getChild(0) instanceof ElementSubclass); + Element copy = (Element) root.copy(); + assertTrue(copy.getChild(0) instanceof ElementSubclass); + } + + + private class ElementSubclass extends Element { + + ElementSubclass(String name) { + super(name); + } + + protected Element shallowCopy() { + return new ElementSubclass(this.getQualifiedName()); + } + + } + + + public void testCommentClassInCopy() { + root.appendChild(new CommentSubclass("value")); + assertTrue(root.getChild(0) instanceof CommentSubclass); + Element copy = (Element) root.copy(); + assertTrue(copy.getChild(0) instanceof CommentSubclass); + } + + + private class CommentSubclass extends Comment { + + CommentSubclass(String value) { + super(value); + } + + public Comment copy() { + return new CommentSubclass(this.getValue()); + } + + } + + + private class DocTypeSubclass extends DocType { + + DocTypeSubclass(String name) { + super(name); + } + + public DocType copy() { + return new DocTypeSubclass(this.getRootElementName()); + } + } + + + public void testProcessingInstructionClassInCopy() { + root.appendChild(new ProcessingInstructionSubclass("target", "value")); + assertTrue(root.getChild(0) instanceof ProcessingInstructionSubclass); + Element copy = (Element) root.copy(); + assertTrue(copy.getChild(0) instanceof ProcessingInstructionSubclass); + } + + + private class ProcessingInstructionSubclass extends ProcessingInstruction { + + ProcessingInstructionSubclass(String target, String data) { + super(target, data); + } + + public ProcessingInstructionSubclass copy() { + return new ProcessingInstructionSubclass(this.getTarget(), this.getValue()); + } + + } + + + public void testProcessingInstructionClassInDocCopy() { + doc.insertChild(new ProcessingInstructionSubclass("target", "value"), 0); + assertTrue(doc.getChild(0) instanceof ProcessingInstructionSubclass); + Document copy = (Document) doc.copy(); + assertTrue(copy.getChild(0) instanceof ProcessingInstructionSubclass); + } + + + public void testCommentClassInDocCopy() { + doc.insertChild(new CommentSubclass("target"), 0); + assertTrue(doc.getChild(0) instanceof CommentSubclass); + Document copy = (Document) doc.copy(); + assertTrue(copy.getChild(0) instanceof CommentSubclass); + } + + + public void testElementClassInDocCopy() { + assertTrue(doc.getChild(0) instanceof ElementSubclass); + Document copy = (Document) doc.copy(); + assertTrue(copy.getChild(0) instanceof ElementSubclass); + } + + + public void testDocTypeClassInDocCopy() { + doc.insertChild(new DocTypeSubclass("root"), 0); + assertTrue(doc.getChild(0) instanceof DocTypeSubclass); + Document copy = (Document) doc.copy(); + assertTrue(copy.getChild(0) instanceof DocTypeSubclass); + } + + +} diff --git a/src/nu/xom/tests/TextTest.java b/src/nu/xom/tests/TextTest.java new file mode 100644 index 0000000..dbe389c --- /dev/null +++ b/src/nu/xom/tests/TextTest.java @@ -0,0 +1,363 @@ +/* Copyright 2002-2006 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.tests; + +import nu.xom.Element; +import nu.xom.IllegalCharacterDataException; +import nu.xom.Text; + +/** + * + *

    + * Basic tests for the Text class. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.3.1 + * + */ +public class TextTest extends XOMTestCase { + + + public TextTest(String name) { + super(name); + } + + + public void testConstructor() { + Text a1 = new Text("test"); + assertEquals("test", a1.getValue()); + } + + + public void testSetter() { + + String[] legal = { + "Hello", + "hello there", + " spaces on both ends ", + " quotes \" \" quotes", + " single \'\' quotes", + " both double and single \"\'\"\' quotes", + " angle brackets < > <<<", + " carriage returns \r\r\r", + " CDATA end: ]]>", + " ", + " & ", + " ampersands & &&& &name; " + }; + + Text t = new Text("name"); + + // Things that shouldn't cause an exception + for (int i = 0; i < legal.length; i++) { + t.setValue(legal[i]); + assertEquals(legal[i], t.getValue()); + } + + t.setValue(null); + assertEquals("", t.getValue()); + + try { + t.setValue("test \u0000 test "); + fail("Should raise an IllegalCharacterDataException"); + } + catch (IllegalCharacterDataException success) { + assertEquals("test \u0000 test ", success.getData()); + assertNotNull(success.getMessage()); + } + + } + + + public void testToXML() { + + String[] easyCases = { + "Hello", + "hello there", + " spaces on both ends ", + " quotes \" \" quotes", + " single \'\' quotes", + " both double and single \"\'\"\' quotes" + }; + + Text t = new Text("name"); + + // Things that shouldn't cause an exception + for (int i = 0; i < easyCases.length; i++) { + t.setValue(easyCases[i]); + assertEquals(easyCases[i], t.toXML()); + } + + t.setValue("<>"); + assertEquals("<>", t.toXML()); + t.setValue("&"); + assertEquals("&amp;", t.toXML()); + t.setValue("]]>"); + assertEquals("]]>", t.toXML()); + t.setValue("\r"); + assertEquals(" ", t.toXML()); + + } + + + public void testPunctuationCharactersInToXML() { + + String data = "=,.!@#$%^*()_-\"'[]{}+/?;:`|\\"; + Text t = new Text(data); + assertEquals(data, t.toXML()); + + } + + + public void testEquals() { + + Text c1 = new Text("test"); + Text c2 = new Text("test"); + Text c3 = new Text("skjlchsakdjh"); + + assertEquals(c1, c1); + assertEquals(c1.hashCode(), c1.hashCode()); + assertFalse(c1.equals(c2)); + assertFalse(c1.equals(c3)); + + } + + + public void testCopy() { + + Text c1 = new Text("test"); + Text c2 = c1.copy(); + + assertEquals(c1.getValue(), c2.getValue()); + assertEquals(c1, c2); + assertFalse(c1.equals(c2)); + assertNull(c2.getParent()); + + } + + + public void testCopyisNotACDATASection() { + + Text c1 = new Text("test"); + Text c2 = c1.copy(); + assertEquals(Text.class, c2.getClass()); + + } + + + // Check passing in a string with broken surrogate pairs + // and with correct surrogate pairs + public void testSurrogates() { + + String goodString = "test: \uD8F5\uDF80 "; + Text c = new Text(goodString); + assertEquals(goodString, c.getValue()); + + // Two high-halves + try { + new Text("test: \uD8F5\uDBF0 "); + fail("Should raise an IllegalCharacterDataException"); + } + catch (IllegalCharacterDataException success) { + assertNotNull(success.getMessage()); + assertEquals("test: \uD8F5\uDBF0 ", success.getData()); + } + + // Two high-halves + try { + new Text("test: \uD8F5\uD8F5 "); + fail("Should raise an IllegalCharacterDataException"); + } + catch (IllegalCharacterDataException success) { + assertEquals("test: \uD8F5\uD8F5 ", success.getData()); + assertNotNull(success.getMessage()); + } + + // One high-half + try { + new Text("test: \uD8F5 "); + fail("Should raise an IllegalCharacterDataException"); + } + catch (IllegalCharacterDataException success) { + assertNotNull(success.getMessage()); + assertEquals("test: \uD8F5 ", success.getData()); + } + + // One low half + try { + new Text("test: \uDF80 "); + fail("Should raise an IllegalCharacterDataException"); + } + catch (IllegalCharacterDataException success) { + assertNotNull(success.getMessage()); + assertEquals("test: \uDF80 ", success.getData()); + } + + // Low half before high half + try { + new Text("test: \uDCF5\uD8F5 "); + fail("Should raise an IllegalCharacterDataException"); + } + catch (IllegalCharacterDataException success) { + assertEquals("test: \uDCF5\uD8F5 ", success.getData()); + assertNotNull(success.getMessage()); + } + + } + + + public void testNonBMPText() { + + StringBuffer sb = new StringBuffer(2); + for (char high = '\uD800'; high <= '\uDB7F'; high++) { + for (char low = '\uDC00'; low <= '\uDFFF'; low++) { + sb.setLength(0); + sb.append(high); + sb.append(low); + String s = sb.toString(); + Text t = new Text(s); + assertEquals(s, t.getValue()); + } + } + + } + + + public void testEndOfBMP() { + + try { + new Text("\uFFFE"); + fail("allowed FFFE"); + } + catch (IllegalCharacterDataException success) { + assertEquals("\uFFFE", success.getData()); + assertNotNull(success.getMessage()); + } + + try { + new Text("\uFFFF"); + fail("allowed FFFF"); + } + catch (IllegalCharacterDataException success) { + assertEquals("\uFFFF", success.getData()); + assertNotNull(success.getMessage()); + } + + } + + + public void testLeafNode() { + + Text c1 = new Text("data"); + assertEquals(0, c1.getChildCount()); + try { + c1.getChild(0); + fail("Didn't throw IndexOutofBoundsException"); + } + catch (IndexOutOfBoundsException success) { + // success + } + + assertNull(c1.getParent()); + + Element element = new Element("test"); + element.appendChild(c1); + assertEquals(element, c1.getParent()); + assertEquals(c1, element.getChild(0)); + + element.removeChild(c1); + assertEquals(0, element.getChildCount()); + + } + + + public void testToStringWithLineFeed() { + + Text t = new Text("content\ncontent"); + assertEquals("[nu.xom.Text: content\\ncontent]", t.toString()); + + } + + + public void testToStringWithCarriageReturn() { + + Text t = new Text("content\rcontent"); + assertEquals("[nu.xom.Text: content\\rcontent]", t.toString()); + + } + + + public void testToStringWithCarriageReturnLinefeed() { + + Text t = new Text("content\r\ncontent"); + assertEquals("[nu.xom.Text: content\\r\\ncontent]", t.toString()); + + } + + + public void testToStringWithTab() { + + Text t = new Text("content\tcontent"); + assertEquals("[nu.xom.Text: content\\tcontent]", t.toString()); + + } + + + public void testToString() { + + Text t = new Text("content"); + assertEquals("[nu.xom.Text: content]", t.toString()); + + t.setValue("012345678901234567890123456789012345678901234567890123456789"); + assertEquals( + "[nu.xom.Text: 01234567890123456789012345678901234...]", + t.toString() + ); + + } + + + // Make sure carriage returns are escaped properly by toXML() + public void testCarriageReturnInText() { + Text text = new Text("data\rdata"); + String xml = text.toXML(); + assertEquals("data data", xml); + } + + + public void testHighSurrogateWithNoLowSurrogate() { + + String data = String.valueOf((char) 0xD800); + try { + new Text(data); + fail("Allowed single high surrogate in text node"); + } + catch (IllegalCharacterDataException success) { + assertEquals(data, success.getData()); + assertNotNull(success.getMessage()); + } + + } + + +} diff --git a/src/nu/xom/tests/ValidityExceptionTest.java b/src/nu/xom/tests/ValidityExceptionTest.java new file mode 100644 index 0000000..c8849aa --- /dev/null +++ b/src/nu/xom/tests/ValidityExceptionTest.java @@ -0,0 +1,162 @@ +/* Copyright 2003, 2004 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.tests; + +import nu.xom.ParsingException; +import nu.xom.ValidityException; + +/** + *

    + * Unit tests for the ValidityException class. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.0 + * + */ +public class ValidityExceptionTest extends XOMTestCase { + + + private ValidityException ex = new ValidityException("message"); + private Exception cause = new Exception(); + private String message = "testing 1-2-3"; + + + public ValidityExceptionTest(String name) { + super(name); + } + + + public void testConstructor() { + ParsingException ex = new ValidityException(message, cause); + assertEquals(message, ex.getMessage()); + assertEquals(cause, ex.getCause()); + } + + + public void testFourArgumentConstructor() { + + ParsingException ex = new ValidityException(message, 10000, 40000, cause); + assertEquals(message, ex.getMessage()); + assertEquals(cause, ex.getCause()); + assertEquals(10000, ex.getLineNumber()); + assertEquals(40000, ex.getColumnNumber()); + + } + + + public void testAnotherFourArgumentConstructor() { + + ParsingException ex = new ValidityException( + message, "http://www.example.com/", 10000, 40000); + assertEquals(message, ex.getMessage()); + assertNull(ex.getCause()); + assertEquals(10000, ex.getLineNumber()); + assertEquals(40000, ex.getColumnNumber()); + assertEquals("http://www.example.com/", ex.getURI()); + + } + + + public void testLineAndColumnNumbers() { + + ValidityException ex = new ValidityException(message, 10, 20); + assertEquals(message, ex.getMessage()); + assertNull(ex.getCause()); + assertEquals(10, ex.getLineNumber()); + assertEquals(20, ex.getColumnNumber()); + + } + + + public void testInitCause() { + + assertNull(ex.getCause()); + ex.initCause(cause); + assertEquals(cause, ex.getCause()); + + try { + ex.initCause(null); + fail("Reinitialized cause over null"); + } + catch (IllegalStateException success) { + // success + } + + try { + ex.initCause(new Exception()); + fail("Reinitialized cause over null"); + } + catch (IllegalStateException success) { + // success + } + + } + + + public void testNullInitCause() { + + ValidityException ex = new ValidityException(null, null); + assertNull(ex.getCause()); + assertNull(ex.getMessage()); + assertEquals(-1, ex.getLineNumber()); + assertEquals(-1, ex.getColumnNumber()); + + try { + ex.initCause(new Exception()); + fail("Reinitialized cause over null"); + } + catch (IllegalStateException result) { + // success + } + + try { + ex.initCause(null); + fail("Reinitialized cause over null"); + } + catch (IllegalStateException result) { + // success + } + + } + + + public void testSelfCause() { + + try { + ex.initCause(ex); + fail("Allowed self-causation"); + } + catch (IllegalArgumentException result) { + // success + } + + } + + + public void testGetMessage() { + Exception ex = new ValidityException("testing"); + assertEquals("testing", ex.getMessage()); + } + + +} diff --git a/src/nu/xom/tests/VerifierTest.java b/src/nu/xom/tests/VerifierTest.java new file mode 100644 index 0000000..2531e9e --- /dev/null +++ b/src/nu/xom/tests/VerifierTest.java @@ -0,0 +1,1066 @@ +/* Copyright 2002-2005, 2018 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.tests; + +import nu.xom.Attribute; +import nu.xom.DocType; +import nu.xom.Element; +import nu.xom.IllegalDataException; +import nu.xom.IllegalNameException; +import nu.xom.MalformedURIException; +import nu.xom.Text; + +import org.apache.xerces.util.XMLChar; + +/** + *

    + * Tests to make sure name and character rules are enforced. + * The rules are tested by comparison with the rules in + * the org.apache.xerces.util.XMLChar class. + * This is an undocumented class so this is potentially dangerous + * in the long run. it also means the tests depend on Xerces 2 + * specifically. However, this dependence does not extend into the + * core API. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.2.11 + * + */ +public class VerifierTest extends XOMTestCase { + + private final static char[] subdelims = {'!', '$', '&', '\'', '(', ')' , '*', '+', ',', ';', '='}; + private final static char[] unreserved = {'-', '.', '_', '~'}; + private final static char[] unwise = {'{', '}', '|', '\\', '^', '[', ']', '`'}; + private final static char[] delims = {'<', '>', '#', '%', '^', '"'}; + + public VerifierTest(String name) { + super(name); + } + + + public void testElementNames() { + + for (char c = 0; c < 65535; c++) { + + // XXX remove dependence on this class by providing + // your own table of name characters as a config file for + // the tests + if (XMLChar.isNCNameStart(c)) { + String name = String.valueOf(c); + Element e = new Element(name); + assertEquals(name, e.getLocalName()); + } + else { + try { + new Element(String.valueOf(c)); + fail("Allowed illegal name start character " + + Integer.toHexString(c) + " in element name"); + } + catch (IllegalNameException success) { + assertNotNull(success.getMessage()); + } + } + + if (XMLChar.isNCName(c)) { + String name = "a" + c; + Element e = new Element(name); + assertEquals(name, e.getLocalName()); + } + else { + try { + new Element(String.valueOf(c)); + fail("Allowed illegal character " + + Integer.toHexString(c) + " in element name"); + } + catch (IllegalNameException success) { + assertNotNull(success.getMessage()); + assertEquals(String.valueOf(c), success.getData()); + } + } + + } + + } + + + // From IRI draft: + /* ucschar = %xA0-D7FF / %xF900-FDCF / %xFDF0-FFEF / + / %x10000-1FFFD / %x20000-2FFFD / %x30000-3FFFD + / %x40000-4FFFD / %x50000-5FFFD / %x60000-6FFFD + / %x70000-7FFFD / %x80000-8FFFD / %x90000-9FFFD + / %xA0000-AFFFD / %xB0000-BFFFD / %xC0000-CFFFD + / %xD0000-DFFFD / %xE1000-EFFFD */ + + // From RFC 2396 reallowed into IRIs + // "{" | "}" | "|" | "\" | "^" | "[" | "]" | "`" + public void testLegalIRIs() { + + int[] legalChars = { + '{', '}', '<', '>', '"', '|', '\\', '^', '`', '\u007F', + 0xA0, 0xD7FF, 0xF900, 0xFDCF, 0xFDF0, + 0xFFEF, 0x10000, 0x1FFFD, 0x20000, 0x2FFFD, 0x30000, + 0x3FFFD, 0x40000, 0x4FFFD, 0x50000, 0x5FFFD, 0x60000, + 0x6FFFD, 0x70000, 0x7FFFD, 0x80000, 0x8FFFD, 0x90000, + 0x9FFFD, 0xA0000, 0xAFFFD, 0xB0000, 0xBFFFD, 0xC0000, + 0xCFFFD, 0xD0000, 0xDFFFD, 0xE1000, 0xEFFFD, 0xCFFFD}; + + Element element = new Element("test"); + for (int i = 0; i < legalChars.length; i++) { + String utf16 = convertToUTF16(legalChars[i]); + String url = "http://www.example.com/" + utf16 + ".xml"; + element.addAttribute(new Attribute("xml:base", + "http://www.w3.org/XML/1998/namespace", url)); + assertEquals(url, element.getAttributeValue("base", + "http://www.w3.org/XML/1998/namespace")); + } + + } + + + public void testAllASCIILettersAllowedToBeginSchemeNames() { + + Element e = new Element("e"); + + for (char c = 'A'; c <= 'Z'; c++) { + String uri = c + "scheme:schemeSpecificData"; + e.setNamespaceURI(uri); + assertEquals(uri, e.getNamespaceURI()); + } + + for (char c = 'a'; c <= 'z'; c++) { + String uri = c + "scheme:schemeSpecificData"; + e.setNamespaceURI(uri); + assertEquals(uri, e.getNamespaceURI()); + } + + } + + + public void testAllASCIILettersAllowedInSchemeNames() { + + Element e = new Element("e"); + + for (char c = 'A'; c <= 'Z'; c++) { + String uri = "scheme" + c + ":schemeSpecificData"; + e.setNamespaceURI(uri); + assertEquals(uri, e.getNamespaceURI()); + } + + for (char c = 'a'; c <= 'z'; c++) { + String uri = "scheme" + c + ":schemeSpecificData"; + e.setNamespaceURI(uri); + assertEquals(uri, e.getNamespaceURI()); + } + + } + + + public void testAllASCIILettersAllowedInQueryStrings() { + + Element e = new Element("e"); + + for (char c = 'A'; c <= 'Z'; c++) { + String uri = "http://www.example.com/?name=" + c; + e.setNamespaceURI(uri); + assertEquals(uri, e.getNamespaceURI()); + } + + for (char c = 'a'; c <= 'z'; c++) { + String uri = "http://www.example.com/?name=" + c; + e.setNamespaceURI(uri); + assertEquals(uri, e.getNamespaceURI()); + } + + } + + + public void testAllASCIIDigitsAllowedInQueryStrings() { + + Element e = new Element("e"); + + for (char c = '0'; c <= '9'; c++) { + String uri = "http://www.example.com/?value=" + c; + e.setNamespaceURI(uri); + assertEquals(uri, e.getNamespaceURI()); + } + + } + + + public void testSlashAllowedInQueryString() { + + Element e = new Element("e"); + + String uri = "http://www.example.com/?path=/home/elharo/docs/"; + e.setNamespaceURI(uri); + assertEquals(uri, e.getNamespaceURI()); + + } + + + public void testQuestionMarkAllowedInQueryString() { + + Element e = new Element("e"); + + String uri = "http://www.example.com/?path=?home?elharo?docs?"; + e.setNamespaceURI(uri); + assertEquals(uri, e.getNamespaceURI()); + + } + + + public void testColonAllowedInQueryString() { + + Element e = new Element("e"); + + String uri = "http://www.example.com/?path=:home:elharo:docs:"; + e.setNamespaceURI(uri); + assertEquals(uri, e.getNamespaceURI()); + + } + + + public void testAtSignAllowedInQueryString() { + + Element e = new Element("e"); + + String uri = "http://www.example.com/?path=@home@elharo@docs@"; + e.setNamespaceURI(uri); + assertEquals(uri, e.getNamespaceURI()); + + } + + + public void testNonASCIICharactersNotAllowedInQueryStrings() { + + Element e = new Element("e"); + + for (char c = 128; c <= 1024; c++) { + String uri = "http://www.example.com/?value=" + c; + try { + e.setNamespaceURI(uri); + fail("Allowed unescaped non-ASCII character " + c + " in query string"); + } + catch (MalformedURIException success) { + assertEquals(uri, success.getData()); + } + } + + } + + + public void testDelimsNotAllowedInQueryStrings() { + + Element e = new Element("e"); + + for (int i = 0; i < delims.length; i++) { + String uri = "http://www.example.com/?value=" + delims[i] + "#Must_Use_Fragment_ID"; + try { + e.setNamespaceURI(uri); + fail("Allowed delimiter character " + delims[i] + " in query string"); + } + catch (MalformedURIException success) { + assertEquals(uri, success.getData()); + } + } + + } + + + public void testUnwiseCharactersNotAllowedInQueryStrings() { + + Element e = new Element("e"); + + for (int i = 0; i < unwise.length; i++) { + String uri = "http://www.example.com/?value=" + unwise[i]; + try { + e.setNamespaceURI(uri); + fail("Allowed unwise character " + unwise[i] + " in query string"); + } + catch (MalformedURIException success) { + assertEquals(uri, success.getData()); + } + } + + } + + + public void testUnwiseCharactersNotAllowedInUserInfo() { + + Element e = new Element("e"); + + for (int i = 0; i < unwise.length; i++) { + String uri = "http://user" + unwise[i] + "name@www.example.com/?value=" + unwise[i]; + try { + e.setNamespaceURI(uri); + fail("Allowed unwise character " + unwise[i] + " in user info"); + } + catch (MalformedURIException success) { + assertEquals(uri, success.getData()); + } + } + + } + + + public void testUnwiseCharactersNotAllowedInHost() { + + Element e = new Element("e"); + + for (int i = 0; i < unwise.length; i++) { + String uri = "http://u" + unwise[i] + "www.example.com/"; + try { + e.setNamespaceURI(uri); + fail("Allowed unwise character " + unwise[i] + " in host"); + } + catch (MalformedURIException success) { + assertEquals(uri, success.getData()); + } + } + + } + + + public void testDelimsNotAllowedInHost() { + + Element e = new Element("e"); + + for (int i = 0; i < delims.length; i++) { + String uri = "http://u" + delims[i] + "www.example.com/#value"; + try { + e.setNamespaceURI(uri); + fail("Allowed unwise character " + delims[i] + " in host"); + } + catch (MalformedURIException success) { + assertEquals(uri, success.getData()); + } + } + + } + + + public void testUnwiseCharactersNotAllowedInPath() { + + Element e = new Element("e"); + + for (int i = 0; i < unwise.length; i++) { + String uri = "http://www.example.com/path" + unwise[i] + "/path"; + try { + e.setNamespaceURI(uri); + fail("Allowed unwise character " + unwise[i] + " in path"); + } + catch (MalformedURIException success) { + assertEquals(uri, success.getData()); + } + } + + } + + + public void testAllASCIILettersAllowedInHostNames() { + + Element e = new Element("e"); + + for (char c = 'A'; c <= 'Z'; c++) { + String uri = "http://" + c + ".com/"; + e.setNamespaceURI(uri); + assertEquals(uri, e.getNamespaceURI()); + } + + for (char c = 'a'; c <= 'z'; c++) { + String uri = "http://" + c + ".com/"; + e.setNamespaceURI(uri); + assertEquals(uri, e.getNamespaceURI()); + } + + } + + + public void testAllASCIIDigitsAllowedInHostNames() { + + Element e = new Element("e"); + + for (char c = '0'; c <= '9'; c++) { + String uri = "http://c" + c + ".com/"; + e.setNamespaceURI(uri); + assertEquals(uri, e.getNamespaceURI()); + } + + } + + + public void testNonASCIICharactersNotAllowedInHostNames() { + + Element e = new Element("e"); + + for (char c = 128; c <= 1024; c++) { + String uri = "http://c" + c + ".com/"; + try { + e.setNamespaceURI(uri); + fail("Allowed unescaped non-ASCII character " + c + " in host name"); + } + catch (MalformedURIException success) { + assertEquals(uri, success.getData()); + } + } + + } + + + public void testAllASCIILettersAllowedInUserInfo() { + + Element e = new Element("e"); + + for (char c = 'A'; c <= 'Z'; c++) { + String uri = "http://" + c + "@c.com/"; + e.setNamespaceURI(uri); + assertEquals(uri, e.getNamespaceURI()); + } + + for (char c = 'a'; c <= 'z'; c++) { + String uri = "http://" + c + "@c.com/"; + e.setNamespaceURI(uri); + assertEquals(uri, e.getNamespaceURI()); + } + + } + + + public void testAllSubDelimsAllowedInUserInfo() { + + Element e = new Element("e"); + + for (int i = 0; i < subdelims.length; i++) { + String uri = "http://c" + subdelims[i] + "x@c.com/"; + e.setNamespaceURI(uri); + assertEquals(uri, e.getNamespaceURI()); + } + + } + + + public void testAllSubDelimsAllowedInPath() { + + Element e = new Element("e"); + + for (int i = 0; i < subdelims.length; i++) { + String uri = "http://cc.com/path" + subdelims[i] +".html"; + e.setNamespaceURI(uri); + assertEquals(uri, e.getNamespaceURI()); + } + + } + + + public void testAllUnreservedPunctuationMarksAllowedInUserInfo() { + + Element e = new Element("e"); + + for (int i = 0; i < unreserved.length; i++) { + String uri = "http://c" + unreserved[i] + "x@c.com/"; + e.setNamespaceURI(uri); + assertEquals(uri, e.getNamespaceURI()); + } + + } + + + public void testAllUnreservedPunctuationMarksAllowedInHost() { + + Element e = new Element("e"); + + for (int i = 0; i < unreserved.length; i++) { + String uri = "http://c" + unreserved[i] + "xc.com/"; + e.setNamespaceURI(uri); + assertEquals(uri, e.getNamespaceURI()); + } + + } + + + public void testAllSubDelimsAllowedInQueryString() { + + Element e = new Element("e"); + + for (int i = 0; i < subdelims.length; i++) { + String uri = "http://cx@c.com/?name=" + subdelims[i]; + e.setNamespaceURI(uri); + assertEquals(uri, e.getNamespaceURI()); + } + + } + + + public void testAllSubDelimsAllowedInHost() { + + Element e = new Element("e"); + + for (int i = 0; i < subdelims.length; i++) { + String uri = "http://cx" + subdelims[i] + "c.com/"; + e.setNamespaceURI(uri); + assertEquals(uri, e.getNamespaceURI()); + } + + } + + + public void testAllUnreservedPunctuationMarksAllowedInQueryString() { + + Element e = new Element("e"); + + for (int i = 0; i < unreserved.length; i++) { + String uri = "http://cx@c.com/?name=" + unreserved[i]; + e.setNamespaceURI(uri); + assertEquals(uri, e.getNamespaceURI()); + } + + } + + + public void testAllASCIIDigitsAllowedInUserInfo() { + + Element e = new Element("e"); + + for (char c = '0'; c <= '9'; c++) { + String uri = "http://" + c + "@c.com/"; + e.setNamespaceURI(uri); + assertEquals(uri, e.getNamespaceURI()); + } + + } + + + public void testNonASCIICharactersNotAllowedInUserInfo() { + + Element e = new Element("e"); + + for (char c = 128; c <= 1024; c++) { + String uri = "http://" + c + "@c.com/"; + try { + e.setNamespaceURI(uri); + fail("Allowed unescaped non-ASCII character " + c + " in user info"); + } + catch (MalformedURIException success) { + assertEquals(uri, success.getData()); + } + } + + } + + + public void testDelimCharactersNotAllowedInUserInfo() { + + Element e = new Element("e"); + + for (int i = 0; i < delims.length; i++) { + String uri = "http://c" + delims[i] + "c@c.com/?name=value#fragID"; + try { + e.setNamespaceURI(uri); + fail("Allowed delim character " + delims[i] + " in user info"); + } + catch (MalformedURIException success) { + assertEquals(uri, success.getData()); + } + } + + } + + + public void testMalformedURI() { + + Element e = new Element("e"); + + String uri = "http://c#c@c.com/?name=value#fragID"; + try { + e.setNamespaceURI(uri); + fail("Allowed http://c#c@c.com/?name=value#fragID as URI"); + } + catch (MalformedURIException success) { + assertEquals(uri, success.getData()); + } + + } + + + public void testFragmentIDContainsQuestionMark() { + + Element e = new Element("e"); + + String uri = "http://cc@c.com/?name=value#fragID?home/?elharo?"; + e.setNamespaceURI(uri); + assertEquals(uri, e.getNamespaceURI()); + + uri = "http://cc@c.com/#fragID?name=value"; + e.setNamespaceURI(uri); + assertEquals(uri, e.getNamespaceURI()); + + } + + + public void testFragmentIDContainsFirstColon() { + + Element e = new Element("e"); + + String uri = "http://c.com/#fragID:home"; + e.setNamespaceURI(uri); + assertEquals(uri, e.getNamespaceURI()); + + uri = "http://c.com/#fragID:home@eharo.com/somewhere"; + e.setNamespaceURI(uri); + assertEquals(uri, e.getNamespaceURI()); + + } + + + public void testEmptyHostAllowed() { + + Element e = new Element("e"); + + String uri = "scheme://elharo@:80/data"; + e.setNamespaceURI(uri); + assertEquals(uri, e.getNamespaceURI()); + + } + + + public void testC0ControlsNotAllowedInUserInfo() { + + Element e = new Element("e"); + + for (char c = 0; c <= ' '; c++) { + String uri = "http://" + c + "@c.com/"; + try { + e.setNamespaceURI(uri); + fail("Allowed C0 control 0x" + Integer.toHexString(c) + " in user info"); + } + catch (MalformedURIException success) { + assertEquals(uri, success.getData()); + } + } + + } + + + public void testC0ControlsNotAllowedInPath() { + + Element e = new Element("e"); + + for (char c = 0; c <= ' '; c++) { + String uri = "http://www.example.com/test/" + c + "data/"; + try { + e.setNamespaceURI(uri); + fail("Allowed C0 control 0x" + Integer.toHexString(c) + " in path"); + } + catch (MalformedURIException success) { + assertEquals(uri, success.getData()); + } + } + + } + + + public void testC0ControlsNotAllowedInQueryString() { + + Element e = new Element("e"); + + for (char c = 0; c <= ' '; c++) { + String uri = "http://www.c.com/?name=" + c + "&value=7"; + try { + e.setNamespaceURI(uri); + fail("Allowed C0 control 0x" + Integer.toHexString(c) + " in query string"); + } + catch (MalformedURIException success) { + assertEquals(uri, success.getData()); + } + } + + } + + + public void testHostNameTooLong() { + + StringBuffer uri = new StringBuffer("http://"); + for (int i = 0; i < 255; i++) uri.append('c'); + uri.append(".com/"); + Element e = new Element("e"); + try { + e.setNamespaceURI(uri.toString()); + fail("Allowed excessively long host name"); + } + catch (MalformedURIException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testSymbolsNotAllowedInSchemeNames() { + + Element e = new Element("e"); + + char[] disallowed = { ';', '@', '&', '=', '$', ',', '"', '?', '#', '/', '\\', '|', + '_', '!', '~', '*', '\'', '(', ')', '<', '>', '[', ']', '{', '}', '^', '`'}; + + for (int i = 0; i < disallowed.length; i++) { + String uri = "scheme" + disallowed[i] + ":schemeSpecificData"; + try { + e.setNamespaceURI(uri); + fail("allowed " + uri + " as namespace URI"); + } + catch (MalformedURIException success) { + assertEquals(uri, success.getData()); + } + } + + } + + + public void testNonASCIILettersNotAllowedToBeginSchemeNames() { + + Element e = new Element("e"); + + for (char c = 'Z' +1; c < 'a'; c++) { + String uri = c + "scheme:schemeSpecificData"; + try { + e.setNamespaceURI(uri); + fail("allowed " + uri + " as namespace URI"); + } + catch (MalformedURIException success) { + assertEquals(uri, success.getData()); + } + } + + } + + + public void testBadHexEscapeInQueryString() { + + Element e = new Element("e"); + + String uri = "scheme:schemeSpecificData?test%5test"; + try { + e.setNamespaceURI(uri); + fail("allowed " + uri + " as namespace URI"); + } + catch (MalformedURIException success) { + assertEquals(uri, success.getData()); + } + + uri = "scheme:schemeSpecificData?test%5"; + try { + e.setNamespaceURI(uri); + fail("allowed " + uri + " as namespace URI"); + } + catch (MalformedURIException success) { + assertEquals(uri, success.getData()); + } + + } + + + public void testHexEscapeInUserInfo() { + + Element e = new Element("e"); + + String uri = "scheme://user%C3%80TED@www.example.com/"; + e.setNamespaceURI(uri); + assertEquals(uri, e.getNamespaceURI()); + + } + + + public void testHexEscapeInHost() { + + Element e = new Element("e"); + + String uri = "scheme://user%C3%80www.example.com/"; + e.setNamespaceURI(uri); + assertEquals(uri, e.getNamespaceURI()); + + } + + + public void testBadHexEscapeInUserInfo() { + + Element e = new Element("e"); + + String uri = "scheme://user%5TED@www.example.com/"; + try { + e.setNamespaceURI(uri); + fail("allowed " + uri + " as namespace URI"); + } + catch (MalformedURIException success) { + assertEquals(uri, success.getData()); + } + + uri = "scheme://user%5@www.example.com/"; + try { + e.setNamespaceURI(uri); + fail("allowed " + uri + " as namespace URI"); + } + catch (MalformedURIException success) { + assertEquals(uri, success.getData()); + } + + } + + + public void testBadHexEscapeInHost() { + + Element e = new Element("e"); + + String uri = "scheme://user%5TEDwww.example.com/"; + try { + e.setNamespaceURI(uri); + fail("allowed " + uri + " as namespace URI"); + } + catch (MalformedURIException success) { + assertEquals(uri, success.getData()); + } + + uri = "scheme://www.example.co%5/"; + try { + e.setNamespaceURI(uri); + fail("allowed " + uri + " as namespace URI"); + } + catch (MalformedURIException success) { + assertEquals(uri, success.getData()); + } + + } + + + public void testQuestionmarkIsNotAHexDigit() { + + Element e = new Element("e"); + + // Have to do this in a fragment ID to keep it from being + // interpreted as a query string separator + String uri = "scheme://user@www.example.com/#fragment%?Adata"; + try { + e.setNamespaceURI(uri); + fail("allowed " + uri + " as namespace URI"); + } + catch (MalformedURIException success) { + assertEquals(uri, success.getData()); + } + + } + + + public void testIllegalIRIs() { + + int[] illegalChars = {0x00, 0xDC00, 0xE7FF, 0xF899, 0xD800, + 0xFDE0, 0xFFFF, 0x1FFFE, 0x2FFFE, 0x3FFFF, 0x4FFFE, + 0x4FFFF, 0x5FFFE, 0x6FFFF, 0x7FFFE, 0x8FFFF, 0x9FFFE, + 0xAFFFE, 0xBFFFF, 0xCFFFE, 0xDFFFE, 0xEFFFF, 0xFDDF}; + + for (int i = 0; i < illegalChars.length; i++) { + String utf16 = convertToUTF16(illegalChars[i]); + String url = "http://www.example.com/" + utf16 + ".xml"; + try { + new DocType("root", url); + fail("Allowed URL containing 0x" + + Integer.toHexString(illegalChars[i]).toUpperCase()); + } + catch (MalformedURIException success) { + assertNotNull(success.getMessage()); + assertEquals(url, success.getData()); + } + } + + } + + + public void testLegalIP6Addresses() { + + String[] addresses = { + "FEDC:BA98:7654:3210:FEDC:BA98:7654:3210", + "1080:0:0:0:8:800:200C:4171", + "3ffe:2a00:100:7031::1", + "1080::8:800:200C:417A", + "::192.9.5.5", + "::FFFF:129.144.52.38", + "2010:836B:4179::836B:4179", + "1080:0:0:0:8:800:200C:417A", + "FF01:0:0:0:0:0:0:101", + "0:0:0:0:0:0:0:1", + "0:0:0:0:0:0:0:0", + "1080::8:800:200C:417A", + "FF01::101", + "::1", + "::", + "0:0:0:0:0:0:13.1.68.3", + "0:0:0:0:0:FFFF:129.144.52.38", + "::13.1.68.3", + "::FFFF:129.144.52.38" + }; + + Element element = new Element("test"); + for (int i = 0; i < addresses.length; i++) { + String url = "http://[" + addresses[i] + "]/"; + element.addAttribute(new Attribute("xml:base", + "http://www.w3.org/XML/1998/namespace", url)); + assertEquals(url, element.getBaseURI()); + } + + } + + + public void testIllegalIP6Addresses() { + + String[] addresses = { + "FEDC:BA98:7654:3210:GEDC:BA98:7654:3 210", + "FEDC:BA98:7654:3210:FEDC:BA98:7654:3210:4352", + "FEDC:BA98:7654:3210:GEDC:BA98:7654:3210", + "FEDC:BA98:7654:3210:GEDC:BA98:7654:G210", + "FEDC:BA98:7654:3210:GEDC:BA98:7654: 3210", + "FEDC:BA98:7654:3210:GEDC:BA98:7654:+3210", + "FEDC:BA98:7654:3210:GEDC:BA98:7654:3210 ", + "FEDC:BA98:7654:3210:GEDC:BA98:7654:32 10", + "1080:0:::8:800:200C:4171", + "3ffe::100:7031::1", + "::192.9.5", + "::FFFF:129.144.52.38.56", + "::FFFF:129.144.52.A3", + "::FFFF:129.144.52.-22", + "::FFFF:129.144.52.+22", + "::FFFF:256.144.52.+22", + "::FFFF:www.apple.com", + "1080:0:0:0:8:800:-200C:417A", + "1080:0:0:0:-8:800:-200C:417A" + }; + + for (int i = 0; i < addresses.length; i++) { + String url = "http://[" + addresses[i] + "]/"; + try { + new DocType("root", url); + fail("Allowed illegal IPv6 address: " + addresses[i]); + } + catch (MalformedURIException success) { + assertNotNull(success.getMessage()); + assertTrue(success.getData().indexOf(addresses[i]) >= 0); + } + } + + } + + + // from Unicode FAQ + // http://www.unicode.org/faq/utf_bom.html#35 + private static int LEAD_OFFSET = 0xD800 - (0x10000 >> 10); + + private static String convertToUTF16(int c) { + + if (c <= 0xFFFF) return String.valueOf((char) c); + char high = (char) (LEAD_OFFSET + (c >> 10)); + char low = (char) (0xDC00 + (c & 0x3FF)); + StringBuffer sb = new StringBuffer(2); + sb.append(high); + sb.append(low); + return sb.toString().toLowerCase(); + + } + + + public void testC0Controls() { + + for (char c = 0; c < '\t'; c++) { + try { + new Text(String.valueOf(c)); + } + catch (IllegalDataException success) { + assertNotNull(success.getMessage()); + } + } + + for (char c = '\r'+1; c < ' '; c++) { + try { + new Text(String.valueOf(c)); + } + catch (IllegalDataException success) { + assertNotNull(success.getMessage()); + assertEquals(String.valueOf(c), success.getData()); + } + } + + } + + + public void testAttributeNameThatEndsWithAColon() { + + try { + new Attribute("name:", "http://www.example.com", "value", Attribute.Type.CDATA); + fail("Allowed attribute name that ends with a colon"); + } + catch (IllegalNameException success) { + assertNotNull(success.getMessage()); + assertEquals("name:", success.getData()); + } + + } + + + public void testAttributeNameThatBeginsWithAColon() { + + try { + new Attribute(":name", "http://www.example.com", "value", Attribute.Type.CDATA); + fail("Allowed attribute name that begins with a colon"); + } + catch (IllegalNameException success) { + assertNotNull(success.getMessage()); + assertEquals(":name", success.getData()); + } + + } + + + public void testElementNameThatEndsWithAColon() { + + try { + new Element("name:", "http://www.example.com"); + fail("Allowed element name that ends with a colon"); + } + catch (IllegalNameException success) { + assertNotNull(success.getMessage()); + assertEquals("name:", success.getData()); + } + + } + + + public void testElementNameThatBeginsWithAColon() { + + try { + new Element(":name", "http://www.example.com"); + fail("Allowed element name that begins with a colon"); + } + catch (IllegalNameException success) { + assertNotNull(success.getMessage()); + assertEquals(":name", success.getData()); + } + + } + + +} diff --git a/src/nu/xom/tests/XIncludeExceptionTest.java b/src/nu/xom/tests/XIncludeExceptionTest.java new file mode 100644 index 0000000..3c76c36 --- /dev/null +++ b/src/nu/xom/tests/XIncludeExceptionTest.java @@ -0,0 +1,167 @@ +/* Copyright 2003-2005 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.tests; + +import nu.xom.xinclude.BadEncodingAttributeException; +import nu.xom.xinclude.BadParseAttributeException; +import nu.xom.xinclude.MisplacedFallbackException; +import nu.xom.xinclude.NoIncludeLocationException; +import nu.xom.xinclude.XIncludeException; + +/** + *

    + * Unit tests for the XIncludeException class. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.1b3 + * + */ +public class XIncludeExceptionTest extends XOMTestCase { + + private XIncludeException ex; + private Exception cause; + + + public XIncludeExceptionTest(String name) { + super(name); + } + + + protected void setUp() { + ex = new XIncludeException("message"); + cause = new Exception(); + } + + + public void testConstructor() { + ex = new XIncludeException("test", "http://ex.com/"); + assertEquals("test", ex.getMessage()); + assertEquals("http://ex.com/", ex.getURI()); + } + + + public void testInitCause() { + + assertNull(ex.getCause()); + ex.initCause(cause); + assertEquals(cause, ex.getCause()); + + try { + ex.initCause(null); + fail("Reinitialized cause over null"); + } + catch (IllegalStateException result) { + // success + } + + try { + ex.initCause(new Exception()); + fail("Reinitialized cause over null"); + } + catch (IllegalStateException result) { + // success + } + + } + + + public void testNullInitCause() { + + XIncludeException ex + = new XIncludeException("message", (Exception) null); + assertNull(ex.getCause()); + + try { + ex.initCause(new Exception()); + fail("Reinitialized cause over null"); + } + catch (IllegalStateException result) { + // success + } + + try { + ex.initCause(null); + fail("Reinitialized cause over null"); + } + catch (IllegalStateException result) { + // success + } + + } + + + public void testSelfCause() { + + try { + ex.initCause(ex); + fail("Allowed self-causation"); + } + catch (IllegalArgumentException success) { + // success + } + + } + + + public void testGetMessage() { + Exception ex = new XIncludeException("testing"); + assertEquals("testing", ex.getMessage()); + } + + + public void testMisplacedFallbackException() { + String message = "message"; + Exception ex = new MisplacedFallbackException(message); + assertEquals(message, ex.getMessage()); + } + + + public void testBadParseAttributeException() { + String message = "message"; + Exception ex = new BadParseAttributeException(message); + assertEquals(message, ex.getMessage()); + } + + + public void testBadEncodingAttributeException() { + String message = "message"; + Exception ex = new BadEncodingAttributeException(message); + assertEquals(message, ex.getMessage()); + } + + + public void testNoIncludeLocationException() { + + String message = "message"; + XIncludeException ex = new NoIncludeLocationException(message); + assertEquals(message, ex.getMessage()); + assertNull(ex.getCause()); + + Exception cause = new Exception(); + ex = new NoIncludeLocationException(message, cause); + assertEquals(message, ex.getMessage()); + assertEquals(cause, ex.getCause()); + + } + +} diff --git a/src/nu/xom/tests/XIncludeTest.java b/src/nu/xom/tests/XIncludeTest.java new file mode 100644 index 0000000..c04ce18 --- /dev/null +++ b/src/nu/xom/tests/XIncludeTest.java @@ -0,0 +1,2478 @@ +/* Copyright 2002-2005, 2011, 2018 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.tests; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.io.PrintStream; +import java.io.Reader; +import java.io.StringReader; +import java.io.Writer; +import java.net.URI; +import java.net.URISyntaxException; + +import nu.xom.Attribute; +import nu.xom.Builder; +import nu.xom.Comment; +import nu.xom.Document; +import nu.xom.Element; +import nu.xom.Namespace; +import nu.xom.Node; +import nu.xom.NodeFactory; +import nu.xom.Nodes; +import nu.xom.ParsingException; +import nu.xom.Serializer; +import nu.xom.Text; +import nu.xom.xinclude.BadEncodingAttributeException; +import nu.xom.xinclude.BadHTTPHeaderException; +import nu.xom.xinclude.BadHrefAttributeException; +import nu.xom.xinclude.BadParseAttributeException; +import nu.xom.xinclude.InclusionLoopException; +import nu.xom.xinclude.NoIncludeLocationException; +import nu.xom.xinclude.XIncludeException; +import nu.xom.xinclude.XIncluder; + +/** + *

    + * Unit tests for the XInclude and XPointer engines. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.3.0 + * + */ +public class XIncludeTest extends XOMTestCase { + + private static boolean windows + = System.getProperty("os.name", "Unix").indexOf("Windows") >= 0; + + + public XIncludeTest(String name) { + super(name); + } + + + private Builder builder = new Builder(); + private File inputDir; + private File outputDir; + + // This class tests error conditions, which Xerces + // annoyingly logs to System.err. This hides System.err + // before each test and restores it after each test. + private PrintStream systemErr = System.err; + + + protected void setUp() { + + System.setErr(new PrintStream(new ByteArrayOutputStream())); + + inputDir = new File("data"); + inputDir = new File(inputDir, "xinclude"); + inputDir = new File(inputDir, "input"); + + outputDir = new File("data"); + outputDir = new File(outputDir, "xinclude"); + outputDir = new File(outputDir, "output"); + + } + + + protected void tearDown() { + System.setErr(systemErr); + } + + + private void dumpResult(File original, Document result) + throws IOException { + + String name = original.getName(); + File debug = new File("data"); + debug = new File(debug, "xinclude"); + debug = new File(debug, "debug"); + File output = new File(debug, name); + FileOutputStream out = new FileOutputStream(output); + Serializer serializer = new Serializer(out); + serializer.write(result); + + } + + + public void testXPointersResolvedAgainstAcquiredInfoset() + throws ParsingException, IOException, XIncludeException { + + File input = new File(inputDir, "tobintop.xml"); + Document doc = builder.build(input); + Document result = XIncluder.resolve(doc); + Document expected = builder.build( + new File(outputDir, "tobintop.xml") + ); + assertEquals(expected, result); + + } + + + public void testXMLBaseUsedToResolveHref() + throws ParsingException, IOException, XIncludeException { + + File input = new File(inputDir, "xmlbasetest.xml"); + Document doc = builder.build(input); + Document result = XIncluder.resolve(doc); + Document expected = builder.build( + new File(outputDir, "xmlbasetest.xml") + ); + assertEquals(expected, result); + + } + + + // Tests that use XPointer to + // grab a part of the document that contains an include element + // and make sure that's fully resolved too + public void testResolveThroughXPointer() + throws ParsingException, IOException, XIncludeException { + + File input = new File(inputDir, "resolvethruxpointer.xml"); + Document doc = builder.build(input); + Document result = XIncluder.resolve(doc); + Document expectedResult = builder.build( + new File(outputDir, "resolvethruxpointer.xml") + ); + assertEquals(expectedResult, result); + + } + + + public void testXMLBaseOnIncludeElementUsedToResolveHref() + throws ParsingException, IOException, XIncludeException { + + File input = new File(inputDir, "xmlbasetest2.xml"); + Document doc = builder.build(input); + Document result = XIncluder.resolve(doc); + Document expected = builder.build( + new File(outputDir, "xmlbasetest2.xml") + ); + assertEquals(expected, result); + + } + + + public void testXMLBaseRetainedFromUnincludedElement() + throws ParsingException, IOException, XIncludeException { + + File input = new File(inputDir, "xmlbasetest3.xml"); + Document doc = builder.build(input); + Document result = XIncluder.resolve(doc); + Document expected = builder.build( + new File(outputDir, "xmlbasetest3.xml") + ); + assertEquals(expected, result); + + } + + + public void testMarsh() + throws ParsingException, IOException, XIncludeException { + + File input = new File(inputDir, "marshtest.xml"); + Document doc = builder.build(input); + Document result = XIncluder.resolve(doc); + Document expectedResult = builder.build( + new File(outputDir, "marshtest.xml") + ); + assertEquals(expectedResult, result); + + } + + + public void testIncludeDocumentThatUsesIntradocumentReferences() + throws ParsingException, IOException, XIncludeException { + + File input = new File(inputDir, "includedocumentwithintradocumentreferences.xml"); + Document doc = builder.build(input); + Document result = XIncluder.resolve(doc); + Document expectedResult = builder.build( + new File(outputDir, "includedocumentwithintradocumentreferences.xml") + ); + assertEquals(expectedResult, result); + + } + + + public void testXMLLangAttributes() + throws ParsingException, IOException, XIncludeException { + + File input = new File(inputDir, "langtest1.xml"); + Document doc = builder.build(input); + Document result = XIncluder.resolve(doc); + Document expectedResult = builder.build( + new File(outputDir, "langtest1.xml") + ); + assertEquals(expectedResult, result); + + } + + + public void testInheritedXMLLangAttributes() + throws ParsingException, IOException, XIncludeException { + + File input = new File(inputDir, "langtest2.xml"); + Document doc = builder.build(input); + Document result = XIncluder.resolve(doc); + Document expectedResult = builder.build( + new File(outputDir, "langtest2.xml") + ); + assertEquals(expectedResult, result); + + } + + + public void testNoLanguageSpecified() + throws ParsingException, IOException, XIncludeException { + + File input = new File(inputDir, "langtest3.xml"); + Document doc = builder.build(input); + Document result = XIncluder.resolve(doc); + Document expectedResult = builder.build( + new File(outputDir, "langtest3.xml") + ); + assertEquals(expectedResult, result); + + } + + + // According to RFC 2396 empty string URI always refers to the + // current document irrespective of base URI + public void testXMLBaseNotUsedToResolveMissingHref() + throws ParsingException, IOException, XIncludeException { + + File input = new File(inputDir, "marshtestwithxmlbase.xml"); + Document doc = builder.build(input); + Document result = XIncluder.resolve(doc); + Document expectedResult = builder.build( + new File(outputDir, "marshtestwithxmlbase.xml") + ); + assertEquals(expectedResult, result); + + } + + + public void testEmptyHrefTreatedSameAsMissingHref() + throws ParsingException, IOException, XIncludeException { + + File input = new File(inputDir, "marshtestwithxmlbaseandemptyhref.xml"); + Document doc = builder.build(input); + Document result = XIncluder.resolve(doc); + Document expectedResult = builder.build( + new File(outputDir, "marshtestwithxmlbase.xml") + ); + assertEquals(expectedResult, result); + + } + + + public void testBaselessDocument() + throws IOException, ParsingException, XIncludeException { + + Element root = new Element("root"); + Element child1 = new Element("xi:include", XIncluder.XINCLUDE_NS); + child1.addAttribute(new Attribute("xpointer", "p1")); + Element child2 = new Element("child2"); + root.appendChild(child1); + root.appendChild(child2); + child2.addAttribute(new Attribute("id", "p1", Attribute.Type.ID)); + Document in = new Document(root); + Document out = XIncluder.resolve(in); + String result = out.toXML(); + assertEquals("\n" + + "\n", result); + + } + + + public void testRelativeURLInBaselessDocument() + throws IOException, ParsingException, XIncludeException { + + Element root = new Element("root"); + Element child1 = new Element("xi:include", XIncluder.XINCLUDE_NS); + child1.addAttribute(new Attribute("href", "test.xml")); + Element child2 = new Element("child2"); + root.appendChild(child1); + root.appendChild(child2); + Document in = new Document(root); + try { + XIncluder.resolve(in); + fail("Resolved relative URI in baseless document"); + } + catch (BadHrefAttributeException success) { + assertEquals( + "Could not resolve relative URI test.xml because the " + + "xi:include element does not have a base URI.", + success.getMessage()); + } + + } + + + public void testIncludeTextWithCustomNodeFactory() + throws ParsingException, IOException, XIncludeException { + + File input = new File(inputDir, "c2.xml"); + Builder builder = new Builder(new TextNodeFactory()); + Document doc = builder.build(input); + Document result = XIncluder.resolve(doc, builder); + Document expectedResult = builder.build( + new File(outputDir, "c2.xml") + ); + assertEquals(expectedResult, result); + Element root = result.getRootElement(); + for (int i = 0; i < root.getChildCount(); i++) { + Node node = root.getChild(i); + if (node instanceof Text) { + assertTrue(node instanceof TextSubclass); + } + } + + } + + + public void testParseEqualsTextWithNodeFactoryThatRemovesAllTextNodes() + throws ParsingException, IOException, XIncludeException { + + File input = new File(inputDir, "c2.xml"); + Document doc = builder.build(input); + Document result = XIncluder.resolve(doc, new Builder(new TextFilter())); + Document expectedResult = builder.build( + new File(outputDir, "c2a.xml") + ); + assertEquals(expectedResult, result); + + } + + + private static class TextFilter extends NodeFactory { + + public Nodes makeText(String data) { + return new Nodes(); + } + + } + + + public void testParseEqualsTextWithNodeFactoryThatReplacesTextNodesWithComments() + throws ParsingException, IOException, XIncludeException { + + File input = new File(inputDir, "c2.xml"); + Document doc = builder.build(input); + Document result = XIncluder.resolve(doc, new Builder(new TextToComment())); + Document expectedResult = builder.build( + new File(outputDir, "c2b.xml") + ); + assertEquals(expectedResult, result); + + } + + + private static class TextToComment extends NodeFactory { + + public Nodes makeText(String data) { + return new Nodes(new Comment(data)); + } + + } + + + public void testParseEqualsTextWithNodeFactoryThatReplacesTextNodesWithAttributes() + throws ParsingException, IOException, XIncludeException { + + File input = new File(inputDir, "c2.xml"); + Document doc = builder.build(input); + Document result = XIncluder.resolve(doc, new Builder(new TextToAttribute())); + Document expectedResult = builder.build( + new File(outputDir, "c2c.xml") + ); + assertEquals(expectedResult, result); + + } + + + public void testParseEqualsTextWithNodeFactoryThatReplacesTextNodesWithTwoElements() + throws ParsingException, IOException, XIncludeException { + + File input = new File(inputDir, "c2.xml"); + Document doc = builder.build(input); + Document result = XIncluder.resolve(doc, new Builder(new TextToElements())); + Document expectedResult = builder.build( + new File(outputDir, "c2d.xml") + ); + assertEquals(expectedResult, result); + + } + + + private static class TextToElements extends NodeFactory { + + public Nodes makeText(String data) { + Nodes result = new Nodes(); + result.append(new Element("empty1")); + result.append(new Element("empty2")); + return result; + } + + } + + + private static class TextToAttribute extends NodeFactory { + + public Nodes makeText(String data) { + return new Nodes(new Attribute("name", data)); + } + + } + + + public void testUnrecognizedXPointerScheme() + throws ParsingException, IOException { + + File input = new File(inputDir, "unrecognizedscheme.xml"); + Document doc = builder.build(input); + try { + XIncluder.resolve(doc); + fail("Allowed unrecognized scheme"); + } + catch (XIncludeException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testUnrecognizedXPointerSchemeWithFallback() + throws IOException, ParsingException, XIncludeException { + + File input = new File(inputDir, "unrecognizedschemewithfallback.xml"); + File output = new File(outputDir, "unrecognizedschemewithfallback.xml"); + Document doc = builder.build(input); + Document actual = XIncluder.resolve(doc); + Document expected = builder.build(output); + assertEquals(expected, actual); + + } + + + public void testIncludeTextWithCustomNodeFactoryThatChangesElementNames() + throws ParsingException, IOException, XIncludeException { + + File input = new File(inputDir, "c1.xml"); + Document doc = builder.build(input); + Document result = XIncluder.resolve(doc, new Builder(new NodeFactoryTest.CFactory())); + Document expectedResult = builder.build( + new File(outputDir, "c1a.xml") + ); + assertEquals(expectedResult, result); + + } + + + public void testIncludeTextWithCustomNodeFactoryThatOnlyReturnsRoot() + throws ParsingException, IOException, XIncludeException { + + File input = new File(inputDir, "c1.xml"); + Document doc = builder.build(input); + Document result = XIncluder.resolve(doc, new Builder(new NodeFactoryTest.MinimizingFactory())); + Document expectedResult = builder.build( + new File(outputDir, "c1b.xml") + ); + assertEquals(expectedResult, result); + + } + + + public void testIncludeTextWithCustomNodeFactoryThatFiltersElementsNamedB() + throws ParsingException, IOException, XIncludeException { + + File input = new File(inputDir, "d1.xml"); + Document doc = builder.build(input); + Document result = XIncluder.resolve(doc, new Builder(new NodeFactoryTest.BFilter())); + Document expectedResult = builder.build( + new File(outputDir, "d1.xml") + ); + assertEquals(expectedResult, result); + + } + + + public void testIncludeTextWithCustomNodeFactoryThatReturnsEachNonRootElementThreeTimes() + throws ParsingException, IOException, XIncludeException { + + File input = new File(inputDir, "c1.xml"); + Document doc = builder.build(input); + Document result = XIncluder.resolve(doc, + new Builder(new NodeFactoryTest.TripleElementFilter())); + Document expectedResult = builder.build( + new File(outputDir, "triple.xml") + ); + assertEquals(expectedResult, result); + + } + + + public void test1() + throws ParsingException, IOException, XIncludeException { + + File input = new File(inputDir, "test.xml"); + Document doc = builder.build(input); + Document result = XIncluder.resolve(doc); + Document expectedResult = builder.build( + new File(outputDir, "test.xml") + ); + assertEquals(expectedResult, result); + + } + + + public void testBaseURIsPreservedInSameDocumentInclusion() + throws ParsingException, IOException, XIncludeException { + + File input = new File(inputDir, "includefromsamedocumentwithbase.xml"); + Document doc = builder.build(input); + Document result = XIncluder.resolve(doc); + Document expectedResult = builder.build( + new File(outputDir, "includefromsamedocumentwithbase.xml") + ); + assertEquals(expectedResult, result); + + } + + + public void testBaseURIsPreservedInResultDocument() + throws ParsingException, IOException, XIncludeException { + + Document doc = new Document(new Element("root")); + doc.setBaseURI("http://www.example.org/"); + Document result = XIncluder.resolve(doc); + assertEquals("http://www.example.org/", result.getBaseURI()); + + } + + + /* public void testResolveNodes() + throws IOException, ParsingException, XIncludeException { + File dir = new File(inputDir, ""); + Element include = new Element("xi:include", XIncluder.XINCLUDE_NS); + include.setBaseURI(dir.toURL().toExternalForm()); + include.addAttribute(new Attribute("href", "disclaimer.xml")); + Nodes in = new Nodes(include); + Nodes out = XIncluder.resolve(in); + assertEquals(1, out.size()); + Element result = (Element) out.get(0); + assertEquals("disclaimer",result.getQualifiedName()); + } */ + + + public void testNullBaseURI() + throws ParsingException, IOException, XIncludeException { + + File input = new File(inputDir, "disclaimer.xml"); + String data = "" + + "\n

    120 Mz is adequate for an average home user.

    " + + "\n \n
    "; + Reader reader = new StringReader(data); + Document doc = builder.build(reader); + Document result = XIncluder.resolve(doc); + Document expectedResult = builder.build( + new File(outputDir, "c1.xml") + ); + assertEquals(expectedResult, result); + + } + + + public void testBadIRIIsAFatalError() + throws IOException, ParsingException, XIncludeException { + + String data = "" + + "" + + "Ooops!"; + Reader reader = new StringReader(data); + Document doc = builder.build(reader); + try { + XIncluder.resolve(doc); + fail("Resolved fallback when encountering a syntactically incorrect URI"); + } + catch (BadHrefAttributeException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testBadIRIWithUnrecognizedSchemeIsAFatalError() + throws IOException, ParsingException, XIncludeException { + + String data = "" + + "" + + "Ooops!"; + Reader reader = new StringReader(data); + Document doc = builder.build(reader); + try { + XIncluder.resolve(doc); + fail("Resolved fallback when encountering a syntactically incorrect URI"); + } + catch (BadHrefAttributeException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testGoodIRIWithUnrecognizedSchemeIsAResourceError() + throws IOException, ParsingException, XIncludeException { + + String data = "" + + "" + + "Correct!"; + Reader reader = new StringReader(data); + Document doc = builder.build(reader); + Document result = XIncluder.resolve(doc); + assertEquals("\n" + + "Correct!\n", + result.toXML()); + + } + + + public void testBadAcceptAttribute() + throws ParsingException, IOException, XIncludeException { + + String data = "" + + "\n

    120 MHz is adequate for an average home user.

    " + + "\n \n
    "; + Reader reader = new StringReader(data); + Document doc = builder.build(reader); + try { + XIncluder.resolve(doc); + fail("Allowed accept header containing carriage return linefeed"); + } + catch (BadHTTPHeaderException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testBadAcceptAttributeWithLatin1Character() + throws ParsingException, IOException, XIncludeException { + + String data = "" + + "\n

    120 MHz is adequate for an average home user.

    " + + "\n \n
    "; + Reader reader = new StringReader(data); + Document doc = builder.build(reader); + try { + XIncluder.resolve(doc); + fail("Allowed accept header containing non-ASCII character"); + } + catch (BadHTTPHeaderException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testUnrecognizedAttributesAreIgnored() + throws ParsingException, IOException, XIncludeException { + + File input = new File(inputDir, "extraattributes.xml"); + Document doc = builder.build(input); + Document result = XIncluder.resolve(doc); + Document expectedResult = builder.build( + new File(outputDir, "c1.xml") + ); + assertEquals(expectedResult, result); + + } + + + public void testEmptyFallback() + throws ParsingException, IOException, XIncludeException { + + File input = new File(inputDir, "emptyfallback.xml"); + Document doc = builder.build(input); + Document result = XIncluder.resolve(doc); + Document expectedResult = builder.build( + new File(outputDir, "emptyfallback.xml") + ); + assertEquals(expectedResult, result); + + } + + + public void testFallbackInIncludedDocument() + throws ParsingException, IOException, XIncludeException { + + File input = new File(inputDir, "metafallbacktest.xml"); + Document doc = builder.build(input); + Document result = XIncluder.resolve(doc); + Document expectedResult = builder.build( + new File(outputDir, "metafallbacktest.xml") + ); + assertEquals(expectedResult, result); + + } + + + public void testFallbackInIncludedDocumentUsesAnIntradocumentXPointer() + throws ParsingException, IOException, XIncludeException { + + File input = new File(inputDir, "metafallbacktest6.xml"); + Document doc = builder.build(input); + Document result = XIncluder.resolve(doc); + Document expectedResult = builder.build( + new File(outputDir, "metafallbacktest6.xml") + ); + assertEquals(expectedResult, result); + + } + + + // changed for b5 + public void testFallbackInIncludedDocumentIncludesADocumentWithParseEqualsText() + throws ParsingException, IOException, XIncludeException { + + File input = new File(inputDir, "metafallbacktest2.xml"); + Document doc = builder.build(input); + Document result = XIncluder.resolve(doc); + Document expectedResult = builder.build( + new File(outputDir, "metafallbacktest2.xml") + ); + assertEquals(expectedResult, result); + + } + + + public void testFallbackInIncludedDocumentWithBadParseAttribute() + throws ParsingException, IOException, XIncludeException { + + File input = new File(inputDir, "metafallbacktest3.xml"); + Document doc = builder.build(input); + try { + XIncluder.resolve(doc); + fail("Allowed bad parse attribute"); + } + catch (BadParseAttributeException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testFallbackInIncludedDocumentWithMissingHrefAndParseAttributes() + throws ParsingException, IOException, XIncludeException { + + File input = new File(inputDir, "metafallbacktest4.xml"); + Document doc = builder.build(input); + try { + XIncluder.resolve(doc); + fail("Allowed bad parse attribute"); + } + catch (NoIncludeLocationException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testFallbackInIncludedDocumentWithFragmentID() + throws ParsingException, IOException, XIncludeException { + + File input = new File(inputDir, "metafallbacktestwithfragmentid.xml"); + Document doc = builder.build(input); + try { + XIncluder.resolve(doc); + fail("Allowed document with fragment ID in href attribute"); + } + catch (BadHrefAttributeException success) { + assertNotNull(success.getMessage()); + } + + } + + + // changed in b5 + public void testXPointerIsNotResolvedAgainstTheSourceInfoset() + throws ParsingException, IOException, XIncludeException { + + File input = new File(inputDir, "metafallbacktest5.xml"); + Document doc = builder.build(input); + try { + XIncluder.resolve(doc); + fail("Allowed XPointer that doesn't resolve against the acquired infoset but does resolve against the source infoset"); + } + catch (XIncludeException ex) { + assertNotNull(ex.getMessage()); + } + + } + + + public void testFallbackInIncludedDocumentThatResolvesToNonElement() + throws ParsingException, IOException, XIncludeException { + + File input = new File(inputDir, "metafallbacktotexttest.xml"); + Document doc = builder.build(input); + Document result = XIncluder.resolve(doc); + Document expectedResult = builder.build( + new File(outputDir, "metafallbacktotexttest.xml") + ); + assertEquals(expectedResult, result); + + } + + + public void testFallbackInIncludedDocumentWithXPointer() + throws ParsingException, IOException, XIncludeException { + // This test case activates processFallbackSilently + File input = new File(inputDir, "metafallbacktestwithxpointer.xml"); + Document doc = builder.build(input); + Document result = XIncluder.resolve(doc); + Document expectedResult = builder.build( + new File(outputDir, "metafallbacktestwithxpointer.xml") + ); + assertEquals(expectedResult, result); + + } + + + // changed in b5 + // test case where fallback falls back to text and comments rather than + // an element + public void testFallbackInIncludedDocumentWithXPointer2() + throws ParsingException, IOException, XIncludeException { + + // This test case activates processFallbackSilently + File input = new File(inputDir, "metafallbacktestwithxpointer2.xml"); + Document doc = builder.build(input); + Document result = XIncluder.resolve(doc); + Document expectedResult = builder.build( + new File(outputDir, "metafallbacktestwithxpointer2.xml") + ); + assertEquals(expectedResult, result); + + } + + + public void testNoFallbackInIncludedDocumentWithXPointer() + throws ParsingException, IOException, XIncludeException { + + // This test case activates processFallbackSilently + File input = new File(inputDir, "metamissingfallbacktestwithxpointer.xml"); + Document doc = builder.build(input); + try { + XIncluder.resolve(doc); + fail("Should have thrown IOException"); + } + catch (IOException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testFallbackInIncludedDocumentHasBadXPointer() + throws ParsingException, IOException, XIncludeException { + // This test case activates processFallbackSilently + File input = new File(inputDir, "metafallbackwithbadxpointertest.xml"); + Document doc = builder.build(input); + try { + XIncluder.resolve(doc); + fail("Should have thrown XIncludeException"); + } + catch (XIncludeException success) { + assertNotNull(success.getMessage()); + assertNotNull(success.getCause()); + } + + } + + + // from the XInclude CR + public void testC1() + throws ParsingException, IOException, XIncludeException { + + File input = new File(inputDir, "c1.xml"); + Document doc = builder.build(input); + Document result = XIncluder.resolve(doc); + Document expectedResult = builder.build( + new File(outputDir, "c1.xml") + ); + assertEquals(expectedResult, result); + + } + + + public void testRelativeURLBaseURIFixup() + throws ParsingException, IOException, XIncludeException { + + File input = new File(inputDir, "relative.xml"); + Document doc = builder.build(input); + Document result = XIncluder.resolve(doc); + assertEquals(doc.getBaseURI(), result.getBaseURI()); + Document expectedResult = builder.build( + new File(outputDir, "relative.xml") + ); + assertEquals(expectedResult, result); + Element root = result.getRootElement(); + Element red = root.getFirstChildElement("red"); + String base = red.getAttributeValue("base", Namespace.XML_NAMESPACE); + assertEquals("basedata/red.xml", base); + + } + + + // same test with explicit parse="xml" + public void testParseEqualsXML() + throws ParsingException, IOException, XIncludeException { + + File input = new File(inputDir, "parseequalxml.xml"); + Document doc = builder.build(input); + Document result = XIncluder.resolve(doc); + Document expectedResult = builder.build( + new File(outputDir, "c1.xml") + ); + assertEquals(expectedResult, result); + + } + + + // changed in b5 + public void testAcceptableCirclePointer() + throws ParsingException, IOException, XIncludeException { + + File input = new File(inputDir, "legalcircle.xml"); + Document doc = builder.build(input); + try { + XIncluder.resolve(doc); + fail("Allowed circular reference"); + } + catch (InclusionLoopException success) { + assertNotNull(success.getMessage()); + } + + } + + + // from the XInclude CR + public void testC2() + throws ParsingException, IOException, XIncludeException { + + File input = new File(inputDir, "c2.xml"); + Document doc = builder.build(input); + Document result = XIncluder.resolve(doc); + Document expectedResult = builder.build( + new File(outputDir, "c2.xml") + ); + assertEquals(expectedResult, result); + + } + + + // from the XInclude CR + public void testC3() + throws ParsingException, IOException, XIncludeException { + + File input = new File(inputDir, "c3.xml"); + Document doc = builder.build(input); + Document result = XIncluder.resolve(doc); + Document expectedResult = builder.build( + new File(outputDir, "c3.xml") + ); + assertEquals(expectedResult, result); + + } + + + // C4 skipped for the moment because it uses XPointers + // that XOM doesn't yet support + + // from the XInclude CR + // Don't use this one yet, because there appear to be + // mistakes in the spec examples + /*public void testC5() throws ParsingException, IOException, XIncludeException { + + File input = new File(inputDir, "c5.xml"); + Document doc = builder.build(input); + Document result = XIncluder.resolve(doc); + Document expectedResult = builder.build(new File(outputDir, "c5.xml")); + XMLAssert.assertEquals(expectedResult, result); + + } */ + + + private static class TextNodeFactory extends NodeFactory { + + public Nodes makeText(String data) { + return new Nodes(new TextSubclass(data)); + } + + } + + private static class TextSubclass extends Text { + + TextSubclass(String data) { + super(data); + } + + public Text copy() { + return new TextSubclass(this.getValue()); + } + + } + + + public void testRecurseWithinSameDocument() + throws ParsingException, IOException, XIncludeException { + + File input = new File(inputDir, "recursewithinsamedocument.xml"); + Document doc = builder.build(input); + Document result = XIncluder.resolve(doc); + Document expectedResult = builder.build( + new File(outputDir, "recursewithinsamedocument.xml") + ); + assertEquals(expectedResult, result); + + } + + + public void testSiblingIncludes() + throws ParsingException, IOException, XIncludeException { + + File input = new File(inputDir, "paralleltest.xml"); + Document doc = builder.build(input); + Document result = XIncluder.resolve(doc); + Document expectedResult = builder.build( + new File(outputDir, "paralleltest.xml") + ); + assertEquals(expectedResult, result); + + } + + + public void testNamespaces() + throws ParsingException, IOException, XIncludeException { + + File input = new File(inputDir, "namespacetest.xml"); + Document doc = builder.build(input); + Document result = XIncluder.resolve(doc); + Document expectedResult = builder.build( + new File(outputDir, "namespacetest.xml") + ); + assertEquals(expectedResult, result); + + } + + + public void testIncludeReferencesItItself() + throws ParsingException, IOException, XIncludeException { + + File input = new File(inputDir, "internalcircular.xml"); + Document doc = builder.build(input); + try { + XIncluder.resolve(doc); + fail("Allowed include element to reference itself"); + } + catch (InclusionLoopException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testIncludeReferencesItsAncestor() + throws ParsingException, IOException, XIncludeException { + + File input = new File(inputDir, "internalcircularviaancestor.xml"); + Document doc = builder.build(input); + try { + XIncluder.resolve(doc); + fail("Allowed include element to reference its own ancestor"); + } + catch (InclusionLoopException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testNoInclusions() + throws ParsingException, IOException, XIncludeException { + + File input = new File(inputDir, "latin1.xml"); + Document doc = builder.build(input); + Document result = XIncluder.resolve(doc); + Document expectedResult = builder.build( + new File(outputDir, "latin1.xml") + ); + assertEquals(expectedResult, result); + + } + + + public void test2() + throws ParsingException, IOException, XIncludeException { + + File input = new File(inputDir, "simple.xml"); + Document doc = builder.build(input); + Document result = XIncluder.resolve(doc); + Document expectedResult = builder.build( + new File(outputDir, "simple.xml") + ); + assertEquals(expectedResult, result); + + } + + + public void testReplaceRoot() + throws ParsingException, IOException, XIncludeException { + + File input = new File(inputDir, "roottest.xml"); + Document doc = builder.build(input); + Document result = XIncluder.resolve(doc); + Document expectedResult = builder.build( + new File(outputDir, "roottest.xml") + ); + assertEquals(expectedResult, result); + + } + + + // In this test the included document has a prolog and an epilog + public void testReplaceRoot2() + throws ParsingException, IOException, XIncludeException { + + File input = new File(inputDir, "roottest2.xml"); + Document doc = builder.build(input); + Document result = XIncluder.resolve(doc); + Document expectedResult = builder.build( + new File(outputDir, "roottest2.xml") + ); + assertEquals(expectedResult, result); + + } + + + public void testIncludeElementsCannotHaveIncludeChildren() + throws ParsingException, IOException, XIncludeException { + File input = new File(inputDir, "nestedxinclude.xml"); + Document doc = builder.build(input); + try { + XIncluder.resolve(doc); + fail("allowed include element to contain another include element"); + } + catch (XIncludeException success) { + assertNotNull(success.getMessage()); + } + } + + + public void testIncludeElementsCannotHaveChildrenFromXIncludeNamespace() + throws ParsingException, IOException, XIncludeException { + File input = new File(inputDir, "nestedxincludenamespace.xml"); + Document doc = builder.build(input); + try { + XIncluder.resolve(doc); + fail("allowed include element to contain another include element"); + } + catch (XIncludeException success) { + assertNotNull(success.getMessage()); + } + } + + + public void testFallbackIsNotChildOfIncludeElement() + throws ParsingException, IOException, XIncludeException { + File input = new File(inputDir, "nakedfallback.xml"); + Document doc = builder.build(input); + try { + XIncluder.resolve(doc); + fail("allowed fallback that was not child of an include element"); + } + catch (XIncludeException success) { + assertNotNull(success.getMessage()); + } + } + + + public void testFallbackCantContainFallbackElement() + throws ParsingException, IOException, XIncludeException { + File input = new File(inputDir, "fallbackcontainsfallback.xml"); + Document doc = builder.build(input); + try { + XIncluder.resolve(doc); + fail("allowed fallback inside another fallback element"); + } + catch (XIncludeException success) { + assertNotNull(success.getMessage()); + } + } + + + // In this test the fallback is activated. + public void testMultipleFallbacks() + throws ParsingException, IOException, XIncludeException { + File input = new File(inputDir, "multiplefallbacks.xml"); + Document doc = builder.build(input); + try { + XIncluder.resolve(doc); + fail("allowed multiple fallback elements"); + } + catch (XIncludeException success) { + assertNotNull(success.getMessage()); + } + } + + + // In this test the fallback is not needed. + public void testMultipleFallbacks2() + throws ParsingException, IOException, XIncludeException { + File input = new File(inputDir, "multiplefallbacks2.xml"); + Document doc = builder.build(input); + try { + XIncluder.resolve(doc); + fail("allowed multiple fallback elements"); + } + catch (XIncludeException success) { + assertNotNull(success.getMessage()); + } + } + + + public void testDocumentIncludesItself() + throws ParsingException, IOException, XIncludeException { + File input = new File(inputDir, "circle1.xml"); + Document doc = builder.build(input); + try { + XIncluder.resolve(doc); + fail("allowed parsed include of self"); + } + catch (InclusionLoopException success) { + assertNotNull(success.getMessage()); + assertEquals(input.toURI().toString(), success.getURI()); + } + } + + + public void testInclusionLoopWithLength2Cycle() + throws ParsingException, IOException, XIncludeException { + + File input = new File(inputDir, "circle2a.xml"); + File errorFile = new File(inputDir, "circle2b.xml"); + Document doc = builder.build(input); + try { + XIncluder.resolve(doc); + fail("allowed circular include, cycle length 1"); + } + catch (InclusionLoopException success) { + assertTrue(success.getMessage().indexOf(errorFile.toURI().toString()) > 1); + assertTrue(success.getMessage().indexOf(input.toURI().toString()) > 1); + assertEquals(errorFile.toURI().toString(), success.getURI()); + } + + } + + + public void testMissingHref() + throws ParsingException, IOException, XIncludeException { + + File input = new File(inputDir, "missinghref.xml"); + Document doc = builder.build(input); + try { + XIncluder.resolve(doc); + fail("allowed missing href"); + } + catch (NoIncludeLocationException success) { + assertNotNull(success.getMessage()); + assertEquals(doc.getBaseURI(), success.getURI()); + } + + } + + + public void testBadParseAttribute() + throws ParsingException, IOException, XIncludeException, URISyntaxException { + + File input = new File(inputDir, "badparseattribute.xml"); + Document doc = builder.build(input); + try { + XIncluder.resolve(doc); + fail("allowed bad parse attribute"); + } + catch (BadParseAttributeException success) { + assertNotNull(success.getMessage()); + URI u1 = input.toURI(); + URI u2 = new URI(success.getURI()); + assertEquals(u1, u2); + } + + } + + + public void testUnavailableResource() + throws ParsingException, IOException, XIncludeException { + File input = new File(inputDir, "missingfile.xml"); + Document doc = builder.build(input); + try { + XIncluder.resolve(doc); + fail("allowed unresolvable resource"); + } + catch (IOException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testFallback() + throws ParsingException, IOException, XIncludeException { + + File input = new File(inputDir, "fallbacktest.xml"); + Document doc = builder.build(input); + Document result = XIncluder.resolve(doc); + Document expectedResult = builder.build( + new File(outputDir, "fallbacktest.xml") + ); + assertEquals(expectedResult, result); + + } + + + public void testFallbackWithRecursiveInclude() + throws ParsingException, IOException, XIncludeException { + + File input = new File(inputDir, "fallbacktest2.xml"); + Document doc = builder.build(input); + Document result = XIncluder.resolve(doc); + Document expectedResult = builder.build( + new File(outputDir, "fallbacktest2.xml") + ); + assertEquals(expectedResult, result); + + } + + + public void testEncodingAttribute() + throws ParsingException, IOException, XIncludeException { + + File input = new File(inputDir, "utf16.xml"); + Document doc = builder.build(input); + Document result = XIncluder.resolve(doc); + Document expectedResult = builder.build( + new File(outputDir, "utf16.xml") + ); + assertEquals(expectedResult, result); + + } + + + public void testXPointerBareNameID() + throws ParsingException, IOException, XIncludeException { + + File input = new File(inputDir, "xptridtest.xml"); + Document doc = builder.build(input); + Document result = XIncluder.resolve(doc); + Document expectedResult = builder.build( + new File(outputDir, "xptridtest.xml") + ); + assertEquals(expectedResult, result); + + } + + + public void testXPointerXMLID() + throws ParsingException, IOException, XIncludeException { + + File input = new File(inputDir, "xmlidtest.xml"); + Document doc = builder.build(input); + Document result = XIncluder.resolve(doc); + Document expectedResult = builder.build( + new File(outputDir, "xmlidtest.xml") + ); + assertEquals(expectedResult, result); + + } + + + public void testShorthandXPointerMatchesNothing() + throws ParsingException, IOException, URISyntaxException { + + File input = new File(inputDir, "xptridtest2.xml"); + Document doc = builder.build(input); + try { + XIncluder.resolve(doc); + fail("Resolved a document with an XPointer " + + "that selects no subresource"); + } + catch (XIncludeException success) { + assertNotNull(success.getMessage()); + // Must compare URLs instead of strings here to avoid + // issues of whether a file URL begins file:/ or file:/// + URI u1 = input.toURI(); + URI u2 = new URI(success.getURI()); + assertEquals(u1, u2); + } + + + /* I used to think this case included nothing. + Now I think an XPointer that matches no + subresource, and does not have a fallback is in error. + Document expectedResult = builder.build( + new File(outputDir, "xptridtest2.xml") + ); + assertEquals(expectedResult, result); + */ + + } + + + public void testXPointerPureTumbler() + throws ParsingException, IOException, XIncludeException { + + File input = new File(inputDir, "xptrtumblertest.xml"); + Document doc = builder.build(input); + Document result = XIncluder.resolve(doc); + Document expectedResult = builder.build( + new File(outputDir, "xptrtumblertest.xml") + ); + assertEquals(expectedResult, result); + + } + + + public void testUnrecognizedColonizedSchemeNameBackedUpByTumbler() + throws ParsingException, IOException, XIncludeException { + + File input = new File(inputDir, "colonizedschemename.xml"); + Document doc = builder.build(input); + Document result = XIncluder.resolve(doc); + Document expectedResult = builder.build( + new File(outputDir, "xptrtumblertest.xml") + ); + assertEquals(expectedResult, result); + + } + + + public void testXPointerSyntaxErrorInSecondPart() + throws ParsingException, IOException { + + File input = new File(inputDir, "laterfailure.xml"); + Document doc = builder.build(input); + try { + XIncluder.resolve(doc); + fail("Didn't find syntax error in 2nd XPointer part" + + " when the first part resolved successfully"); + } + catch (XIncludeException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testBadElementSchemeDataIsNotAnError() + throws ParsingException, IOException, XIncludeException { + + File input = new File(inputDir, "badelementschemedata.xml"); + Document doc = builder.build(input); + Document result = XIncluder.resolve(doc); + Document expectedResult = builder.build( + new File(outputDir, "badelementschemedata.xml") + ); + // dumpResult(input, result); + + assertEquals(expectedResult, result); + + } + + + public void testXPointerSyntaxErrorMissingFinalParenthesis() + throws ParsingException, IOException { + + File input = new File(inputDir, "laterfailure2.xml"); + Document doc = builder.build(input); + try { + XIncluder.resolve(doc); + fail("Didn't find syntax error in 2nd XPointer part" + + " when the first part resolved successfully"); + } + catch (XIncludeException success) { + assertNotNull(success.getMessage()); + } + + } + + + // Test we can include from same document using only + // an xpointer attribute + public void testOnlyXPointer() + throws ParsingException, IOException, XIncludeException { + + File input = new File(inputDir, "onlyxpointer.xml"); + Document doc = builder.build(input); + Document result = XIncluder.resolve(doc); + Document expectedResult = builder.build( + new File(outputDir, "onlyxpointer.xml") + ); + + assertEquals(expectedResult, result); + + } + + + // Test with 3 element schemes in the XPointer. + // The first and second one point to nothing. The third one + // selects something. + public void testXPointerTripleTumbler() + throws ParsingException, IOException, XIncludeException { + + File input = new File(inputDir, "xptr2tumblertest.xml"); + Document doc = builder.build(input); + Document result = XIncluder.resolve(doc); + Document expectedResult = builder.build( + new File(outputDir, "xptrtumblertest.xml") + ); + assertEquals(expectedResult, result); + + } + + + // Test with 2 element schemes in the XPointer. + // The first one uses an ID that doesn't exist + // and points to nothing. The second one + // selects something. + public void testXPointerDoubleTumbler() + throws ParsingException, IOException, XIncludeException { + + File input = new File(inputDir, "xptrdoubletumblertest.xml"); + Document doc = builder.build(input); + Document result = XIncluder.resolve(doc); + Document expectedResult = builder.build( + new File(outputDir, "xptrtumblertest.xml") + ); + assertEquals(expectedResult, result); + + } + + + // Test with 2 element schemes in the XPointer. + // The first one uses an ID that points to something. + // The second one points to something too. Both element schemes + // use IDs exclusively, no child sequences. + public void testXPointerDoubleElementByID() + throws ParsingException, IOException, XIncludeException { + + File input = new File(inputDir, "xptrdoubleelementtest.xml"); + Document doc = builder.build(input); + Document result = XIncluder.resolve(doc); + Document expectedResult = builder.build( + new File(outputDir, "xptrdoubleelementtest.xml") + ); + assertEquals(expectedResult, result); + + } + + + // Test with 2 element schemes in the XPointer. + // The first one uses a child sequence that points to something. + // The second one points to something too. Both element schemes + // use child sequences exclusively, no IDs. + public void testXPointerDoubleElementByChildSequence() + throws ParsingException, IOException, XIncludeException { + + File input = new File(inputDir, "xptrdoublechildsequence.xml"); + Document doc = builder.build(input); + Document result = XIncluder.resolve(doc); + Document expectedResult = builder.build( + new File(outputDir, "xptrdoubleelementtest.xml") + ); + assertEquals(expectedResult, result); + + } + + + // Make sure XPointer failures are treated as a resource error, + // not a fatal error. + public void testXPointerFailureIsAResourceError() + throws ParsingException, IOException, XIncludeException { + + File input = new File( + "data/xinclude/input/xptrtumblerfailsbutfallback.xml" + ); + Document doc = builder.build(input); + Document result = XIncluder.resolve(doc); + // For debugging + // dumpResult(input, result); + Document expectedResult = builder.build( + new File(outputDir, "xptrtumblertest.xml") + ); + assertEquals(expectedResult, result); + + } + + + // Make sure XPointer syntax errors are treated as a resource + // error, not a fatal error per section 4.2 of XInclude CR + /* Resources that are unavailable for any reason + (for example the resource doesn't exist, connection + difficulties or security restrictions prevent it from being + fetched, the URI scheme isn't a fetchable one, the resource + is in an unsuppored encoding, the resource is determined + through implementation-specific mechanisms not to be XML, or a + syntax error in an [XPointer Framework]) result in a resource + error. */ + public void testXPointerSyntaxErrorIsAResourceError() + throws ParsingException, IOException, XIncludeException { + + File input = new File( + "data/xinclude/input/xptrsyntaxerrorbutfallback.xml" + ); + Document doc = builder.build(input); + Document result = XIncluder.resolve(doc); + Document expectedResult = builder.build( + new File(outputDir, "xptrtumblertest.xml") + ); + assertEquals(expectedResult, result); + + } + + + // Test with 3 element schemes in the XPointer, + // separated by white space. + // The first one points to nothing. The third one + // selects something. + public void testXPointerTumblerWithWhiteSpace() + throws ParsingException, IOException, XIncludeException { + + File input + = new File(inputDir, "xptrtumblertest3.xml"); + Document doc = builder.build(input); + Document result = XIncluder.resolve(doc); + Document expectedResult = builder.build( + new File(outputDir, "xptrtumblertest.xml") + ); + assertEquals(expectedResult, result); + + } + + + public void testXPointerTumblerMatchesNothing() + throws ParsingException, IOException, URISyntaxException { + + File input = new File( + "data/xinclude/input/xptrtumblertest2.xml" + ); + Document doc = builder.build(input); + try { + XIncluder.resolve(doc); + fail("Did not error on XPointer matching nothing"); + } + catch (XIncludeException success) { + assertNotNull(success.getMessage()); + URI u1 = input.toURI(); + URI u2 = new URI(success.getURI()); + assertEquals(u1, u2); + } + + } + + + public void testMalformedXPointer() + throws ParsingException, IOException, URISyntaxException { + + File input = new File(inputDir, "badxptr.xml"); + Document doc = builder.build(input); + try { + XIncluder.resolve(doc); + fail("Allowed malformed XPointer"); + } + catch (XIncludeException success) { + assertNotNull(success.getMessage()); + URI u1 = input.toURI(); + URI u2 = new URI(success.getURI()); + assertEquals(u1, u2); + } + + } + + + public void testXPointerExceptionSelfCausation() + throws ParsingException, IOException { + + File input = new File(inputDir, "badxptr.xml"); + Document doc = builder.build(input); + try { + XIncluder.resolve(doc); + fail("Allowed malformed XPointer"); + } + catch (XIncludeException success) { + Exception cause = (Exception) success.getCause(); + assertNotNull(cause.getMessage()); + try { + cause.initCause(cause); + fail("Self causation"); + } + catch (IllegalArgumentException ex) { + assertNotNull(ex.getMessage()); + } + } + + } + + + public void testXPointerExceptionGetCause() + throws ParsingException, IOException { + + File input = new File(inputDir, "badxptr.xml"); + Document doc = builder.build(input); + try { + XIncluder.resolve(doc); + fail("Allowed malformed XPointer"); + } + catch (XIncludeException success) { + Exception cause = (Exception) success.getCause(); + Exception ex = new Exception(); + cause.initCause(ex); + assertEquals(ex, cause.getCause()); + } + + } + + + public void testAnotherMalformedXPointer() + throws ParsingException, IOException, URISyntaxException { + + // testing use of non NCNAME as ID + File input = new File(inputDir, "badxptr2.xml"); + Document doc = builder.build(input); + try { + XIncluder.resolve(doc); + fail("Allowed another malformed XPointer"); + } + catch (XIncludeException success) { + assertNotNull(success.getMessage()); + URI u1 = input.toURI(); + URI u2 = new URI(success.getURI()); + assertEquals(u1, u2); + } + + } + + + public void testMalformedXPointerWithFallback() + throws ParsingException, IOException, XIncludeException { + + File input = new File(inputDir, "xptrfallback.xml"); + Document doc = builder.build(input); + Document result = XIncluder.resolve(doc); + Document expectedResult = builder.build(new File( + "data/xinclude/output/xptrfallback.xml") + ); + assertEquals(expectedResult, result); + + } + + + public void testIDAndTumbler() + throws ParsingException, IOException, XIncludeException { + + File input = new File( + "data/xinclude/input/xptridandtumblertest.xml" + ); + Document doc = builder.build(input); + Document result = XIncluder.resolve(doc); + Document expectedResult = builder.build(new File( + "data/xinclude/output/xptridandtumblertest.xml") + ); + assertEquals(expectedResult, result); + + } + + + public void testAutoDetectUTF16BigEndianWithByteOrderMark() + throws ParsingException, IOException, XIncludeException { + + File input = new File( + "data/xinclude/input/UTF16BigEndianWithByteOrderMark.xml" + ); + Document doc = builder.build(input); + Document result = XIncluder.resolve(doc); + Document expectedResult = builder.build(new File( + "data/xinclude/output/UTF16BigEndianWithByteOrderMark.xml") + ); + assertEquals(expectedResult, result); + + } + + + public void testAutoDetectUTF16LittleEndianWithByteOrderMark() + throws ParsingException, IOException, XIncludeException { + + File input = new File( + "data/xinclude/input/UTF16LittleEndianWithByteOrderMark.xml" + ); + Document doc = builder.build(input); + Document result = XIncluder.resolve(doc); + Document expectedResult = builder.build(new File( + "data/xinclude/output/UTF16LittleEndianWithByteOrderMark.xml" + )); + assertEquals(expectedResult, result); + + } + + + public void testAutoDetectUTF8WithByteOrderMark() + throws ParsingException, IOException, XIncludeException { + + File input = new File( + "data/xinclude/input/UTF8WithByteOrderMark.xml" + ); + Document doc = builder.build(input); + Document result = XIncluder.resolve(doc); + Document expectedResult = builder.build( + new File(outputDir, "UTF8WithByteOrderMark.xml") + ); + assertEquals(expectedResult, result); + + } + + + public void testAutoDetectUnicodeBigUnmarked() + throws ParsingException, IOException, XIncludeException { + + File input = new File( + "data/xinclude/input/UnicodeBigUnmarked.xml" + ); + Document doc = builder.build(input); + Document result = XIncluder.resolve(doc); + Document expectedResult = builder.build( + new File(outputDir, "UnicodeBigUnmarked.xml") + ); + assertEquals(expectedResult, result); + + } + + + public void testUnicodeLittleUnmarked() + throws ParsingException, IOException, XIncludeException { + + File input = new File( + "data/xinclude/input/UnicodeLittleUnmarked.xml" + ); + Document doc = builder.build(input); + Document result = XIncluder.resolve(doc); + Document expectedResult = builder.build( + new File(outputDir, "UnicodeLittleUnmarked.xml") + ); + assertEquals(expectedResult, result); + + } + + +/*// Java doesn't yet support the UTF-32BE and UTF32LE encodings + public void testUTF32BE() + throws ParsingException, IOException, XIncludeException { + + File input = new File( + "data/xinclude/input/UTF32BE.xml" + ); + Document doc = builder.build(input); + Document result = XIncluder.resolve(doc); + Document expectedResult = builder.build( + new File(outputDir, "UTF32BE.xml") + ); + assertEquals(expectedResult, result); + + } + + public void testUTF32LE() + throws ParsingException, IOException, XIncludeException { + + File input = new File( + "data/xinclude/input/UTF32LE.xml" + ); + Document doc = builder.build(input); + Document result = XIncluder.resolve(doc); + Document expectedResult = builder.build( + new File(outputDir, "UTF32LE.xml") + ); + assertEquals(expectedResult, result); + + } +*/ + + + public void testEBCDIC() + throws ParsingException, IOException, XIncludeException { + + File input = new File(inputDir, "EBCDIC.xml"); + Document doc = builder.build(input); + Document result = XIncluder.resolve(doc); + Document expected = builder.build(new File(outputDir, "EBCDIC.xml")); + assertEquals(expected, result); + + } + + + // Turn off these tests because Java doesn't support UCS4 yet + /* public void testAutoDetectUCS4BE() + throws ParsingException, IOException, XIncludeException { + + File input = new File(inputDir, "UCS4BE.xml"); + Document doc = builder.build(input); + Document result = XIncluder.resolve(doc); + Document expectedResult = builder.build( + new File(outputDir, "UTF8WithByteOrderMark.xml") + ); + assertEquals(expectedResult, result); + + } + + public void testAutoDetectUCS4LE() + throws ParsingException, IOException, XIncludeException { + + File input = new File(inputDir, "UCS4LE.xml"); + Document doc = builder.build(input); + Document result = XIncluder.resolve(doc); + Document expectedResult = builder.build( + new File(outputDir, "UTF8WithByteOrderMark.xml") + ); + assertEquals(expectedResult, result); + + } */ + + + // Need a test case where A includes B, B includes C + // and B encounters the error (e.g. a missing href) + // to make sure B's URL is in the error message, not A's + public void testChildDocumentSetsErrorURI() + throws ParsingException, IOException, XIncludeException, URISyntaxException { + + File input = new File(inputDir, "toplevel.xml"); + File error = new File(inputDir, "onedown.xml"); + Document doc = builder.build(input); + try { + XIncluder.resolve(doc); + fail("Missing HREF not detected"); + } + catch (NoIncludeLocationException success) { + assertNotNull(success.getMessage()); + URI u1 = error.toURI(); + URI u2 = new URI(success.getURI()); + assertEquals(u1, u2); + } + + } + + + public void testColonizedNameForIdValueInElementScheme() + throws ParsingException, IOException { + + File input = new File(inputDir, "badxptr3.xml"); + Document doc = builder.build(input); + try { + XIncluder.resolve(doc); + fail("Bad ID in element not detected"); + } + catch (XIncludeException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testBadIdValueInElementScheme() + throws ParsingException, IOException { + + File input = new File(inputDir, "badxptr4.xml"); + Document doc = builder.build(input); + try { + XIncluder.resolve(doc); + fail("Bad ID in element not detected"); + } + catch (XIncludeException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testCirclePointer() + throws ParsingException, IOException, XIncludeException { + + File input = new File(inputDir, "circlepointer1.xml"); + Document doc = builder.build(input); + try { + XIncluder.resolve(doc); + fail("Allowed circular reference via XPointer"); + } + catch (InclusionLoopException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testXPointerOverridesFragmentID() + throws ParsingException, IOException, XIncludeException { + + File input = new File(inputDir, "xpointeroverridesfragmentid.xml" + ); + Document doc = builder.build(input); + try { + XIncluder.resolve(doc); + fail("Allowed href attribute with fragment ID"); + } + catch (XIncludeException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testFailsOnFragmentID() + throws ParsingException, IOException, XIncludeException { + + File input = new File(inputDir, "ignoresfragmentid.xml"); + Document doc = builder.build(input); + try { + XIncluder.resolve(doc); + fail("Allowed href attribute with fragment ID"); + } + catch (XIncludeException success) { + assertNotNull(success.getMessage()); + } + + } + + + // This also tests that the base URI applied to an element is as set by the xml:base + // attribute, not the document. + public void testFragmentIDsAreRemovedFromElementBaseURIsAfterInclusion() + throws ParsingException, IOException, XIncludeException { + + File input = new File(inputDir, "basewithfragmentid.xml"); + Document doc = builder.build(input); + Document result = XIncluder.resolve(doc); + Document expectedResult = builder.build( + new File(outputDir, "basewithfragmentid.xml") + ); + assertEquals(expectedResult, result); + + } + + + public void testIncludeLowerCaseFileNames() + throws ParsingException, IOException, XIncludeException { + + File input = new File(inputDir, "lowercasealphabet.xml"); + Document doc = builder.build(input); + Document result = XIncluder.resolve(doc); + Document expectedResult = builder.build( + new File(outputDir, "lowercasealphabet.xml") + ); + assertEquals(expectedResult, result); + + } + + + public void testIncludeUpperCaseFileNames() + throws ParsingException, IOException, XIncludeException { + + File input = new File(inputDir, "uppercasealphabet.xml"); + Document doc = builder.build(input); + Document result = XIncluder.resolve(doc); + Document expectedResult = builder.build( + new File(outputDir, "uppercasealphabet.xml") + ); + assertEquals(expectedResult, result); + + } + + + public void testIncludeDigitFileNames() + throws ParsingException, IOException, XIncludeException { + + File input = new File(inputDir, "numeric.xml"); + Document doc = builder.build(input); + Document result = XIncluder.resolve(doc); + Document expectedResult = builder.build( + new File(outputDir, "numeric.xml") + ); + assertEquals(expectedResult, result); + + } + + + public void testIncludeHighPunctuationFileNames() + throws ParsingException, IOException, XIncludeException { + + // Windows has a problem with some of these file names so + // first we have to generate the file, just to avoid storing + // it in the zip archive + try { + File f = new File(inputDir, "{|}.txt"); + Writer out = new OutputStreamWriter( + new FileOutputStream(f), "UTF8"); + out.write("{|}"); + out.flush(); + out.close(); + + File input = new File(inputDir, "punctuation.xml"); + Document doc = builder.build(input); + Document result = XIncluder.resolve(doc); + Document expectedResult = builder.build( + new File(outputDir, "punctuation.xml") + ); + f.delete(); + assertEquals(expectedResult, result); + } + catch (FileNotFoundException ex) { + // This file can't even exist on Windows. + // We can only test this on Unix. + if (!windows) throw ex; + } + + } + + + public void testMiddlePunctuationError() + throws ParsingException, IOException, XIncludeException { + + File input = new File(inputDir, "middlepunctuationerror.xml"); + Document doc = builder.build(input); + try { + XIncluder.resolve(doc); + fail("Allowed illegal IRI with right square bracket ]"); + } + catch (BadHrefAttributeException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testIncludeLowerPunctuationFileNames() + throws ParsingException, IOException, XIncludeException { + + try { + File f = new File(inputDir, "!\"$&'+,.txt"); + Writer out = new OutputStreamWriter( + new FileOutputStream(f), "UTF8"); + out.write("!\"$&'+,"); + out.flush(); + out.close(); + + File input = new File(inputDir, "lowerpunctuation.xml"); + Document doc = builder.build(input); + Document result = XIncluder.resolve(doc); + Document expectedResult = builder.build( + new File(outputDir, "lowerpunctuation.xml") + ); + f.delete(); + assertEquals(expectedResult, result); + } + catch (FileNotFoundException ex) { + // This file can't even exist on Windows. + // We can only test this on Unix. + if (!windows) throw ex; + } + + } + + + public void testLineEnds() + throws ParsingException, IOException, XIncludeException { + + File input = new File(inputDir, "lineends.xml"); + Document doc = builder.build(input); + Document result = XIncluder.resolve(doc); + Document expected = builder.build( + new File(outputDir, "lineends.xml") + ); + assertEquals(expected, result); + + } + + + // This is semantically bad; but still meets the + // syntax of fragment IDs from RFC 2396 + public void testBadXPointerInFragmentIDIsFatalError() + throws ParsingException, IOException, XIncludeException { + + File input = new File( + "data/xinclude/input/meaninglessfragmentid.xml"); + Document doc = builder.build(input); + try { + XIncluder.resolve(doc); + fail("Allowed href attribute with fragment ID"); + } + catch (XIncludeException success) { + assertNotNull(success.getMessage()); + } + + } + + + // These tests actually connect to IBiblio to load the included + // data. This is necessary because file URLs don't support + // content negotiation + public void testAcceptLanguageFrench() + throws ParsingException, IOException, XIncludeException { + + File input = new File(inputDir, "acceptfrench.xml"); + Document doc = builder.build(input); + Document result = XIncluder.resolve(doc); + Document expectedResult = builder.build( + new File(outputDir, "acceptfrench.xml") + ); + assertEquals(expectedResult, result); + + } + + + public void testAcceptLanguageEnglish() + throws ParsingException, IOException, XIncludeException { + + File input = new File(inputDir, "acceptenglish.xml"); + Document doc = builder.build(input); + Document result = XIncluder.resolve(doc); + Document expectedResult = builder.build( + new File(outputDir, "acceptenglish.xml") + ); + assertEquals(expectedResult, result); + + } + + + public void testAcceptPlainText() + throws ParsingException, IOException, XIncludeException { + + File input = new File(inputDir, "acceptplaintext.xml"); + Document doc = builder.build(input); + Document result = XIncluder.resolve(doc); + Document expectedResult = builder.build( + new File(outputDir, "acceptplaintext.xml") + ); + assertEquals(expectedResult, result); + + } + + + public void testAcceptHTML() + throws ParsingException, IOException, XIncludeException { + + File input = new File(inputDir, "accepthtml.xml"); + Document doc = builder.build(input); + Document result = XIncluder.resolve(doc); + Document expectedResult = builder.build( + new File(outputDir, "accepthtml.xml") + ); + assertEquals(expectedResult, result); + + } + + + public void testBadHTTPHeaderExceptionConstructor() { + + String message = "test"; + XIncludeException ex = new BadHTTPHeaderException( + message, "http://www.example.com/"); + assertEquals(message, ex.getMessage()); + assertEquals("http://www.example.com/", ex.getURI()); + + } + + + public void testBadHrefAttributerExceptionConstructor() { + + String message = "test"; + Exception ex = new BadHrefAttributeException(message); + assertEquals(message, ex.getMessage()); + + } + + + public void testPercentEscapesAreNotAllowedInXPointerAttributes() + throws ParsingException, IOException, XIncludeException { + + File input = new File(inputDir, "xpointerwithpercentescape.xml"); + Document doc = builder.build(input); + try { + XIncluder.resolve(doc); + fail("Allowed xpointer attribute with percent escape"); + } + catch (XIncludeException success) { + assertNotNull(success.getMessage()); + Exception cause = (Exception) success.getCause(); + assertNotNull(cause); + } + + } + + public void testXPointerExceptionReinitializeCause() + throws ParsingException, IOException, XIncludeException { + + File input = new File(inputDir, "xpointerwithpercentescape.xml"); + Document doc = builder.build(input); + try { + XIncluder.resolve(doc); + fail("Allowed xpointer attribute with percent escape"); + } + catch (XIncludeException success) { + Exception cause = (Exception) success.getCause(); + try { + cause.initCause(new Exception()); + fail("Reinitialized cause"); + } + catch (IllegalStateException ex) { + assertNotNull(ex.getMessage()); + } + } + + } + + + + // WARNING: this test is one interpretation of the XInclude + // proposed recommendation. It asserts that encoding attributes + // that do not contain legal encoding names are fatal errors. + // This is far from certain. It is also possible the working group + // will choose to interpret these as resource errors. + public void testMalformedEncodingAttribute() + throws IOException, ParsingException, XIncludeException { + + File input = new File(inputDir, "badencoding.xml"); + Document doc = builder.build(input); + try { + XIncluder.resolve(doc); + fail("Allowed encoding attribute with white space"); + } + catch (BadEncodingAttributeException success) { + assertNotNull(success.getMessage()); + assertTrue(success.getURI().endsWith(input.getName())); + } + + } + + + public void testEmptyEncodingAttribute() + throws IOException, ParsingException, XIncludeException { + + File input = new File(inputDir, "emptyencoding.xml"); + Document doc = builder.build(input); + try { + XIncluder.resolve(doc); + fail("Allowed encoding attribute with no value"); + } + catch (BadEncodingAttributeException success) { + assertNotNull(success.getMessage()); + assertTrue(success.getURI().endsWith(input.getName())); + } + + } + + + public void testEncodingAttributeStartsWithDigit() + throws IOException, ParsingException, XIncludeException { + + File input = new File(inputDir, "digitencoding.xml"); + Document doc = builder.build(input); + try { + XIncluder.resolve(doc); + fail("Allowed encoding attribute starting with digit"); + } + catch (BadEncodingAttributeException success) { + assertNotNull(success.getMessage()); + assertTrue(success.getURI().endsWith(input.getName())); + } + + } + + + // Test that a malformed parse attribute is not thrown when the + // fallback element containing it is not activated. + public void testHiddenError() + throws ParsingException, IOException, XIncludeException { + + File input = new File(inputDir, "hiddenerror.xml"); + Document doc = builder.build(input); + XIncluder.resolve(doc); + + } + + + // Test that an href attribute that has a fragment identifier + // is not a fatal error when the fallback element containing + // it is not activated. + public void testHiddenError2() + throws ParsingException, IOException, XIncludeException { + + File input = new File(inputDir, "hiddenerror2.xml"); + Document doc = builder.build(input); + XIncluder.resolve(doc); + + } + + + // Test that a fallback element with a non-include parent is not a + // fatal error when the ancestor fallback element containing it is + // not activated. + public void testHiddenError3() + throws ParsingException, IOException, XIncludeException { + + File input = new File(inputDir, "hiddenerror3.xml"); + Document doc = builder.build(input); + XIncluder.resolve(doc); + + } + + + // Test that an xpointer attribute that uses percent escapes + // is a not a fatal error when the + // fallback element containing it is not activated. See + // http://lists.w3.org/Archives/Public/www-xml-xinclude-comments/2004Oct/0008.html + public void testXpointerAttributeContainsPercentEscapeInUnactivatedFallback() + throws ParsingException, IOException, XIncludeException { + + File input = new File(inputDir, "hiddenerror3.xml"); + Document doc = builder.build(input); + XIncluder.resolve(doc); + + } + + +} \ No newline at end of file diff --git a/src/nu/xom/tests/XMLExceptionTest.java b/src/nu/xom/tests/XMLExceptionTest.java new file mode 100644 index 0000000..8dfd539 --- /dev/null +++ b/src/nu/xom/tests/XMLExceptionTest.java @@ -0,0 +1,227 @@ +/* Copyright 2003, 2004 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.tests; + +import java.io.PrintWriter; +import java.io.StringWriter; + +import nu.xom.CycleException; +import nu.xom.IllegalAddException; +import nu.xom.IllegalDataException; +import nu.xom.IllegalNameException; +import nu.xom.IllegalTargetException; +import nu.xom.MalformedURIException; +import nu.xom.MultipleParentException; +import nu.xom.NamespaceConflictException; +import nu.xom.NoSuchAttributeException; +import nu.xom.NoSuchChildException; +import nu.xom.XMLException; + +/** + *

    + * This class provides unit tests for the XMLException + * class. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.1b2 + * + */ +public class XMLExceptionTest extends XOMTestCase { + + private XMLException ex; + private Exception cause; + private String message = "testing 1-2-3"; + + public XMLExceptionTest(String name) { + super(name); + } + + + protected void setUp() { + ex = new XMLException("message"); + cause = new Exception(); + } + + + public void testConstructor() { + XMLException ex = new XMLException(message, cause); + assertEquals(message, ex.getMessage()); + assertEquals(cause, ex.getCause()); + } + + + public void testPrintStackTrace() { + XMLException ex = new XMLException(message, cause); + StringWriter out = new StringWriter(); + PrintWriter pw = new PrintWriter(out); + ex.printStackTrace(pw); + pw.close(); + assertTrue(out.toString().indexOf("Caused by: ") > 0); + } + + + public void testMalformedURIExceptionConstructor() { + XMLException ex = new MalformedURIException(message, cause); + assertEquals(message, ex.getMessage()); + assertEquals(cause, ex.getCause()); + } + + + public void testValidityExceptionConstructor() { + XMLException ex = new MalformedURIException(message, cause); + assertEquals(message, ex.getMessage()); + assertEquals(cause, ex.getCause()); + } + + + public void testNamespaceConflictExceptionConstructor() { + XMLException ex = new NamespaceConflictException(message, cause); + assertEquals(message, ex.getMessage()); + assertEquals(cause, ex.getCause()); + } + + + public void testMultipleParentExceptionConstructor() { + XMLException ex = new MultipleParentException(message, cause); + assertEquals(message, ex.getMessage()); + assertEquals(cause, ex.getCause()); + } + + + public void testNoSuchAttributeExceptionConstructor() { + XMLException ex = new NoSuchAttributeException(message, cause); + assertEquals(message, ex.getMessage()); + assertEquals(cause, ex.getCause()); + } + + + public void testNoSuchChildExceptionConstructor() { + XMLException ex = new NoSuchChildException(message, cause); + assertEquals(message, ex.getMessage()); + assertEquals(cause, ex.getCause()); + } + + + public void testCycleExceptionConstructor() { + XMLException ex = new CycleException(message, cause); + assertEquals(message, ex.getMessage()); + assertEquals(cause, ex.getCause()); + } + + + public void testIllegalNameExceptionConstructor() { + XMLException ex = new IllegalNameException(message, cause); + assertEquals(message, ex.getMessage()); + assertEquals(cause, ex.getCause()); + } + + + public void testIllegalTargetExceptionConstructor() { + XMLException ex = new IllegalTargetException(message, cause); + assertEquals(message, ex.getMessage()); + assertEquals(cause, ex.getCause()); + } + + + public void testIllegalAddExceptionConstructor() { + XMLException ex = new IllegalAddException(message, cause); + assertEquals(message, ex.getMessage()); + assertEquals(cause, ex.getCause()); + } + + + public void testIllegalDataExceptionConstructor() { + XMLException ex = new IllegalDataException(message, cause); + assertEquals(message, ex.getMessage()); + assertEquals(cause, ex.getCause()); + } + + public void testInitCause() { + + assertNull(ex.getCause()); + ex.initCause(cause); + assertEquals(cause, ex.getCause()); + + try { + ex.initCause(null); + fail("Reinitialized cause over null"); + } + catch (IllegalStateException result) { + // success + } + + try { + ex.initCause(new Exception()); + fail("Reinitialized cause over null"); + } + catch (IllegalStateException result) { + // success + } + + } + + + public void testNullInitCause() { + + ex = new XMLException(null, null); + assertNull(ex.getCause()); + + try { + ex.initCause(new Exception()); + fail("Reinitialized cause over null"); + } + catch (IllegalStateException result) { + // success + } + + try { + ex.initCause(null); + fail("Reinitialized cause over null"); + } + catch (IllegalStateException result) { + // success + } + + } + + + public void testSelfCause() { + + try { + ex.initCause(ex); + fail("Allowed self-causation"); + } + catch (IllegalArgumentException result) { + // success + } + + } + + + public void testGetMessage() { + Exception ex = new XMLException("testing"); + assertEquals("testing", ex.getMessage()); + } + + +} diff --git a/src/nu/xom/tests/XMLWriter.java b/src/nu/xom/tests/XMLWriter.java new file mode 100644 index 0000000..eb9cce4 --- /dev/null +++ b/src/nu/xom/tests/XMLWriter.java @@ -0,0 +1,1177 @@ +// XMLWriter.java - serialize an XML document. +// Written by David Megginson, david@megginson.com +// NO WARRANTY! This class is in the public domain. + + +package nu.xom.tests; + +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.io.Writer; +import java.util.Enumeration; +import java.util.Hashtable; + +import org.xml.sax.Attributes; +import org.xml.sax.SAXException; +import org.xml.sax.ext.LexicalHandler; +import org.xml.sax.helpers.AttributesImpl; +import org.xml.sax.helpers.NamespaceSupport; +import org.xml.sax.helpers.XMLFilterImpl; + + +/** + *

    + * The original version of this class was written and placed in the + * public domain by David Megginson. Elliotte Rusty Harold added + * LexicalHandler support. It is included here purely + * for help with testing the SAXConverter class. It is + * not part of the XOM API; nor is it used internally by XOM anywhere + * except in the SAXConverter tests. + *

    + * + *

    + * This class does not properly preserve additional namespace + * declarations in non-root elements. If you encounter that, the + * bug is here, not in SAXConverter. + *

    + * + * @author David Megginson, Elliotte Rusty Harold + * @version 1.0 + */ +class XMLWriter extends XMLFilterImpl implements LexicalHandler { + + /////////////////////////////////////////////////////////////////// + // Constructors. + /////////////////////////////////////////////////////////////////// + + + /** + *

    + * Create a new XML writer. + *

    + * + *

    Write to standard output.

    + */ + public XMLWriter() { + init(null); + } + + + /** + *

    + * Create a new XML writer. + *

    + * + *

    Write to the writer provided.

    + * + * @param writer the output destination, or null to use standard + * output + */ + public XMLWriter (Writer writer) { + init(writer); + } + + /** + *

    + * Internal initialization method. + *

    + * + *

    All of the public constructors invoke this method. + * + * @param writer the output destination, or null to use + * standard output. + */ + private void init (Writer writer) { + setOutput(writer); + nsSupport = new NamespaceSupport(); + prefixTable = new Hashtable(); + forcedDeclTable = new Hashtable(); + doneDeclTable = new Hashtable(); + } + + + /////////////////////////////////////////////////////////////////// + // Public methods. + /////////////////////////////////////////////////////////////////// + + + /** + *

    + * Reset the writer. + *

    + * + *

    This method is especially useful if the writer throws an + * exception before it is finished, and you want to reuse the + * writer for a new document. It is usually a good idea to + * invoke {@link #flush flush} before resetting the writer, + * to make sure that no output is lost.

    + * + *

    This method is invoked automatically by the + * {@link #startDocument startDocument} method before writing + * a new document.

    + * + *

    Note: this method will not + * clear the prefix or URI information in the writer or + * the selected output writer.

    + * + * @see #flush + */ + public void reset() { + elementLevel = 0; + prefixCounter = 0; + nsSupport.reset(); + } + + + /** + *

    + * Flush the output. + *

    + * + *

    This method flushes the output stream. It is especially useful + * when you need to make certain that the entire document has + * been written to output but do not want to close the output + * stream.

    + * + *

    This method is invoked automatically by the + * {@link #endDocument endDocument} method after writing a + * document.

    + * + * @throws IOException + * + * @see #reset + * + */ + public void flush() throws IOException { + output.flush(); + } + + + /** + *

    + * Set a new output destination for the document. + *

    + * + * @param writer the output destination, or null to use + * standard output + * + * @return the current output writer + * + * @see #flush + */ + public void setOutput (Writer writer) { + if (writer == null) { + output = new OutputStreamWriter(System.out); + } + else { + output = writer; + } + } + + + /** + *

    + * Specify a preferred prefix for a namespace URI. + *

    + * + *

    Note that this method does not actually force the namespace + * to be declared; to do that, use the {@link + * #forceNSDecl(java.lang.String) forceNSDecl} method as well.

    + * + * @param uri the namespace URI + * @param prefix the preferred prefix, or "" to select + * the default namespace + * + * @see #getPrefix + * @see #forceNSDecl(java.lang.String) + * @see #forceNSDecl(java.lang.String,java.lang.String) + */ + public void setPrefix (String uri, String prefix) { + prefixTable.put(uri, prefix); + } + + + /** + *

    + * Get the current or preferred prefix for a namespace URI. + *

    + * + * @param uri the namespace URI + * + * @return the preferred prefix, or "" for the default namespace + * + * @see #setPrefix + */ + public String getPrefix (String uri) { + return (String)prefixTable.get(uri); + } + + public void startPrefixMapping(String prefix, String uri) { + this.forceNSDecl(uri, prefix); + } + + /** + *

    + * Force a namespace to be declared on the root element. + *

    + * + *

    By default, the XMLWriter will declare only the namespaces + * needed for an element; as a result, a namespace may be + * declared many places in a document if it is not used on the + * root element.

    + * + *

    This method forces a namespace to be declared on the root + * element even if it is not used there, and reduces the number + * of xmlns attributes in the document.

    + * + * @param uri the namespace URI to declare + * + * @see #forceNSDecl(java.lang.String,java.lang.String) + * @see #setPrefix + */ + public void forceNSDecl (String uri) { + forcedDeclTable.put(uri, Boolean.TRUE); + } + + + /** + *

    + * Force a namespace declaration with a preferred prefix. + *

    + * + *

    This is a convenience method that invokes {@link + * #setPrefix setPrefix} then {@link #forceNSDecl(java.lang.String) + * forceNSDecl}.

    + * + * @param uri the namespace URI to declare on the root element + * @param prefix the preferred prefix for the namespace, or "" + * for the default namespace + * + * @see #setPrefix + * @see #forceNSDecl(java.lang.String) + */ + public void forceNSDecl (String uri, String prefix) { + setPrefix(uri, prefix); + forceNSDecl(uri); + } + + + /////////////////////////////////////////////////////////////////// + // Methods from org.xml.sax.ContentHandler. + /////////////////////////////////////////////////////////////////// + + + /** + *

    + * Write the XML declaration at the beginning of the document. + *

    + * + *

    + * Pass the event on down the filter chain for further processing. + *

    + * + * @throws org.xml.sax.SAXException if there is an error + * writing the XML declaration, or if a handler further + * down the filter chain raises an exception + * + * @see org.xml.sax.ContentHandler#startDocument + */ + public void startDocument() throws SAXException { + reset(); + write("\n\n"); + super.startDocument(); + } + + + /** + *

    + * Write a newline at the end of the document. + *

    + * + *

    + * Pass the event on down the filter chain for further processing. + *

    + * + * @throws org.xml.sax.SAXException if there is an error + * writing the newline, or if a handler further down + * the filter chain raises an exception + * + * @see org.xml.sax.ContentHandler#endDocument + */ + public void endDocument() throws SAXException { + write('\n'); + super.endDocument(); + try { + flush(); + } catch (IOException ex) { + throw new SAXException(ex); + } + } + + + /** + *

    + * Write a start-tag. + *

    + * + *

    + * Pass the event on down the filter chain for further processing. + *

    + * + * @param uri the namespace URI, or the empty string if none + * is available + * @param localName the element's local (unprefixed) name (required) + * @param qualifiedName the element's qualified (prefixed) name, + * or the empty string is none is available. This method + * will use the qualified name as a template for generating + * a prefix if necessary, but it is not guaranteed to use + * the same qualified name. + * @param atts the element's attribute list (must not be null) + * + * @throws org.xml.sax.SAXException if there is an error + * writing the start-tag, or if a handler further down + * the filter chain raises an exception + * + * @see org.xml.sax.ContentHandler#startElement + */ + public void startElement (String uri, String localName, + String qualifiedName, Attributes atts) throws SAXException { + elementLevel++; + nsSupport.pushContext(); + write('<'); + writeName(uri, localName, qualifiedName, true); + writeAttributes(atts); + if (elementLevel == 1) { + forceNSDecls(); + } + writeNSDecls(); + write('>'); + super.startElement(uri, localName, qualifiedName, atts); + } + + + /** + *

    + * Write an end-tag. + *

    + * + *

    + * Pass the event on down the filter chain for further processing. + *

    + * + * @param uri the namespace URI, or the empty string if none + * is available + * @param localName the element's local (unprefixed) name (required) + * @param qualifiedName the element's qualified (prefixed) name, or the + * empty string is none is available. This method will + * use the qName as a template for generating a prefix + * if necessary, but it is not guaranteed to use the + * same qualified name. + * + * @throws org.xml.sax.SAXException if there is an error + * writing the end-tag, or if a handler further down + * the filter chain raises an exception + * + * @see org.xml.sax.ContentHandler#endElement + */ + public void endElement (String uri, String localName, String qualifiedName) + throws SAXException { + write("'); + if (elementLevel == 1) { + write('\n'); + } + super.endElement(uri, localName, qualifiedName); + nsSupport.popContext(); + elementLevel--; + } + + + /** + *

    + * Write character data. + *

    + * + *

    + * Pass the event on down the filter chain for further processing. + *

    + * + * @param ch the array of characters to write + * @param start the starting position in the array + * @param length the number of characters to write + * + * @throws org.xml.sax.SAXException if there is an error + * writing the characters, or if a handler further down + * the filter chain raises an exception + * + * @see org.xml.sax.ContentHandler#characters + */ + public void characters (char[] ch, int start, int length) + throws SAXException { + writeEsc(ch, start, length, false); + super.characters(ch, start, length); + } + + + /** + *

    + * Write ignorable whitespace. + *

    + * + *

    + * Pass the event on down the filter chain for further processing. + *

    + * + * @param ch the array of characters to write + * @param start the starting position in the array + * @param length the number of characters to write + * + * @throws org.xml.sax.SAXException if there is an error + * writing the whitespace, or if a handler further down + * the filter chain raises an exception + * + * @see org.xml.sax.ContentHandler#ignorableWhitespace + */ + public void ignorableWhitespace (char[] ch, int start, int length) + throws SAXException { + writeEsc(ch, start, length, false); + super.ignorableWhitespace(ch, start, length); + } + + + + /** + *

    + * Write a processing instruction. + *

    + * + *

    + * Pass the event on down the filter chain for further processing. + *

    + * + * @param target the processing instruction target + * @param data the processing instruction data + * + * @throws org.xml.sax.SAXException if there is an error + * writing the PI, or if a handler further down + * the filter chain raises an exception + * + * @see org.xml.sax.ContentHandler#processingInstruction + */ + public void processingInstruction (String target, String data) + throws SAXException { + write(""); + if (elementLevel < 1) { + write('\n'); + } + super.processingInstruction(target, data); + } + + + + /////////////////////////////////////////////////////////////////// + // Additional markup. + /////////////////////////////////////////////////////////////////// + + /** + *

    + * Write an empty element. + *

    + * + *

    + * This method writes an empty-element tag rather than a start-tag + * followed by an end-tag. Both a {@link #startElement + * startElement} and an {@link #endElement endElement} event will + * be passed on down the filter chain. + *

    + * + * @param uri the element's namespace URI, or the empty string + * if the element has no namespace or if namespace + * processing is not being performed + * @param localName the element's local name (without prefix). This + * parameter must be provided. + * @param qualifiedName the element's qualified name (with prefix), or + * the empty string if none is available. This parameter + * is strictly advisory: the writer may or may not use + * the prefix attached. + * @param atts the element's attribute list + * + * @throws org.xml.sax.SAXException if there is an error + * writing the empty tag, or if a handler further down + * the filter chain raises an exception + * + * @see #startElement + * @see #endElement + */ + public void emptyElement (String uri, String localName, + String qualifiedName, Attributes atts) throws SAXException { + nsSupport.pushContext(); + write('<'); + writeName(uri, localName, qualifiedName, true); + writeAttributes(atts); + if (elementLevel == 1) { + forceNSDecls(); + } + writeNSDecls(); + write("/>"); + super.startElement(uri, localName, qualifiedName, atts); + super.endElement(uri, localName, qualifiedName); + } + + + + /////////////////////////////////////////////////////////////////// + // Convenience methods. + /////////////////////////////////////////////////////////////////// + + + + /** + *

    + * Start a new element without a qualified name or attributes. + *

    + * + *

    This method will provide a default empty attribute + * list and an empty string for the qualified name. + * It invokes {@link + * #startElement(String, String, String, Attributes)} + * directly.

    + * + * @param uri the element's namespace URI + * @param localName the element's local name + * + * @throws org.xml.sax.SAXException if there is an error + * writing the start-tag, or if a handler further down + * the filter chain raises an exception + * + * @see #startElement(String, String, String, Attributes) + */ + public void startElement (String uri, String localName) + throws SAXException { + startElement(uri, localName, "", EMPTY_ATTS); + } + + + /** + *

    + * Start a new element without a qualified name, + * attributes or a namespace URI.

    + * + *

    This method will provide an empty string for the + * namespace URI, and empty string for the qualified name, + * and a default empty attribute list. It invokes + * #startElement(String, String, String, Attributes)} + * directly.

    + * + * @param localName the element's local name + * + * @throws org.xml.sax.SAXException if there is an error + * writing the start-tag, or if a handler further down + * the filter chain raises an exception + * + * @see #startElement(String, String, String, Attributes) + */ + public void startElement (String localName) throws SAXException { + startElement("", localName, "", EMPTY_ATTS); + } + + + /** + *

    + * End an element without a qualfied name. + *

    + * + *

    This method will supply an empty string for the qName. + * It invokes {@link #endElement(String, String, String)} + * directly.

    + * + * @param uri the element's namespace URI + * @param localName the element's local name + * + * @throws org.xml.sax.SAXException if there is an error + * writing the end-tag, or if a handler further down + * the filter chain raises an exception + * + * @see #endElement(String, String, String) + */ + public void endElement(String uri, String localName) + throws SAXException { + endElement(uri, localName, ""); + } + + + /** + *

    + * End an element without a namespace URI or qualfiied name. + *

    + * + *

    This method will supply an empty string for the qName + * and an empty string for the namespace URI. + * It invokes {@link #endElement(String, String, String)} + * directly.

    + * + * @param localName the element's local name` + * + * @throws org.xml.sax.SAXException if there is an error + * writing the end-tag, or if a handler further down + * the filter chain raises an exception + * + * @see #endElement(String, String, String) + */ + public void endElement(String localName) throws SAXException { + endElement("", localName, ""); + } + + + /** + *

    + * Add an empty element without a qualified name or attributes. + *

    + * + *

    This method will supply an empty string for the qualified name + * and an empty attribute list. It invokes + * {@link #emptyElement(String, String, String, Attributes)} + * directly.

    + * + * @param uri the element's namespace URI + * @param localName the element's local name + * + * @throws org.xml.sax.SAXException if there is an error + * writing the empty tag, or if a handler further down + * the filter chain raises an exception + * + * @see #emptyElement(String, String, String, Attributes) + */ + public void emptyElement (String uri, String localName) + throws SAXException { + emptyElement(uri, localName, "", EMPTY_ATTS); + } + + + /** + *

    + * Add an empty element without a namespace URI, qualified + * name or attributes. + *

    + * + *

    This method will supply an empty string for the qualified + * name, and empty string for the namespace URI, and an empty + * attribute list. It invokes + * {@link #emptyElement(String, String, String, Attributes)} + * directly.

    + * + * @param localName the element's local name + * + * @throws org.xml.sax.SAXException if there is an error + * writing the empty tag, or if a handler further down + * the filter chain raises an exception + * + * @see #emptyElement(String, String, String, Attributes) + */ + public void emptyElement (String localName) throws SAXException { + emptyElement("", localName, "", EMPTY_ATTS); + } + + + /** + *

    + * Write an element with character data content. + *

    + * + *

    This is a convenience method to write a complete element + * with character data content, including the start-tag + * and end-tag.

    + * + *

    This method invokes + * {@link #startElement(String, String, String, Attributes)}, + * followed by + * {@link #characters(String)}, followed by + * {@link #endElement(String, String, String)}.

    + * + * @param uri the element's namespace URI + * @param localName the element's local name + * @param qualifiedName the element's default qualified name + * @param atts the element's attributes + * @param content the character data content + * + * @throws org.xml.sax.SAXException if there is an error + * writing the empty tag, or if a handler further down + * the filter chain raises an exception + * + * @see #startElement(String, String, String, Attributes) + * @see #characters(String) + * @see #endElement(String, String, String) + */ + public void dataElement (String uri, String localName, + String qualifiedName, Attributes atts, String content) + throws SAXException { + startElement(uri, localName, qualifiedName, atts); + characters(content); + endElement(uri, localName, qualifiedName); + } + + + /** + *

    + * Write an element with character data content but no attributes. + *

    + * + *

    This is a convenience method to write a complete element + * with character data content, including the start-tag + * and end-tag. This method provides an empty string + * for the qualified name and an empty attribute list.

    + * + *

    This method invokes + * {@link #startElement(String, String, String, Attributes)}, + * followed by + * {@link #characters(String)}, followed by + * {@link #endElement(String, String, String)}.

    + * + * @param uri the element's namespace URI + * @param localName the element's local name + * @param content the character data content + * + * @throws org.xml.sax.SAXException if there is an error + * writing the empty tag, or if a handler further down + * the filter chain raises an exception + * + * @see #startElement(String, String, String, Attributes) + * @see #characters(String) + * @see #endElement(String, String, String) + */ + public void dataElement(String uri, String localName, String content) + throws SAXException { + dataElement(uri, localName, "", EMPTY_ATTS, content); + } + + + /** + *

    + * Write an element with character data content but no attributes + * or namespace URI. + *

    + * + *

    This is a convenience method to write a complete element + * with character data content, including the start-tag + * and end-tag. The method provides an empty string for the + * namespace URI, and empty string for the qualified name, + * and an empty attribute list.

    + * + *

    This method invokes + * {@link #startElement(String, String, String, Attributes)}, + * followed by + * {@link #characters(String)}, followed by + * {@link #endElement(String, String, String)}.

    + * + * @param localName the element's local name + * @param content the character data content + * + * @throws org.xml.sax.SAXException if there is an error + * writing the empty tag, or if a handler further down + * the filter chain raises an exception + * + * @see #startElement(String, String, String, Attributes) + * @see #characters(String) + * @see #endElement(String, String, String) + */ + public void dataElement (String localName, String content) + throws SAXException { + dataElement("", localName, "", EMPTY_ATTS, content); + } + + /** + *

    + * Write a string of character data, with XML escaping. + *

    + * + *

    This is a convenience method that takes an XML + * String, converts it to a character array, then invokes + * {@link #characters(char[], int, int)}.

    + * + * @param data the character data + * @throws org.xml.sax.SAXException if there is an error + * writing the string, or if a handler further down + * the filter chain raises an exception + * @see #characters(char[], int, int) + */ + public void characters(String data) throws SAXException { + char[] ch = data.toCharArray(); + characters(ch, 0, ch.length); + } + + + + /////////////////////////////////////////////////////////////////// + // Internal methods. + /////////////////////////////////////////////////////////////////// + + + /** + *

    + * Force all namespaces to be declared. + *

    + * + *

    + * This method is used on the root element to ensure that + * the predeclared namespaces all appear. + *

    + */ + private void forceNSDecls() { + Enumeration prefixes = forcedDeclTable.keys(); + while (prefixes.hasMoreElements()) { + String prefix = (String)prefixes.nextElement(); + doPrefix(prefix, null, true); + } + } + + + /** + *

    + * Determine the prefix for an element or attribute name. + *

    + * + * TODO: this method probably needs some cleanup. + * + * @param uri the namespace URI + * @param qName the qualified name (optional); this will be used + * to indicate the preferred prefix if none is currently + * bound. + * @param isElement true if this is an element name, false + * if it is an attribute name (which cannot use the + * default namespace). + */ + private String doPrefix (String uri, String qName, boolean isElement) { + String defaultNS = nsSupport.getURI(""); + if ("".equals(uri)) { + if (isElement && defaultNS != null) + nsSupport.declarePrefix("", ""); + return null; + } + String prefix; + if (isElement && defaultNS != null && uri.equals(defaultNS)) { + prefix = ""; + } else { + prefix = nsSupport.getPrefix(uri); + } + if (prefix != null) { + return prefix; + } + prefix = (String) doneDeclTable.get(uri); + if (prefix != null && + ((!isElement || defaultNS != null) && + "".equals(prefix) || nsSupport.getURI(prefix) != null)) { + prefix = null; + } + if (prefix == null) { + prefix = (String) prefixTable.get(uri); + if (prefix != null && + ((!isElement || defaultNS != null) && + "".equals(prefix) || nsSupport.getURI(prefix) != null)) { + prefix = null; + } + } + if (prefix == null && qName != null && !"".equals(qName)) { + int i = qName.indexOf(':'); + if (i == -1) { + if (isElement && defaultNS == null) { + prefix = ""; + } + } else { + prefix = qName.substring(0, i); + } + } + for (; + prefix == null || nsSupport.getURI(prefix) != null; + prefix = "__NS" + ++prefixCounter) + ; + nsSupport.declarePrefix(prefix, uri); + doneDeclTable.put(uri, prefix); + return prefix; + } + + + /** + *

    + * Write a raw character. + *

    + * + * @param c the character to write + * + * @throws org.xml.sax.SAXException if there is an error writing + * the character, this method will throw an IOException + * wrapped in a SAXException + */ + private void write(char c) throws SAXException { + try { + output.write(c); + } + catch (IOException ex) { + throw new SAXException(ex); + } + } + + + /** + *

    + * Write a raw string. + *

    + * + * @param s + * + * @throws org.xml.sax.SAXException if there is an error writing + * the string, this method will throw an IOException + * wrapped in a SAXException + */ + private void write (String s) throws SAXException { + try { + output.write(s); + } + catch (IOException e) { + throw new SAXException(e); + } + } + + + /** + *

    + * Write out an attribute list, escaping values. + *

    + * + *

    + * The names will have prefixes added to them. + *

    + * + * @param atts the attribute list to write + * + * @throws org.xml.SAXException if there is an error writing + * the attribute list, this method will throw an + * IOException wrapped in a SAXException + */ + private void writeAttributes (Attributes atts) + throws SAXException { + int len = atts.getLength(); + for (int i = 0; i < len; i++) { + char[] ch = atts.getValue(i).toCharArray(); + write(' '); + writeName(atts.getURI(i), atts.getLocalName(i), + atts.getQName(i), false); + write("=\""); + writeEsc(ch, 0, ch.length, true); + write('"'); + } + } + + + /** + *

    + * Write an array of data characters with escaping. + *

    + * + * @param ch the array of characters + * @param start the starting position + * @param length the number of characters to use + * @param isAttVal true if this is an attribute value literal + * + * @throws org.xml.SAXException if there is an error writing + * the characters, this method will throw an + * IOException wrapped in a SAXException + */ + private void writeEsc (char[] ch, int start, int length, boolean isAttVal) + throws SAXException { + for (int i = start; i < start + length; i++) { + switch (ch[i]) { + case '&': + write("&"); + break; + case '<': + write("<"); + break; + case '>': + write(">"); + break; + case '\"': + if (isAttVal) { + write("""); + } + else { + write('\"'); + } + break; + default: + if (ch[i] > '\u007f') { + write("&#"); + write(Integer.toString(ch[i])); + write(';'); + } + else { + write(ch[i]); + } + } + } + } + + /** + *

    + * Write an array of data characters without escaping. + *

    + * + * @param ch the array of characters + * @param start the starting position + * @param length the number of characters to use + * + * @throws org.xml.SAXException if there is an error writing + * the characters, this method will throw an + * IOException wrapped in a SAXException. + */ + private void write(char[] ch, int start, int length) + throws SAXException { + + try { + output.write(ch, start, length); + } + catch (IOException e) { + throw new SAXException(e); + } + + } + + + /** + *

    + * Write out the list of namespace declarations. + *

    + * + * @throws org.xml.sax.SAXException This method will throw + * an IOException wrapped in a SAXException if + * there is an error writing the namespace + * declarations + */ + private void writeNSDecls() throws SAXException { + Enumeration prefixes = nsSupport.getDeclaredPrefixes(); + while (prefixes.hasMoreElements()) { + String prefix = (String) prefixes.nextElement(); + String uri = nsSupport.getURI(prefix); + if (uri == null) { + uri = ""; + } + char[] ch = uri.toCharArray(); + write(' '); + if ("".equals(prefix)) { + write("xmlns=\""); + } + else { + write("xmlns:"); + write(prefix); + write("=\""); + } + writeEsc(ch, 0, ch.length, true); + write('\"'); + } + } + + + /** + *

    + * Write an element or attribute name. + *

    + * + * @param uri the namespace URI + * @param localName the local name + * @param qualifiedName the prefixed name, if available, + * or the empty string. + * @param isElement true if this is an element name, false if it + * is an attribute name + * + * @throws org.xml.sax.SAXException this method will throw an + * IOException wrapped in a SAXException if there is + * an error writing the name + */ + private void writeName (String uri, String localName, + String qualifiedName, boolean isElement) + throws SAXException { + String prefix = doPrefix(uri, qualifiedName, isElement); + if (prefix != null && !"".equals(prefix)) { + write(prefix); + write(':'); + } + write(localName); + } + + + + //////////////////////////////////////////////////////////////////// + // Constants. + //////////////////////////////////////////////////////////////////// + + private final Attributes EMPTY_ATTS = new AttributesImpl(); + + + //////////////////////////////////////////////////////////////////// + // Internal state. + //////////////////////////////////////////////////////////////////// + + private Hashtable prefixTable; + private Hashtable forcedDeclTable; + private Hashtable doneDeclTable; + private int elementLevel = 0; + private Writer output; + private NamespaceSupport nsSupport; + private int prefixCounter = 0; + + + /////////////////////////////////////////////////////////////////// + // LexicalHandler methods. + /////////////////////////////////////////////////////////////////// + + + public void endCDATA() {} + + public void endDTD() throws SAXException { + write(">"); + } + + public void startCDATA() {} + + public void comment(char[] ch, int start, int length) + throws SAXException { + write(""); + if (elementLevel < 1) { + write('\n'); + } + } + + public void endEntity(String name) {} + public void startEntity(String name) {} + + public void startDTD(String name, String publicID, String systemID) + throws SAXException { + write(" + * Provides utility methods to compare nodes for deep equality in an + * infoset sense. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.1a2 + * + */ +public class XOMTestCase extends TestCase { + + /** + *

    + * Create a new XOMTestCase with the specified name. + *

    + */ + public XOMTestCase(String name) { + super(name); + } + + /** + *

    + * Asserts that two text nodes are equal. Text nodes are considered + * equal if they are identical char by char, or if both are null. + * Unicode and whitespace normalization is not performed before + * comparison. If the two nodes are not equal, a + * ComparisonFailure is thrown. + *

    + * + * @param expected the text the test should produce + * @param actual the text the test does produce + * + * @throws ComparisonFailure if the text nodes are not equal + */ + public static void assertEquals(Text expected, Text actual) { + assertEquals(null, expected, actual); + } + + + /** + *

    + * Asserts that two text nodes are equal. Text nodes are considered + * equal if they are identical char by char, or if both are null. + * Unicode and whitespace normalization is not performed before + * comparison. If the two nodes are not equal, a + * ComparisonFailure is thrown with the given + * message. + *

    + * + * @param message printed if the texts are not equal + * @param expected the text the test should produce + * @param actual the text the test does produce + * + * @throws ComparisonFailure if the text nodes are not equal + */ + public static void assertEquals( + String message, Text expected, Text actual) { + + if (actual == expected) return; + nullCheck(message, expected, actual); + + assertEquals(message, expected.getValue(), actual.getValue()); + } + + + private static void nullCheck(String message, Node expected, Node actual) { + + if (expected == null) { + throw new ComparisonFailure(message, null, actual.toXML()); + } + else if (actual == null) { + throw new ComparisonFailure(message, expected.toXML(), null); + } + + } + + + /** + *

    + * Asserts that two attribute nodes are equal. + * Attribute nodes are considered equal if their + * qualified names, namespace URIs, and values + * are equal. The type is not considered because it tends not to + * survive a roundtrip. If the two nodes are not equal, a + * ComparisonFailure is thrown. + *

    + * + *

    + * There is special handling for the xml:base + * attribute. In order to facilitate comparison between relative + * and absolute URIs, two xml:base attributes are + * considered equal if one might be a relative form of the other. + *

    + * + * @param expected the attribute the test should produce + * @param actual the attribute the test does produce + * + * @throws ComparisonFailure if the sttributes are not equal + */ + public static void assertEquals( + Attribute expected, Attribute actual) { + assertEquals(null, expected, actual); + } + + + /** + *

    + * Asserts that two attribute nodes are equal. + * Attribute nodes are considered equal if their + * qualified names, namespace URIs, and values + * are equal. The type is not considered because this tends not to + * survive a roundtrip. If the two nodes are not equal, a + * ComparisonFailure is thrown with the given + * message. + *

    + * + *

    + * There is special handling for the xml:base + * attribute. In order to facilitate comparison between relative and + * absolute URIs, two xml:base attributes are + * considered equal if one might be a relative form of the other. + *

    + * + * @param message printed if the attributes are not equal + * @param expected the attribute the test should produce + * @param actual the attribute the test does produce + * + * @throws ComparisonFailure if the attributes are not equal + */ + public static void assertEquals( + String message, Attribute expected, Attribute actual) { + + if (actual == expected) return; + nullCheck(message, expected, actual); + + String value1 = expected.getValue(); + String value2 = actual.getValue(); + if ("xml:base".equals(expected.getQualifiedName())) { + // handle possibility that one is relative and other is not + if (value1.equals(value2)) return; + if (value1.startsWith("../")) { + assertTrue(message, value2.endsWith(value1.substring(2))); + } + else { + assertTrue(message, + value1.endsWith('/' + value2) || value2.endsWith('/' + value1)); + } + } + else { + assertEquals(message, value1, value2); + assertEquals(message, expected.getLocalName(), actual.getLocalName()); + assertEquals(message, + expected.getQualifiedName(), actual.getQualifiedName() + ); + assertEquals(message, + expected.getNamespaceURI(), actual.getNamespaceURI() + ); + } + + } + + + /** + *

    + * Asserts that two DocType nodes are equal. + * DocType nodes are considered equal if their + * root element names, public IDs, and system IDs + * are equal. The internal DTD subsets are not considered. + * If the two nodes are not equal, a + * ComparisonFailure is thrown. + *

    + * + * @param expected the DOCTYPE declaration the test should produce + * @param actual the DOCTYPE declaration the test does produce + * + * @throws ComparisonFailure if the document type declarations + * are not equal + */ + public static void assertEquals(DocType expected, DocType actual) { + assertEquals(null, expected, actual); + } + + + /** + *

    + * Asserts that two DocType nodes are equal. + * DocType nodes are considered equal if their + * root element name, public ID, and system ID + * are equal. The internal DTD subsets are not considered. + * If the two nodes are not equal, a + * ComparisonFailure is thrown with the given + * message. + *

    + * + * @param message printed if the DOCTYPE declarations are not equal + * @param expected the DOCTYPE declaration the test should produce + * @param actual the DOCTYPE declaration the test does produce + * + * @throws ComparisonFailure if the document type declarations + * are not equal + * + */ + public static void assertEquals( + String message, DocType expected, DocType actual) { + + if (actual == expected) return; + nullCheck(message, expected, actual); + + assertEquals(message, + expected.getPublicID(), + actual.getPublicID() + ); + assertEquals(message, + expected.getSystemID(), + actual.getSystemID() + ); + assertEquals(message, + expected.getRootElementName(), + actual.getRootElementName() + ); + } + + + /** + *

    + * Asserts that two element nodes are equal. + * Element nodes are considered equal if their + * qualified names, namespace URI, attributes, + * declared namespaces, and children + * are equal. Consecutive text node children are coalesced + * before the comparison is made. If the two nodes are not equal, + * a ComparisonFailure is thrown. + *

    + * + * @param expected the element the test should produce + * @param actual the element the test does produce + * + * @throws ComparisonFailure if the elements are not equal + */ + public static void assertEquals( + Element expected, Element actual) { + assertEquals(null, expected, actual); + + } + + + /** + *

    + * Asserts that two element nodes are equal. + * Element nodes are considered equal if their + * qualified names, namespace URI, attributes, + * declared namespaces, and children + * are equal. Consecutive text node children are coalesced + * before the comparison is made. Empty text nodes are removed. + * If the two nodes are not equal, + * a ComparisonFailure is thrown with the given + * message. + *

    + * + * @param message printed if the elements are not equal + * @param expected the element the test should produce + * @param actual the element the test does produce + * + * @throws ComparisonFailure if the elements are not equal + */ + public static void assertEquals(String message, + Element expected, Element actual) { + + if (actual == expected) return; + nullCheck(message, expected, actual); + + assertEquals(message, + expected.getLocalName(), + actual.getLocalName() + ); + assertEquals(message, + expected.getNamespacePrefix(), + actual.getNamespacePrefix() + ); + assertEquals(message, + expected.getNamespaceURI(), + actual.getNamespaceURI() + ); + + assertEquals(message, + expected.getAttributeCount(), + actual.getAttributeCount() + ); + + for (int i = 0; i < expected.getAttributeCount(); i++ ) { + Attribute att1 = expected.getAttribute(i); + Attribute att2 + = actual.getAttribute( + att1.getLocalName(), + att1.getNamespaceURI() + ); + assertNotNull(message, att2); + assertEquals(message, att1, att2); + } + + // Check declared namespaces by listing all the prefixes + // on element1 and making sure element2 gives the same value + // for those prefixes, and vice versa. This is necessary + // to handle a few weird cases that arise in XInclude + // when prefixes are declared multiple times, to account for + // the fact that some serializers may drop redundant + // namespace declarations. + for (int i = 0; + i < expected.getNamespaceDeclarationCount(); + i++ ) { + String prefix1 = expected.getNamespacePrefix(i); + String uri1 = expected.getNamespaceURI(prefix1); + assertNotNull(message, actual.getNamespaceURI(prefix1)); + assertEquals(message, + uri1, actual.getNamespaceURI(prefix1) + ); + } + for (int i = 0; + i < actual.getNamespaceDeclarationCount(); + i++ ) { + String prefix1 = actual.getNamespacePrefix(i); + String uri1 = actual.getNamespaceURI(prefix1); + assertNotNull(message, expected.getNamespaceURI(prefix1)); + assertEquals(message, + uri1, expected.getNamespaceURI(prefix1) + ); + } + + compareChildren(message, expected, actual); + + } + + + private static boolean hasAdjacentTextNodes(Element element) { + + boolean previousWasText = false; + int count = element.getChildCount(); + for (int i = 0; i < count; i++) { + Node child = element.getChild(i); + if (child instanceof Text) { + if (previousWasText) return true; + else if ("".equals(child.getValue())) return true; + else previousWasText = true; + } + else { + previousWasText = false; + } + } + return false; + + } + + + private static void compareChildren(String message, Element expected, Element actual) { + + Element expectedCopy = expected; + Element actualCopy = actual; + if (hasAdjacentTextNodes(expected)) { + expectedCopy = combineTextNodes(expected); + } + if (hasAdjacentTextNodes(actual)) { + actualCopy = combineTextNodes(actual); + } + + int count = expectedCopy.getChildCount(); + assertEquals(message, count, actualCopy.getChildCount()); + for (int i = 0; i < count; i++) { + Node child1 = expectedCopy.getChild(i); + // could remove this instanceof Test by having combineTextNodes + // set a list of text indices + if (child1 instanceof Text) { + Node child2 = actualCopy.getChild(i); + assertEquals(message, child1, child2); + } + } + + // now compare everything that isn't text using the original + // element objects; we already know they have the same number + // of non-text nodes + int a = 0; // actualIndex + int e = expected.getChildCount(); + for (int i = 0; i < e; i++) { + Node expectedChild = expected.getChild(i); + if (expectedChild instanceof Text) continue; + // find the next nontext child of actual + Node actualChild = actual.getChild(a); + a++; + while (actualChild instanceof Text) { + actualChild = actual.getChild(a); + a++; + } + assertEquals(message, expectedChild, actualChild); + } + + } + + /* We only need to make an element that has the combined text + * nodes, and something as a child placeholder. + * It does need to have the other pieces. + */ + private static Element combineTextNodes(Element element) { + + Element stub = new Element("a"); + Comment stubc = new Comment("c"); + StringBuffer sb = new StringBuffer(); + int count = element.getChildCount(); + for (int i = 0; i < count; i++) { + Node child = element.getChild(i); + if (child instanceof Text) { + if ("".equals(child.getValue())) continue; + sb.setLength(0); + do { + sb.append(child.getValue()); + i++; + if (i == count) { + break; + } + child = element.getChild(i); + } while (child instanceof Text); + i--; + stub.appendChild(sb.toString()); + } + else { + stub.appendChild(stubc.copy()); + } + } + return stub; + + } + + + /** + *

    + * Asserts that two document nodes are equal. + * Document nodes are considered equal if their + * children are equal. If the two nodes are not equal, + * a ComparisonFailure is thrown. + *

    + * + * @param expected the document the test should produce + * @param actual the document the test does produce + * + * @throws ComparisonFailure if the documents are not equal + */ + public static void assertEquals( + Document expected, Document actual) { + assertEquals(null, expected, actual); + } + + + /** + *

    + * Asserts that two document nodes are equal. + * Document nodes are considered equal if their + * children are equal. If the two nodes are not equal, + * a ComparisonFailure is thrown with the given + * message. + *

    + * + * @param message printed if the documents are not equal + * @param expected the document the test should produce + * @param actual the document the test does produce + * + * @throws ComparisonFailure if the documents are not equal + */ + public static void assertEquals( + String message, Document expected, Document actual) { + + if (actual == expected) return; + nullCheck(message, expected, actual); + + assertEquals(message, + expected.getChildCount(), + actual.getChildCount() + ); + for (int i = 0; i < actual.getChildCount(); i++) { + Node child1 = expected.getChild(i); + Node child2 = actual.getChild(i); + assertEquals(message, child1, child2); + } + + } + + + /** + *

    + * Asserts that two comment nodes are equal. Comment nodes are + * considered equal if they are identical char by char, or if both + * are null. Unicode and whitespace normalization is not performed + * before comparison. If the two nodes are not equal, a + * ComparisonFailure is thrown. + *

    + * + * @param expected the comment the test should produce + * @param actual the comment the test does produce + * + * @throws ComparisonFailure if the comments are not equal + */ + public static void assertEquals(Comment expected, Comment actual) { + assertEquals(null, expected, actual); + } + + + /** + *

    + * Asserts that two comment nodes are equal. Comment nodes are considered + * equal if they are identical char by char, or if both are null. + * Unicode and whitespace normalization is not performed before + * comparison. If the two nodes are not equal, a + * ComparisonFailure is thrown with the given + * message. + *

    + * + * @param message printed if the comments are not equal + * @param expected the comment the test should produce + * @param actual the comment the test does produce + * + * @throws ComparisonFailure if the comments are not equal + */ + public static void assertEquals( + String message, Comment expected, Comment actual) { + + if (actual == expected) return; + nullCheck(message, expected, actual); + assertEquals(message, expected.getValue(), actual.getValue()); + + } + + + /** + *

    + * Asserts that two processing instruction nodes are equal. + * Processing instruction nodes are considered + * equal if they have the same target and the same value. + * If the two nodes are not equal, a + * ComparisonFailure is thrown. + *

    + * + * @param expected the processing instruction the test should produce + * @param actual the processing instruction the test does produce + * + * @throws ComparisonFailure if the processing instructions + * are not equal + */ + public static void assertEquals(ProcessingInstruction expected, + ProcessingInstruction actual) { + assertEquals(null, expected, actual); + } + + + /** + *

    + * Asserts that two processing instruction nodes are equal. + * Processing instruction nodes are considered + * equal if they have the same target and the same value. + * If the two nodes are not equal, a + * ComparisonFailure is thrown with the given + * message. + *

    + * + * @param message printed if the processing instructions are + * not equal + * @param expected the processing instruction the test + * should produce + * @param actual the processing instruction the test does produce + * + * @throws ComparisonFailure if the processing instructions + * are not equal + */ + public static void assertEquals(String message, + ProcessingInstruction expected, + ProcessingInstruction actual) { + + if (actual == expected) return; + nullCheck(message, expected, actual); + + assertEquals(message, expected.getValue(), actual.getValue()); + assertEquals(message, expected.getTarget(), actual.getTarget()); + + } + + + /** + *

    + * Asserts that two namespace nodes are equal. + * Namespace nodes are considered + * equal if they have the same prefix and the same URI. + * If the two nodes are not equal, a + * ComparisonFailure is thrown with the given + * message. + *

    + * + * @param message printed if the namespaces are not equal + * @param expected the namespace the test should produce + * @param actual the namespace the test does produce + * + * @throws ComparisonFailure if the namespaces are not equal + */ + public static void assertEquals(String message, + Namespace expected, Namespace actual) { + + if (actual == expected) return; + nullCheck(message, expected, actual); + + assertEquals(message, expected.getValue(), actual.getValue()); + assertEquals(message, expected.getPrefix(), actual.getPrefix()); + + } + + + /** + *

    + * Asserts that two nodes are equal. If the two nodes are not + * equal a ComparisonFailure is thrown. + * The subclass is not considered. The basic XOM class + * is considered, but the subclass is not. For example, + * a Text object can be equal to an object that + * is an HTMLText, but it can never be equal to + * a Comment. + *

    + * + * @param expected the node the test should produce + * @param actual the node the test does produce + * + * @throws ComparisonFailure if the nodes are not equal + */ + public static void assertEquals(Node expected, Node actual) { + assertEquals(null, expected, actual); + } + + + /** + *

    + * Asserts that two nodes are equal. If the two nodes are not + * equal a ComparisonFailure is thrown with the given + * message. The subclass is not considered. The basic XOM class + * is considered, but the subclass is not. For example, + * a Text object can be equal to an an + * HTMLText object, but it can never be equal to + * a Comment. + *

    + * + * @param message printed if the nodes are not equal + * @param expected the node the test should produce + * @param actual the node the test does produce + * + * @throws ComparisonFailure if the nodes are not equal + */ + public static void assertEquals( + String message, Node expected, Node actual) { + + if (actual == expected) return; + nullCheck(message, expected, actual); + + try { + if (expected instanceof Document) { + assertEquals(message, (Document) expected, (Document) actual); + } + else if (expected instanceof Element) { + assertEquals(message, (Element) expected, (Element) actual); + } + else if (expected instanceof Text) { + assertEquals(message, (Text) expected, (Text) actual); + } + else if (expected instanceof DocType) { + assertEquals(message, + (DocType) expected, + (DocType) actual + ); + } + else if (expected instanceof Comment) { + assertEquals(message, + (Comment) expected, + (Comment) actual + ); + } + else if (expected instanceof ProcessingInstruction) { + assertEquals(message, + (ProcessingInstruction) expected, + (ProcessingInstruction) actual + ); + } + else if (expected instanceof Attribute) { + assertEquals(message, + (Attribute) expected, + (Attribute) actual + ); + } + else if (expected instanceof Namespace) { + assertEquals(message, + (Namespace) expected, + (Namespace) actual + ); + } + else { + throw new IllegalArgumentException( + "Unexpected node type " + + expected.getClass().getName() + ); + } + } + catch (ClassCastException ex) { + throw new ComparisonFailure(message + + "; Mismatched node types: " + + expected.getClass().getName() + " != " + + actual.getClass().getName(), + expected.toXML(), actual.toXML()); + } + + } + + +} diff --git a/src/nu/xom/tests/XOMTestCaseTest.java b/src/nu/xom/tests/XOMTestCaseTest.java new file mode 100644 index 0000000..8fb94a5 --- /dev/null +++ b/src/nu/xom/tests/XOMTestCaseTest.java @@ -0,0 +1,239 @@ +/* Copyright 2005 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ +package nu.xom.tests; + +import junit.framework.ComparisonFailure; +import nu.xom.Attribute; +import nu.xom.Comment; +import nu.xom.Element; +import nu.xom.Namespace; +import nu.xom.Node; +import nu.xom.Text; + +/** + *

    + * Unit tests for XOMTestCase. Added after the first bug discovered in + * XOM 1.0 showed up in XOMTestCase. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.1a2 + * + */ +public class XOMTestCaseTest extends XOMTestCase { + + + public XOMTestCaseTest(String name) { + super(name); + } + + + public void testNullCheck() { + + Text t = new Text(""); + try { + assertEquals(t, null); + fail("Allowed comparison with null"); + } + catch (ComparisonFailure ex) { + assertNotNull(ex.getMessage()); + } + + try { + assertEquals(null, t); + fail("Allowed comparison with null"); + } + catch (ComparisonFailure ex) { + assertNotNull(ex.getMessage()); + } + + } + + + public void testNamespaceEqualsItself() { + Namespace ns = new Namespace("pre", "http://www.example.org", null); + assertEquals(ns, ns); + } + + + public void testCompareMismatchedTypes() { + + Node n1 = new Text(""); + Node n2 = new Attribute("name", "value"); + + try { + assertEquals(n1, n2); + fail("Text equals Attribute?!"); + } + catch (ComparisonFailure ex) { + assertNotNull(ex.getMessage()); + } + + try { + assertEquals(n2, n1); + fail("Text equals Attribute?!"); + } + catch (ComparisonFailure ex) { + assertNotNull(ex.getMessage()); + } + + } + + + public void testCompareChildren() { + + Element e1 = new Element("e"); + Element e2 = new Element("e"); + e1.appendChild(new Comment("a")); + e2.appendChild(new Comment("b")); + try { + assertEquals("BOO!", e1, e2); + fail("didn't check children"); + } + catch (ComparisonFailure ex) { + assertTrue(ex.getMessage().indexOf("BOO!") >= 0 ); + } + + } + + + public void testCompareMismatchedNullNodeTypes() { + + Node n1 = new Text(""); + Node n2 = null; + + try { + assertEquals(n1, n2); + fail("Text equals null?!"); + } + catch (ComparisonFailure ex) { + assertNotNull(ex.getMessage()); + } + + } + + + public void testCompareAttributesAsNodes() { + + Node a1 = new Attribute("test", "value"); + Node a2 = a1.copy(); + assertEquals(a1, a2); + + } + + + public void testCombineTextNodes() { + + Element e1 = new Element("test"); + e1.appendChild("1"); + e1.appendChild("2"); + Element e2 = new Element("test"); + e2.appendChild("12"); + assertEquals(e1, e2); + assertEquals(2, e1.getChildCount()); + + } + + + public void testTrickyCombineTextNodes() { + + Element e1 = new Element("test"); + e1.appendChild("12"); + e1.appendChild("3"); + Element e2 = new Element("test"); + e2.appendChild("1"); + e2.appendChild("23"); + assertEquals(e1, e2); + assertEquals(2, e1.getChildCount()); + + } + + + public void testCombineThreeTextNodes() { + + Element e1 = new Element("test"); + e1.appendChild("1"); + e1.appendChild("2"); + e1.appendChild("3"); + Element e2 = new Element("test"); + e2.appendChild("123"); + assertEquals(e1, e2); + + } + + + public void testCombineThreeTextNodes2() { + + Element e1 = new Element("test"); + e1.appendChild("\n"); + e1.appendChild(new Element("p")); + e1.appendChild("1"); + e1.appendChild("2"); + e1.appendChild("3"); + Element e2 = new Element("test"); + e2.appendChild("\n"); + e2.appendChild(new Element("p")); + e2.appendChild("123"); + assertEquals(e2, e1); + + } + + + public void testUnequalElements() { + + Element e1 = new Element("test"); + e1.appendChild("1"); + e1.appendChild(new Element("b")); + e1.appendChild("3"); + Element e2 = new Element("test"); + e2.appendChild("1"); + e2.appendChild(new Element("c")); + e2.appendChild("3"); + try { + assertEquals(e1, e2); + fail("Unequal elements compared equal"); + } + catch (ComparisonFailure success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testCompareXMLBaseAttributes() { + + Node a1 = new Attribute("xml:base", Namespace.XML_NAMESPACE, "value.xml"); + Node a2 = new Attribute("xml:base", Namespace.XML_NAMESPACE, "./value.xml"); + assertEquals(a1, a2); + + } + + + public void testAssertEqualsEmptyElementsAndElementThatOnlyContainsEmptyText() { + + Element e1 = new Element("a"); + Element e2 = new Element("a"); + e2.appendChild(""); + assertEquals(e1, e2); + + } + +} diff --git a/src/nu/xom/tests/XOMTests.java b/src/nu/xom/tests/XOMTests.java new file mode 100644 index 0000000..7243622 --- /dev/null +++ b/src/nu/xom/tests/XOMTests.java @@ -0,0 +1,94 @@ +/* Copyright 2002-2005, 2009 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.tests; + +import junit.framework.Test; +import junit.framework.TestSuite; + +/** + *

    + * Collect most of XOM's test cases into a single class, + * excepting those tests that are insanely expensive or + * that fail due to Java bugs. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.2b3 + * + */ +public class XOMTests { + + + public static Test suite() { + + TestSuite result = new TestSuite(); + result.addTest(new TestSuite(IDTest.class)); + result.addTest(new TestSuite(XOMTestCaseTest.class)); + result.addTest(new TestSuite(XPathTest.class)); + result.addTest(new TestSuite(VerifierTest.class)); + result.addTest(new TestSuite(SubclassTest.class)); + result.addTest(new TestSuite(NodeFactoryTest.class)); + result.addTest(new TestSuite(ParentNodeTest.class)); + result.addTest(new TestSuite(LeafNodeTest.class)); + result.addTest(new TestSuite(AttributeTest.class)); + result.addTest(new TestSuite(AttributeTypeTest.class)); + result.addTest(new TestSuite(ElementTest.class)); + result.addTest(new TestSuite(CommentTest.class)); + result.addTest(new TestSuite(ProcessingInstructionTest.class)); + result.addTest(new TestSuite(DocumentTest.class)); + // this next test will fail is run after DocType test. + // needs more investigation + result.addTest(new TestSuite(XSLTransformTest.class)); + result.addTest(new TestSuite(DocTypeTest.class)); + result.addTest(new TestSuite(AttributesTest.class)); + result.addTest(new TestSuite(NamespaceNodeTest.class)); + result.addTest(new TestSuite(NamespacesTest.class)); + // Too slow to run routinely + // result.addTest(new TestSuite(MegaTest.class)); + result.addTest(new TestSuite(XMLExceptionTest.class)); + result.addTest(new TestSuite(XPathExceptionTest.class)); + result.addTest(new TestSuite(ValidityExceptionTest.class)); + result.addTest(new TestSuite(ParsingExceptionTest.class)); + result.addTest(new TestSuite(XSLExceptionTest.class)); + result.addTest(new TestSuite(XIncludeExceptionTest.class)); + result.addTest(new TestSuite(CanonicalizationExceptionTest.class)); + result.addTest(new TestSuite(CDATASectionTest.class)); + result.addTest(new TestSuite(NodesTest.class)); + // EBCDIC test fails due to bugs in the Java class library + // result.addTest(new TestSuite(EBCDICTest.class)); + result.addTest(new TestSuite(RoundTripTest.class)); + result.addTest(new TestSuite(DOMConverterTest.class)); + result.addTest(new TestSuite(InfoTest.class)); + result.addTest(new TestSuite(SerializerTest.class)); + result.addTest(new TestSuite(CanonicalizerTest.class)); + result.addTest(new TestSuite(BuilderTest.class)); + result.addTest(new TestSuite(XIncludeTest.class)); + result.addTest(new TestSuite(SAXConverterTest.class)); + result.addTest(new TestSuite(BaseURITest.class)); + result.addTest(new TestSuite(TextTest.class)); + result.addTest(new TestSuite(EncodingTest.class)); + return result; + + } + + +} diff --git a/src/nu/xom/tests/XPathExceptionTest.java b/src/nu/xom/tests/XPathExceptionTest.java new file mode 100644 index 0000000..a7865e1 --- /dev/null +++ b/src/nu/xom/tests/XPathExceptionTest.java @@ -0,0 +1,170 @@ +/* Copyright 2005 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.tests; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectOutputStream; + +import nu.xom.Element; +import nu.xom.XPathException; +import nu.xom.XPathTypeException; + +/** + *

    + * Unit tests for the XPathException class. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.1b4 + * + */ +public class XPathExceptionTest extends XOMTestCase { + + + private XPathException ex; + private Exception cause; + + + public XPathExceptionTest(String name) { + super(name); + } + + + protected void setUp() { + ex = new XPathException("message"); + cause = new Exception(); + } + + + public void testConstructor() { + String message = "testing 1-2-3"; + XPathException ex = new XPathException(message, cause); + assertEquals(message, ex.getMessage()); + assertEquals(cause, ex.getCause()); + } + + + public void testInitCause() { + + assertNull(ex.getCause()); + ex.initCause(cause); + assertEquals(cause, ex.getCause()); + + try { + ex.initCause(null); + fail("Reinitialized cause over null"); + } + catch (IllegalStateException success) { + assertNotNull(success.getMessage()); + } + + try { + ex.initCause(new Exception()); + fail("Reinitialized cause over null"); + } + catch (IllegalStateException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testNullInitCause() { + + XPathException ex = new XPathException(null, null); + assertNull(ex.getCause()); + + try { + ex.initCause(new Exception()); + fail("Reinitialized cause over null"); + } + catch (IllegalStateException success) { + assertNotNull(success.getMessage()); + } + + try { + ex.initCause(null); + fail("Reinitialized cause over null"); + } + catch (IllegalStateException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testSelfCause() { + + try { + ex.initCause(ex); + fail("Allowed self-causation"); + } + catch (IllegalArgumentException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testGetMessage() { + Exception ex = new XPathException("testing"); + assertEquals("testing", ex.getMessage()); + } + + public void testGetXPathExpression() { + + Element parent = new Element("Test"); + + try { + parent.query("This is not an XPath expression"); + fail("Allowed malformed query"); + } + catch (XPathException success) { + assertEquals( + "This is not an XPath expression", success.getXPath()); + } + + } + + public void testSerializeXPathTypeException() throws IOException { + + Element parent = new Element("Test"); + Element child = new Element("child"); + parent.appendChild(child); + + try { + parent.query("count(*)"); + fail("Allowed query to return number"); + } + catch (XPathTypeException success) { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + ObjectOutputStream oout = new ObjectOutputStream(out); + oout.writeObject(success); + oout.close(); + } + + } + + + +} diff --git a/src/nu/xom/tests/XPathTest.java b/src/nu/xom/tests/XPathTest.java new file mode 100644 index 0000000..38d6eaa --- /dev/null +++ b/src/nu/xom/tests/XPathTest.java @@ -0,0 +1,2461 @@ +/* Copyright 2005, 2006, 2012, 2018 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.tests; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.PrintStream; +import java.net.MalformedURLException; + +import nu.xom.Attribute; +import nu.xom.Builder; +import nu.xom.Comment; +import nu.xom.DocType; +import nu.xom.Document; +import nu.xom.Element; +import nu.xom.Elements; +import nu.xom.IllegalNameException; +import nu.xom.Namespace; +import nu.xom.NamespaceConflictException; +import nu.xom.Node; +import nu.xom.Nodes; +import nu.xom.ParsingException; +import nu.xom.ProcessingInstruction; +import nu.xom.Text; +import nu.xom.XPathContext; +import nu.xom.XPathException; +import nu.xom.XPathTypeException; + +/** + *

    + * Unit tests for XPath functionality + *

    + * + * @author Elliotte Rusty Harold + * @version 1.2.11 + * + */ +public class XPathTest extends XOMTestCase { + + public XPathTest(String name) { + super(name); + } + + + // This class tests error conditions, which Xerces + // annoyingly logs to System.err. This hides System.err + // before each test and restores it after each test. + private PrintStream systemErr = System.err; + + protected void setUp() { + System.setErr(new PrintStream(new ByteArrayOutputStream())); + } + + + protected void tearDown() { + System.setErr(systemErr); + } + + public void testAllNodesQuery() { + + Document doc = new Document(new Element("doc")); + Nodes subset = doc.query("//. | /"); + assertEquals(2, subset.size()); + + } + + + /* +1 + +2 +3 + +4 + */ + public void testDescendantAxisOrder() { + + Element a = new Element("a"); + Document doc = new Document(a); + Element x1 = new Element("x"); + x1.appendChild("a1"); + Element x2 = new Element("x"); + x2.appendChild("b2"); + Element x3 = new Element("x"); + x3.appendChild("c3"); + Element x4 = new Element("x"); + x4.appendChild("d4"); + a.appendChild(x1); + Element b = new Element("b"); + b.appendChild(x2); + b.appendChild(x3); + a.appendChild(b); + a.appendChild(x4); + Nodes result = doc.query("//x"); + assertEquals(4, result.size()); + assertTrue(result.get(0) instanceof Element); + assertTrue(result.get(1) instanceof Element); + assertTrue(result.get(2) instanceof Element); + assertTrue(result.get(3) instanceof Element); + assertEquals(x1, result.get(0)); + assertEquals(x2, result.get(1)); + assertEquals(x3, result.get(2)); + assertEquals(x4, result.get(3)); + + } + + + public void testSimpleQuery() { + + Element parent = new Element("Test"); + Element child = new Element("child"); + parent.appendChild(child); + + Nodes result = parent.query("*"); + assertEquals(1, result.size()); + assertEquals(child, result.get(0)); + + } + + + public void testChildWildCard() { + + Element a = new Element("a"); + Element b = new Element("b"); + Element c = new Element("c"); + Element d = new Element("d"); + + a.appendChild(b); + b.appendChild(c); + c.appendChild(d); + + Nodes result = a.query("/a/b/*/d"); + assertEquals(1, result.size()); + assertEquals(d, result.get(0)); + + } + + + public void testSimpleChild() { + + Element a = new Element("a"); + Nodes result = a.query("/a"); + assertEquals(1, result.size()); + + } + + + public void testSimpleWildCard() { + + Element a = new Element("a"); + Nodes result = a.query("/*"); + assertEquals(1, result.size()); + + } + + + public void testLongs() { + + Document doc = new Document(new Element("root")); + + Nodes result = doc.query("/*[5000000000=5000000000]"); + assertEquals(1, result.size()); + + result = doc.query("/*[5000000000 < 5000000001]"); + assertEquals(1, result.size()); + result = doc.query("/*[5000000000 > 5000000001]"); + assertEquals(0, result.size()); + result = doc.query("/*[5000000001 >= 5000000001]"); + assertEquals(1, result.size()); + result = doc.query("/*[5000000001 > 5000000001]"); + assertEquals(0, result.size()); + + } + + + public void testNamespaceNodeParent() { + + Element root = new Element("root"); + Document doc = new Document(root); + Element child = new Element("pre:child", "http://www.ietf.org"); + root.appendChild(child); + + Nodes result = doc.query("/root/*/namespace::*/parent::*"); + assertEquals(1, result.size()); + assertEquals(child, result.get(0)); + + } + + + public void testNamespaceNodeChild() { + + Element root = new Element("root"); + Document doc = new Document(root); + Element child = new Element("pre:child", "http://www.ietf.org"); + root.appendChild(child); + + Nodes result = doc.query("/root/*/namespace::*/child::*"); + assertEquals(0, result.size()); + + } + + + // JAXEN-46: http://jira.codehaus.org/browse/JAXEN-46 + public void testHashInAttributeValue() { + + Element root = new Element("test"); + Document doc = new Document(root); + Attribute test = new Attribute("test", "SUBSCRIBER"); + Attribute test1 = new Attribute("test1", "SUBSCRIBER#"); + root.addAttribute(test); + root.addAttribute(test1); + + Nodes result = doc.query("test[@test='SUBSCRIBER']"); + assertEquals(1, result.size()); + assertEquals(root, result.get(0)); + + result = doc.query("test[@test1='SUBSCRIBER#']"); + assertEquals(1, result.size()); + assertEquals(root, result.get(0)); + + } + + + public void testUseRootNodeWhenQueryingDocumentLessElements() { + + Element test = new Element("Test"); + + Nodes result = test.query("/*"); + assertEquals(1, result.size()); + assertEquals(test, result.get(0)); + + try { + test.query("/"); + fail("Did not throw exception when querying rootless document for root"); + } + catch (XPathException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testUseRootNodeWhenQueryingDocumentLessElements2() { + + Element test = new Element("Test"); + Nodes result = test.query("/None"); + assertEquals(0, result.size()); + + } + + + public void testUseRootNodeWhenQueryingDocumentLessElements3() { + + Element test = new Element("Test"); + + Nodes result = test.query("//*"); + assertEquals(1, result.size()); + + } + + + public void testUseRootNodeWhenQueryingDocumentLessElements4() { + + Element test = new Element("Test"); + + Nodes result = test.query("//* | /"); + assertEquals(1, result.size()); + + } + + + public void testUnionOfNodesWithInts() { + + Element parent = new Element("Test"); + Element child = new Element("child"); + parent.appendChild(child); + + try { + parent.query("* | count(/*)"); + fail("Allowed query returning non-node-set"); + } + catch (XPathException success) { + assertNotNull(success.getMessage()); + } + + } + + /* Commented out because files have been removed + public void testFranceschet1() throws ParsingException, IOException { + + Builder builder = new Builder(); + Document doc = builder.build( + "http://staff.science.uva.nl/~francesc/xpathmark/benchmark_canon.xml" + ); + Element root = doc.getRootElement(); + Elements inputs = root.getChildElements("document"); + Element input = inputs.get(0).getFirstChildElement("site"); + input.detach(); + + Nodes doc1Queries = root.query("child::query[starts-with(@id, 'Q')]"); + + for (int i = 0; i < doc1Queries.size(); i++) { + Element query = (Element) doc1Queries.get(i); + String xpath = query.getFirstChildElement("syntax").getValue(); + String id = query.getAttributeValue("id"); + // this query needs special comparison code due to + // adjacent text nodes + if ("Q21".equals(id)) continue; + // test suite bug relating to id() function + else if (xpath.indexOf("id(") >= 0) continue; + Nodes result = input.query(xpath); + Element answer = query.getFirstChildElement("answer"); + Nodes expected = new Nodes(); + for (int j = 0; j < answer.getChildCount(); j++) { + Node node = answer.getChild(j); + if (node instanceof Text) { + if (!("".equals(node.getValue().trim()))) { + expected.append(node); + } + } + else { + expected.append(node); + } + } + assertEquals("Failed query " + id, expected.size(), result.size()); + for (int j = 0; j < result.size(); j++) { + Node expectedNode = expected.get(j); + Node actualNode = result.get(j); + assertEquals(id + " " + expectedNode.toXML() + " " + actualNode.toXML(), expectedNode, actualNode); + } + } + + } + + + public void testFranceschet2() throws ParsingException, IOException { + + Builder builder = new Builder(); + Document doc = builder.build( + "http://staff.science.uva.nl/~francesc/xpathmark/benchmark_canon.xml" + ); + Element root = doc.getRootElement(); + Elements inputs = root.getChildElements("document"); + Element input = inputs.get(1); + Document html = new Document(new Element("fake")); + int p = 0; + while (true) { + Node node = input.getChild(0); + if (node instanceof Element) break; + else { + node.detach(); + if (node instanceof Text) continue; + html.insertChild(node, p++); + } + } + Node newroot = input.getChild(0); + newroot.detach(); + html.setRootElement((Element) newroot); + while (input.getChildCount() > 0) { + Node node = input.getChild(0); + node.detach(); + if (node instanceof Text) continue; + html.appendChild(node); + } + + Nodes doc2Queries = root.query("child::query[starts-with(@id, 'A')]"); + + XPathContext context = new XPathContext(); + context.addNamespace("svg", "http://www.w3.org/2000/svg"); + context.addNamespace("xlink", "http://www.w3.org/1999/xlink"); + + for (int i = 0; i < doc2Queries.size(); i++) { + Element query = (Element) doc2Queries.get(i); + String xpath = query.getFirstChildElement("syntax").getValue(); + String id = query.getAttributeValue("id"); + + Nodes result = html.query(xpath, context); + Element answer = query.getFirstChildElement("answer"); + Nodes expected = new Nodes(); + for (int j = 0; j < answer.getChildCount(); j++) { + Node node = answer.getChild(j); + if (node instanceof Text) { + if (!("".equals(node.getValue().trim()))) { + expected.append(node); + } + } + else { + expected.append(node); + } + } + assertEquals("Failed query " + id, expected.size(), result.size()); + for (int j = 0; j < result.size(); j++) { + Node expectedNode = expected.get(j); + Node actualNode = result.get(j); + assertEquals(id + " " + expectedNode.toXML() + " " + + actualNode.toXML(), expectedNode, actualNode); + } + } + + } */ + + + public void testQueryThatReturnsNumber() { + + Element parent = new Element("Test"); + Element child = new Element("child"); + parent.appendChild(child); + + try { + parent.query("count(*)"); + fail("Allowed query to return number"); + } + catch (XPathTypeException success) { + assertNotNull(success.getMessage()); + assertEquals(new Double(1), success.getReturnValue()); + assertEquals("count(*)", success.getXPath()); + } + + } + + + public void testStartsWith() { + + Element parent = new Element("Test"); + Element child = new Element("child"); + child.addAttribute(new Attribute("foo", "bar")); + parent.appendChild(child); + Element child2 = new Element("child"); + child2.addAttribute(new Attribute("foo", "big")); + parent.appendChild(child2); + + Nodes result = parent.query(".//*[starts-with(@foo, 'ba')]"); + assertEquals(1, result.size()); + assertEquals(child, result.get(0)); + + } + + + // a jaxen extension function + public void testEndsWith() { + + Element parent = new Element("Test"); + Element child = new Element("child"); + child.addAttribute(new Attribute("foo", "bar")); + parent.appendChild(child); + + try { + parent.query(".//*[ends-with(@foo, 'ar')]"); + fail("Allowed Jaxen extension function"); + } + catch (XPathException success) { + assertTrue(success.getMessage().indexOf("ends-with") >= 0); + } + + } + + + public void testEmptyTextNodesDontCount() { + + Element parent = new Element("Test"); + Element child1 = new Element("child1"); + parent.appendChild(child1); + parent.appendChild(new Text("")); + parent.appendChild(new Text("")); + Element child2 = new Element("child2"); + parent.appendChild(child2); + + Nodes result = parent.query("*"); + assertEquals(2, result.size()); + assertEquals(child1, result.get(0)); + + result = parent.query("*[2]"); + assertEquals(1, result.size()); + assertEquals(child2, result.get(0)); + + } + + + public void testEmptyTextNodesDontCount2() { + + Element parent = new Element("Test"); + parent.appendChild(new Text("")); + Element child1 = new Element("child1"); + parent.appendChild(child1); + Element child2 = new Element("child2"); + parent.appendChild(child2); + + Nodes result = parent.query("node()"); + assertEquals(2, result.size()); + assertEquals(child1, result.get(0)); + assertEquals(child2, result.get(1)); + + result = parent.query("node()[1]"); + assertEquals(1, result.size()); + assertEquals(child1, result.get(0)); + + } + + + public void testEmptyTextNodesAtEndOfParent() { + + Element parent = new Element("Test"); + Element child1 = new Element("child1"); + parent.appendChild(child1); + Element child2 = new Element("child2"); + parent.appendChild(child2); + parent.appendChild(new Text("")); + parent.appendChild(new Text("")); + parent.appendChild(new Text("")); + + Nodes result = parent.query("node()"); + assertEquals(2, result.size()); + assertEquals(child1, result.get(0)); + assertEquals(child2, result.get(1)); + + result = parent.query("node()[1]"); + assertEquals(1, result.size()); + assertEquals(child1, result.get(0)); + + } + + + public void testEmptyTextNodeNextToNonEmptyTextNode() { + + Element parent = new Element("Test"); + Text empty = new Text(""); + parent.appendChild(empty); + Text nonempty = new Text("value"); + parent.appendChild(nonempty); + Element child1 = new Element("child1"); + parent.appendChild(child1); + Element child2 = new Element("child2"); + parent.appendChild(child2); + + Nodes result = parent.query("node()"); + assertEquals(4, result.size()); + assertEquals(empty, result.get(0)); + assertEquals(nonempty, result.get(1)); + + result = parent.query("node()[1]"); + assertEquals(2, result.size()); + assertEquals(empty, result.get(0)); + + } + + + public void testBasicPredicate() { + + Element parent = new Element("Test"); + Element child1 = new Element("child"); + child1.appendChild("1"); + parent.appendChild(child1); + Element child2 = new Element("child"); + child2.appendChild("2"); + parent.appendChild(child2); + Element child3 = new Element("child"); + child3.appendChild("3"); + parent.appendChild(child3); + + Nodes result = parent.query("*[.='2']"); + assertEquals(1, result.size()); + assertEquals(child2, result.get(0)); + + } + + + public void testXMLLang() { + + Element parent = new Element("Test"); + Element child1 = new Element("child"); + child1.addAttribute(new Attribute("xml:lang", + "http://www.w3.org/XML/1998/namespace", "en")); + parent.appendChild(child1); + Element child2 = new Element("child"); + child2.appendChild("2"); + child2.addAttribute(new Attribute("xml:lang", + "http://www.w3.org/XML/1998/namespace", "fr")); + parent.appendChild(child2); + Element child3 = new Element("child"); + child3.appendChild("3"); + parent.appendChild(child3); + Element child4 = new Element("child"); + child4.appendChild("4"); + child4.addAttribute(new Attribute("xml:lang", + "http://www.w3.org/XML/1998/namespace", "en-US")); + parent.appendChild(child4); + + Nodes result = parent.query("child::*[lang('en')]"); + assertEquals(2, result.size()); + assertEquals(child1, result.get(0)); + assertEquals(child4, result.get(1)); + + } + + + public void testXMLPrefixIsAlwaysBound() { + + Element parent = new Element("Test"); + Element child1 = new Element("child"); + child1.addAttribute(new Attribute("xml:lang", + "http://www.w3.org/XML/1998/namespace", "en")); + parent.appendChild(child1); + Element child2 = new Element("child"); + child2.appendChild("2"); + child2.addAttribute(new Attribute("xml:lang", + "http://www.w3.org/XML/1998/namespace", "fr")); + parent.appendChild(child2); + Element child3 = new Element("child"); + child3.appendChild("3"); + parent.appendChild(child3); + Element child4 = new Element("child"); + child4.appendChild("4"); + child4.addAttribute(new Attribute("xml:lang", + "http://www.w3.org/XML/1998/namespace", "en-US")); + parent.appendChild(child4); + + Nodes result = parent.query("child::*/@xml:lang"); + assertEquals(3, result.size()); + + } + + + public void testCantRebindXMLPrefix() { + + XPathContext context = new XPathContext(); + try { + context.addNamespace("xml", "http://www.example.org"); + fail("Rebound xml prefix"); + } + catch (NamespaceConflictException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testCantBindNonPrefix() { + + XPathContext context = new XPathContext(); + try { + context.addNamespace("foo:bar", "http://www.example.org"); + fail("Allowed preix with colon"); + } + catch (IllegalNameException success) { + assertNotNull(success.getMessage()); + } + + try { + context.addNamespace("foo bar", "http://www.example.org"); + fail("Allowed preix with space"); + } + catch (IllegalNameException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testLookupPrefix() { + XPathContext context = new XPathContext(); + context.addNamespace("foo", "http://www.example.org"); + String url = context.lookup("foo"); + assertEquals("http://www.example.org", url); + } + + + public void testLookupNonexistentPrefix() { + XPathContext context = new XPathContext(); + context.addNamespace("foo", "http://www.example.org"); + String url = context.lookup("bar"); + assertNull(url); + } + + + public void testParentAxis() { + + Element parent = new Element("Test"); + Element child = new Element("child"); + parent.appendChild(child); + + Nodes result = child.query("parent::*"); + assertEquals(1, result.size()); + assertEquals(parent, result.get(0)); + + } + + + public void testAncestorAxis() { + + Element grandparent = new Element("Test"); + new Document(grandparent); + Element parent = new Element("Test"); + Element child = new Element("child"); + parent.appendChild(child); + grandparent.appendChild(parent); + + Nodes result = child.query("ancestor::*"); + assertEquals(2, result.size()); + assertEquals(grandparent, result.get(0)); + assertEquals(parent, result.get(1)); + + } + + + public void testParentAxisWithDocument() { + + Element root = new Element("Test"); + new Document(root); + + Nodes result = root.query("parent::*"); + assertEquals(0, result.size()); + + } + + + public void testParentAxisWithNodeMatchingDocument() { + + Element root = new Element("Test"); + Document doc = new Document(root); + + Nodes result = root.query("parent::node()"); + assertEquals(1, result.size()); + assertEquals(doc, result.get(0)); + + } + + + public void testSubstringFunction() { + + Element root = new Element("Test"); + new Document(root); + + Nodes result = root.query("/*[substring('12345', 0, 3)='12']"); + assertEquals(1, result.size()); + assertEquals(root, result.get(0)); + + } + + + public void testPrecedingAxisWithElementName() { + + Element root = new Element("Test"); + Document doc = new Document(root); + + Nodes result = doc.query("/descendant::*/preceding::x"); + assertEquals(0, result.size()); + + } + + + + + public void testDocTypeIsNotAnXPathNode() { + + Element root = new Element("root"); + Document doc = new Document(root); + DocType doctype = new DocType("root"); + doc.setDocType(doctype); + + Nodes result = doc.query("child::node()[1]"); + assertEquals(1, result.size()); + assertEquals(root, result.get(0)); + + } + + + public void testGetNodeBeforeDocType() { + + Element root = new Element("root"); + Document doc = new Document(root); + DocType doctype = new DocType("root"); + doc.setDocType(doctype); + Comment c = new Comment("test"); + doc.insertChild(c, 0); + + Nodes result = doc.query("child::node()[1]"); + assertEquals(1, result.size()); + assertEquals(c, result.get(0)); + + } + + + public void testCantUseDocTypeAsXPathContextNode() { + + Element root = new Element("root"); + Document doc = new Document(root); + DocType doctype = new DocType("root"); + doc.setDocType(doctype); + + try { + doctype.query("/"); + fail("Allowed DocType as context node"); + } + catch (XPathException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testDescendantAxis() { + + Element grandparent = new Element("Test"); + Document doc = new Document(grandparent); + Element parent = new Element("Test"); + Element child = new Element("child"); + parent.appendChild(child); + grandparent.appendChild(parent); + + Nodes result = doc.query("descendant::*"); + assertEquals(3, result.size()); + assertEquals(grandparent, result.get(0)); + assertEquals(parent, result.get(1)); + assertEquals(child, result.get(2)); + + } + + + public void testGetElementQName() { + + Element grandparent = new Element("Test"); + Document doc = new Document(grandparent); + Element parent = new Element("Test"); + Element child1 = new Element("pre:child", "http://www.example.org/"); + Element child2 = new Element("pre:child", "http://www.example.com/"); + parent.appendChild(child1); + parent.appendChild(child2); + grandparent.appendChild(parent); + + Nodes result = doc.query("descendant::*[name(.)='pre:child']"); + assertEquals(2, result.size()); + assertEquals(child1, result.get(0)); + assertEquals(child2, result.get(1)); + + } + + + // This does not actually hit the method I was aiming at. + // Apparently, Jaxen never actually invokes + // getElementStringValue() + public void testGetElementStringValue() { + + Element grandparent = new Element("Test"); + Document doc = new Document(grandparent); + Element parent = new Element("Test"); + Element child1 = new Element("child"); + child1.appendChild("foo"); + Element child2 = new Element("child"); + child2.appendChild("bar"); + parent.appendChild(child1); + parent.appendChild(child2); + grandparent.appendChild(parent); + + Nodes result = doc.query("descendant::*[.='foo']"); + assertEquals(1, result.size()); + assertEquals(child1, result.get(0)); + + } + + + public void testGetNonExistentNode() { + + Element grandparent = new Element("Test"); + Document doc = new Document(grandparent); + Element parent = new Element("Test"); + Element child1 = new Element("child"); + child1.appendChild("foo"); + Element child2 = new Element("child"); + child2.appendChild("bar"); + parent.appendChild(child1); + parent.appendChild(child2); + grandparent.appendChild(parent); + + Nodes result = doc.query("/Test/Test/*[12]"); + assertEquals(0, result.size()); + + } + + + public void testGetAttributeQName() { + + Element grandparent = new Element("Test"); + Document doc = new Document(grandparent); + Element parent = new Element("Test"); + Attribute a1 = new Attribute("pre:attribute", "http://www.example.org/", "test"); + Attribute a2 = new Attribute("pre:attribute", "http://www.example.com/", "test"); + parent.addAttribute(a2); + grandparent.addAttribute(a1); + grandparent.appendChild(parent); + + Nodes result = doc.query("descendant::*/attribute::*[name(.)='pre:attribute']"); + assertEquals(2, result.size()); + assertTrue(result.contains(a1)); + assertTrue(result.contains(a2)); + + } + + + public void testGetNamespaceStringValue() { + + Element test = new Element("Test", "http://www.example.com/"); + + Nodes result = test.query("self::*[contains(namespace::*, 'http://')]"); + assertEquals(1, result.size()); + assertEquals(test, result.get(0)); + + } + + + private File inputDir = new File("data"); + + + public void testGetDocument() throws MalformedURLException { + + Element element = new Element("test"); + File f = new File(inputDir, "prettyxml.xml"); + String url = f.toURI().toString(); + try { + element.query("document('" + url + "')/*"); + fail("allowed document() function"); + } + catch(XPathException success) { + assertTrue(success.getMessage().indexOf("document") >= 0); + } + + } + + + public void testGetMultipleNodesViaDocumentFunction() + throws MalformedURLException { + + Element element = new Element("test"); + File f = new File(inputDir, "prettyxml.xml"); + String url = f.toURI().toString(); + try { + element.query("document('" + url + "')//*"); + fail("allowed document() function"); + } + catch(XPathException success) { + assertTrue(success.getMessage().indexOf("document") >= 0); + } + + } + + + public void testDoubleDocument() throws MalformedURLException { + + Element element = new Element("test"); + File f1 = new File(inputDir, "prettyxml.xml"); + String url1 = f1.toURI().toString(); + File f2 = new File(inputDir, "test.xml"); + String url2 = f2.toURI().toString(); + try { + element.query("document('" + url1 + "')/* | " + + "document('" + url2 + "')/*"); + fail("allowed document() function"); + } + catch(XPathException success) { + assertTrue(success.getMessage().indexOf("document") >= 0); + } + + } + + + public void testGetNonExistentDocument() { + + Element element = new Element("test"); + try { + element.query("document('http://www.ibiblio.org/aksdjhk/')/*"); + fail("That file doesn't exist!"); + } + catch (XPathException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testMalformedDocument() { + + Element element = new Element("test"); + try { + element.query("document('http://www.cafeaulait.org/formatter/Formatter.java')/*"); + fail("Queried malformed document!"); + } + catch (XPathException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testGetDocumentNode() { + + Element element = new Element("test"); + Document doc = new Document(element); + Nodes result = element.query("/"); + assertEquals(1, result.size()); + assertEquals(doc, result.get(0)); + + } + + + public void testCommentNodeTest() { + + Element grandparent = new Element("Test"); + Document doc = new Document(grandparent); + Element parent = new Element("Test"); + Element child = new Element("child"); + parent.appendChild(child); + grandparent.appendChild(parent); + + Comment c1 = new Comment("c1"); + Comment c2 = new Comment("c2"); + Comment c3 = new Comment("c3"); + Comment c4 = new Comment("c4"); + + doc.insertChild(c1, 0); + grandparent.insertChild(c2, 0); + parent.insertChild(c3, 0); + child.insertChild(c4, 0); + + Nodes result = doc.query("descendant::comment()"); + assertEquals(4, result.size()); + assertEquals(c1, result.get(0)); + assertEquals(c2, result.get(1)); + assertEquals(c3, result.get(2)); + assertEquals(c4, result.get(3)); + + } + + + public void testCommentStringValue() { + + Element grandparent = new Element("Test"); + Document doc = new Document(grandparent); + Element parent = new Element("Test"); + Element child = new Element("child"); + parent.appendChild(child); + grandparent.appendChild(parent); + + Comment c1 = new Comment("c1"); + Comment c2 = new Comment("c2"); + Comment c3 = new Comment("c3"); + Comment c4 = new Comment("c4"); + + doc.insertChild(c1, 0); + grandparent.insertChild(c2, 0); + parent.insertChild(c3, 0); + child.insertChild(c4, 0); + + Nodes result = doc.query("descendant::comment()[.='c3']"); + assertEquals(1, result.size()); + assertEquals(c3, result.get(0)); + + } + + + public void testGetProcessingInstructionData() { + + Element grandparent = new Element("Test"); + Document doc = new Document(grandparent); + Element parent = new Element("Test"); + Element child = new Element("child"); + parent.appendChild(child); + grandparent.appendChild(parent); + + ProcessingInstruction p1 = new ProcessingInstruction("c1", "1"); + ProcessingInstruction p2 = new ProcessingInstruction("c1", "2"); + ProcessingInstruction p3 = new ProcessingInstruction("c1", "3"); + ProcessingInstruction p4 = new ProcessingInstruction("c1", "4"); + + doc.insertChild(p1, 0); + grandparent.insertChild(p2, 0); + parent.insertChild(p3, 0); + child.insertChild(p4, 0); + + Nodes result = doc.query("descendant::processing-instruction()[.='3']"); + assertEquals(1, result.size()); + assertEquals(p3, result.get(0)); + + } + + + public void testProcessingInstructionNodeTest() { + + Element grandparent = new Element("Test"); + Document doc = new Document(grandparent); + Element parent = new Element("Test"); + Element child = new Element("child"); + parent.appendChild(child); + grandparent.appendChild(parent); + + Comment c1 = new Comment("c1"); + Comment c2 = new Comment("c2"); + Comment c3 = new Comment("c3"); + Comment c4 = new Comment("c4"); + + doc.insertChild(c1, 0); + grandparent.insertChild(c2, 0); + parent.insertChild(c3, 0); + child.insertChild(c4, 0); + ProcessingInstruction pi = new ProcessingInstruction("appendix", "text"); + doc.appendChild(pi); + ProcessingInstruction pi2 = new ProcessingInstruction("test", "text"); + parent.appendChild(pi2); + + Nodes result = doc.query("descendant::processing-instruction('test')"); + assertEquals(1, result.size()); + assertEquals(pi2, result.get(0)); + + } + + + public void testDescendantOrSelfAxis() { + + Element grandparent = new Element("Test"); + Element parent = new Element("Test"); + Element child = new Element("child"); + parent.appendChild(child); + grandparent.appendChild(parent); + + Nodes result = grandparent.query("descendant-or-self::*"); + assertEquals(3, result.size()); + assertEquals(grandparent, result.get(0)); + assertEquals(parent, result.get(1)); + assertEquals(child, result.get(2)); + + } + + + public void testAncestorOrSelfAxis() { + + Element grandparent = new Element("Test"); + new Document(grandparent); + Element parent = new Element("Test"); + Element child = new Element("child"); + parent.appendChild(child); + grandparent.appendChild(parent); + + Nodes result = child.query("ancestor-or-self::*"); + assertEquals(3, result.size()); + assertEquals(child, result.get(2)); + assertEquals(parent, result.get(1)); + assertEquals(grandparent, result.get(0)); + + } + + + public void testSelfAxis() { + + Element parent = new Element("Test"); + Element child = new Element("child"); + parent.appendChild(child); + + Nodes result = child.query("self::*"); + assertEquals(1, result.size()); + assertEquals(child, result.get(0)); + result = parent.query("self::*"); + assertEquals(1, result.size()); + assertEquals(parent, result.get(0)); + + } + + + public void testSelfAxisWithUnparentedText() { + + Text text = new Text("test"); + Nodes result = text.query("self::text()"); + assertEquals(1, result.size()); + assertEquals(text, result.get(0)); + + } + + public void testSelfAxisWithAttribute() { + + Element e = new Element("child"); + e.addAttribute(new Attribute("test", "value")); + Nodes result = e.query("@*[self::test]"); + assertEquals(0, result.size()); + + } + + + public void testSelfAxisWithTextChild() { + + Element parent = new Element("parent"); + Node child = new Text("child"); + parent.appendChild(child); + Nodes result = child.query("self::text()"); + assertEquals(1, result.size()); + assertEquals(child, result.get(0)); + + } + + + public void testSelfAxisWithTextChildren() { + + Element parent = new Element("parent"); + Node child1 = new Text("1"); + Node child2 = new Text("2"); + Node child3 = new Text("3"); + Node child4 = new Text("4"); + parent.appendChild(child1); + parent.appendChild(child2); + parent.appendChild(child3); + parent.appendChild(child4); + Nodes result = child1.query("self::text()"); + assertEquals(4, result.size()); + assertEquals(child1, result.get(0)); + assertEquals(child2, result.get(1)); + assertEquals(child3, result.get(2)); + assertEquals(child4, result.get(3)); + + } + + + public void testSelfAxisWithTextChildren2() { + + Element parent = new Element("parent"); + Node child1 = new Text("1"); + Node child2 = new Text("2"); + Node child3 = new Text("3"); + Node child4 = new Text("4"); + parent.appendChild(child1); + parent.appendChild(child2); + parent.appendChild(child3); + parent.appendChild(child4); + Nodes result = child3.query("self::text()"); + assertEquals(4, result.size()); + assertEquals(child1, result.get(0)); + assertEquals(child2, result.get(1)); + assertEquals(child3, result.get(2)); + assertEquals(child4, result.get(3)); + + } + + + public void testSelfAxisWithTextChildAndNoParent() { + + Node child = new Text("child"); + Nodes result = child.query("self::text()"); + assertEquals(1, result.size()); + assertEquals(child, result.get(0)); + + } + + + public void testAttributeAxis() { + + Element parent = new Element("Test"); + Element child = new Element("child"); + parent.appendChild(child); + parent.addAttribute(new Attribute("name", "value")); + parent.addAttribute(new Attribute("name2", "value")); + + Nodes result = child.query("attribute::*"); + assertEquals(0, result.size()); + result = parent.query("attribute::*"); + assertEquals(2, result.size()); + result = parent.query("attribute::name"); + assertEquals(1, result.size()); + + } + + + public void testAttributeAxisOnNonElement() { + + Text text = new Text("Test"); + Nodes result = text.query("attribute::*"); + assertEquals(0, result.size()); + + } + + + public void testEmptyParentAxis() { + + Element parent = new Element("Test"); + Element child = new Element("child"); + parent.appendChild(child); + + Nodes result = parent.query("parent::*"); + assertEquals(0, result.size()); + + } + + + public void testPrecedingSiblingAxis() { + + Element parent = new Element("Test"); + Element child1 = new Element("child1"); + Element child2 = new Element("child2"); + Element child3 = new Element("child3"); + parent.appendChild(child1); + parent.appendChild(child2); + parent.appendChild(child3); + + Nodes result = child1.query("preceding-sibling::*"); + assertEquals(0, result.size()); + result = child2.query("preceding-sibling::*"); + assertEquals(1, result.size()); + assertEquals(child1, result.get(0)); + result = child3.query("preceding-sibling::*"); + assertEquals(2, result.size()); + + } + + + public void testAttributeHasNoSiblings() { + + Element parent = new Element("Test"); + Element child1 = new Element("child1"); + Element child2 = new Element("child2"); + Element child3 = new Element("child3"); + parent.appendChild(child1); + parent.appendChild(child2); + parent.appendChild(child3); + Attribute a1 = new Attribute("a1", "value"); + Attribute a2 = new Attribute("a2", "value"); + Attribute a3 = new Attribute("a3", "value"); + child2.addAttribute(a1); + child2.addAttribute(a2); + child2.addAttribute(a3); + + Nodes result = a2.query("preceding-sibling::node()"); + assertEquals(0, result.size()); + result = a2.query("following-sibling::node()"); + assertEquals(0, result.size()); + + } + + + public void testIDFunction() { + + Element parent = new Element("Test"); + Element child1 = new Element("child1"); + Element child2 = new Element("child2"); + Element child3 = new Element("child3"); + Attribute id = new Attribute("a", "anchor"); + id.setType(Attribute.Type.ID); + child2.addAttribute(id); + + parent.appendChild(child1); + parent.appendChild(child2); + parent.appendChild(child3); + + Nodes result = parent.query("id('anchor')"); + assertEquals(1, result.size()); + assertEquals(child2, result.get(0)); + + } + + + public void testIDQueryOnDocumentNode() { + + Element parent = new Element("Test"); + Element child1 = new Element("child1"); + Element child2 = new Element("child2"); + Element child3 = new Element("child3"); + Attribute id = new Attribute("a", "anchor"); + id.setType(Attribute.Type.ID); + child2.addAttribute(id); + + parent.appendChild(child1); + parent.appendChild(child2); + parent.appendChild(child3); + Document doc = new Document(parent); + + Nodes result = doc.query("id('anchor')"); + assertEquals(1, result.size()); + assertEquals(child2, result.get(0)); + + } + + + public void testIDFunctionWithoutType() { + + Element parent = new Element("Test"); + Element child1 = new Element("child1"); + Element child2 = new Element("child2"); + Element child3 = new Element("child3"); + Attribute id = new Attribute("id", "anchor"); + child2.addAttribute(id); + + parent.appendChild(child1); + parent.appendChild(child2); + parent.appendChild(child3); + + Nodes result = parent.query("id('anchor')"); + assertEquals(0, result.size()); + + } + + + public void testIDFunctionFromTextNode() { + + Element parent = new Element("Test"); + Element child1 = new Element("child1"); + Element child2 = new Element("child2"); + Element child3 = new Element("child3"); + Text text = new Text("test"); + child3.appendChild(text); + Attribute id = new Attribute("a", "anchor"); + id.setType(Attribute.Type.ID); + child2.addAttribute(id); + + parent.appendChild(child1); + parent.appendChild(child2); + parent.appendChild(child3); + + Nodes result = text.query("id('anchor')"); + assertEquals(1, result.size()); + assertEquals(child2, result.get(0)); + + } + + + public void testIDFunctionFromUnparentedTextNode() { + + Text text = new Text("test"); + Nodes result = text.query("id('anchor')"); + assertEquals(0, result.size()); + + } + + + public void testIDFunctionFromDisconnectedTextNode() { + + Text text = new Text("test"); + Nodes result = text.query("id('anchor')"); + assertEquals(0, result.size()); + + } + + + public void testFollowingSiblingAxis() { + + Element parent = new Element("Test"); + Element child1 = new Element("child1"); + Element child2 = new Element("child2"); + Element child3 = new Element("child3"); + parent.appendChild(child1); + parent.appendChild(child2); + parent.appendChild(child3); + + Nodes result = child3.query("following-sibling::*"); + assertEquals(0, result.size()); + result = child2.query("following-sibling::*"); + assertEquals(1, result.size()); + assertEquals(child3, result.get(0)); + result = child1.query("following-sibling::*"); + assertEquals(2, result.size()); + + } + + + public void testNamespaceQuery() { + + Element parent = new Element("Test", "http://www.example.org"); + Element child = new Element("child", "http://www.example.org"); + parent.appendChild(child); + + XPathContext context = new XPathContext("pre", "http://www.example.org"); + Nodes result = parent.query("child::pre:child", context); + assertEquals(1, result.size()); + assertEquals(child, result.get(0)); + + } + + + public void testNamespaceAxis() { + + Element parent = new Element("Test", "http://www.example.org"); + + Nodes result = parent.query("namespace::*"); + assertEquals(2, result.size()); + Namespace n1 = (Namespace) result.get(0); + Namespace n2 = (Namespace) result.get(1); + assertTrue(n1.getPrefix().equals("") || n2.getPrefix().equals("")); + assertTrue(n1.getPrefix().equals("xml") || n2.getPrefix().equals("xml")); + assertTrue(n1.getValue().equals("http://www.example.org") + || n2.getValue().equals("http://www.example.org")); + assertTrue(n1.getValue().equals(Namespace.XML_NAMESPACE) + || n2.getValue().equals(Namespace.XML_NAMESPACE)); + + } + + + public void testNamespaceAxisFromNonElement() { + + Text text = new Text("test"); + + Nodes result = text.query("namespace::*"); + assertEquals(0, result.size()); + + } + + + public void testPredicateWithNamespaceAxis() { + + Element parent = new Element("Test"); + Element child = new Element("child", "http://www.example.com"); + Element grandchild = new Element("child", "http://www.example.com"); + grandchild.addNamespaceDeclaration("pre", "http://www.w3.org/"); + parent.appendChild(child); + child.appendChild(grandchild); + + // Every node has at least a mapping for xml prefix. + Nodes result = parent.query("self::*[count(namespace::*)=0]"); + assertEquals(0, result.size()); + + result = parent.query("self::*[count(namespace::*)=1]"); + assertEquals(1, result.size()); + assertEquals(parent, result.get(0)); + + result = child.query("self::*[count(namespace::*)=2]"); + assertEquals(1, result.size()); + assertEquals(child, result.get(0)); + + result = grandchild.query("self::*[count(namespace::*)=3]"); + assertEquals(1, result.size()); + assertEquals(grandchild, result.get(0)); + + } + + + public void testPredicateWithNamespaceAxis2() { + + Element parent = new Element("Test"); + Element child = new Element("child", "http://www.example.com"); + Element grandchild = new Element("child", "http://www.example.com"); + grandchild.addNamespaceDeclaration("pre", "http://www.w3.org/"); + parent.appendChild(child); + child.appendChild(grandchild); + + // Every node has at least a mapping for xml prefix. + Nodes result = parent.query("*[count(namespace::*)=0]"); + assertEquals(0, result.size()); + + result = parent.query(".//self::*[count(namespace::*)=1]"); + assertEquals(1, result.size()); + assertEquals(parent, result.get(0)); + + result = parent.query(".//*[count(namespace::*)=2]"); + assertEquals(1, result.size()); + assertEquals(child, result.get(0)); + + result = parent.query(".//*[count(namespace::*)=3]"); + assertEquals(1, result.size()); + assertEquals(grandchild, result.get(0)); + + } + + + public void testNamespaceQueryWithNullPrefix() { + + try { + XPathContext context = new XPathContext("pre", "http://www.example.org"); + context.addNamespace(null, "http://www.w3.org"); + fail("Allowed null prefix"); + } + catch (NullPointerException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testNamespaceQueryWithNullPrefix2() { + + try { + new XPathContext(null, "http://www.example.org"); + fail("Allowed null prefix"); + } + catch (NullPointerException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testNamespaceQueryWithEmptyPrefix() { + + try { + XPathContext context = new XPathContext("pre", "http://www.example.org"); + context.addNamespace("", "http://www.w3.org"); + } + catch (NamespaceConflictException success) { + assertTrue(success.getMessage().length() > 1); + } + + } + + + public void testNamespaceQueryWithEmptyPrefix2() { + + try { + new XPathContext("", "http://www.example.org"); + } + catch (NamespaceConflictException success) { + assertTrue(success.getMessage().length() > 1); + } + + } + + + public void testNamespaceQueryWithNullURI() { + + Element parent = new Element("Test", "http://www.example.org"); + Element child = new Element("child", "http://www.example.org"); + parent.appendChild(child); + + XPathContext context = new XPathContext("pre", null); + try { + parent.query("child::pre:child", context); + fail("Allowed null URI"); + } + catch (XPathException success) { + assertNotNull(success.getCause()); + assertNotNull(success.getMessage()); + } + + } + + + public void testNamespaceQueryWithEmptyURI() { + + Element parent = new Element("Test", "http://www.example.org"); + Element child = new Element("child", "http://www.example.org"); + parent.appendChild(child); + + XPathContext context = new XPathContext("pre", ""); + try { + parent.query("child::pre:child", context); + fail("Allowed empty string as namespace URI"); + } + catch (XPathException success) { + assertNotNull(success.getCause()); + assertNotNull(success.getMessage()); + } + + } + + + public void testNamespaceQueryWithReboundPrefix() { + + Element parent = new Element("Test", "http://www.example.org"); + Element child = new Element("child", "http://www.example.org"); + parent.appendChild(child); + + XPathContext context = new XPathContext("pre", "http://www.example.com"); + Nodes result = parent.query("child::pre:child", context); + assertEquals(0, result.size()); + + context.addNamespace("pre", "http://www.example.org"); + result = parent.query("child::pre:child", context); + assertEquals(1, result.size()); + assertEquals(child, result.get(0)); + + } + + + public void testNamespaceQueryWithUnboundPrefix() { + + Element parent = new Element("Test", "http://www.example.org"); + Element child = new Element("child", "http://www.example.org"); + parent.appendChild(child); + + XPathContext context = new XPathContext("not", "http://www.example.com"); + try { + parent.query("child::pre:child", context); + fail("Queried with unbound prefix"); + } + catch (XPathException success) { + assertNotNull(success.getMessage()); + assertNotNull(success.getCause()); + } + + try { + parent.query("child::pre:child"); + fail("Queried with unbound prefix"); + } + catch (XPathException success) { + assertNotNull(success.getMessage()); + assertNotNull(success.getCause()); + } + + } + + + public void testElementBasedNamespaceContext() { + + Element parent = new Element("Test", "http://www.example.org"); + Element child = new Element("child", "http://www.example.org"); + parent.appendChild(child); + + Element test = new Element("pre:test", "http://www.example.org"); + XPathContext context = XPathContext.makeNamespaceContext(test); + Nodes result = parent.query("child::pre:child", context); + assertEquals(1, result.size()); + assertEquals(child, result.get(0)); + + } + + + public void testAttributeBasedNamespaceContext() { + + Element parent = new Element("Test", "http://www.example.org"); + Element child = new Element("child", "http://www.example.org"); + parent.appendChild(child); + + Element test = new Element("test"); + test.addAttribute(new Attribute("pre:test", "http://www.example.org", "value")); + XPathContext context = XPathContext.makeNamespaceContext(test); + Nodes result = parent.query("child::pre:child", context); + assertEquals(1, result.size()); + assertEquals(child, result.get(0)); + + } + + + public void testAdditionalNamespaceBasedNamespaceContext() { + + Element parent = new Element("Test", "http://www.example.org"); + Element child = new Element("child", "http://www.example.org"); + parent.appendChild(child); + + Element test = new Element("test"); + test.addNamespaceDeclaration("pre", "http://www.example.org"); + XPathContext context = XPathContext.makeNamespaceContext(test); + Nodes result = parent.query("child::pre:child", context); + assertEquals(1, result.size()); + assertEquals(child, result.get(0)); + + } + + + public void testAncestorElementBasedNamespaceContext() { + + Element parent = new Element("Test", "http://www.example.org"); + Element child = new Element("child", "http://www.example.org"); + parent.appendChild(child); + + Element test = new Element("pre:test", "http://www.example.org"); + Element testChild = new Element("testchild"); + test.appendChild(testChild); + XPathContext context = XPathContext.makeNamespaceContext(testChild); + Nodes result = parent.query("child::pre:child", context); + assertEquals(1, result.size()); + assertEquals(child, result.get(0)); + + } + + + public void testAncestorAttributeBasedNamespaceContext() { + + Element parent = new Element("Test", "http://www.example.org"); + Element child = new Element("child", "http://www.example.org"); + parent.appendChild(child); + + Element test = new Element("test"); + test.addAttribute(new Attribute("pre:test", "http://www.example.org", "value")); + Element testChild = new Element("testchild"); + test.appendChild(testChild); + XPathContext context = XPathContext.makeNamespaceContext(testChild); + Nodes result = parent.query("child::pre:child", context); + assertEquals(1, result.size()); + assertEquals(child, result.get(0)); + + } + + + public void testAncestorAdditionalNamespaceBasedNamespaceContext() { + + Element parent = new Element("Test", "http://www.example.org"); + Element child = new Element("child", "http://www.example.org"); + parent.appendChild(child); + + Element test = new Element("test"); + test.addNamespaceDeclaration("pre", "http://www.example.org"); + Element testChild = new Element("testchild"); + test.appendChild(testChild); + XPathContext context = XPathContext.makeNamespaceContext(testChild); + Nodes result = parent.query("child::pre:child", context); + assertEquals(1, result.size()); + assertEquals(child, result.get(0)); + + } + + + public void testPrefixedNamespaceQuery() { + + Element parent = new Element("a:Test", "http://www.example.org"); + Element child = new Element("b:child", "http://www.example.org"); + Attribute att = new Attribute("c:dog", "http://www.cafeconleche.org/", "test"); + parent.appendChild(child); + child.addAttribute(att); + + XPathContext context = new XPathContext("pre", "http://www.example.org"); + context.addNamespace("c", "http://www.cafeconleche.org/"); + Nodes result = parent.query("child::pre:child", context); + assertEquals(1, result.size()); + assertEquals(child, result.get(0)); + + result = child.query("@c:*", context); + assertEquals(1, result.size()); + assertEquals(att, result.get(0)); + + } + + + public void testBradley() { + + Element element = new Element("root"); + Text t1 = new Text("makes "); + Text t2 = new Text("a"); + Text t3 = new Text(" good"); + Text t4 = new Text(" point."); + Element child = new Element("someElement"); + Text t5 = new Text(" Yes"); + Text t6 = new Text(" he"); + Text t7 = new Text(" does!"); + element.appendChild(t1); + element.appendChild(t2); + element.appendChild(t3); + element.appendChild(t4); + element.appendChild(child); + element.appendChild(t5); + element.appendChild(t6); + element.appendChild(t7); + + Nodes result = element.query("./text()[contains(., 'o')]"); + assertEquals(7, result.size()); + assertEquals(t1, result.get(0)); + assertEquals(t2, result.get(1)); + assertEquals(t3, result.get(2)); + assertEquals(t4, result.get(3)); + assertEquals(t5, result.get(4)); + assertEquals(t6, result.get(5)); + assertEquals(t7, result.get(6)); + + + } + + + public void testNamespaceQueryWithAdjacentTextNodes() { + + Element parent = new Element("Test", "http://www.example.org"); + Element child = new Element("child", "http://www.example.org"); + parent.appendChild(child); + child.appendChild("1"); + child.appendChild("2"); + + XPathContext context = new XPathContext("pre", "http://www.example.org"); + Nodes result = parent.query("descendant::text()", context); + assertEquals(2, result.size()); + assertEquals("1", result.get(0).getValue()); + assertEquals("2", result.get(1).getValue()); + + } + + + public void testNamespaceQueryWithoutPrefixMapping() { + + Element parent = new Element("Test", "http://www.example.org"); + Element child = new Element("child", "http://www.example.org"); + parent.appendChild(child); + + Nodes result = parent.query("child"); + assertEquals(0, result.size()); + + } + + + public void testAdjacentTextObjects() { + + Element parent = new Element("Test"); + parent.appendChild("test"); + parent.appendChild("again"); + + Nodes result = parent.query("text()"); + assertEquals(2, result.size()); + assertEquals("test", result.get(0).getValue()); + assertEquals("again", result.get(1).getValue()); + + } + + + public void testQueryCrossesAdjacentTextObjects() { + + Element parent = new Element("Test"); + parent.appendChild("test"); + parent.appendChild("again"); + + Nodes result = parent.query("node()[contains(., 'tag')]"); + assertEquals(2, result.size()); + assertEquals("test", result.get(0).getValue()); + assertEquals("again", result.get(1).getValue()); + + } + + + // According to section 5.7 of the XPath 1.0 spec, + // "As much character data as possible is grouped into each text + // node: a text node never has an immediately following or + // preceding sibling that is a text node." + public void testAdjacentTextNodes2() { + + Element parent = new Element("Test"); + parent.appendChild("test"); + parent.appendChild("again"); + + Nodes result = parent.query("child::text()[1]"); + assertEquals(2, result.size()); + assertEquals("test", result.get(0).getValue()); + assertEquals("again", result.get(1).getValue()); + + } + + + // According to section 5.7 of the XPath 1.0 spec, + // "A text node always has at least one character of data." + public void testEmptyTextNodes() { + + Element parent = new Element("Test"); + parent.appendChild(""); + + Nodes result = parent.query("child::text()"); + assertEquals(0, result.size()); + + } + + + public void testEmptyTextFollowsNonEmptyText() { + + Element parent = new Element("parent"); + Text empty = new Text(""); + Text nonempty = new Text("value"); + parent.appendChild(nonempty); + parent.appendChild(empty); + Nodes result = parent.query("node()"); + assertEquals(2, result.size()); + + } + + + public void testCountAdjacentEmptyAndNonEmptyTextNodes() { + + Element parent = new Element("parent"); + Text empty = new Text(""); + Text nonempty = new Text("value"); + parent.appendChild(nonempty); + parent.appendChild(empty); + + Nodes result2 = parent.query("/*[count(node())=1]"); + assertEquals(1, result2.size()); + assertEquals(parent, result2.get(0)); + + Nodes nodes1 = parent.query("node()[1]"); + assertEquals(2, nodes1.size()); + Nodes nodes2 = parent.query("node()[2]"); + assertEquals(0, nodes2.size()); + + } + + + public void testAdjacentEmptyAndNonEmptyTextNodes() { + + Element parent = new Element("parent"); + Text empty = new Text(""); + Text nonempty = new Text("value"); + Text nonempty2 = new Text("value2"); + parent.appendChild(empty); + parent.appendChild(nonempty); + parent.appendChild(nonempty2); + + Nodes result = parent.query("node()"); + assertEquals(3, result.size()); + + Nodes result2 = parent.query("/*[count(node())=1]"); + assertEquals(1, result2.size()); + assertEquals(parent, result2.get(0)); + + Nodes nodes1 = parent.query("node()[1]"); + assertEquals(3, nodes1.size()); + + Nodes nodes2 = parent.query("node()[2]"); + assertEquals(0, nodes2.size()); + + } + + + public void testBadXPathExpression() { + + Element parent = new Element("Test"); + + try { + parent.query("This is not an XPath expression"); + fail("Allowed malformed query"); + } + catch (XPathException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testNaNEvaluatesToFalse() { + + Element root = new Element("root"); + Document doc = new Document(root); + + Nodes result = doc.query("/*[boolean(0 div 0)]"); + assertEquals(0, result.size()); + + } + + + /* +

    + +

    +
    + */ + public void testPrecedingAxis() { + + Element body = new Element("body"); + Element p = new Element("p"); + body.appendChild(p); + Element span = new Element("span"); + p.appendChild(span); + Element div = new Element("div"); + body.appendChild(div); + + Nodes result = div.query("preceding::*[1]"); + assertEquals(1, result.size()); + assertEquals(span, result.get(0)); + + } + + + public void testRootNodeValueIsNonEmpty() { + + Element root = new Element("html"); + Document doc = new Document(root); + root.appendChild("test"); + + Nodes result = doc.query("/*[string(/) != '']"); + assertEquals(1, result.size()); + assertEquals(root, result.get(0)); + + } + + + public void testContextPositionForParaentlessNodeIs1() { + + Element root = new Element("html"); + + Nodes result = root.query("self::*[1]"); + assertEquals(1, result.size()); + assertEquals(root, result.get(0)); + + } + + + public void testContextSizeForParaentlessNodeIs1() { + + Element root = new Element("html"); + + Nodes result = root.query("self::*[last()=1]"); + assertEquals(1, result.size()); + assertEquals(root, result.get(0)); + + } + + + public void testLastFunction() { + + Element root = new Element("html"); + Element child1 = new Element("child1"); + Element child2 = new Element("child2"); + root.appendChild(child1); + root.appendChild(child2); + new Document(root); + + Nodes result = child2.query("self::*[position()=last()]"); + assertEquals(1, result.size()); + assertEquals(child2, result.get(0)); + + } + + + public void testJaxen51() throws ParsingException, IOException { + + String data = "\n \n" + + " DeviceInfoServlet\n" + + " /tools/*\n" + + "\n"; + + Builder builder = new Builder(); + Document doc = builder.build(data, null); + + Nodes result = doc.query("//*[./../servlet-name = 'DeviceInfoServlet']"); + assertEquals(2, result.size()); + + } + + + public void testXPathNamespaceParentage() + throws ParsingException, IOException { + + String input = "\n" + + "\n" + + "]>\n" + + "\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + ""; + + Document doc = (new Builder()).build(input, null); + String xpath = "(/*/* | /*/*/namespace::*)\n"; + Nodes result = doc.query(xpath); + assertEquals(4, result.size()); + Element parent = (Element) result.get(0); + for (int i = 1; i < 4; i++) { + Namespace namespace = (Namespace) result.get(i); + assertEquals(parent, namespace.getParent()); + } + + } + + + public void testAttributesFollowElementsInDocumentOrder() + throws ParsingException, IOException { + + String input = "\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + ""; + + Document doc = (new Builder()).build(input, null); + String xpath = "(/*/* | /*/*/attribute::*)\n"; + Nodes result = doc.query(xpath); + assertEquals(4, result.size()); + Element parent = (Element) result.get(0); + for (int i = 1; i < 4; i++) { + Attribute attribute = (Attribute) result.get(i); + assertEquals(parent, attribute.getParent()); + } + + } + + + public void testDoubleSlashIsIncorrect() { + + Element root = new Element("root", "http://www.example.org"); + Document doc = new Document(root); + root.appendChild(new Element("child")); + root.appendChild("test"); + root.addAttribute(new Attribute("test", "test")); + + try { + doc.query("//"); + fail("Queried //"); + } + catch (XPathException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testDoubleSlashIsIncorrect2() { + + Element root = new Element("root", "http://www.example.org"); + Document doc = new Document(root); + root.appendChild(new Element("child")); + root.appendChild("test"); + root.addAttribute(new Attribute("test", "test")); + + try { + doc.query("// "); + fail("Queried // "); + } + catch (XPathException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testFunctionWithDefaultNamespace() throws ParsingException, IOException { + + Element root = new Element("root", "http://www.example.org"); + Document doc = new Document(root); + XPathContext context = XPathContext.makeNamespaceContext(doc.getRootElement()); + context.addNamespace("pre", doc.getRootElement().getNamespaceURI()); + Nodes query = doc.query("//*[not(self::pre:test)]", context); + assertEquals(1, query.size()); + + } + + + public void testWhiteSpaceIsAllowedBetweenTokens() { + + Element root = new Element("root", "http://www.example.org"); + Document doc = new Document(root); + root.appendChild(new Element("child")); + root.appendChild("test"); + root.addAttribute(new Attribute("test", "test")); + + Nodes nodes = doc.query("// a"); + assertEquals(0, nodes.size()); + + } + + +/* public void testMassimo() throws ParsingException, IOException { + + Builder builder = new Builder(); + Document doc = builder.build("http://staff.science.uva.nl/~francesc/xpathmark/benchmark_canon.xml"); + Element root = doc.getRootElement(); + Element input_1 = root.getFirstChildElement("document_1"); + Element input_2 = root.getFirstChildElement("document_2"); + + Nodes queries = root.query("child::query[starts-with('@id', 'Q')]"); + for (int i = 0; i < queries.size(); i++) { + Element query = (Element) queries.get(i); + String xpath = query.getFirstChildElement("syntax").getValue(); + Nodes actual = input_1.query(xpath); + Elements expected = query.getChildElements(); + } + + } */ + + + public void testJaxenIntegrationTest() throws ParsingException, IOException { + + // TODO point this, at least optionally, at the jaxen directory in the zip file instead. + // However, first you'll have to push a jaxen 1.2.1 that fixes tests.xml. + String base = "https://raw.githubusercontent.com/jaxen-xpath/jaxen/0405c6aeea1955ca92012d2934cf18e80b367963/"; + Builder builder = new Builder(); + Document testDoc = builder.build(base + "xml/test/tests.xml"); + Elements documents = testDoc.getRootElement().getChildElements("document"); + for (int i = 0; i < documents.size(); i++) { + Element documentElement = documents.get(i); + String url = documentElement.getAttributeValue("url"); + Document source = builder.build( + base + url); + Elements contextElements = documentElement.getChildElements("context"); + for (int j = 0; j < contextElements.size(); j++) { + Element contextElement = contextElements.get(j); + + // skip tests that use variables + // because XOM doesn't support variables in + // XPath expressions + if (queryUsesVars(contextElement)) continue; + + String xpath = contextElement.getAttributeValue("select"); + XPathContext namespaces = getXPathContext(contextElement); + Node context = source.query(xpath).get(0); + + // process counts + Elements tests = contextElement.getChildElements("test"); + for (int k = 0; k < tests.size(); k++) { + Element test = tests.get(k); + + String select = test.getAttributeValue("select"); + Attribute countAttribute = test.getAttribute("count"); + int count = -1; + if (countAttribute != null) { + count = Integer.parseInt(countAttribute.getValue()); + } + + boolean exceptional = false; + String exception = test.getAttributeValue("exception"); + if ("true".equals(exception)) { + exceptional = true; + } + + if (exceptional) { + try { + context.query(select, namespaces); + fail("Evaluated " + select); + } + catch (XPathException success) { + assertNotNull(success.getMessage()); + } + } + else { + try { + Nodes results = context.query(select, namespaces); + if (count != -1) { + assertEquals(select, count, results.size()); + } + Elements valueOfs = test.getChildElements("valueOf"); + for (int v = 0; v < valueOfs.size(); v++) { + Element vo = valueOfs.get(v); + checkValueOf(results.get(0), vo, namespaces); + } + } + catch (XPathException ex) { + if (ex.getMessage().equalsIgnoreCase("XPath error: No such function document") + || ex.getMessage().equalsIgnoreCase("XPath error: No such function evaluate")) { + continue; + } + throw ex; + } + + } + } + + // process valueOfs + Elements valueOfs = contextElement.getChildElements("valueOf"); + for (int k = 0; k < valueOfs.size(); k++) { + checkValueOf(context, valueOfs.get(k), namespaces); + } + + } + } + } + + + private void checkValueOf(Node context, Element valueOf, XPathContext namespaces) { + + String select = valueOf.getAttributeValue("select"); + try { + Nodes nodes = context.query(select, namespaces); + String result = nodes.get(0).getValue(); + assertEquals(valueOf.getValue(), result); + } + catch (XPathTypeException ex) { + assertNotNull(ex.getMessage()); + } + catch (XPathException ex) { + if (ex.getMessage().equalsIgnoreCase("XPath error: No such function document") + || ex.getMessage().equalsIgnoreCase("XPath error: No such function upper-case") + || ex.getMessage().equalsIgnoreCase("XPath error: No such function lower-case") + || ex.getMessage().equalsIgnoreCase("XPath error: No such function ends-with") + ) { + return; + } + throw ex; + } + + } + + + private XPathContext getXPathContext(Element contextElement) { + + XPathContext context = new XPathContext(); + for (int i = 0; i < contextElement.getNamespaceDeclarationCount(); i++) { + String prefix = contextElement.getNamespacePrefix(i); + if (! "".equals(prefix)) { + context.addNamespace(prefix, contextElement.getNamespaceURI(prefix)); + } + } + return context; + } + + + private boolean queryUsesVars(Element testElement) { + + for (int i = 0; i < testElement.getAttributeCount(); i++) { + Attribute a = testElement.getAttribute(i); + if ("http://jaxen.org/test-harness/var".equals(a.getNamespaceURI())) { + return true; + } + } + return false; + } + + + public void testTextChildInPredicate() { + + Element item = new Element("item"); + Document doc = new Document(item); + Element name = new Element("name"); + name.appendChild("a"); + item.appendChild(name); + Element value = new Element("value"); + value.appendChild("b"); + item.appendChild(value); + Nodes result = doc.query("/item[name/text()='a']/value"); + assertEquals(1, result.size()); + assertEquals("b", result.get(0).getValue()); + + } + + + public void testSimpleTextChildInPredicate() { + + Element item = new Element("item"); + Document doc = new Document(item); + item.appendChild("a"); + Nodes result = doc.query("/item[text()='a']"); + assertEquals(1, result.size()); + + } + + + public void testPrecedingAxisInDocumentOrder() { + + Element root = new Element("root"); + new Document(root); + + Element a = new Element("a"); + root.appendChild(a); + Element b = new Element("b"); + root.appendChild(b); + Element c = new Element("c"); + a.appendChild(c); + + Nodes result = b.query("preceding::*"); + assertEquals(2, result.size()); + assertEquals(a, result.get(0)); + assertEquals(c, result.get(1)); + + } + + + public void testAttributeWithUnderscore() { + + Element a = new Element("a"); + Attribute foo = new Attribute("_foo", "bar"); + a.addAttribute(foo); + Nodes results = a.query("//@_foo"); + assertEquals(1, results.size()); + assertEquals(foo, results.get(0)); + + } + + +} diff --git a/src/nu/xom/tests/XSLExceptionTest.java b/src/nu/xom/tests/XSLExceptionTest.java new file mode 100644 index 0000000..c79f97c --- /dev/null +++ b/src/nu/xom/tests/XSLExceptionTest.java @@ -0,0 +1,128 @@ +/* Copyright 2003-2005 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.tests; + +import nu.xom.xslt.XSLException; + +/** + *

    + * Unit tests for the XSLException class. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.1d2 + * + */ +public class XSLExceptionTest extends XOMTestCase { + + + private XSLException ex; + private Exception cause; + + + public XSLExceptionTest(String name) { + super(name); + } + + + protected void setUp() { + ex = new XSLException("message"); + cause = new Exception(); + } + + + public void testConstructor() { + String message = "testing 1-2-3"; + XSLException ex = new XSLException(message, cause); + assertEquals(message, ex.getMessage()); + assertEquals(cause, ex.getCause()); + } + + + public void testInitCause() { + + assertNull(ex.getCause()); + ex.initCause(cause); + assertEquals(cause, ex.getCause()); + + try { + ex.initCause(null); + fail("Reinitialized cause over null"); + } + catch (IllegalStateException success) { + assertNotNull(success.getMessage()); + } + + try { + ex.initCause(new Exception()); + fail("Reinitialized cause over null"); + } + catch (IllegalStateException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testNullInitCause() { + + XSLException ex = new XSLException(null, null); + assertNull(ex.getCause()); + + try { + ex.initCause(new Exception()); + fail("Reinitialized cause over null"); + } + catch (IllegalStateException success) { + assertNotNull(success.getMessage()); + } + + try { + ex.initCause(null); + fail("Reinitialized cause over null"); + } + catch (IllegalStateException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testSelfCause() { + + try { + ex.initCause(ex); + fail("Allowed self-causation"); + } + catch (IllegalArgumentException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testGetMessage() { + Exception ex = new XSLException("testing"); + assertEquals("testing", ex.getMessage()); + } + +} diff --git a/src/nu/xom/tests/XSLTransformTest.java b/src/nu/xom/tests/XSLTransformTest.java new file mode 100644 index 0000000..20f4bac --- /dev/null +++ b/src/nu/xom/tests/XSLTransformTest.java @@ -0,0 +1,1133 @@ +/* Copyright 2002-2006, 2009, 2018 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ +package nu.xom.tests; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.io.PrintStream; + +import nu.xom.Attribute; +import nu.xom.Builder; +import nu.xom.Comment; +import nu.xom.DocType; +import nu.xom.Document; +import nu.xom.Element; +import nu.xom.Namespace; +import nu.xom.Node; +import nu.xom.NodeFactory; +import nu.xom.Nodes; +import nu.xom.ParentNode; +import nu.xom.ParsingException; +import nu.xom.ProcessingInstruction; +import nu.xom.Serializer; +import nu.xom.Text; +import nu.xom.XMLException; +import nu.xom.xslt.XSLException; +import nu.xom.xslt.XSLTransform; + +/** + *

    + * Unit tests for the XSLT engine. + *

    + * + *

    + * Many of the tests in this suite use an identity transformation. + * This is often done to make sure I get a particular content into + * the output tree in order to test the XSLTHandler. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.2.11 + * + */ +public class XSLTransformTest extends XOMTestCase { + + + public XSLTransformTest(String name) { + super(name); + } + + + // Not a literal result element as stylesheet + // because it's missing the xsl:version attribute + private String notAStyleSheet = + "" + + "" + + "Hello dear" + + "\r\n" + + "very important" + + "here's the link\r\n" + + "" + + "text in a namespace\r\n" + + "" + + "text in a namespace" + + "\r\n" + + ""; + + + // This class tests a lot of error conditions, which + // Xalan annoyingly logs to System.err. This hides System.err + // before each test and restores it after each test. + private PrintStream systemErr = System.err; + + private File inputDir; + + protected void setUp() { + + System.setErr(new PrintStream(new ByteArrayOutputStream())); + + inputDir = new File("data"); + inputDir = new File(inputDir, "xslt"); + inputDir = new File(inputDir, "input"); + + } + + + protected void tearDown() { + System.setErr(systemErr); + } + + + // Primarily this makes sure the XSLTHandler can handle various + // edge cases + public void testIdentityTransform() + throws ParsingException, IOException, XSLException { + + File stylesheet = new File(inputDir, "identity.xsl"); + Builder builder = new Builder(); + Document stylesheetDoc = builder.build(stylesheet); + XSLTransform xform = new XSLTransform(stylesheetDoc); + Element root = new Element("root", "http://www.example.org"); + root.appendChild(new Text("some data")); + root.appendChild(new Element("something")); + root.addAttribute(new Attribute("test", "test")); + root.addAttribute(new Attribute("pre:red", "http://www.red.com/", "value")); + Document input = new Document(root); + Nodes output = xform.transform(input); + assertEquals(root, output.get(0)); + + } + + + public void testPrefixMappingIssues() + throws XSLException, ParsingException, IOException { + + String doc = "" + + "" + + "" + + ""; + File stylesheet = new File(inputDir, "identity.xsl"); + Builder builder = new Builder(); + Document stylesheetDoc = builder.build(stylesheet); + XSLTransform xform = new XSLTransform(stylesheetDoc); + Document input = builder.build(doc, "http://example.org/"); + Nodes result = xform.transform(input); + assertEquals(input.getRootElement(), result.get(0)); + + } + + + public void testDocumentConstructor() + throws ParsingException, IOException { + + try { + Builder builder = new Builder(); + Document doc = builder.build(notAStyleSheet, + "http://www.example.com"); + new XSLTransform(doc); + /*Document input = builder.build("", "http://example.org/"); + Nodes result = xform.transform(input); */ + fail("Compiled non-stylesheet"); + } + catch (XSLException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testLiteralResultElementUsedAsStylesheet() + throws ParsingException, IOException, XSLException { + + String literalResultElementAsStylesheet = + "\n" + + " \n" + + " Expense Report Summary\n" + + " \n" + + " \n" + + "

    Total Amount:

    \n" + + " \n" + + "\n"; + + Builder builder = new Builder(); + Document stylesheet = builder.build(literalResultElementAsStylesheet, + "http://www.example.com"); + XSLTransform transform = new XSLTransform(stylesheet); + Document doc = builder.build(notAStyleSheet, + "http://www.example.com"); + Nodes result = transform.transform(doc); + Element root = (Element) (result.get(0)); + assertEquals("html", root.getQualifiedName()); + assertEquals(2, root.getChildCount()); + + } + + +/* + some data and for a test + Remember, the XSLT processor is going to strip out the literal white space + some data and for a + + test + PIs are not treated as literals in XSLT? + */ + + public void testCreateDocumentFragment() + throws ParsingException, IOException, XSLException { + + Element element1 = new Element("element1"); + element1.appendChild("some data and "); + element1.appendChild(new Element("content")); + element1.appendChild(" for a test"); + + Element element2 = new Element("element2"); + element2.appendChild( + "Remember, the XSLT processor is going to strip out the literal white space" + ); + File doc = new File(inputDir, "8-14.xml"); + File stylesheet = new File(inputDir, "fragment.xsl"); + Builder builder = new Builder(); + Document stylesheetDoc = builder.build(stylesheet); + XSLTransform xform = new XSLTransform(stylesheetDoc); + Document input = builder.build(doc); + Nodes output = xform.transform(input); + assertEquals(6, output.size()); + assertEquals(element1, output.get(0)); + assertEquals(element2, output.get(1)); + assertEquals(new Element("element4"), output.get(3)); + assertEquals(new Comment("test"), output.get(4)); + assertEquals(new ProcessingInstruction("test", + "PIs are not treated as literals in XSLT?"), output.get(5)); + + } + + + public void testTransform() + throws ParsingException, IOException, XSLException { + + File doc = new File(inputDir, "8-1.xml"); + File stylesheet = new File(inputDir, "8-8.xsl"); + Builder builder = new Builder(); + Document stylesheetDoc = builder.build(stylesheet); + XSLTransform xform = new XSLTransform(stylesheetDoc); + Nodes output = xform.transform(builder.build(doc)); + assertEquals(1, output.size()); + Document result = new Document((Element) (output.get(0))); + + Document expected = builder.build("data/xslt/output/8-8.xml"); + assertEquals(expected, result); + + } + + + public void testSetParameter() + throws ParsingException, IOException, XSLException { + + File doc = new File(inputDir, "8-1.xml"); + File stylesheet = new File(inputDir, "paramtest.xsl"); + Builder builder = new Builder(); + Document stylesheetDoc = builder.build(stylesheet); + XSLTransform xform = new XSLTransform(stylesheetDoc); + xform.setParameter("test", new Double(2)); + Nodes output = xform.transform(builder.build(doc)); + assertEquals(1, output.size()); + Element result = (Element) output.get(0); + assertEquals("2", result.toXML()); + + } + + + public void testSetParameterWithNamespace() + throws ParsingException, IOException, XSLException { + + File doc = new File(inputDir, "8-1.xml"); + File stylesheet = new File(inputDir, "namespaceparamtest.xsl"); + Builder builder = new Builder(); + Document stylesheetDoc = builder.build(stylesheet); + XSLTransform xform = new XSLTransform(stylesheetDoc); + xform.setParameter("test", "http://www.xom.nu/", new Double(2)); + Nodes output = xform.transform(builder.build(doc)); + assertEquals(1, output.size()); + Element result = (Element) output.get(0); + assertEquals("2", + result.toXML()); + + } + + + public void testRemoveParameterWithNamespace() + throws ParsingException, IOException, XSLException { + + File doc = new File(inputDir, "8-1.xml"); + File stylesheet = new File(inputDir, "namespaceparamtest.xsl"); + Builder builder = new Builder(); + Document stylesheetDoc = builder.build(stylesheet); + XSLTransform xform = new XSLTransform(stylesheetDoc); + xform.setParameter("test", "http://www.xom.nu/", new Double(2)); + xform.setParameter("test", "http://www.xom.nu/", null); + Nodes output = xform.transform(builder.build(doc)); + assertEquals(1, output.size()); + Element result = (Element) output.get(0); + assertEquals("1", + result.toXML()); + + } + + + public void testRemoveParameter() + throws ParsingException, IOException, XSLException { + + File doc = new File(inputDir, "8-1.xml"); + File stylesheet = new File(inputDir, "paramtest.xsl"); + Builder builder = new Builder(); + Document stylesheetDoc = builder.build(stylesheet); + XSLTransform xform = new XSLTransform(stylesheetDoc); + xform.setParameter("test", new Double(2)); + xform.setParameter("test", null); + Nodes output = xform.transform(builder.build(doc)); + assertEquals(1, output.size()); + Element result = (Element) output.get(0); + assertEquals("1", result.toXML()); + + } + + + public void testTransformWithCFilter() + throws ParsingException, IOException, XSLException { + + File doc = new File(inputDir, "8-1.xml"); + File stylesheet = new File(inputDir, "8-8.xsl"); + Builder builder = new Builder(); + Document stylesheetDoc = builder.build(stylesheet); + XSLTransform xform = new XSLTransform( + stylesheetDoc, new NodeFactoryTest.CFactory()); + + Nodes output = xform.transform(builder.build(doc)); + assertEquals(1, output.size()); + Document result = new Document((Element) (output.get(0))); + + Document expected = builder.build("data/xslt/output/8-8c.xml"); + assertEquals(expected, result); + + } + + + public void testCreateDocumentFragmentWithCommentFilter() + throws ParsingException, IOException, XSLException { + + Element element1 = new Element("element1"); + element1.appendChild("some data and "); + element1.appendChild(new Element("content")); + element1.appendChild(" for a test"); + + Element element2 = new Element("element2"); + element2.appendChild( + "Remember, the XSLT processor is going to strip out the literal white space" + ); + File doc = new File(inputDir, "8-14.xml"); + File stylesheet = new File(inputDir, "fragment.xsl"); + Builder builder = new Builder(); + Document stylesheetDoc = builder.build(stylesheet); + XSLTransform xform = new XSLTransform( + stylesheetDoc, new NodeFactoryTest.CommentFilter()); + + Document input = builder.build(doc); + Nodes output = xform.transform(input); + assertEquals(5, output.size()); + assertEquals(element1, output.get(0)); + assertEquals(element2, output.get(1)); + assertEquals(new Element("element4"), output.get(3)); + assertEquals(new ProcessingInstruction("test", + "PIs are not treated as literals in XSLT?"), output.get(4)); + + } + + + public void testCreateDocumentFragmentWithProcessingInstructionFilter() + throws ParsingException, IOException, XSLException { + + Element element1 = new Element("element1"); + element1.appendChild("some data and "); + element1.appendChild(new Element("content")); + element1.appendChild(" for a test"); + + Element element2 = new Element("element2"); + element2.appendChild( + "Remember, the XSLT processor is going to strip out the literal white space" + ); + File doc = new File(inputDir, "8-14.xml"); + File stylesheet = new File(inputDir, "fragment.xsl"); + Builder builder = new Builder(); + Document stylesheetDoc = builder.build(stylesheet); + XSLTransform xform = new XSLTransform(stylesheetDoc, + new NodeFactoryTest.ProcessingInstructionFilter()); + + Document input = builder.build(doc); + Nodes output = xform.transform(input); + assertEquals(5, output.size()); + assertEquals(element1, output.get(0)); + assertEquals(element2, output.get(1)); + assertEquals(new Element("element4"), output.get(3)); + assertEquals(new Comment("test"), output.get(4)); + + } + + + public void testCreateDocumentFragmentWithUncommentFilter() + throws ParsingException, IOException, XSLException { + + Element element1 = new Element("element1"); + element1.appendChild("some data and "); + element1.appendChild(new Element("content")); + element1.appendChild(" for a test"); + + Element element2 = new Element("element2"); + element2.appendChild( + "Remember, the XSLT processor is going to strip out the literal white space" + ); + File doc = new File(inputDir, "8-14.xml"); + File stylesheet = new File(inputDir, "fragment.xsl"); + Builder builder = new Builder(); + Document stylesheetDoc = builder.build(stylesheet); + XSLTransform xform = new XSLTransform(stylesheetDoc, + new NodeFactoryTest.UncommentFilter()); + + Document input = builder.build(doc); + Nodes output = xform.transform(input); + assertEquals(6, output.size()); + assertEquals(element1, output.get(0)); + assertEquals(element2, output.get(1)); + assertEquals(new Element("element4"), output.get(3)); + assertEquals(new Text("test"), output.get(4)); + assertEquals(new ProcessingInstruction("test", + "PIs are not treated as literals in XSLT?"), output.get(5)); + + } + + + public void testTransform2() + throws ParsingException, IOException, XSLException { + + File doc = new File(inputDir, "8-1.xml"); + File stylesheet = new File(inputDir, "8-12.xsl"); + Builder builder = new Builder(); + Document stylesheetDoc = builder.build(stylesheet); + XSLTransform xform = new XSLTransform(stylesheetDoc); + Nodes output = xform.transform(builder.build(doc)); + assertEquals(1, output.size()); + Document result = new Document((Element) (output.get(0))); + + Document expected = builder.build("data/xslt/output/8-12.xml"); + assertEquals(expected, result); + + } + + + // For debugging + private static void dumpResult(Document result, String filename) + throws IOException { + + File debug = new File("data"); + debug = new File(debug, "xslt"); + debug = new File(debug, "debug/" + filename); + OutputStream out = new FileOutputStream(debug); + Serializer serializer = new Serializer(out); + serializer.write(result); + serializer.flush(); + out.close(); + + } + + + public void testTransformFromDocument() + throws ParsingException, IOException, XSLException { + + File doc = new File(inputDir, "8-1.xml"); + Builder builder = new Builder(); + Document stylesheet = builder.build("data/xslt/input/8-12.xsl"); + XSLTransform xform = new XSLTransform(stylesheet); + Nodes output = xform.transform(builder.build(doc)); + assertEquals(1, output.size()); + Document result = new Document((Element) (output.get(0))); + + Document expected = builder.build("data/xslt/output/8-12.xml"); + assertEquals(expected, result); + + } + + + public void testTransformFromSystemID() + throws ParsingException, IOException, XSLException { + + File doc = new File(inputDir, "8-1.xml"); + Builder builder = new Builder(); + String stylesheet = "data/xslt/input/8-12.xsl"; + Document stylesheetDoc = builder.build(stylesheet); + XSLTransform xform = new XSLTransform(stylesheetDoc); + Nodes output = xform.transform(builder.build(doc)); + assertEquals(1, output.size()); + Document result = new Document((Element) (output.get(0))); + + Document expected = builder.build("data/xslt/output/8-12.xml"); + assertEquals(expected, result); + + } + + + public void testTransformWithNamespaces() + throws ParsingException, IOException, XSLException { + + File doc = new File(inputDir, "8-14.xml"); + File stylesheet = new File(inputDir, "8-15.xsl"); + Builder builder = new Builder(); + Document stylesheetDoc = builder.build(stylesheet); + XSLTransform xform = new XSLTransform(stylesheetDoc); + Document input = builder.build(doc); + Nodes output = xform.transform(input); + assertEquals(1, output.size()); + Document result = new Document((Element) (output.get(0))); + + Document expected = builder.build("data/xslt/output/8-15.xml"); + assertEquals(expected, result); + + } + + + public void testSingleTextNode() + throws ParsingException, IOException, XSLException { + + File doc = new File(inputDir, "8-14.xml"); + File stylesheet = new File(inputDir, "singlestring.xsl"); + Builder builder = new Builder(); + Document stylesheetDoc = builder.build(stylesheet); + XSLTransform xform = new XSLTransform(stylesheetDoc); + Document input = builder.build(doc); + Nodes output = xform.transform(input); + assertEquals(1, output.size()); + Text data = (Text) (output.get(0)); + assertEquals("Data", data.getValue()); + + } + + + public void testToString() + throws XSLException, ParsingException, IOException { + + File stylesheet = new File(inputDir, "singlestring.xsl"); + Builder builder = new Builder(); + Document stylesheetDoc = builder.build(stylesheet); + XSLTransform xform = new XSLTransform(stylesheetDoc); + assertTrue(xform.toString().startsWith("[nu.xom.xslt.XSLTransform: ")); + + } + + + // Make sure that method="text" doesn't affect what we get + // since this is not a serialized transform + // See http://issues.apache.org/bugzilla/show_bug.cgi?id=30197 + public void testTextMethod() + throws ParsingException, IOException, XSLException { + + File doc = new File(inputDir, "8-14.xml"); + File stylesheet = new File(inputDir, "textmethod.xsl"); + Builder builder = new Builder(); + Document stylesheetDoc = builder.build(stylesheet); + XSLTransform xform = new XSLTransform(stylesheetDoc); + Document input = builder.build(doc); + Nodes output = xform.transform(input); + assertEquals(6, output.size()); + assertEquals("12345", output.get(0).getValue()); + assertEquals("67890", output.get(1).getValue()); + assertEquals("", output.get(2).getValue()); + assertEquals("0987654321", output.get(3).getValue()); + assertTrue(output.get(4) instanceof Comment); + assertTrue(output.get(5) instanceof ProcessingInstruction); + + } + + + public void testCommentWithParent() + throws XSLException, ParsingException, IOException { + + Builder builder = new Builder(); + File stylesheet = new File(inputDir, "commentwithparent.xsl"); + Document stylesheetDoc = builder.build(stylesheet); + XSLTransform xform = new XSLTransform(stylesheetDoc); + Document input = new Document(new Element("root")); + Nodes output = xform.transform(input); + assertEquals(1, output.size()); + assertEquals("", output.get(0).getValue()); + Element root = (Element) output.get(0); + assertEquals(1, root.getChildCount()); + Comment child = (Comment) root.getChild(0); + assertEquals("test", child.getValue()); + + } + + + public void testProcessingInstructionWithParent() + throws XSLException, ParsingException, IOException { + + Builder builder = new Builder(); + File stylesheet = new File(inputDir, "piwithparent.xsl"); + Document stylesheetDoc = builder.build(stylesheet); + XSLTransform xform = new XSLTransform(stylesheetDoc); + Document input = new Document(new Element("root")); + Nodes output = xform.transform(input); + assertEquals(1, output.size()); + assertEquals("", output.get(0).getValue()); + Element root = (Element) output.get(0); + assertEquals(1, root.getChildCount()); + ProcessingInstruction child = (ProcessingInstruction) root.getChild(0); + assertEquals("target", child.getTarget()); + assertEquals("test", child.getValue()); + + } + + + public void testTransformNodes() + throws XSLException, ParsingException, IOException { + + File stylesheet = new File(inputDir, "piwithparent.xsl"); + Builder builder = new Builder(); + Nodes input = new Nodes(new Element("root")); + Document stylesheetDoc = builder.build(stylesheet); + XSLTransform xform = new XSLTransform(stylesheetDoc); + Nodes output = xform.transform(input); + assertEquals(1, output.size()); + assertEquals("", output.get(0).getValue()); + Element root = (Element) output.get(0); + assertEquals(1, root.getChildCount()); + ProcessingInstruction child = (ProcessingInstruction) root.getChild(0); + assertEquals("target", child.getTarget()); + assertEquals("test", child.getValue()); + + } + + + public void testCopyXMLBaseAttribute() + throws XSLException, ParsingException, IOException { + + File stylesheet = new File(inputDir, "id_transform.xsl"); + Builder builder = new Builder(); + Element root = new Element("root"); + root.addAttribute(new Attribute("xml:base", + Namespace.XML_NAMESPACE, "http://www.example.org/")); + Document input = new Document(root); + Document stylesheetDoc = builder.build(stylesheet); + XSLTransform xform = new XSLTransform(stylesheetDoc); + Nodes output = xform.transform(input); + assertEquals(1, output.size()); + assertEquals("", output.get(0).getValue()); + Element rootOut = (Element) output.get(0); + assertEquals(0, rootOut.getChildCount()); + assertEquals(1, rootOut.getAttributeCount()); + assertEquals("http://www.example.org/", rootOut.getBaseURI()); + + } + + + public void testCopyXMLLangAttribute() + throws XSLException, ParsingException, IOException { + + File stylesheet = new File(inputDir, "id_transform.xsl"); + Builder builder = new Builder(); + Element root = new Element("root"); + root.addAttribute(new Attribute("xml:lang", + Namespace.XML_NAMESPACE, "http://www.example.org/")); + Document input = new Document(root); + Document stylesheetDoc = builder.build(stylesheet); + XSLTransform xform = new XSLTransform(stylesheetDoc); + Nodes output = xform.transform(input); + assertEquals(1, output.size()); + assertEquals("", output.get(0).getValue()); + Element rootOut = (Element) output.get(0); + assertEquals(0, rootOut.getChildCount()); + assertEquals(1, rootOut.getAttributeCount()); + + } + + + public void testTriple() + throws IOException, ParsingException, XSLException { + + File stylesheet = new File(inputDir, "identity.xsl"); + Builder builder = new Builder(); + Document stylesheetDoc = builder.build(stylesheet); + XSLTransform xform = new XSLTransform(stylesheetDoc, + new NodeFactoryTest.TripleElementFilter()); + + String data = ""; + Document doc = builder.build(data, "http://www.example.org/"); + + Nodes result = xform.transform(doc); + + assertEquals(3, result.size()); + assertEquals(result.get(0), result.get(1)); + assertEquals(result.get(1), result.get(2)); + Element a = (Element) result.get(2); + assertEquals("a", a.getLocalName()); + assertEquals(3, a.getChildCount()); + assertEquals(0, a.getAttributeCount()); + Element b = (Element) a.getChild(1); + assertEquals(3, b.getChildCount()); + assertEquals("b", b.getLocalName()); + + } + + + public void testPassingNullSetsDefaultFactory() + throws IOException, ParsingException, XSLException { + + File stylesheet = new File(inputDir, "identity.xsl"); + Builder builder = new Builder(); + Document stylesheetDoc = builder.build(stylesheet); + XSLTransform xform = new XSLTransform(stylesheetDoc, null); + + String data = ""; + Document doc = builder.build(data, "http://www.example.org/"); + + Nodes result = xform.transform(doc); + + assertEquals(1, result.size()); + Element a = (Element) result.get(0); + assertEquals("a", a.getLocalName()); + assertEquals(1, a.getChildCount()); + assertEquals(0, a.getAttributeCount()); + Element b = (Element) a.getChild(0); + assertEquals(1, b.getChildCount()); + assertEquals("b", b.getLocalName()); + + } + + + public void testTransformEmptyNodesList() + throws IOException, ParsingException, XSLException { + + File stylesheet = new File(inputDir, "identity.xsl"); + Builder builder = new Builder(); + Document stylesheetDoc = builder.build(stylesheet); + XSLTransform xform = new XSLTransform(stylesheetDoc); + + Nodes result = xform.transform(new Nodes()); + + assertEquals(0, result.size()); + + } + + + public void testMinimizingFactory() + throws XSLException, ParsingException, IOException { + + File stylesheet = new File(inputDir, "identity.xsl"); + Builder builder = new Builder(); + Document stylesheetDoc = builder.build(stylesheet); + XSLTransform xform = new XSLTransform(stylesheetDoc, + new NodeFactoryTest.MinimizingFactory()); + + Document input = builder.build("" + + "data\r\ntest" + + "", "http://example.org/"); + Nodes output = xform.transform(input); + assertEquals(0, output.size()); + + } + + + public void testIllegalTransform() + throws XSLException, ParsingException, IOException { + + File stylesheet = new File(inputDir, "illegaltransform.xsl"); + Builder builder = new Builder(); + Document stylesheetDoc = builder.build(stylesheet); + XSLTransform xform = new XSLTransform(stylesheetDoc); + Element root = new Element("root", "http://www.example.org"); + Document input = new Document(root); + try { + xform.transform(input); + fail("Allowed illegal transform"); + } + catch (XSLException ex) { + assertNotNull(ex.getMessage()); + } + + } + + + public void testRemapPrefixToSameURI() + throws IOException, ParsingException, XSLException { + + File stylesheet = new File(inputDir, "identity.xsl"); + Builder builder = new Builder(); + Document stylesheetDoc = builder.build(stylesheet); + XSLTransform xform = new XSLTransform(stylesheetDoc); + + String data = "" + + "in B"; + Document doc = builder.build(data, "http://www.example.org/"); + + Nodes result = xform.transform(doc); + + assertEquals(doc.getRootElement(), result.get(0)); + + } + + + public void testElementsToAttributes() + throws IOException, ParsingException, XSLException { + + File stylesheet = new File(inputDir, "identity.xsl"); + Builder builder = new Builder(); + Document stylesheetDoc = builder.build(stylesheet); + XSLTransform xform = new XSLTransform(stylesheetDoc, + new AttributeFactory()); + + String data = "in Bin C"; + Document doc = builder.build(data, "http://www.example.org/"); + + Nodes result = xform.transform(doc); + + assertEquals(1, result.size()); + Element a = (Element) result.get(0); + assertEquals("a", a.getLocalName()); + assertEquals(0, a.getChildCount()); + assertEquals(1, a.getAttributeCount()); + assertEquals("in B", a.getAttribute("b").getValue()); + + } + + + private static class AttributeFactory extends NodeFactory { + + public Nodes finishMakingElement(Element element) { + ParentNode parent = element.getParent(); + if (parent == null || parent instanceof Document) { + return new Nodes(element); + } + return new Nodes(new Attribute(element.getQualifiedName(), + element.getNamespaceURI(), element.getValue())); + } + + } + + + public void testAttributesToElements() + throws IOException, ParsingException, XSLException { + + File stylesheet = new File(inputDir, "identity.xsl"); + Builder builder = new Builder(); + Document stylesheetDoc = builder.build(stylesheet); + XSLTransform xform = new XSLTransform(stylesheetDoc, + new AttributesToElements()); + + String data = ""; + Document doc = builder.build(data, "http://www.example.org/"); + + Nodes result = xform.transform(doc); + + assertEquals(1, result.size()); + Element a = (Element) result.get(0); + assertEquals("a", a.getLocalName()); + assertEquals(2, a.getChildCount()); + assertEquals(0, a.getAttributeCount()); + Element name = (Element) a.getChild(0); + assertEquals("name", name.getLocalName()); + assertEquals("value", name.getValue()); + Element b = (Element) a.getChild(1); + assertEquals("b", b.getLocalName()); + assertEquals(2, b.getChildCount()); + assertEquals("y", b.getFirstChildElement("x").getValue()); + assertEquals("b", b.getFirstChildElement("a").getValue()); + + } + + + private static class AttributesToElements extends NodeFactory { + + public Nodes makeAttribute(String name, String URI, + String value, Attribute.Type type) { + Element element = new Element(name, URI); + element.appendChild(value); + return new Nodes(element); + } + + } + + + public void testCommentsAreTransformed() + throws IOException, ParsingException, XSLException { + + File stylesheet = new File(inputDir, "identity.xsl"); + Builder builder = new Builder(); + Document stylesheetDoc = builder.build(stylesheet); + XSLTransform xform = new XSLTransform(stylesheetDoc); + + String data = ""; + Document doc = builder.build(data, "http://www.example.org/"); + + Nodes result = xform.transform(doc); + + assertEquals(1, result.size()); + Element a = (Element) result.get(0); + assertEquals("a", a.getLocalName()); + assertEquals(1, a.getChildCount()); + assertEquals(0, a.getAttributeCount()); + Node child = a.getChild(0); + assertTrue(child instanceof Comment); + assertTrue(child.getValue().equals("test")); + + } + + + public void testCommentToAttribute() + throws IOException, ParsingException, XSLException { + + File stylesheet = new File(inputDir, "identity.xsl"); + Builder builder = new Builder(); + Document stylesheetDoc = builder.build(stylesheet); + XSLTransform xform = new XSLTransform(stylesheetDoc, + new NodeFactory() { + public Nodes makeComment(String text) { + return new Nodes(new Attribute("comment", text)); + } + }); + + String data = ""; + Document doc = builder.build(data, "http://www.example.org/"); + + Nodes result = xform.transform(doc); + + assertEquals(1, result.size()); + Element a = (Element) result.get(0); + assertEquals("a", a.getLocalName()); + assertEquals(0, a.getChildCount()); + assertEquals(1, a.getAttributeCount()); + Attribute comment = a.getAttribute(0); + assertEquals("comment", comment.getLocalName()); + assertEquals("test", comment.getValue()); + + } + + + public void testAdditionalDefaultNamespace() + throws IOException, ParsingException, XSLException { + + File stylesheet = new File(inputDir, "identity.xsl"); + Builder builder = new Builder(); + Document stylesheetDoc = builder.build(stylesheet); + XSLTransform xform = new XSLTransform(stylesheetDoc); + + String data = "data"; + Document doc = builder.build(data, "http://www.example.org/"); + + Nodes result = xform.transform(doc); + + assertEquals(1, result.size()); + Element a = (Element) result.get(0); + assertEquals("a", a.getLocalName()); + assertEquals("pre:a", a.getQualifiedName()); + assertEquals("data", a.getValue()); + assertEquals("http://www.example.org", a.getNamespaceURI("pre")); + assertEquals("http://www.example.net", a.getNamespaceURI("")); + assertEquals(2, a.getNamespaceDeclarationCount()); + + } + + + public void testToDocumentWithEmptyNodes() { + + try { + XSLTransform.toDocument(new Nodes()); + fail("Converted empty nodes to document"); + } + catch (XMLException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testToDocumentWithNoRoot() { + + Nodes input = new Nodes(); + input.append(new Comment("data")); + try { + XSLTransform.toDocument(new Nodes()); + fail("Converted comment to document"); + } + catch (XMLException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testToDocumentWithText() { + + Nodes input = new Nodes(); + Element root = new Element("root"); + Comment comment = new Comment("data"); + ProcessingInstruction pi = new ProcessingInstruction("target", "data"); + input.append(comment); + input.append(root); + input.append(pi); + input.append(new Text("text")); + try { + XSLTransform.toDocument(new Nodes()); + fail("Converted text to document"); + } + catch (XMLException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testToDocumentWithAttribute() { + + Nodes input = new Nodes(); + Element root = new Element("root"); + Comment comment = new Comment("data"); + ProcessingInstruction pi = new ProcessingInstruction("target", "data"); + input.append(comment); + input.append(root); + input.append(pi); + input.append(new Attribute("name", "text")); + try { + XSLTransform.toDocument(new Nodes()); + fail("Converted text to document"); + } + catch (XMLException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testToDocumentWithDocType() { + + Nodes input = new Nodes(); + Element root = new Element("root"); + DocType doctype = new DocType("root"); + Comment comment = new Comment("data"); + ProcessingInstruction pi = new ProcessingInstruction("target", "data"); + input.append(comment); + input.append(doctype); + input.append(root); + input.append(pi); + Document output = XSLTransform.toDocument(input); + assertEquals(root, output.getRootElement()); + assertEquals(comment, output.getChild(0)); + assertEquals(doctype, output.getChild(1)); + assertEquals(pi, output.getChild(3)); + assertEquals(input.size(), output.getChildCount()); + + } + + + public void testToDocumentWithDocTypeInEpilog() { + + Nodes input = new Nodes(); + Element root = new Element("root"); + DocType doctype = new DocType("root"); + Comment comment = new Comment("data"); + ProcessingInstruction pi = new ProcessingInstruction("target", "data"); + input.append(comment); + input.append(root); + input.append(doctype); + input.append(pi); + try { + XSLTransform.toDocument(input); + fail("Allowed doctype in epilog"); + } + catch (XMLException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testToDocumentWithDoubleRoot() { + + Nodes input = new Nodes(); + Element root = new Element("root"); + Comment comment = new Comment("data"); + input.append(comment); + input.append(root); + input.append(new Element("root2")); + try { + XSLTransform.toDocument(input); + fail("Allowed two root elements"); + } + catch (XMLException success) { + assertNotNull(success.getMessage()); + } + + } + + + public void testToDocumentWithSingleRoot() { + + Nodes input = new Nodes(); + Element root = new Element("root"); + input.append(root); + Document output = XSLTransform.toDocument(input); + assertEquals(root, output.getRootElement()); + assertEquals(input.size(), output.getChildCount()); + + } + + + public void testToDocumentWithPrologAndEpilog() { + + Nodes input = new Nodes(); + Element root = new Element("root"); + Comment comment = new Comment("data"); + ProcessingInstruction pi = new ProcessingInstruction("target", "data"); + input.append(comment); + input.append(root); + input.append(pi); + Document output = XSLTransform.toDocument(input); + assertEquals(root, output.getRootElement()); + assertEquals(comment, output.getChild(0)); + assertEquals(pi, output.getChild(2)); + assertEquals(input.size(), output.getChildCount()); + + } + + +} diff --git a/src/nu/xom/tests/package.html b/src/nu/xom/tests/package.html new file mode 100644 index 0000000..e314029 --- /dev/null +++ b/src/nu/xom/tests/package.html @@ -0,0 +1,83 @@ + + + + + + +

    +nu.xom.tests contains the JUnit +based test suite for XOM. JUnit 3.7 or later is required. +(Earlier versions don't have the assertTrue method +these tests depend on.) +

    + +

    +The XOMTests class runs all the tests, except those that +take an exceptionally long time to run (MegaTest). +Some of the tests, especially the builder tests, +depend on the underlying parser, and are known to fail +if the parser is buggy. Xerces 2.6.1 is the only parser which is currently +known to be able to run all the tests. Earlier versions of Xerces 2.x +cannot successfully run all the tests. +The IBM JVM 1.4.1 bundles an earlier version of Xerces which overrides the one bundled with XOM. +For this reason, a few tests will fail when using that VM. These tests should pass when using the +Sun VMs. +

    + +

    +Similarly some of the XSLT tests depend on the +underlying TrAX engine. Xalan 2.7 and Saxon 6.5.4 both do a pretty good job, +but neither can pass all thne tests included with XOM. +testOASISMicrosoftConformanceSuite +is the most likely test to fail. +

    + + +

    +A few tests rely on access to external network resources, + and may fail sproadically if the remote server is down or unreachable. +However, in general, they pass. +

    + +

    +The only other test in this class which is known to fail is +testBuildFromFileThatContainsPlane1CharacterInName. This test fails on Mac OS X due to bugs in that platform's VM. However, it passes on other platforms. +

    + +

    +There are numerous public classes and methods in this package because JUnit requires +all test cases to be public. However, the only published class is +XOMTestCase which provides methods for asserting the equality +of documents, elements, processing instructions, and other node types. +This may be useful for your own tests. The remainder of this package +should be treated as unstable and your code should not depend on it. +

    + +@since 1.0 + + + diff --git a/src/nu/xom/tools/XHTMLJavaDoc.java b/src/nu/xom/tools/XHTMLJavaDoc.java new file mode 100644 index 0000000..f08e870 --- /dev/null +++ b/src/nu/xom/tools/XHTMLJavaDoc.java @@ -0,0 +1,170 @@ +/* Copyright 2004 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.tools; + +import java.io.File; +import java.io.FileFilter; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +import nu.xom.Attribute; +import nu.xom.Builder; +import nu.xom.DocType; +import nu.xom.Document; +import nu.xom.Element; +import nu.xom.NodeFactory; +import nu.xom.Nodes; +import nu.xom.ParsingException; +import nu.xom.Serializer; + + +/** + *

    + * This class converts standard Sun JavaDoc to well-formed + * XHTML. It requires the use of John Cowan's TagSoup. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.0 + * + */ +class XHTMLJavaDoc { + + private static Builder builder + = new Builder(new org.ccil.cowan.tagsoup.Parser(), + false, new HTMLFixFactory()); + + + private static class HTMLFilter implements FileFilter { + + public boolean accept(File pathname) { + if (pathname.getName().endsWith(".html")) return true; + if (pathname.isDirectory()) return true; + return false; + } + + } + + + public static void main(String[] args) { + + try { + File indir = new File(args[0]); + process(indir); + } + catch (Exception ex) { + ex.printStackTrace(); + } + + } + + + private static void process(File indir) { + + FileFilter htmlfilter = new HTMLFilter(); + if (indir.exists() && indir.isDirectory()) { + File[] files = indir.listFiles(htmlfilter); + for (int i = 0; i < files.length; i++) { + File f = files[i]; + if (f.isDirectory()) { + process(f); + } + else { + try { + Document doc = builder.build(f); + DocType doctype = new DocType("html", + "-//W3C//DTD XHTML 1.0 Frameset//EN", + "http://www.w3.org/TR/2000/REC-xhtml1-20000126/DTD/xhtml1-frameset.dtd"); + doc.setDocType(doctype); + Attribute en = new Attribute("lang", "en-US"); + Attribute xmlen = new Attribute("xml:lang", + "http://www.w3.org/XML/1998/namespace", "en-US"); + Element root = doc.getRootElement(); + root.addAttribute(en); + root.addAttribute(xmlen); + Attribute version = root.getAttribute("version"); + if (version != null) root.removeAttribute(version); + Element body = root.getFirstChildElement("body", "http://www.w3.org/1999/xhtml"); + Element frameset = root.getFirstChildElement("frameset", "http://www.w3.org/1999/xhtml"); + if (frameset != null && body != null) { + root.removeChild(body); + } + Serializer serializer = new HTMLSerializer(new FileOutputStream(f)); + serializer.write(doc); + serializer.flush(); + } + catch (ParsingException ex) { + ex.printStackTrace(); + } + catch (IOException ex) { + ex.printStackTrace(); + } + } + } + } + else { + System.err.println("Could not locate source directory: " + indir); + } + + } + + + private static class HTMLFixFactory extends NodeFactory { + + public Nodes finishMakingElement(Element element) { + + if (element.getLocalName().equals("i")) { + element.setLocalName("span"); + element.addAttribute(new Attribute("style", "font-style: italic")); + } + else if (element.getLocalName().equals("b")) { + element.setLocalName("span"); + element.addAttribute(new Attribute("style", "font-weight: bold")); + } + + return new Nodes(element); + + } + + } + + + private static class HTMLSerializer extends Serializer { + + HTMLSerializer(OutputStream out) { + super(out); + } + + protected void writeXMLDeclaration() { + } + + protected void writeEmptyElementTag(Element element) + throws IOException { + super.writeStartTag(element); + super.writeEndTag(element); + } + + } + + +} diff --git a/src/nu/xom/tools/package.html b/src/nu/xom/tools/package.html new file mode 100644 index 0000000..6e8648c --- /dev/null +++ b/src/nu/xom/tools/package.html @@ -0,0 +1,41 @@ + + + + + + + +

    +nu.xom.tools contains +classes that are used to build XOM. +They are not part of the published API. +

    + +@since 1.0 + + + \ No newline at end of file diff --git a/src/nu/xom/xinclude/BadEncodingAttributeException.java b/src/nu/xom/xinclude/BadEncodingAttributeException.java new file mode 100644 index 0000000..5a52d2b --- /dev/null +++ b/src/nu/xom/xinclude/BadEncodingAttributeException.java @@ -0,0 +1,68 @@ +/* Copyright 2004 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.xinclude; + +/** + *

    + * Indicates that the encoding attribute + * is not an EncName as defined in the + * XML specification, section 4.3.3, rule 81. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.1b3 + */ +public class BadEncodingAttributeException extends XIncludeException { + + private static final long serialVersionUID = -8856360955880988486L; + + + /** + *

    + * Constructs a BadEncodingAttributeException with + * the specified detail message. + *

    + * + * @param message a string indicating the specific problem + */ + public BadEncodingAttributeException(String message) { + super(message); + } + + + /** + *

    + * Creates a new BadEncodingAttributeException with a + * detail message and the URI of the document that caused + * the exception. + *

    + * + * @param message a string indicating the specific problem + * @param uri the URI of the document that caused this exception + */ + public BadEncodingAttributeException(String message, String uri) { + super(message, uri); + } + + +} \ No newline at end of file diff --git a/src/nu/xom/xinclude/BadHTTPHeaderException.java b/src/nu/xom/xinclude/BadHTTPHeaderException.java new file mode 100644 index 0000000..30cc730 --- /dev/null +++ b/src/nu/xom/xinclude/BadHTTPHeaderException.java @@ -0,0 +1,69 @@ +/* Copyright 2002-2005 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.xinclude; + +/** + *

    + * Indicates that an accept or + * accept-language attribute contains C0 control + * characters (including linefeed, tab, or carriage return) + * or non-ASCII characters. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.1b3 + */ +public class BadHTTPHeaderException extends XIncludeException { + + + private static final long serialVersionUID = 6854042776873380044L; + + + /** + *

    + * Constructs a BadHTTPHeaderException with + * the specified detail message. + *

    + * + * @param message a string indicating the specific problem + */ + public BadHTTPHeaderException(String message) { + super(message); + } + + + /** + *

    + * Creates a new BadHTTPHeaderException with a detail + * message and a URI of the document + * that caused the exception. + *

    + * + * @param message a string indicating the specific problem + * @param uri the URI of the document that caused this exception + */ + public BadHTTPHeaderException(String message, String uri) { + super(message, uri); + } + + +} \ No newline at end of file diff --git a/src/nu/xom/xinclude/BadHrefAttributeException.java b/src/nu/xom/xinclude/BadHrefAttributeException.java new file mode 100644 index 0000000..421be00 --- /dev/null +++ b/src/nu/xom/xinclude/BadHrefAttributeException.java @@ -0,0 +1,68 @@ +/* Copyright 2004, 2005 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.xinclude; + +/** + *

    + * Indicates that an href attribute contains an illegal + * value, such as a URI with a fragment identifier, or a syntactically + * incorrect IRI. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.1b3 + */ +public class BadHrefAttributeException extends XIncludeException { + + + private static final long serialVersionUID = 4349654324645147403L; + + + /** + *

    + * Constructs a BadHrefAttributeException with + * the specified detail message. + *

    + * + * @param message a string indicating the specific problem + */ + public BadHrefAttributeException(String message) { + super(message); + } + + + /** + *

    + * Creates a new BadHrefAttributeException with a + * detail message and the URI of the document that caused + * the exception. + *

    + * + * @param message a string indicating the specific problem + * @param uri the URI of the document that caused this exception + */ + public BadHrefAttributeException(String message, String uri) { + super(message, uri); + } + + +} \ No newline at end of file diff --git a/src/nu/xom/xinclude/BadParseAttributeException.java b/src/nu/xom/xinclude/BadParseAttributeException.java new file mode 100644 index 0000000..9699194 --- /dev/null +++ b/src/nu/xom/xinclude/BadParseAttributeException.java @@ -0,0 +1,67 @@ +/* Copyright 2002-2005 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.xinclude; + +/** + *

    + * Indicates that the parse attribute has some value other + * than xml or text. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.1b3 + */ +public class BadParseAttributeException extends XIncludeException { + + + private static final long serialVersionUID = 8893893462108158307L; + + + /** + *

    + * Constructs a BadParseAttributeException with + * the specified detail message. + *

    + * + * @param message a string indicating the specific problem + */ + public BadParseAttributeException(String message) { + super(message); + } + + + /** + *

    + * Creates a new BadParseAttributeException with a + * detail message and the URI of the document that caused + * the exception. + *

    + * + * @param message a string indicating the specific problem + * @param uri the URI of the document that caused this exception + */ + public BadParseAttributeException(String message, String uri) { + super(message, uri); + } + + +} \ No newline at end of file diff --git a/src/nu/xom/xinclude/EncodingHeuristics.java b/src/nu/xom/xinclude/EncodingHeuristics.java new file mode 100644 index 0000000..8c47ca2 --- /dev/null +++ b/src/nu/xom/xinclude/EncodingHeuristics.java @@ -0,0 +1,231 @@ +/* Copyright 2002, 2003, 2005, 2006 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.xinclude; + +import java.io.IOException; +import java.io.InputStream; + +/** + *

    + * EncodingHeuristics reads from a stream + * (which should be buffered) and attempts to guess + * what the encoding of the text in the stream is. + * Byte order marks are stripped from the stream. + * If it fails to determine the type of the encoding, + * it returns the default UTF-8. + *

    + * + * + * @author Elliotte Rusty Harold + * @version 1.2d1 + */ +class EncodingHeuristics { + + // No instances allowed + private EncodingHeuristics() {} + + + /** + *

    + * This utility method uses a variety of heuristics to + * attempt to guess the encoding from the initial + * characters. + *

    + * + * @param in InputStream to read from. + * @return String The name of the encoding. + * @throws IOException if the stream cannot be reset back + * to where it was when the method was invoked. + */ + public static String readEncodingFromStream(InputStream in) + throws IOException { + + // This may fail if there are a lot of space + // characters before the end of the encoding declaration + in.mark(1024); + + try { + // Lots of things can go wrong here. If any do, + // return "UTF-8" as the default. + int byte1 = in.read(); + int byte2 = in.read(); + if (byte1 == 0xFE && byte2 == 0xFF) { + // Don't reset because the byte order mark should not be + // included per section 4.3 of the XInclude spec + return "UnicodeBig"; + } + else if (byte1 == 0xFF && byte2 == 0xFE) { + // Don't reset because the byte order mark should not be + // included per section 4.3 of the XInclude spec + return "UnicodeLittle"; + } + + /* In accordance with the Character Model, + when the text format is a Unicode encoding, the XInclude + processor must fail the inclusion when the text in the + selected range is non-normalized. When transcoding + characters to a Unicode encoding from a legacy encoding, + a normalizing transcoder must be used. */ + + int byte3 = in.read(); + // check for UTF-8 byte order mark + if (byte1 == 0xEF && byte2 == 0xBB && byte3 == 0xBF) { + // Don't reset because the byte order mark should not be + // included per section 4.3 of the XInclude spec + return "UTF-8"; + } + + int byte4 = in.read(); + if (byte1 == 0x00 + && byte2 == 0x00 && byte3 == 0xFE && byte4 == 0xFF) { + // Don't reset because the byte order mark should not be + // included per section 4.3 of the XInclude spec + // Most Java VMs don't support this next one + return "UTF32BE"; + } + else if (byte1 == 0x00 && byte2 == 0x00 + && byte3 == 0xFF && byte4 == 0xFE) { + // Don't reset because the byte order mark should not be + // included per section 4.3 of the XInclude spec + // Most Java VMs don't support this next one + return "UTF32LE"; + } + + // no byte order mark present; first character must be + // less than sign or white space + // Let's look for less-than signs first + if (byte1 == 0x00 && byte2 == 0x00 + && byte3 == 0x00 && byte4 == '<') { + in.reset(); + return "UTF32BE"; + } + else if (byte1 == '<' && byte2 == 0x00 + && byte3 == 0x00 && byte4 == 0x00) { + in.reset(); + return "UTF32LE"; + } + else if (byte1 == 0x00 && byte2 == '<' + && byte3 == 0x00 && byte4 == '?') { + in.reset(); + return "UnicodeBigUnmarked"; + } + else if (byte1 == '<' && byte2 == 0x00 + && byte3 == '?' && byte4 == 0x00) { + in.reset(); + return "UnicodeLittleUnmarked"; + } + else if (byte1 == '<' && byte2 == '?' + && byte3 == 'x' && byte4 == 'm') { + // ASCII compatible, must read encoding declaration. + // 1024 bytes will be far enough to read most + // XML declarations + byte[] data = new byte[1024]; + data[0] = (byte) byte1; + data[1] = (byte) byte2; + data[2] = (byte) byte3; + data[3] = (byte) byte4; + int length = in.read(data, 4, 1020) + 4; + // Use Latin-1 (ISO-8859-1) because it's ASCII compatible + // and all byte sequences are legal Latin-1 sequences + // so I don't have to worry about encoding errors if I + // slip past the end of the XML/text declaration + String declaration = new String(data, 0, length, "8859_1"); + + // If any of these throw a + // StringIndexOutOfBoundsException, + // we just fall into the catch block and return null + // since this can't be well-formed XML + String encoding = findEncodingDeclaration(declaration); + in.reset(); + return encoding; + + } + else if (byte1 == 0x4C && byte2 == 0x6F + && byte3 == 0xA7 && byte4 == 0x94) { + // EBCDIC compatible, must read encoding declaration + byte[] buffer = new byte[1016]; + for (int i = 0; i < buffer.length; i++) { + int c = in.read(); + if (c == -1) break; + buffer[i] = (byte) c; + } + in.reset(); + // Most EBCDIC encodings are compatible with Cp037 over + // the range we care about + return findEncodingDeclaration(new String(buffer, "Cp037")); + } + + } + catch (IOException ex) { + in.reset(); + return "UTF-8"; + } + catch (RuntimeException ex) { + in.reset(); + return "UTF-8"; + } + + // no XML or text declaration present + in.reset(); + return "UTF-8"; + + } + + + private static String findEncodingDeclaration(String declaration) + throws IOException { + + int position = declaration.indexOf("encoding") + 8; + char c; + // get rid of white space before equals sign + while (true) { + c = declaration.charAt(position++); + if (c !=' ' && c != '\t' && c != '\r' && c != '\n') { + break; + } + } + if (c != '=') { // malformed + throw new IOException("Couldn't determine encoding"); + } + // get rid of white space after equals sign + while (true) { + c = declaration.charAt(position++); + if (c !=' ' && c != '\t' && c != '\r' && c != '\n') { + break; + } + } + char delimiter = c; + if (delimiter != '\'' && delimiter != '"') { // malformed + return "UTF-8"; + } + // now positioned to read encoding name + StringBuffer encodingName = new StringBuffer(); + while (true) { + c = declaration.charAt(position++); + if (c == delimiter) break; + encodingName.append(c); + } + return encodingName.toString(); + + } + +} \ No newline at end of file diff --git a/src/nu/xom/xinclude/InclusionLoopException.java b/src/nu/xom/xinclude/InclusionLoopException.java new file mode 100644 index 0000000..d0db4dc --- /dev/null +++ b/src/nu/xom/xinclude/InclusionLoopException.java @@ -0,0 +1,67 @@ +/* Copyright 2002-2005 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.xinclude; + +/** + *

    + * Indicates that an included document attempts to include itself or + * one of its ancestor documents, directly or indirectly. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.1b3 + */ +public class InclusionLoopException extends XIncludeException { + + + private static final long serialVersionUID = 130645121676656036L; + + + /** + *

    + * Constructs an InclusionLoopException with + * the specified detail message. + *

    + * + * @param message a string indicating the specific problem + */ + public InclusionLoopException(String message) { + super(message); + } + + + /** + *

    + * Creates a new InclusionLoopException with a detail + * message, line and column numbers, and a URI of the document + * that caused the exception. + *

    + * + * @param message a string indicating the specific problem + * @param uri the URI of the document that caused this exception + */ + public InclusionLoopException(String message, String uri) { + super(message, uri); + } + + +} \ No newline at end of file diff --git a/src/nu/xom/xinclude/MisplacedFallbackException.java b/src/nu/xom/xinclude/MisplacedFallbackException.java new file mode 100644 index 0000000..72c227e --- /dev/null +++ b/src/nu/xom/xinclude/MisplacedFallbackException.java @@ -0,0 +1,67 @@ +/* Copyright 2002-2005 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.xinclude; + +/** + *

    + * Indicates that an xinclude:fallback element + * was found outside of an xinclude:include element. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.1b3 + */ +public class MisplacedFallbackException extends XIncludeException { + + + private static final long serialVersionUID = -6264070699717750818L; + + + /** + *

    + * Constructs a MisplacedFallbackException with + * the specified detail message. + *

    + * + * @param message a string indicating the specific problem + */ + public MisplacedFallbackException(String message) { + super(message); + } + + + /** + *

    + * Creates a new MisplacedFallbackException with a detail + * message, line and column numbers, and a URI of the document + * that caused the exception. + *

    + * + * @param message a string indicating the specific problem + * @param uri the URI of the document that caused this exception + */ + public MisplacedFallbackException(String message, String uri) { + super(message, uri); + } + + +} \ No newline at end of file diff --git a/src/nu/xom/xinclude/NoIncludeLocationException.java b/src/nu/xom/xinclude/NoIncludeLocationException.java new file mode 100644 index 0000000..4156aa6 --- /dev/null +++ b/src/nu/xom/xinclude/NoIncludeLocationException.java @@ -0,0 +1,84 @@ +/* Copyright 2002-2005 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.xinclude; + +/** + *

    + * Indicates that an xinclude:include element has neither + * an href attribute nor an xpointer + * attribute. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.1b3 + */ +public class NoIncludeLocationException extends XIncludeException { + + + private static final long serialVersionUID = -2839892819780144596L; + + + /** + *

    + * Constructs a NoIncludeLocationException with the + * specified message. + *

    + * + * @param message a string indicating the specific problem + */ + public NoIncludeLocationException(String message) { + super(message); + } + + + /** + *

    + * Constructs a NoIncludeLocationException with the + * specified detail message and root cause. + *

    + * + * @param message a string indicating the specific problem + * @param cause the initial exception that caused this + * NoIncludeLocationException + */ + public NoIncludeLocationException(String message, Throwable cause) { + super(message); + initCause(cause); + } + + + /** + *

    + * Creates a new NoIncludeLocationException with a + * detail message, line and column numbers, and a URI of the + * document that caused the exception. + *

    + * + * @param message a string indicating the specific problem + * @param uri the URI of the document that caused this exception + */ + public NoIncludeLocationException(String message, String uri) { + super(message, uri); + } + + +} \ No newline at end of file diff --git a/src/nu/xom/xinclude/XIncludeException.java b/src/nu/xom/xinclude/XIncludeException.java new file mode 100644 index 0000000..466a539 --- /dev/null +++ b/src/nu/xom/xinclude/XIncludeException.java @@ -0,0 +1,159 @@ +/* Copyright 2002-2005 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.xinclude; + +/** + *

    + * The generic superclass for all checked exceptions that may be thrown + * as a result of a violation of XInclude's rules. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.1b3 + */ +public class XIncludeException extends Exception { + + + private static final long serialVersionUID = 969926723618314277L; + + private String uri; + + /** + *

    + * Constructs an XIncludeException with the specified + * detail message. + *

    + * + * @param message a string indicating the specific problem + */ + public XIncludeException(String message) { + super(message); + } + + + /** + *

    + * Constructs an XIncludeException with the specified + * detail message and initial cause. The error message string + * message can later be retrieved by the + * {@link java.lang.Throwable#getMessage} + * method of class java.lang.Throwable. + *

    + * + * @param message a string indicating the specific problem + * @param cause the initial cause of the exception + */ + public XIncludeException(String message, Throwable cause) { + super(message); + initCause(cause); + } + + + /** + *

    + * Creates a new XIncludeException with a detail + * message, line and column numbers, and the URI of the document + * that caused the exception. + *

    + * + * @param message a string indicating the specific problem + * @param uri the URI of the document that caused this exception + */ + public XIncludeException(String message, String uri) { + super(message); + this.uri = uri; + } + + + /** + *

    + * Returns the URI of the document that caused this exception. + * If the URI is not known, null is returned. + *

    + * + * @return URI of the document where the exception occurred + */ + public String getURI() { + return this.uri; + } + + + private Throwable cause; + + + /** + *

    + * When an IOException, + * MalformedURLException, or other generic + * exception is thrown while processing an XML document + * for XIncludes, it is customarily replaced + * by some form of XIncludeException. + * This method allows you to retrieve the original exception. + * It returns null if no such exception caused this + * XIncludeException. + *

    + * + * @return the underlying exception which + * caused this XIncludeException to be thrown + */ + public Throwable getCause() { + return this.cause; + } + + + // null is insufficient for detecting an uninitialized cause. + // The cause may be set to null which may not then be reset. + private boolean causeSet = false; + + + /** + *

    + * When an IOException, + * MalformedURLException, or other generic exception + * is thrown while processing an XML document + * for XIncludes, it is customarily replaced + * by some form of XIncludeException. + * This method allows you to store the original exception. + *

    + * + * @param cause the root cause of this exception + * + * @return this XIncludeException + * + * @throws IllegalArgumentException if the cause is this exception + * (An exception cannot be its own cause.) + * @throws IllegalStateException if this method is called twice + */ + public Throwable initCause(Throwable cause) { + if (causeSet) { + throw new IllegalStateException("Can't overwrite cause"); + } + else if (cause == this) { + throw new IllegalArgumentException("Self-causation not permitted"); + } + else this.cause = cause; + causeSet = true; + return this; + } + + +} \ No newline at end of file diff --git a/src/nu/xom/xinclude/XIncluder.java b/src/nu/xom/xinclude/XIncluder.java new file mode 100644 index 0000000..201c067 --- /dev/null +++ b/src/nu/xom/xinclude/XIncluder.java @@ -0,0 +1,983 @@ +/* Copyright 2002-2006, 2019 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.xinclude; + +import java.io.BufferedInputStream; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.io.UnsupportedEncodingException; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLConnection; +import java.util.Locale; +import java.util.ArrayList; + +import nu.xom.Attribute; +import nu.xom.Builder; +import nu.xom.DocType; +import nu.xom.Document; +import nu.xom.Element; +import nu.xom.Elements; +import nu.xom.MalformedURIException; +import nu.xom.Node; +import nu.xom.NodeFactory; +import nu.xom.Nodes; +import nu.xom.ParentNode; +import nu.xom.ParsingException; +import nu.xom.Text; + +/** + *

    + * Implements XInclude resolution as specified in + * XML Inclusions (XInclude) Version + * 1.0. Fallbacks are supported. + * The XPointer element() scheme and + * shorthand XPointers are also supported. The XPointer + * xpointer() scheme is not supported. + * The accept and accept-language + * attributes are supported. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.3.1 + * + */ +public class XIncluder { + + private static String version = System.getProperty("java.version"); + + // could rewrite this to handle only elements in documents + // (no parentless elements) and then add code to handle Nodes + // and parentless elements by sticking each one in a Document + + // prevent instantiation + private XIncluder() {} + + /** + *

    + * The namespace name of all XInclude elements. + *

    + */ + public final static String XINCLUDE_NS + = "http://www.w3.org/2001/XInclude"; + + /** + *

    + * Returns a copy of the document in which all + * xinclude:include elements have been + * replaced by their referenced content. The original + * Document object is not modified. + * Resolution is recursive; that is, include elements + * in the included documents are themselves resolved. + * The Document returned contains no + * include elements. + *

    + * + * @param in the document in which include elements + * should be resolved + * + * @return copy of the document in which + * all xinclude:include elements + * have been replaced by their referenced content + * + * @throws BadParseAttributeException if an include + * element has a parse attribute with any value + * other than text or parse + * @throws InclusionLoopException if the document + * contains an XInclude element that attempts to include + * a document in which this element is directly or indirectly + * included. + * @throws IOException if an included document could not be loaded, + * and no fallback was available + * @throws NoIncludeLocationException if an xinclude:include + * element does not have an href attribute + * @throws ParsingException if an included XML document + * was malformed + * @throws UnsupportedEncodingException if an included document + * used an encoding this parser does not support, and no + * fallback was available + * @throws XIncludeException if the document violates the + * syntax rules of XInclude + * @throws XMLException if resolving an include element would + * result in a malformed document + */ + public static Document resolve(Document in) + throws BadParseAttributeException, InclusionLoopException, + IOException, NoIncludeLocationException, ParsingException, + UnsupportedEncodingException, XIncludeException { + + Builder builder = new Builder(); + return resolve(in, builder); + + } + + /** + *

    + * Returns a copy of the document in which all + * xinclude:include elements have been + * replaced by their referenced content as loaded by the builder. + * The original Document object is not modified. + * Resolution is recursive; that is, include elements + * in the included documents are themselves resolved. + * The document returned contains no include elements. + *

    + * + * @param in the document in which include elements + * should be resolved + * @param builder the builder used to build the + * nodes included from other documents + * + * @return copy of the document in which + * all xinclude:include elements + * have been replaced by their referenced content + * + * @throws BadParseAttributeException if an include + * element has a parse attribute with any value + * other than text or parse + * @throws InclusionLoopException if the document + * contains an XInclude element that attempts to include + * a document in which this element is directly or indirectly + * included. + * @throws IOException if an included document could not be loaded, + * and no fallback was available + * @throws NoIncludeLocationException if an xinclude:include + * element does not have an href attribute. + * @throws ParsingException if an included XML document + * was malformed + * @throws UnsupportedEncodingException if an included document + * used an encoding this parser does not support, and no + * fallback was available + * @throws XIncludeException if the document violates the + * syntax rules of XInclude + * @throws XMLException if resolving an include element would + * result in a malformed document + */ + public static Document resolve(Document in, Builder builder) + throws BadParseAttributeException, InclusionLoopException, + IOException, NoIncludeLocationException, ParsingException, + UnsupportedEncodingException, XIncludeException { + + Document copy = new Document(in); + resolveInPlace(copy, builder); + return copy; + + } + + /** + *

    + * Modifies a document by replacing all + * xinclude:include elements + * by their referenced content. + * Resolution is recursive; that is, include elements + * in the included documents are themselves resolved. + * The resolved document contains no + * xinclude:include elements. + *

    + * + *

    + * If the inclusion fails for any reason—XInclude syntax + * error, missing resource with no fallback, etc.—the document + * may be left in a partially resolved state. + *

    + * + * @param in the document in which include elements + * should be resolved + * + * @throws BadParseAttributeException if an include + * element has a parse attribute + * with any value other than text + * or parse + * @throws InclusionLoopException if the document + * contains an XInclude element that attempts to include a + * document in which this element is directly or indirectly + * included + * @throws IOException if an included document could not be loaded, + * and no fallback was available + * @throws NoIncludeLocationException if an xinclude:include + * element does not have an href attribute + * @throws ParsingException if an included XML document + * was malformed + * @throws UnsupportedEncodingException if an included document + * used an encoding this parser does not support, and no + * fallback was available + * @throws XIncludeException if the document violates the + * syntax rules of XInclude + * @throws XMLException if resolving an include element would + * result in a malformed document + */ + public static void resolveInPlace(Document in) + throws BadParseAttributeException, InclusionLoopException, + IOException, NoIncludeLocationException, ParsingException, + UnsupportedEncodingException, XIncludeException { + resolveInPlace(in, new Builder()); + } + + /** + *

    + * Modifies a document by replacing all + * xinclude:include elements with their referenced + * content as loaded by the builder. Resolution is recursive; + * that is, include elements in the included documents + * are themselves resolved. The resolved document contains no + * xinclude:include elements. + *

    + * + *

    + * If the inclusion fails for any reason — XInclude syntax + * error, missing resource with no fallback, etc. — the + * document may be left in a partially resolved state. + *

    + * + * @param in the document in which include elements + * should be resolved + * @param builder the builder used to build the + * nodes included from other documents + * + * @throws BadParseAttributeException if an include + * element has a parse attribute + * with any value other than text + * or parse + * @throws InclusionLoopException if this element + * contains an XInclude element that attempts to include a + * document in which this element is directly or indirectly + * included + * @throws IOException if an included document could not be loaded, + * and no fallback was available + * @throws NoIncludeLocationException if an xinclude:include + * element does not have an href attribute. + * @throws ParsingException if an included XML document + * was malformed + * @throws UnsupportedEncodingException if an included document + * used an encoding this parser does not support, and no + * fallback was available + * @throws XIncludeException if the document violates the + * syntax rules of XInclude + * @throws XMLException if resolving an include element would + * result in a malformed document + */ + public static void resolveInPlace(Document in, Builder builder) + throws BadParseAttributeException, InclusionLoopException, + IOException, NoIncludeLocationException, ParsingException, + UnsupportedEncodingException, XIncludeException { + + ArrayList stack = new ArrayList(); + resolveInPlace(in, builder, stack); + + } + + + private static void resolveInPlace( + Document in, Builder builder, ArrayList baseURLs) + throws IOException, ParsingException, XIncludeException { + + String base = in.getBaseURI(); + // workaround a bug in Sun VMs + if (base != null && base.startsWith("file:///")) { + base = "file:/" + base.substring(8); + } + + baseURLs.add(base); + Element root = in.getRootElement(); + resolve(root, builder, baseURLs); + baseURLs.remove(baseURLs.size()-1); + + } + + + private static void resolve( + Element element, Builder builder, ArrayList baseURLs) + throws IOException, ParsingException, XIncludeException { + + resolve(element, builder, baseURLs, null); + + } + + + private static void resolve( + Element element, Builder builder, ArrayList baseURLs, Document originalDoc) + throws IOException, ParsingException, XIncludeException { + + if (isIncludeElement(element)) { + verifyIncludeElement(element); + + String parse = element.getAttributeValue("parse"); + if (parse == null) parse = "xml"; + String xpointer = element.getAttributeValue("xpointer"); + String encoding = element.getAttributeValue("encoding"); + String href = element.getAttributeValue("href"); + // empty string href is same as no href attribute + if ("".equals(href)) href = null; + + ParentNode parent = element.getParent(); + String base = element.getBaseURI(); + URL baseURL = null; + try { + baseURL = new URL(base); + } + catch (MalformedURLException ex) { + // don't use base + } + URL url = null; + try { + // xml:base attributes added to maintain the + // base URI should not have fragment IDs + + if (baseURL != null && href != null) { + url = absolutize(baseURL, href); + } + else if (href != null) { + try { + testURISyntax(href); + url = new URL(href); + } + catch (MalformedURIException ex) { + if (baseURL == null) { + throw new BadHrefAttributeException( + "Could not resolve relative URI " + href + + " because the xi:include element does" + + " not have a base URI.", href); + } + throw new BadHrefAttributeException("Illegal IRI in href attribute", href); + } + } + + String accept = element.getAttributeValue("accept"); + checkHeader(accept); + String acceptLanguage = element.getAttributeValue("accept-language"); + checkHeader(acceptLanguage); + + if (parse.equals("xml")) { + + String parentLanguage = ""; + if (parent instanceof Element) { + parentLanguage = getXMLLangValue((Element) parent); + } + + Nodes replacements; + if (url != null) { + replacements = downloadXMLDocument(url, + xpointer, builder, baseURLs, accept, acceptLanguage, parentLanguage); + // Add base URIs. Base URIs added by XInclusion require + // the element to maintain the same base URI as it had + // in the original document. Since its base URI in the + // original document does not contain a fragment ID, + // therefore its base URI after inclusion shouldn't, + // and this special case is unnecessary. Base URI fixup + // should not add the fragment ID. + for (int i = 0; i < replacements.size(); i++) { + Node child = replacements.get(i); + if (child instanceof Element) { + String noFragment = child.getBaseURI(); + if (noFragment.indexOf('#') >= 0) { + noFragment = noFragment.substring( + 0, noFragment.indexOf('#')); + } + Element baseless = (Element) child; + + // parent is null here; need to get real parent + String parentBase = parent.getBaseURI(); + if (parentBase != null && ! "".equals(parentBase)) { + parentBase = getDirectoryBase(parentBase); + } + + if (noFragment.startsWith(parentBase)) { + noFragment = noFragment.substring(parentBase.length()); + } + Attribute baseAttribute = new Attribute( + "xml:base", + "http://www.w3.org/XML/1998/namespace", + noFragment + ); + baseless.addAttribute(baseAttribute); + + } + } + } + else { + Document parentDoc = element.getDocument(); + if (parentDoc == null) { + parentDoc = originalDoc; + } + Nodes originals = XPointer.query(parentDoc, xpointer); + replacements = new Nodes(); + for (int i = 0; i < originals.size(); i++) { + Node original = originals.get(i); + // current implementation of XPointer never returns non-elements + if (contains((Element) original, element)) { + throw new InclusionLoopException( + "Element tried to include itself" + ); + } + Node copy = original.copy(); + replacements.append(copy); + } + replacements = resolveXPointerSelection( + replacements, builder, baseURLs, parentDoc); + + } + + // Will fail if we're replacing the root element with + // a node list containing zero or multiple elements, + // but that should fail. However, I may wish to + // adjust the type of exception thrown. This is only + // relevant if I add support for the xpointer scheme + // since otherwise you can only point at one element + // or document. + if (parent instanceof Element) { + int position = parent.indexOf(element); + for (int i = 0; i < replacements.size(); i++) { + Node child = replacements.get(i); + parent.insertChild(child, position+i); + } + element.detach(); + } + else { // root element needs special treatment + // I am assuming here that it is not possible + // for parent to be null. I think this is true + // in the current version, but it could change + // if I made it possible to directly resolve an + // element or a Nodes. + Document doc = (Document) parent; + int i = 0; + // prolog and root + while (true) { + Node child = replacements.get(i); + i++; + if (child instanceof Element) { + doc.setRootElement((Element) child); + break; + } + else { + doc.insertChild( + child, doc.indexOf(element) + ); + } + + } + // epilog + Element root = doc.getRootElement(); + int position = doc.indexOf(root); + for (int j=i; j < replacements.size(); j++) { + doc.insertChild( + replacements.get(j), position+1+j-i + ); + } + } + } + else if (parse.equals("text")) { + Nodes replacements + = downloadTextDocument(url, encoding, builder, accept, acceptLanguage); + for (int j = 0; j < replacements.size(); j++) { + Node replacement = replacements.get(j); + if (replacement instanceof Attribute) { + ((Element) parent).addAttribute((Attribute) replacement); + } + else { + parent.insertChild(replacement, parent.indexOf(element)); + } + } + parent.removeChild(element); + } + else { + throw new BadParseAttributeException( + "Bad value for parse attribute: " + parse, + element.getDocument().getBaseURI()); + } + + } + catch (IOException ex) { + processFallback(element, builder, baseURLs, parent, ex); + } + catch (XPointerSyntaxException ex) { + processFallback(element, builder, baseURLs, parent, ex); + } + catch (XPointerResourceException ex) { + // Process fallbacks; I'm not sure this is correct + // behavior. Possibly this should include nothing. See + // http://lists.w3.org/Archives/Public/www-xml-xinclude-comments/2003Aug/0000.html + // Daniel Veillard thinks this is correct. See + // http://lists.w3.org/Archives/Public/www-xml-xinclude-comments/2003Aug/0001.html + processFallback(element, builder, baseURLs, parent, ex); + } + + } + else if (isFallbackElement(element)) { + throw new MisplacedFallbackException( + "Fallback element outside include element", + element.getDocument().getBaseURI() + ); + } + else { + Elements children = element.getChildElements(); + for (int i = 0; i < children.size(); i++) { + resolve(children.get(i), builder, baseURLs); + } + } + + } + + + // ???? Move this into URIUtil when it goes public + private static String getDirectoryBase(String parentBase) { + if (parentBase.endsWith("/")) return parentBase; + int lastSlash = parentBase.lastIndexOf('/'); + return parentBase.substring(0, lastSlash+1); + } + + + + private static void verifyIncludeElement(Element element) + throws XIncludeException { + + testHref(element); + testForFragmentIdentifier(element); + verifyEncoding(element); + testForForbiddenChildElements(element); + } + + + private static void testHref(Element include) throws NoIncludeLocationException { + + String href = include.getAttributeValue("href"); + String xpointer = include.getAttributeValue("xpointer"); + if (href == null && xpointer == null) { + throw new NoIncludeLocationException( + "Missing href attribute", + include.getDocument().getBaseURI() + ); + } + } + + + private static void testForFragmentIdentifier(Element include) + throws BadHrefAttributeException { + + String href = include.getAttributeValue("href"); + if (href != null) { + if (href.indexOf('#') > -1) { + throw new BadHrefAttributeException( + "fragment identifier in URI " + href, include.getBaseURI() + ); + } + } + + } + + + private static void verifyEncoding(Element include) + throws BadEncodingAttributeException { + + String encoding = include.getAttributeValue("encoding"); + if (encoding == null) return; + // production 81 of XML spec + // EncName :=[A-Za-z] ([A-Za-z0-9._] | '-')* + char[] text = encoding.toCharArray(); + if (text.length == 0) { + throw new BadEncodingAttributeException( + "Empty encoding attribute", include.getBaseURI()); + } + char c = text[0]; + if (!((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'))) { + throw new BadEncodingAttributeException( + "Illegal value for encoding attribute: " + encoding, include.getBaseURI() + ); + } + for (int i = 1; i < text.length; i++) { + c = text[i]; + if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') + || (c >= '0' && c <= '9') || c == '-' || c == '_' || c == '.') { + continue; + } + throw new BadEncodingAttributeException( + "Illegal value for encoding attribute: " + encoding, include.getBaseURI() + ); + } + + } + + + // hack because URIUtil isn't public + private static URL absolutize(URL baseURL, String href) + throws MalformedURLException, BadHrefAttributeException { + + Element parent = new Element("c"); + String base = baseURL.toExternalForm(); + parent.setBaseURI(base); + Element child = new Element("c"); + parent.appendChild(child); + child.addAttribute(new Attribute( + "xml:base", "http://www.w3.org/XML/1998/namespace", href)); + URL result = new URL(child.getBaseURI()); + // avoid URL DNS lookup; see + // http://michaelscharf.blogspot.com/2006/11/javaneturlequals-and-hashcode-make.html + if (!"".equals(href) + && result.toExternalForm().equals(base)) { + if (! baseURL.toExternalForm().endsWith(href)) { + throw new BadHrefAttributeException(href + + " is not a syntactically correct IRI"); + } + } + return result; + + } + + + private static void testURISyntax(String href) { + Element e = new Element("e"); + e.setNamespaceURI(href); + } + + + private static String getXMLLangValue(Element element) { + + while (true) { + Attribute lang = element.getAttribute( + "lang", "http://www.w3.org/XML/1998/namespace"); + if (lang != null) return lang.getValue(); + ParentNode parent = element.getParent(); + if (parent == null) return ""; + else if (parent instanceof Document) return ""; + else element = (Element) parent; + } + + } + + + // This assumes current implementation of XPointer that + // always selects exactly one element or throws an exception. + private static Nodes resolveXPointerSelection(Nodes in, + Builder builder, ArrayList baseURLs, Document original) + throws IOException, ParsingException, XIncludeException { + + Element preinclude = (Element) in.get(0); + return resolveSilently(preinclude, builder, baseURLs, original); + + } + + + private static boolean contains(ParentNode ancestor, Node descendant) { + + for (Node parent = descendant; + parent != null; + parent=parent.getParent()) { + if (parent == ancestor) return true; + } + + return false; + + } + + + private static Nodes resolveSilently( + Element element, Builder builder, ArrayList baseURLs, Document originalDoc) + throws IOException, ParsingException, XIncludeException { + + // There is no possibility the element passed to this method + // is an include or a fallback element + if (isIncludeElement(element) || isFallbackElement(element) ) { + throw new RuntimeException( + "XOM BUG: include or fallback element passed to resolveSilently;" + + " please report with a test case"); + } + + Elements children = element.getChildElements(); + for (int i = 0; i < children.size(); i++) { + resolve(children.get(i), builder, baseURLs, originalDoc); + } + return new Nodes(element); + + } + + + private static void testForForbiddenChildElements(Element element) + throws XIncludeException { + + int fallbacks = 0; + Elements children = element.getChildElements(); + int size = children.size(); + for (int i = 0; i < size; i++) { + Element child = children.get(i); + if (XINCLUDE_NS.equals(child.getNamespaceURI())) { + if ("fallback".equals(child.getLocalName())) { + fallbacks++; + if (fallbacks > 1) { + throw new XIncludeException("Multiple fallback elements", + element.getDocument().getBaseURI()); + } + } + else { + throw new XIncludeException( + "Include element contains an include child", + element.getDocument().getBaseURI()); + } + } + } + + } + + + private static void processFallback(Element includeElement, + Builder builder, ArrayList baseURLs, ParentNode parent, Exception ex) + throws XIncludeException, IOException, ParsingException { + + Element fallback + = includeElement.getFirstChildElement("fallback", XINCLUDE_NS); + if (fallback == null) { + if (ex instanceof IOException) throw (IOException) ex; + XIncludeException ex2 = new XIncludeException( + ex.getMessage(), includeElement.getDocument().getBaseURI()); + ex2.initCause(ex); + throw ex2; + } + + while (fallback.getChildCount() > 0) { + Node child = fallback.getChild(0); + if (child instanceof Element) { + resolve((Element) child, builder, baseURLs); + } + child = fallback.getChild(0); + child.detach(); + parent.insertChild(child, parent.indexOf(includeElement)); + } + includeElement.detach(); + + } + + + // I could probably move the xpointer out of this method + private static Nodes downloadXMLDocument( + URL source, String xpointer, Builder builder, ArrayList baseURLs, + String accept, String acceptLanguage, String parentLanguage) + throws IOException, ParsingException, XIncludeException, + XPointerSyntaxException, XPointerResourceException { + + String base = source.toExternalForm(); + if (xpointer == null && baseURLs.indexOf(base) != -1) { + throw new InclusionLoopException( + "Tried to include the already included document " + base + + " from " + baseURLs.get(baseURLs.size()-1), (String) baseURLs.get(baseURLs.size()-1)); + } + + URLConnection uc = source.openConnection(); + setHeaders(uc, accept, acceptLanguage); + InputStream in = new BufferedInputStream(uc.getInputStream()); + Document doc; + try { + doc = builder.build(in, source.toExternalForm()); + } + finally { + in.close(); + } + + resolveInPlace(doc, builder, baseURLs); + Nodes included; + if (xpointer != null && xpointer.length() != 0) { + included = XPointer.query(doc, xpointer); + // fill in lang attributes here + for (int i = 0; i < included.size(); i++) { + Node node = included.get(i); + // Current implementation can only select elements + Element top = (Element) node; + Attribute lang = top.getAttribute("lang", + "http://www.w3.org/XML/1998/namespace"); + if (lang == null) { + String childLanguage = getXMLLangValue(top); + if (!parentLanguage.equals(childLanguage)) { + top.addAttribute(new Attribute("xml:lang", + "http://www.w3.org/XML/1998/namespace", + childLanguage)); + } + } + } + } + else { + included = new Nodes(); + for (int i = 0; i < doc.getChildCount(); i++) { + Node child = doc.getChild(i); + if (!(child instanceof DocType)) { + included.append(child); + } + } + } + // so we can detach the old root if necessary + doc.setRootElement(new Element("f")); + for (int i = 0; i < included.size(); i++) { + Node node = included.get(i); + // Take account of xml:base attribute, which we normally + // don't do when detaching + String noFragment = node.getBaseURI(); + if (noFragment.indexOf('#') >= 0) { + noFragment = noFragment.substring(0, noFragment.indexOf('#')); + } + node.detach(); + if (node instanceof Element) { + ((Element) node).setBaseURI(noFragment); + } + } + + return included; + + } + + + /** + *

    + * This utility method reads a document at a specified URL + * and returns the contents of that document as a Text. + * It's used to include files with parse="text". + *

    + * + * @param source URL of the document to download + * @param encoding encoding of the document; e.g. UTF-8, + * ISO-8859-1, etc. + * @param builder the Builder used to build the + * nodes included from other documents + * + * @return the document retrieved from the source URL + * + * @throws IOException if the remote document cannot + * be read due to an I/O error + */ + private static Nodes downloadTextDocument( + URL source, String encoding, Builder builder, + String accept, String language) + throws IOException, XIncludeException { + + if (encoding == null || encoding.length() == 0) { + encoding = "UTF-8"; + } + + URLConnection uc = source.openConnection(); + setHeaders(uc, accept, language); + + String encodingFromHeader = uc.getContentEncoding(); + String contentType = uc.getContentType(); + int contentLength = uc.getContentLength(); + if (contentLength < 0) contentLength = 1024; + InputStream in = new BufferedInputStream(uc.getInputStream()); + try { + if (encodingFromHeader != null) encoding = encodingFromHeader; + else { + if (contentType != null) { + contentType = contentType.toLowerCase(Locale.ENGLISH); + if (contentType.equals("text/xml") + || contentType.equals("application/xml") + || (contentType.startsWith("text/") + && contentType.endsWith("+xml") ) + || (contentType.startsWith("application/") + && contentType.endsWith("+xml"))) { + encoding + = EncodingHeuristics.readEncodingFromStream(in); + } + } + } + // workaround for pre-1.3 VMs that don't recognize UTF-16 + if (version.startsWith("1.2") || version.startsWith("1.1")) { + if (encoding.equalsIgnoreCase("UTF-16")) { + // is it big-endian or little-endian? + in.mark(2); + int first = in.read(); + if (first == 0xFF) encoding = "UnicodeLittle"; + else encoding="UnicodeBig"; + in.reset(); + } + else if (encoding.equalsIgnoreCase("UnicodeBigUnmarked")) { + encoding = "UnicodeBig"; + } + else if (encoding.equalsIgnoreCase("UnicodeLittleUnmarked")) { + encoding = "UnicodeLittle"; + } + } + Reader reader = new BufferedReader( + new InputStreamReader(in, encoding) + ); + StringBuffer sb = new StringBuffer(contentLength); + for (int c = reader.read(); c != -1; c = reader.read()) { + sb.append((char) c); + } + + NodeFactory factory = builder.getNodeFactory(); + if (factory != null) { + return factory.makeText(sb.toString()); + } + else return new Nodes(new Text(sb.toString())); + } + finally { + in.close(); + } + + } + + + private static void setHeaders(URLConnection uc, String accept, + String language) throws BadHTTPHeaderException { + + if (accept != null) { + checkHeader(accept); + uc.setRequestProperty("accept", accept); + } + if (language != null) { + checkHeader(language); + uc.setRequestProperty("accept-language", language); + } + + } + + + private static void checkHeader(String header) + throws BadHTTPHeaderException { + + if (header == null) return; + int length = header.length(); + for (int i = 0; i < length; i++) { + char c = header.charAt(i); + if (c < 0x20 || c > 0x7E) { + throw new BadHTTPHeaderException( + "Header contains illegal character 0x" + + Integer.toHexString(c).toUpperCase()); + } + } + + } + + + private static boolean isIncludeElement(Element element) { + + return element.getLocalName().equals("include") + && element.getNamespaceURI().equals(XINCLUDE_NS); + + } + + + private static boolean isFallbackElement(Element element) { + + return element.getLocalName().equals("fallback") + && element.getNamespaceURI().equals(XINCLUDE_NS); + + } + + +} \ No newline at end of file diff --git a/src/nu/xom/xinclude/XPointer.java b/src/nu/xom/xinclude/XPointer.java new file mode 100644 index 0000000..9aa581d --- /dev/null +++ b/src/nu/xom/xinclude/XPointer.java @@ -0,0 +1,332 @@ +/* Copyright 2002-2006, 2019 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + + + +package nu.xom.xinclude; + +import java.util.ArrayList; +import java.util.List; + +import nu.xom.Attribute; +import nu.xom.Document; +import nu.xom.Element; +import nu.xom.IllegalNameException; +import nu.xom.Node; +import nu.xom.Nodes; +import nu.xom.ParentNode; +import nu.xom.XMLException; + +/** + * + *

    + * Right now this is just for XInclude, and hence is non-public. + * Once it's more baked it will probably become public and move + * to a package of its own. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.3.1 + * + */ +class XPointer { + + + // prevent instantiation + private XPointer() {} + + + static Nodes query(Document doc, String xptr) + throws XPointerSyntaxException, XPointerResourceException { + + Nodes result = new Nodes(); + boolean found = false; + + try { // Is this a shorthand XPointer? + // Need to include a URI in case this is a colonized scheme name + new Element(xptr, "http://www.example.com"); + Element identified = findByID(doc.getRootElement(), xptr); + if (identified != null) { + result.append(identified); + return result; + } + } + catch (IllegalNameException ex) { + // not a bare name; try element() scheme + List elementSchemeData = findElementSchemeData(xptr); + if (elementSchemeData.isEmpty()) { + // This may be a legal XPointer, but it doesn't + // have an element() scheme so we can't handle it. + throw new XPointerSyntaxException( + "No supported XPointer schemes found" + ); + } + + for (String currentData : elementSchemeData) { + int[] keys = new int[0]; + ParentNode current = doc; + if (currentData.indexOf('/') == -1) { + // raw id in element like element(f2) + try { + new Element(currentData); + } + catch (IllegalNameException inex) { + // not a bare name; and doesn't contain a / + // This doesn't adhere to the element scheme. + // Therefore, according to the XPointer element + // scheme spec, " if scheme data in a pointer + // part with the element() scheme does not + // conform to the syntax defined in this + // section the pointer part does not identify + // a subresource." + continue; + } + Element identified = findByID( + doc.getRootElement(), currentData); + if (identified != null) { + if (!found) result.append(identified); + found = true; + } + } + else if (!currentData.startsWith("/")) { + String id = currentData.substring( + 0, currentData.indexOf('/')); + // Check to make sure this is a legal + // XML name/ID value + try { + new Element(id); + } + catch (XMLException inex) { + // doesn't adhere to the element scheme spec; + // Therefore this pointer part does not identify + // a subresource (See 2nd paragraph of section + // 3 of http://www.w3.org/TR/xptr-element/ ) + // This is not a resource error unless no + // XPointer part identifies a subresource. + continue; + } + current = findByID(doc.getRootElement(), id); + keys = split(currentData.substring( + currentData.indexOf('/'))); + + if (current == null) continue; + } + else { + keys = split(currentData); + } + + for (int j = 0; j < keys.length; j++) { + current = findNthChildElement(current, keys[j]); + if (current == null) break; + } + + if (current != doc && current != null) { + if (!found) result.append(current); + found = true; + } + + } + + } + + if (found) return result; + else { + // If we get here and still haven't been able to match an + // element, the XPointer has failed. + throw new XPointerResourceException( + "XPointer " + xptr + + " did not locate any nodes in the document " + + doc.getBaseURI() + ); + } + + } + + + private static Element findNthChildElement( + ParentNode parent, int position) { + // watch out for 1-based indexing of tumblers + int elementCount = 1; + for (int i = 0; i < parent.getChildCount(); i++) { + Node child = parent.getChild(i); + if (child instanceof Element) { + if (elementCount == position) return (Element) child; + elementCount++; + } + } + return null; + } + + + private static int[] split(String tumbler) + throws XPointerSyntaxException { + + int numberOfParts = 0; + for (int i = 0; i < tumbler.length(); i++) { + if (tumbler.charAt(i) == '/') numberOfParts++; + } + + int[] result = new int[numberOfParts]; + int index = 0; + StringBuffer part = new StringBuffer(3); + try { + for (int i = 1; i < tumbler.length(); i++) { + if (tumbler.charAt(i) == '/') { + result[index] = Integer.parseInt(part.toString()); + index++; + part = new StringBuffer(3); + } + else { + part.append(tumbler.charAt(i)); + } + } + result[result.length-1] = Integer.parseInt(part.toString()); + } + catch (NumberFormatException ex) { + XPointerSyntaxException ex2 + = new XPointerSyntaxException(tumbler + + " is not syntactically correct", ex); + throw ex2; + } + + return result; + } + + private static List findElementSchemeData(String xpointer) + throws XPointerSyntaxException { + + List result = new ArrayList(1); + + StringBuffer xptr = new StringBuffer(xpointer.trim()); + StringBuffer scheme = new StringBuffer(); + int i = 0; + while (i < xptr.length()) { + char c = xptr.charAt(i); + if (c == '(') break; + else scheme.append(c); + i++; + } + + // need to verify that scheme is a QName + try { + // ugly hack because Verifier isn't public + new Element(scheme.toString(), "http://www.example.com/"); + } + catch (IllegalNameException ex) { + throw new XPointerSyntaxException(ex.getMessage()); + } + + int open = 1; // parentheses count + i++; + StringBuffer schemeData = new StringBuffer(); + try { + while (open > 0) { + char c = xptr.charAt(i); + if (c == '^') { + c = xptr.charAt(i+1); + schemeData.append(c); + if (c != '^' && c != '(' && c != ')') { + throw new XPointerSyntaxException( + "Illegal XPointer escape sequence" + ); + } + i++; + } + else if (c == '(') { + schemeData.append(c); + open++; + } + else if (c == ')') { + open--; + if (open > 0) schemeData.append(c); + } + else { + schemeData.append(c); + } + i++; + } + } + catch (StringIndexOutOfBoundsException ex) { + throw new XPointerSyntaxException("Unbalanced parentheses"); + } + + if (scheme.toString().equals("element")) { + result.add(schemeData.toString()); + } + + if (i + 1 < xptr.length()) { + result.addAll(findElementSchemeData(xptr.substring(i))); + } + + return result; + } + + + static Element findByID(Element element, String id) { + + Node current = element; + boolean end = false; + int index = -1; + while (true) { + + if (current instanceof Element) { + Element currentElement = (Element) current; + for (int i = 0; i < currentElement.getAttributeCount(); i++) { + Attribute att = currentElement.getAttribute(i); + if (att.getType() == Attribute.Type.ID) { + if (att.getValue().trim().equals(id)) { + return currentElement; + } + } + } + } + + if (!end && current.getChildCount() > 0) { + current = current.getChild(0); + index = 0; + } + else { + if (end) { + if (current == element) break; + } + end = false; + ParentNode parent = current.getParent(); + if (parent.getChildCount() - 1 == index) { + current = parent; + if (current != element) { + parent = current.getParent(); + index = parent.indexOf(current); + } + end = true; + } + else { + index++; + current = parent.getChild(index); + } + } + } + + return null; + + } + + +} diff --git a/src/nu/xom/xinclude/XPointerException.java b/src/nu/xom/xinclude/XPointerException.java new file mode 100644 index 0000000..6a6ddae --- /dev/null +++ b/src/nu/xom/xinclude/XPointerException.java @@ -0,0 +1,124 @@ +/* Copyright 2003-2005 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.xinclude; + +/** + *

    + * Indicates an error as defined by the XPointer specification. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.1b3 + */ +class XPointerException extends Exception { + + + private static final long serialVersionUID = -533329510409448717L; + + private Throwable cause = null; + + /** + *

    + * Constructs an XPointerException with the + * specified detail message. + *

    + * + * @param message a string indicating the specific problem + */ + XPointerException(String message) { + super(message); + } + + + /** + *

    + * Constructs an XPointerException with the + * specified detail message and root cause. + *

    + * + * @param message a string indicating the specific problem + * @param cause the initial exception which caused this + * XPointerException + */ + XPointerException(String message, Throwable cause) { + super(message); + initCause(cause); + } + + + /** + *

    + * When an IOException, + * MalformedURLException, or other generic + * exception is thrown while processing an XML document + * for XPointer, it is customarily replaced + * by some form of XPointerSyntaxException. + * This method allows you to retrieve the original exception. + * It returns null if no such exception caused this + * XPointerSyntaxException. + *

    + * + * @return the underlying exception which + * caused this XPointerSyntaxException to be thrown + */ + public Throwable getCause() { + return this.cause; + } + + + // null is insufficient for detecting an uninitialized cause. + // The cause may be set to null which may not then be reset. + private boolean causeSet = false; + + + /** + *

    + * When an IOException, + * MalformedURLException, or other generic exception + * is thrown while processing an XML document + * for XPointers, it is customarily replaced + * by some form of XPointerException. + * This method allows you to store the original exception. + *

    + * + * @param cause the root cause of this exception + * + * @return this XPointerException + * + * @throws IllegalArgumentException if the cause is this exception + * (An exception cannot be its own cause.) + * @throws IllegalStateException if this method is called twice + */ + public Throwable initCause(Throwable cause) { + if (causeSet) { + throw new IllegalStateException("Can't overwrite cause"); + } + else if (cause == this) { + throw new IllegalArgumentException("Self-causation not permitted"); + } + else this.cause = cause; + causeSet = true; + return this; + } + + +} diff --git a/src/nu/xom/xinclude/XPointerResourceException.java b/src/nu/xom/xinclude/XPointerResourceException.java new file mode 100644 index 0000000..344a572 --- /dev/null +++ b/src/nu/xom/xinclude/XPointerResourceException.java @@ -0,0 +1,54 @@ +/* Copyright 2003-2005 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.xinclude; + +/** + *

    + * Thrown when no XPointer part identifies a subresource. This + * corresponds to section 3.3 + * of the XPointer Framework which states, + * "If no pointer part identifies subresources, it is an error." + *

    + * + * @author Elliotte Rusty Harold + * @version 1.1b3 + */ +class XPointerResourceException extends XPointerException { + + + private static final long serialVersionUID = -3854144696916677840L; + + /** + *

    + * Constructs an XPointerResourceException with the + * specified detail message. + *

    + * + * @param message a string indicating the specific problem + */ + XPointerResourceException(String message) { + super(message); + } + + +} diff --git a/src/nu/xom/xinclude/XPointerSyntaxException.java b/src/nu/xom/xinclude/XPointerSyntaxException.java new file mode 100644 index 0000000..c6e7138 --- /dev/null +++ b/src/nu/xom/xinclude/XPointerSyntaxException.java @@ -0,0 +1,67 @@ +/* Copyright 2002-2005 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.xinclude; + +/** + *

    + * The generic superclass for all checked exceptions thrown as a result + * of a violation of the XPointer grammar. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.1b3 + */ +class XPointerSyntaxException extends XPointerException { + + + private static final long serialVersionUID = -6764746558725637517L; + + + /** + *

    + * Constructs an XPointerSyntaxException with the + * specified detail message. + *

    + * + * @param message a string indicating the specific problem + */ + public XPointerSyntaxException(String message) { + super(message); + } + + + /** + *

    + * Constructs an XPointerSyntaxException with the + * specified detail message and initial cause. + *

    + * + * @param message a string indicating the specific problem + * @param cause the initial exception which caused this + * XPointerSyntaxException + */ + public XPointerSyntaxException(String message, Throwable cause) { + super(message, cause); + } + + +} diff --git a/src/nu/xom/xinclude/package.html b/src/nu/xom/xinclude/package.html new file mode 100644 index 0000000..9643751 --- /dev/null +++ b/src/nu/xom/xinclude/package.html @@ -0,0 +1,44 @@ + + + + + + + +

    +nu.xom.xinclude supports XInclude processing of XOM documents and elements. +It is conformant to the +final recommendation of XML Inclusions (XInclude) Version 1.0 +including fallbacks, XPointer bare names, the accept +and accept-language attributes, and the XPointer +element() scheme. +

    + +@since 1.0 + + + diff --git a/src/nu/xom/xslt/XOMInputSource.java b/src/nu/xom/xslt/XOMInputSource.java new file mode 100644 index 0000000..1a171b8 --- /dev/null +++ b/src/nu/xom/xslt/XOMInputSource.java @@ -0,0 +1,45 @@ +/* Copyright 2004 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.xslt; + +import nu.xom.Nodes; + +import org.xml.sax.InputSource; + + +class XOMInputSource extends InputSource { + + + private final Nodes nodes; + + + XOMInputSource(Nodes nodes) { + this.nodes = nodes; + } + + + Nodes getNodes() { + return this.nodes; + } + + +} diff --git a/src/nu/xom/xslt/XOMReader.java b/src/nu/xom/xslt/XOMReader.java new file mode 100644 index 0000000..284d38a --- /dev/null +++ b/src/nu/xom/xslt/XOMReader.java @@ -0,0 +1,156 @@ +/* Copyright 2004 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.xslt; + +import java.io.IOException; + +import nu.xom.converters.SAXConverter; + +import org.xml.sax.ContentHandler; +import org.xml.sax.DTDHandler; +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.SAXNotSupportedException; +import org.xml.sax.XMLReader; +import org.xml.sax.ext.LexicalHandler; + +/** + * + *

    + * This is just for XSLTransform, and implements only the functionality + * that class requires. Other classes should not use this. + * It is far from a conformant implementation of XMLReader. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.0 + * + */ +class XOMReader implements XMLReader { + + private SAXConverter converter; + + public boolean getFeature(String uri) + throws SAXNotRecognizedException, SAXNotSupportedException { + + if ("http://xml.org/sax/features/namespace-prefixes".equals(uri) + || "http://xml.org/sax/features/namespaces".equals(uri)) { + return true; + } + throw new SAXNotRecognizedException("XOMReader doesn't support features"); + + } + + + public void setFeature(String uri, boolean value) + throws SAXNotRecognizedException, SAXNotSupportedException { + + } + + + public Object getProperty(String uri) throws SAXNotRecognizedException, + SAXNotSupportedException { + + if ("http://xml.org/sax/properties/lexical-handler".equals(uri)) { + return converter.getLexicalHandler(); + } + else { + throw new SAXNotRecognizedException("XOMReader doesn't support features"); + } + + } + + + public void setProperty(String uri, Object value) + throws SAXNotRecognizedException, SAXNotSupportedException { + + if ("http://xml.org/sax/properties/lexical-handler".equals(uri)) { + LexicalHandler handler = (LexicalHandler) value; + converter.setLexicalHandler(handler); + } + else { + throw new SAXNotRecognizedException( + "XOMReader doesn't support " + uri); + } + + } + + + public void setEntityResolver(EntityResolver resolver) { + throw new UnsupportedOperationException(); + } + + + public EntityResolver getEntityResolver() { + return null; + } + + + public void setDTDHandler(DTDHandler handler) { + // throw new UnsupportedOperationException(); + } + + + public DTDHandler getDTDHandler() { + return null; + } + + + public void setContentHandler(ContentHandler handler) { + converter = new SAXConverter(handler); + // warn the SAXConverter that we're using this for XSLT + converter.setContentHandler(new XSLTHandler(null)); + } + + + public ContentHandler getContentHandler() { + return null; + } + + + public void setErrorHandler(ErrorHandler handler) { + } + + + public ErrorHandler getErrorHandler() { + return null; + } + + + public void parse(InputSource source) + throws IOException, SAXException { + + XOMInputSource xis = (XOMInputSource) source; + converter.convert(xis.getNodes()); + + } + + + public void parse(String url) throws IOException, SAXException { + throw new UnsupportedOperationException(); + } + + +} diff --git a/src/nu/xom/xslt/XOMResult.java b/src/nu/xom/xslt/XOMResult.java new file mode 100644 index 0000000..ecd25b1 --- /dev/null +++ b/src/nu/xom/xslt/XOMResult.java @@ -0,0 +1,54 @@ +/* Copyright 2002-2004 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.xslt; + +import javax.xml.transform.sax.SAXResult; + +import nu.xom.NodeFactory; +import nu.xom.Nodes; +import org.xml.sax.ext.LexicalHandler; + +/** + *

    + * Interface to TrAX. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.0 + * + */ +class XOMResult extends SAXResult { + + + XOMResult(NodeFactory factory) { + super(new XSLTHandler(factory)); + this.setLexicalHandler((LexicalHandler) this.getHandler()); + } + + + public Nodes getResult() { + XSLTHandler handler = (XSLTHandler) this.getHandler(); + return handler.getResult(); + } + + +} diff --git a/src/nu/xom/xslt/XOMSource.java b/src/nu/xom/xslt/XOMSource.java new file mode 100644 index 0000000..78127fb --- /dev/null +++ b/src/nu/xom/xslt/XOMSource.java @@ -0,0 +1,86 @@ +/* Copyright 2002-2004 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.xslt; + +import javax.xml.transform.sax.SAXSource; + +import nu.xom.Document; +import nu.xom.Nodes; + +import org.xml.sax.InputSource; +import org.xml.sax.XMLReader; + +/** + * @author Elliotte Rusty Harold + * @version 1.1b4 + * + */ +class XOMSource extends SAXSource { + + + private final Nodes source; + + + /** + *

    + * Creates a new XOMSource object from a + * Document. The Document object + * is read but not changed by any method in this class. + *

    + * + * @param source + */ + XOMSource(Document source) { + this.source = new Nodes(source); + } + + + /** + *

    + * Creates a new XOMSource object + * from a Nodes. + *

    + * + * @param source + */ + public XOMSource(Nodes source) { + this.source = source; + } + + + public InputSource getInputSource() { + return new XOMInputSource(source); + } + + + public XMLReader getXMLReader() { + return new XOMReader(); + } + + + public String getSystemId() { + if (this.source.size() == 0) return null; + else return this.source.get(0).getBaseURI(); + } + + +} diff --git a/src/nu/xom/xslt/XSLException.java b/src/nu/xom/xslt/XSLException.java new file mode 100644 index 0000000..d58f613 --- /dev/null +++ b/src/nu/xom/xslt/XSLException.java @@ -0,0 +1,122 @@ +/* Copyright 2002-2005 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.xslt; + +/** + *

    + * Thrown when an XSL stylesheet fails to compile + * or an XSL transform fails. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.1b3 + */ +public class XSLException extends Exception { + + private static final long serialVersionUID = -8605437693812807627L; + + private Throwable cause; + + + /** + *

    + * Creates a new XSLException with the specified + * detail message and an underlying root cause. + *

    + * + * @param message information about the cause of the exception + * @param cause the nested exception that caused this exception + */ + public XSLException(String message, Throwable cause) { + super(message); + this.initCause(cause); + } + + + /** + *

    + * Creates a new XSLException + * with the specified detail message. + *

    + * + * @param message information about the cause of the exception + */ + public XSLException(String message) { + super(message); + } + + + // null is insufficient for detecting an uninitialized cause. + // The cause may be set to null which may not then be reset. + private boolean causeSet = false; + + /** + *

    + * Sets the root cause of this exception. This may + * only be called once. Subsequent calls throw an + * IllegalStateException. + *

    + * + *

    + * This method is unnecessary in Java 1.4 where it could easily be + * inherited from the superclass. However, including it here + * allows this method to be used in Java 1.3 and earlier. + *

    + * + * @param cause the root cause of this exception + * + * @return this XSLException + * + * @throws IllegalArgumentException if the cause is this exception + * (An exception cannot be its own cause.) + * @throws IllegalStateException if this method is called twice + */ + public final Throwable initCause(Throwable cause) { + + if (causeSet) { + throw new IllegalStateException("Can't overwrite cause"); + } + else if (cause == this) { + throw new IllegalArgumentException( + "Self-causation not permitted"); + } + else this.cause = cause; + causeSet = true; + return this; + + } + + + /** + *

    + * Returns the underlying exception that caused this exception. + *

    + * + * @return the initial exception that caused this exception + * to be thrown + */ + public Throwable getCause() { + return this.cause; + } + + +} diff --git a/src/nu/xom/xslt/XSLTHandler.java b/src/nu/xom/xslt/XSLTHandler.java new file mode 100644 index 0000000..15ae3e1 --- /dev/null +++ b/src/nu/xom/xslt/XSLTHandler.java @@ -0,0 +1,373 @@ +/* Copyright 2002-2005, 2019 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.xslt; + +import java.util.ArrayList; + +import nu.xom.Attribute; +import nu.xom.Element; +import nu.xom.NamespaceConflictException; +import nu.xom.Node; +import nu.xom.NodeFactory; +import nu.xom.Nodes; +import nu.xom.ParentNode; +import nu.xom.XMLException; + +import org.xml.sax.Attributes; +import org.xml.sax.ContentHandler; +import org.xml.sax.Locator; +import org.xml.sax.SAXException; +import org.xml.sax.ext.LexicalHandler; +import org.xml.sax.helpers.AttributesImpl; + +/** + *

    + * As currently designed this class is non-public and never + * reused. A new XSLTHandler is used for each call to transform(). + * Therefore we do not actually need to reset. This is important + * because some XSLT processors call startDocument() and + * endDocument() and some don't, especially when the output + * of a transform is a document fragment. + *

    + * + * @author Elliotte Rusty Harold + * @version 1.3.1 + * + */ +class XSLTHandler + implements ContentHandler, LexicalHandler { + + private final Nodes result; + private final ArrayList parents; + private final NodeFactory factory; + private StringBuffer buffer; + + + XSLTHandler(NodeFactory factory) { + this.factory = factory; + result = new Nodes(); + parents = new ArrayList(); + buffer = new StringBuffer(); + } + + + Nodes getResult() { + flushText(); // to handle case where there's no endDocument + return result; + } + + + public void setDocumentLocator(Locator locator) {} + public void startDocument() {} + public void endDocument() {} + + private Element current; + + public void startElement(String namespaceURI, String localName, + String qualifiedName, Attributes attributes) { + + flushText(); + + // mix namespaceDeclarations into attributes + int length = attributes.getLength(); + for (int i = 0; i < length; i++) { + namespaceDeclarations.addAttribute( + attributes.getURI(i), + attributes.getLocalName(i), + attributes.getQName(i), + attributes.getType(i), + attributes.getValue(i) + ); + } + attributes = namespaceDeclarations; + + Element element + = factory.startMakingElement(qualifiedName, namespaceURI); + + if (parents.isEmpty()) { + // won't append until finishMakingElement() + current = element; + } + else { + ParentNode parent = (ParentNode) parents.get(parents.size()-1); + parent.appendChild(element); + } + parents.add(element); + + // Attach the attributes + length = attributes.getLength(); + for (int i = 0; i < length; i++) { + String attributeName = attributes.getQName(i); + // handle namespaces later + if (attributeName.equals("xmlns") + || attributeName.startsWith("xmlns:")) { + continue; + } + String namespace = attributes.getURI(i); + String value = attributes.getValue(i); + + Nodes nodes = factory.makeAttribute( + attributeName, + namespace, + value, + Attribute.Type.UNDECLARED + ); + int size = nodes.size(); + for (int j=0; j < size; j++) { + Node node = nodes.get(j); + if (node instanceof Attribute) { + Attribute attribute = (Attribute) node; + while (true) { + try { + element.addAttribute(attribute); + break; + } + catch (NamespaceConflictException ex) { + // According to section 7.1.3 of XSLT spec we + // need to remap the prefix here; ideally the + // XSLT processor should do this but many don't + // for instance, see + // http://nagoya.apache.org/bugzilla/show_bug.cgi?id=5389 + attribute.setNamespace( + "p"+attribute.getNamespacePrefix(), + attribute.getNamespaceURI() + ); + } + } + } + else { + element.appendChild(node); + } + } + } + + // Attach any additional namespaces + for (int i = 0; i < length; i++) { + String qName = attributes.getQName(i); + if (qName.startsWith("xmlns:")) { + String namespaceName = attributes.getValue(i); + String namespacePrefix = qName.substring(6); + String currentValue + = element.getNamespaceURI(namespacePrefix); + if (!namespaceName.equals(currentValue)) { + try { + element.addNamespaceDeclaration( + namespacePrefix, namespaceName); + } + catch (NamespaceConflictException ex) { + // skip it; see attribset40 test case; + // This should only happen if an attribute's + // namespace conflicts with the element's + // namespace; in which case we already remapped + // the prefix when adding the attribute + } + } + } + else if (qName.equals("xmlns")) { + String namespaceName = attributes.getValue(i); + if (namespaceName == null) { // Work around a Xalan bug + namespaceName = ""; + } + String namespacePrefix = ""; + String currentValue + = element.getNamespaceURI(namespacePrefix); + if (!namespaceName.equals(currentValue)) { + try { + element.addNamespaceDeclaration(namespacePrefix, + namespaceName); + } + catch (NamespaceConflictException ex) { + // work around Bug 27937 in Xalan + // http://nagoya.apache.org/bugzilla/show_bug.cgi?id=27937 + // Xalan sometimes use the XML namespace + // http://www.w3.org/XML/1998/namespace where it + // should use the empty string + if ("http://www.w3.org/XML/1998/namespace".equals(namespaceName) + && "".equals(namespacePrefix)) { + element.addNamespaceDeclaration("", ""); + } + } + } + } + } + + // reset namespaceDeclarations + namespaceDeclarations = new AttributesImpl(); + + } + + + public void endElement(String namespaceURI, String localName, + String qualifiedName) { + + flushText(); + Element element = (Element) parents.remove(parents.size()-1); + if (parents.isEmpty()) { + Nodes nodes = factory.finishMakingElement(current); + for (int i = 0; i < nodes.size(); i++) { + result.append(nodes.get(i)); + } + current = null; + } + else { + Nodes nodes = factory.finishMakingElement(element); + ParentNode parent = element.getParent(); + element.detach(); + for (int i = 0; i < nodes.size(); i++) { + Node node = nodes.get(i); + if (node instanceof Attribute) { + ((Element) parent).addAttribute((Attribute) node); + } + else { + parent.appendChild(node); + } + } + } + + } + + + public void characters(char[] text, int start, int length) { + buffer.append(text, start, length); + } + + + // accumulate all text that's in the buffer into a text node + private void flushText() { + if (buffer.length() > 0) { + Nodes text = factory.makeText(buffer.toString()); + addToResultTree(text); + buffer = new StringBuffer(); + } + } + + + public void ignorableWhitespace(char[] text, int start, int length) { + characters(text, start, length); + } + + + public void processingInstruction(String target, String data) + throws SAXException { + + // See http://saxon.sourceforge.net/saxon6.5.2/extensibility.html#Writing-output-filters + // to understand why we need to work around Saxon here + if ("saxon:warning".equals(target)) { + throw new SAXException("continue"); + } + else if ("javax.xml.transform.disable-output-escaping".equals(target) + || "javax.xml.transform.enable-output-escaping".equals(target)) { + // Xalan workaround + return; + } + + flushText(); + // Xalan fails to split the ?> before passing such data to + // this method, so we have to do it + int position = data.indexOf("?>"); + while (position != -1) { + data = data.substring(0, position) + "? >" + data.substring(position+2); + position = data.indexOf("?>"); + } + Nodes nodes = factory.makeProcessingInstruction(target, data); + addToResultTree(nodes); + + } + + + private void addToResultTree(Nodes nodes) { + + if (parents.isEmpty()) { + for (int i = 0; i < nodes.size(); i++) { + result.append(nodes.get(i)); + } + } + else { + ParentNode parent = (ParentNode) parents.get(parents.size()-1); + for (int i = 0; i < nodes.size(); i++) { + Node node = nodes.get(i); + if (node instanceof Attribute) { + ((Element) parent).addAttribute((Attribute) node); + } + else { + parent.appendChild(node); + } + } + } + + } + + + public void endPrefixMapping(String prefix) {} + + + private AttributesImpl namespaceDeclarations = new AttributesImpl(); + + public void startPrefixMapping(String prefix, String uri) { + + if ("".equals(prefix)) { + namespaceDeclarations.addAttribute("", "xmlns", "xmlns", "CDATA", uri); + } + else { + namespaceDeclarations.addAttribute("", "xmlns:" + prefix, "xmlns:" + prefix, "CDATA", uri); + } + + } + + + public void skippedEntity(String name) { + flushText(); + throw new XMLException("Could not resolve entity " + name); + } + + + // LexicalHandler events + public void startCDATA() {} + public void endCDATA() {} + // ???? For Bill Pugh, would this method be called if xsl:output + // specifies a Doctype? If it is, then we coudl add a DOCTYPE to the result tree. + public void startDTD(String name, String publicID, String systemID) {} + public void endDTD() {} + public void startEntity(String name) {} + public void endEntity(String name) {} + + + public void comment(char[] text, int start, int length) { + + flushText(); + + String data = new String(text, start, length); + // Xalan should add spaces as necessary to split up double hyphens + // in comments but it doesn't + int position = data.indexOf("--"); + while (position != -1) { + data = data.substring(0, position) + "- -" + data.substring(position+2); + position = data.indexOf("--"); + } + if (data.endsWith("-")) data += ' '; + + addToResultTree(factory.makeComment(data)); + + } + + +} \ No newline at end of file diff --git a/src/nu/xom/xslt/XSLTransform.java b/src/nu/xom/xslt/XSLTransform.java new file mode 100644 index 0000000..b3bec37 --- /dev/null +++ b/src/nu/xom/xslt/XSLTransform.java @@ -0,0 +1,450 @@ +/* Copyright 2002-2005 Elliotte Rusty Harold + + This library is free software; you can redistribute it and/or modify + it under the terms of version 2.1 of the GNU Lesser General Public + License as published by the Free Software Foundation. + + This library 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + You can contact Elliotte Rusty Harold by sending e-mail to + elharo@ibiblio.org. Please include the word "XOM" in the + subject line. The XOM home page is located at http://www.xom.nu/ +*/ + +package nu.xom.xslt; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import javax.xml.transform.ErrorListener; +import javax.xml.transform.OutputKeys; +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.TransformerFactoryConfigurationError; +import javax.xml.transform.TransformerFactory; + +import org.xml.sax.SAXParseException; + +import nu.xom.Document; +import nu.xom.Element; +import nu.xom.NodeFactory; +import nu.xom.Nodes; +import nu.xom.XMLException; + +/** + *

    + * Serves as an interface to a TrAX aware XSLT processor such as Xalan + * or Saxon. The following example shows how to apply an XSL + * Transformation to a XOM document and get the transformation result + * in the form of a XOM Nodes object:

    + *
    public static Nodes transform(Document in) 
    + *   throws XSLException, ParsingException, IOException {
    + *     Builder builder = new Builder();
    + *     Document stylesheet = builder.build("mystylesheet.xsl");
    + *     XSLTransform transform = new XSLTransform(stylesheet);
    + *     return transform.transform(in);
    + * } 
    + * + *

    + * XOM relies on TrAX to perform the transformation. + * The javax.xml.transform.TransformerFactory Java + * system property determines which XSLT engine TrAX uses. Its + * value should be the fully qualified name of the implementation + * of the abstract javax.xml.transform.TransformerFactory + * class. Values of this property for popular XSLT processors include: + *

    + *
      + *
    • Saxon 6.x: + * com.icl.saxon.TransformerFactoryImpl + *
    • + *
    • Saxon 7.x through 9.x: + * net.sf.saxon.TransformerFactoryImpl + *
    • + *
    • Xalan interpretive: + * org.apache.xalan.processor.TransformerFactoryImpl + *
    • + *
    • Xalan XSLTC: + * org.apache.xalan.xsltc.trax.TransformerFactoryImpl + *
    • + *
    • jd.xslt: + * jd.xml.xslt.trax.TransformerFactoryImpl + *
    • + *
    • Oracle: + * oracle.xml.jaxp.JXSAXTransformerFactory + *
    • + *
    • Java bundled Xalan: + * com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl + *
    • + *
    + *

    + * This property can be set in all the usual ways a Java system + * property can be set. TrAX picks from them in this order:

    + *
      + *
    1. The most recent value specified by invoking + * System.setProperty("javax.xml.transform.TransformerFactory", + * "classname")
    2. + *
    3. The value specified at the command line using the + * -Djavax.xml.transform.TransformerFactory=classname + * option to the java interpreter
    4. + *
    5. The class named in the lib/jaxp.properties + * properties file in the JRE directory, in a line like this one: + *
      javax.xml.transform.TransformerFactory=classname
      + *
    6. + *
    7. The class named in the + * META-INF/services/javax.xml.transform.TransformerFactory + * file in the JAR archives available to the runtime
    8. + *
    9. Finally, if all of the above options fail, + * a default implementation is chosen. In Sun's JDK this is + * usually com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl. + *
    10. + *
    + * + * @author Elliotte Rusty Harold + * @version 1.2.11 + */ +public final class XSLTransform { + + + /** + *

    + * The compiled form of the XSLT stylesheet that this object + * represents. This can be safely used across multiple threads + * unlike a Transformer object. + *

    + */ + private Templates templates; + private NodeFactory factory; + private Map parameters = new HashMap(); + private static ErrorListener errorsAreFatal = new FatalListener(); + + + private static class FatalListener implements ErrorListener { + + public void warning(TransformerException exception) {} + + public void error(TransformerException exception) + throws TransformerException { + throw exception; + } + + public void fatalError(TransformerException exception) + throws TransformerException { + throw exception; + } + + } + + // I could use one TransformerFactory field instead of local + // variables but then I'd have to synchronize it; and it would + // be hard to change the class used to transform + + + /** + *

    + * Creates a new XSLTransform by + * reading the stylesheet from the specified source. + *

    + * + * @param source TrAX Source object from + * which the input document is read + * + * @throws XSLException when an IOException, + * format error, or something else prevents the stylesheet + * from being compiled + */ + private XSLTransform(Source source) throws XSLException { + + try { + TransformerFactory factory + = TransformerFactory.newInstance(); + factory.setErrorListener(errorsAreFatal); + this.templates = factory.newTemplates(source); + } + catch (TransformerFactoryConfigurationError error) { + throw new XSLException( + "Could not locate a TrAX TransformerFactory", error + ); + } + catch (TransformerConfigurationException ex) { + throw new XSLException( + "Syntax error in stylesheet", ex + ); + } + + } + + + /** + *

    + * Creates a new XSLTransform by + * reading the stylesheet from the supplied document. + *

    + * + * @param stylesheet document containing the stylesheet + * + * @throws XSLException when the supplied document + * is not syntactically correct XSLT + */ + public XSLTransform(Document stylesheet) throws XSLException { + this(stylesheet, new NodeFactory()); + } + + + /** + *

    + * Creates a new XSLTransform by + * reading the stylesheet from the supplied document. + * The supplied factory will be used to create all nodes + * in the result tree, so that a transform can create + * instances of subclasses of the standard XOM classes. + * Because an XSL transformation generates a list of nodes rather + * than a document, the factory's startMakingDocument + * and finishMakingDocument methods are not called. + *

    + * + * @param stylesheet document containing the stylesheet + * @param factory the factory used to build nodes in the result tree + * + * @throws XSLException when the supplied document + * is not syntactically correct XSLT + */ + public XSLTransform(Document stylesheet, NodeFactory factory) + throws XSLException { + + this(new XOMSource(stylesheet)); + if (factory == null) this.factory = new NodeFactory(); + else this.factory = factory; + + } + + + /** + *

    + * Creates a new Nodes from the + * input Document by applying this object's + * stylesheet. The original Document is not + * changed. + *

    + * + * @param in document to transform + * + * @return a Nodes containing the result of the + * transformation + * + * @throws XSLException if the transformation fails, normally + * due to an XSLT error + */ + public Nodes transform(Document in) throws XSLException { + return transform(new XOMSource(in)); + } + + + /** + *

    + * Supply a parameter to transformations performed by this object. + * The value is normally a Boolean, + * Double, or String. However, it may be + * another type if the underlying XSLT processor supports that + * type. Passing null for the value removes the parameter. + *

    + * + * @param name the name of the parameter + * @param value the value of the parameter + */ + public void setParameter(String name, Object value) { + this.setParameter(name, null, value); + } + + + /** + *

    + * Supply a parameter to transformations performed by this object. + * The value is normally a Boolean, + * Double, or String. However, it may be + * another type if the underlying XSLT processor supports that + * type. Passing null for the value removes the parameter. + *

    + * + * @param name the name of the parameter + * @param namespace the namespace URI of the parameter + * @param value the value of the parameter + */ + public void setParameter(String name, String namespace, Object value) { + + + if (namespace == null || "".equals(namespace)) { + _setParameter(name, value); + } + else { + _setParameter("{" + namespace + "}" + name, value); + } + + } + + + private void _setParameter(String name, Object value) { + + if (value == null) { + parameters.remove(name); + } + else { + parameters.put(name, value); + } + + } + + + /** + *

    + * Creates a new Nodes object from the + * input Nodes object by applying this object's + * stylesheet. The original Nodes object is not + * changed. + *

    + * + * @param in document to transform + * + * @return a Nodes containing the result of + * the transformation + * + * @throws XSLException if the transformation fails, normally + * due to an XSLT error + */ + public Nodes transform(Nodes in) throws XSLException { + + if (in.size() == 0) return new Nodes(); + XOMSource source = new XOMSource(in); + return transform(source); + + } + + + /** + *

    + * Creates a new Nodes object from the + * input Source object by applying this object's + * stylesheet. + *

    + * + * @param in TrAX Source to transform + * + * @return a Nodes object containing the result of + * the transformation + * + * @throws XSLException if the transformation fails, normally + * due to an XSLT error + */ + private Nodes transform(Source in) throws XSLException { + + try { + XOMResult out = new XOMResult(factory); + Transformer transformer = templates.newTransformer(); + // work around Xalan bug + transformer.setOutputProperty(OutputKeys.METHOD, "xml"); + // work around a Xalan 2.7.0 bug + transformer.setErrorListener(errorsAreFatal); + Iterator iterator = parameters.keySet().iterator(); + while (iterator.hasNext()) { + String key = iterator.next(); + Object value = parameters.get(key); + transformer.setParameter(key, value); + } + transformer.transform(in, out); + return out.getResult(); + } + catch (Exception ex) { + // workaround bugs that wrap RuntimeExceptions + Throwable cause = ex; + if (cause instanceof TransformerException) { + TransformerException tex = (TransformerException) cause; + Throwable nested = tex.getException(); + if (nested != null) { + cause = nested; + if (cause instanceof SAXParseException) { + nested = ((SAXParseException) cause).getException(); + if (nested != null) cause = nested; + } + } + } + throw new XSLException(ex.getMessage(), cause); + } + + } + + + /** + *

    + * Builds a Document object from a + * Nodes object. This is useful when the stylesheet + * is known to produce a well-formed document with a single root + * element. That is, the Node returned contains + * only comments, processing instructions, and exactly one + * element. If the stylesheet produces anything else, + * this method throws XMLException. + *

    + * + * @param nodes the nodes to be placed in the new document + * + * @return a document containing the nodes + * + * @throws XMLException if nodes does not contain + * exactly one element or if it contains any text nodes or + * attributes + */ + public static Document toDocument(Nodes nodes) { + + Element root = null; + int rootPosition = 0; + for (int i = 0; i < nodes.size(); i++) { + if (nodes.get(i) instanceof Element) { + rootPosition = i; + root = (Element) nodes.get(i); + break; + } + } + + if (root == null) { + throw new XMLException("No root element"); + } + + Document result = new Document(root); + + for (int i = 0; i < rootPosition; i++) { + result.insertChild(nodes.get(i), i); + } + + for (int i = rootPosition+1; i < nodes.size(); i++) { + result.appendChild(nodes.get(i)); + } + + return result; + + } + + + /** + *

    + * Returns a string form of this XSLTransform, + * suitable for debugging. + *

    + * + * @return debugging string + */ + public String toString() { + return "[" + getClass().getName() + ": " + templates + "]"; + } + + +} \ No newline at end of file diff --git a/src/nu/xom/xslt/package.html b/src/nu/xom/xslt/package.html new file mode 100644 index 0000000..9c6a33a --- /dev/null +++ b/src/nu/xom/xslt/package.html @@ -0,0 +1,40 @@ + + + + + + + +

    +nu.xom.xslt connects +XOM documents to TrAX-based XSLT processors. +

    + +@since 1.0 + + +

    + * Indicates that a string + * intended for use as a URI or URI reference (typically a namespace + * name or a base URI) is not syntactically correct according to + * RFC 3986. + *

    +nu.xom.samples includes several dozen example programs that +demonstrate different parts of the XOM API. +These are intended for tutorial purposes. They are not designed as reusable code +for the most part. Most of the examples are adapted +from Processing XML with Java +by Elliotte Rusty Harold. Please consult that book +(available in its entirety online) if you'd like more information +about what particular samples are intended to show. +That book provides SAX, DOM, and/or JDOM implementations +of most of these samples. +

+nu.xom.canonical +supports the output of +Canonical XML +from XOM. +For basic canonicalization of existing documents, this (or any other tree-based +solution) is likely not as efficient as a streaming, SAX-based solution. +However, this is useful for output from XOM in canonical form. +