/** * The following method will run the output data through each of the three base formatters, raw, * compact, and pretty. It will also run each of those formatters as the outputString(content), * output(content, OutputStream) and output(content, Writer). * * <p>The expectation is that the results of the three output forms (String, OutputStream, and * Writer) will be identical, and that it will match the expected value for the appropriate * formatter. * * @param content The content to output * @param methodprefix What the methods are called * @param clazz The class used as the parameter for the methods. * @param setup A callback mechanism to modify the formatters * @param raw What we expect the content to look like with the RAW format * @param compact What we expect the content to look like with the COMPACT format * @param pretty What we expect the content to look like with the PRETTY format * @param trimfw What we expect the content to look like with the TRIM_FULL_WHITE format */ protected void checkOutput( Object content, String methodprefix, Class<?> clazz, FormatSetup setup, String raw, String compact, String pretty, String tso, String trimfw) { Method meth = getMyMethod(methodprefix + "String", Format.class, clazz); if (meth == null) { return; } String[] descn = new String[] {"Raw", "Compact", "Pretty", "PrettySpecifiedOnly", "TrimFullWhite"}; Format ftrimfw = Format.getPrettyFormat(); ftrimfw.setTextMode(TextMode.TRIM_FULL_WHITE); Format fattspec = Format.getPrettyFormat(); fattspec.setSpecifiedAttributesOnly(true); Format[] formats = new Format[] { getFormat(setup, Format.getRawFormat()), getFormat(setup, Format.getCompactFormat()), getFormat(setup, Format.getPrettyFormat()), getFormat(setup, fattspec), getFormat(setup, ftrimfw) }; String[] result = new String[] {raw, compact, pretty, tso, trimfw}; for (int i = 0; i < 5; i++) { String mstring; try { mstring = (String) meth.invoke(this, formats[i], content); } catch (Exception e) { e.printStackTrace(); throw new IllegalStateException(e); } String msg = "outputString Format " + descn[i]; assertEquals(msg, expect(result[i]), mstring); } }
@SuppressWarnings("javadoc") public abstract class AbstractTestOutputter { protected static interface FormatSetup { public void setup(Format fmt); } private final boolean cr2xD; private final boolean pademptyelement; private final boolean forceexpand; private final boolean padpi; private final boolean usesrawxmlout; public AbstractTestOutputter( boolean cr2xD, boolean padpreempty, boolean padpi, boolean forceexpand, boolean usesrawxmlout) { this.cr2xD = cr2xD; this.pademptyelement = padpreempty; this.forceexpand = forceexpand; this.padpi = padpi; this.usesrawxmlout = usesrawxmlout; } protected final String expect(String expect) { if (cr2xD) { expect = expect.replaceAll("\r", "
"); } if (forceexpand) { expect = expect.replaceAll("<(\\w+(:\\w+)?)\\s*/>", "<$1></$1>"); expect = expect.replaceAll("<(\\w+(:\\w+)?)\\s+(.+?)\"\\s*/>", "<$1 $3\"></$1>"); } if (padpi) { expect = expect.replaceAll("(<\\?\\w+)(\\?>)", "$1 $2"); } if (pademptyelement) { // expect = expect.replaceAll("\">", "\" >"); // expect = expect.replaceAll("<(\\w+)>", "<$1 >"); expect = expect.replaceAll("<(\\w+(:\\w+)?)/>", "<$1 />"); expect = expect.replaceAll("<(\\w+(:\\w+)?\\s.+\")/>", "<$1 />"); } else { // expect = expect.replaceAll("<(\\w+)\\s+>", "<$1>"); expect = expect.replaceAll("<(\\w+)(\\s+.+?)?\\s+/>", "<$1$2/>"); expect = expect.replaceAll("<(\\w+:\\w+)(\\s+.+?)?\\s+/>", "<$1$2/>"); } // if (rawoutsideroot) { // // outside the root element will be raw-formatted. // StringBuilder sb = new StringBuilder(expect.length()); // int gotstuff = 0; // boolean indoctype = false; // boolean gotroot = false; // int depth = 0; // char[] chars = expect.toCharArray(); // int i = 0; // while (i < chars.length && Verifier.isXMLWhitespace(chars[i])) { // // skip initial whitespace. // i++; // } // for (; i < chars.length; i++) { // char c = chars[i]; // sb.append(c); // if (!gotroot) { // if (c == '<') { // if (depth == 0) { // if (i < chars.length - 2) { // if (chars[i + 1] == '?') { // // PI or XML Declaration // gotstuff++; // } else if (chars[i + 1] == '!') { // // Comment of DOCTYPE // gotstuff++; // if (chars[i + 2] == 'D') { // // DOCTYPE // indoctype = true; // } // } else { // // root element // gotroot = true; // } // } else { // gotroot = true; // } // } // depth++; // } else if (c == '>') { // depth--; // if (depth == 0) { // if (indoctype) { // sb.append('\n'); // indoctype = false; // } // while (i+1 < chars.length && Verifier.isXMLWhitespace(chars[i + 1])) { // // skip whitespace after top-level content. // i++; // } // } // } // } // } // while (Verifier.isXMLWhitespace(sb.charAt(sb.length() - 1))) { // // eliminate trailing whitespace. // sb.setLength(sb.length() - 1); // } // if (gotstuff > 1 || (gotroot && gotstuff > 0)) { // // there is multiple content stuff, need to trim the whitespace.... // expect = sb.toString(); // } // } return expect; } /** * Return a string representing a {@link Document}. Uses an internal StringWriter. * * <p><b>Warning</b>: a String is Unicode, which may not match the outputter's specified encoding. * * @param doc <code>Document</code> to format. * @return the input content formatted as an XML String. * @throws NullPointerException if the specified content is null. */ public abstract String outputString(Format format, Document doc); /** * Return a string representing a {@link DocType}. * * <p><b>Warning</b>: a String is Unicode, which may not match the outputter's specified encoding. * * @param doctype <code>DocType</code> to format. * @return the input content formatted as an XML String. * @throws NullPointerException if the specified content is null. */ public abstract String outputString(Format format, DocType doctype); /** * Return a string representing an {@link Element}. * * <p><b>Warning</b>: a String is Unicode, which may not match the outputter's specified encoding. * * @param element <code>Element</code> to format. * @return the input content formatted as an XML String. * @throws NullPointerException if the specified content is null. */ public abstract String outputString(Format format, Element element); /** * Return a string representing a List of {@link Content} nodes. <br> * The list is assumed to contain legal JDOM nodes. If other content is coerced on to the list it * will cause ClassCastExceptions, and null List members will cause NullPointerException. * * <p><b>Warning</b>: a String is Unicode, which may not match the outputter's specified encoding. * * @param list <code>List</code> to format. * @return the input content formatted as an XML String. * @throws ClassCastException if non-{@link Content} is forced in to the list * @throws NullPointerException if the List is null or contains null members. */ public abstract String outputString(Format format, List<? extends Content> list); /** * Return a string representing a {@link CDATA} node. * * <p><b>Warning</b>: a String is Unicode, which may not match the outputter's specified encoding. * * @param cdata <code>CDATA</code> to format. * @return the input content formatted as an XML String. * @throws NullPointerException if the specified content is null. */ public abstract String outputString(Format format, CDATA cdata); /** * Return a string representing a {@link Text} node. * * <p><b>Warning</b>: a String is Unicode, which may not match the outputter's specified encoding. * * @param text <code>Text</code> to format. * @return the input content formatted as an XML String. * @throws NullPointerException if the specified content is null. */ public abstract String outputString(Format format, Text text); /** * Return a string representing a {@link Comment}. * * <p><b>Warning</b>: a String is Unicode, which may not match the outputter's specified encoding. * * @param comment <code>Comment</code> to format. * @return the input content formatted as an XML String. * @throws NullPointerException if the specified content is null. */ public abstract String outputString(Format format, Comment comment); /** * Return a string representing a {@link ProcessingInstruction}. * * <p><b>Warning</b>: a String is Unicode, which may not match the outputter's specified encoding. * * @param pi <code>ProcessingInstruction</code> to format. * @return the input content formatted as an XML String. * @throws NullPointerException if the specified content is null. */ public abstract String outputString(Format format, ProcessingInstruction pi); /** * Return a string representing an {@link EntityRef}. * * <p><b>Warning</b>: a String is Unicode, which may not match the outputter's specified encoding. * * @param entity <code>EntityRef</code> to format. * @return the input content formatted as an XML String. * @throws NullPointerException if the specified content is null. */ public abstract String outputString(Format format, EntityRef entity); /** * This will handle printing out an <code>{@link * Element}</code>'s content only, not including its tag, and attributes. This can be useful for * printing the content of an element that contains HTML, like "<description>JDOM is * <b>fun>!</description>". * * <p><b>Warning</b>: a String is Unicode, which may not match the outputter's specified encoding. * * @param element <code>Element</code> to output. * @return the input content formatted as an XML String. * @throws NullPointerException if the specified content is null. */ public abstract String outputElementContentString(Format format, Element element); protected static final Format fraw = Format.getRawFormat(); protected static final Format fcompact = Format.getCompactFormat(); protected static final Format fpretty = Format.getPrettyFormat(); protected static final Format ftso = Format.getPrettyFormat(); protected static final Format ftfw = Format.getPrettyFormat(); static { fraw.setLineSeparator("\n"); fcompact.setLineSeparator("\n"); fpretty.setLineSeparator("\n"); ftso.setLineSeparator("\n"); ftso.setSpecifiedAttributesOnly(true); ftfw.setLineSeparator("\n"); ftfw.setTextMode(TextMode.TRIM_FULL_WHITE); } @Test public void testTextEmpty() { Text content = new Text(""); assertEquals("", outputString(fraw, content)); assertEquals("", outputString(fcompact, content)); assertEquals("", outputString(fpretty, content)); assertEquals("", outputString(ftso, content)); assertEquals("", outputString(ftfw, content)); } @Test public void testTextWhitespace() { Text content = new Text(" \r \n \t "); assertEquals(expect(" \r \n \t "), outputString(fraw, content)); assertEquals("", outputString(fcompact, content)); assertEquals("", outputString(fpretty, content)); assertEquals("", outputString(ftso, content)); assertEquals("", outputString(ftfw, content)); } @Test public void testTextWithText() { Text content = new Text(" \r & \n \t "); assertEquals(expect(" \r & \n \t "), outputString(fraw, content)); assertEquals(expect("&"), outputString(fcompact, content)); assertEquals(expect("&"), outputString(fpretty, content)); assertEquals(expect("&"), outputString(ftso, content)); assertEquals(expect(" \r & \n \t "), outputString(ftfw, content)); } @Test public void testCDATAEmpty() { CDATA content = new CDATA(""); assertEquals("<![CDATA[]]>", outputString(fraw, content)); assertEquals("", outputString(fcompact, content)); assertEquals("", outputString(fpretty, content)); assertEquals("", outputString(ftso, content)); assertEquals("", outputString(ftfw, content)); } @Test public void testCDATAWhitespace() { CDATA content = new CDATA(" \r \n \t "); assertEquals("<![CDATA[ \r \n \t ]]>", outputString(fraw, content)); assertEquals("", outputString(fcompact, content)); assertEquals("", outputString(fpretty, content)); assertEquals("", outputString(ftso, content)); assertEquals("", outputString(ftfw, content)); } @Test public void testCDATAWithText() { CDATA content = new CDATA(" \r & \n \t "); assertEquals("<![CDATA[ \r & \n \t ]]>", outputString(fraw, content)); assertEquals("<![CDATA[&]]>", outputString(fcompact, content)); assertEquals("<![CDATA[&]]>", outputString(fpretty, content)); assertEquals("<![CDATA[&]]>", outputString(ftso, content)); assertEquals("<![CDATA[ \r & \n \t ]]>", outputString(ftfw, content)); } @Test public void testEntityRef() { EntityRef content = new EntityRef("ref"); assertEquals("&ref;", outputString(fraw, content)); assertEquals("&ref;", outputString(fcompact, content)); assertEquals("&ref;", outputString(fpretty, content)); assertEquals("&ref;", outputString(ftso, content)); assertEquals("&ref;", outputString(ftfw, content)); } @Test public void testProcessingInstructionTargetOnly() { ProcessingInstruction content = new ProcessingInstruction("target"); assertEquals(expect("<?target?>"), outputString(fraw, content)); assertEquals(expect("<?target?>"), outputString(fcompact, content)); assertEquals(expect("<?target?>"), outputString(fpretty, content)); assertEquals(expect("<?target?>"), outputString(ftfw, content)); } @Test public void testProcessingInstructionTargetWithData() { ProcessingInstruction content = new ProcessingInstruction("target", "data"); assertEquals("<?target data?>", outputString(fraw, content)); assertEquals("<?target data?>", outputString(fcompact, content)); assertEquals("<?target data?>", outputString(fpretty, content)); assertEquals("<?target data?>", outputString(ftso, content)); assertEquals("<?target data?>", outputString(ftfw, content)); } @Test public void testComment() { Comment content = new Comment("comment"); assertEquals("<!--comment-->", outputString(fraw, content)); assertEquals("<!--comment-->", outputString(fcompact, content)); assertEquals("<!--comment-->", outputString(fpretty, content)); assertEquals("<!--comment-->", outputString(ftso, content)); assertEquals("<!--comment-->", outputString(ftfw, content)); } @Test public void testDocTypeSimple() { DocType content = new DocType("root"); assertEquals("<!DOCTYPE root>", outputString(fraw, content)); assertEquals("<!DOCTYPE root>", outputString(fcompact, content)); assertEquals("<!DOCTYPE root>", outputString(fpretty, content)); assertEquals("<!DOCTYPE root>", outputString(ftso, content)); assertEquals("<!DOCTYPE root>", outputString(ftfw, content)); } @Test public void testDocTypeSimpleISS() { DocType content = new DocType("root"); content.setInternalSubset("<!ENTITY name \"value\">"); assertEquals("<!DOCTYPE root [\n<!ENTITY name \"value\">]>", outputString(fraw, content)); assertEquals("<!DOCTYPE root [\n<!ENTITY name \"value\">]>", outputString(fcompact, content)); assertEquals("<!DOCTYPE root [\n<!ENTITY name \"value\">]>", outputString(fpretty, content)); assertEquals("<!DOCTYPE root [\n<!ENTITY name \"value\">]>", outputString(ftso, content)); assertEquals("<!DOCTYPE root [\n<!ENTITY name \"value\">]>", outputString(ftfw, content)); } @Test public void testDocTypeSystemID() { DocType content = new DocType("root", "sysid"); assertEquals("<!DOCTYPE root SYSTEM \"sysid\">", outputString(fraw, content)); assertEquals("<!DOCTYPE root SYSTEM \"sysid\">", outputString(fcompact, content)); assertEquals("<!DOCTYPE root SYSTEM \"sysid\">", outputString(fpretty, content)); assertEquals("<!DOCTYPE root SYSTEM \"sysid\">", outputString(ftso, content)); assertEquals("<!DOCTYPE root SYSTEM \"sysid\">", outputString(ftfw, content)); } @Test public void testDocTypeSystemIDISS() { DocType content = new DocType("root", "sysid"); content.setInternalSubset("internal"); assertEquals("<!DOCTYPE root SYSTEM \"sysid\" [\ninternal]>", outputString(fraw, content)); assertEquals("<!DOCTYPE root SYSTEM \"sysid\" [\ninternal]>", outputString(fcompact, content)); assertEquals("<!DOCTYPE root SYSTEM \"sysid\" [\ninternal]>", outputString(fpretty, content)); assertEquals("<!DOCTYPE root SYSTEM \"sysid\" [\ninternal]>", outputString(ftso, content)); assertEquals("<!DOCTYPE root SYSTEM \"sysid\" [\ninternal]>", outputString(ftfw, content)); } @Test public void testDocTypePublicSystemID() { DocType content = new DocType("root", "pubid", "sysid"); assertEquals("<!DOCTYPE root PUBLIC \"pubid\" \"sysid\">", outputString(fraw, content)); assertEquals("<!DOCTYPE root PUBLIC \"pubid\" \"sysid\">", outputString(fcompact, content)); assertEquals("<!DOCTYPE root PUBLIC \"pubid\" \"sysid\">", outputString(fpretty, content)); assertEquals("<!DOCTYPE root PUBLIC \"pubid\" \"sysid\">", outputString(ftso, content)); assertEquals("<!DOCTYPE root PUBLIC \"pubid\" \"sysid\">", outputString(ftfw, content)); } @Test public void testDocTypePublicSystemIDISS() { DocType content = new DocType("root", "pubid", "sysid"); content.setInternalSubset("internal"); assertEquals( "<!DOCTYPE root PUBLIC \"pubid\" \"sysid\" [\ninternal]>", outputString(fraw, content)); assertEquals( "<!DOCTYPE root PUBLIC \"pubid\" \"sysid\" [\ninternal]>", outputString(fcompact, content)); assertEquals( "<!DOCTYPE root PUBLIC \"pubid\" \"sysid\" [\ninternal]>", outputString(fpretty, content)); assertEquals( "<!DOCTYPE root PUBLIC \"pubid\" \"sysid\" [\ninternal]>", outputString(ftso, content)); assertEquals( "<!DOCTYPE root PUBLIC \"pubid\" \"sysid\" [\ninternal]>", outputString(ftfw, content)); } @Test public void testMultiWhiteText() { Element root = new Element("root"); root.addContent(new CDATA(" ")); root.addContent(new Text(" ")); root.addContent(new Text(" ")); root.addContent(new Text("")); root.addContent(new Text(" ")); root.addContent(new Text(" \n \n ")); root.addContent(new Text(" \t ")); root.addContent(new Text(" ")); assertEquals( expect("<root><![CDATA[ ]]> \n \n \t </root>"), outputString(fraw, root)); assertEquals(expect("<root/>"), outputString(fcompact, root)); assertEquals(expect("<root/>"), outputString(fpretty, root)); assertEquals(expect("<root/>"), outputString(ftso, root)); assertEquals(expect("<root/>"), outputString(ftfw, root)); } @Test public void testMultiText() { Element root = new Element("root"); root.addContent(new CDATA(" ")); root.addContent(new Text(" ")); root.addContent(new Text(" ")); root.addContent(new Text("")); root.addContent(new Text("X")); root.addContent(new Text(" \n \n ")); root.addContent(new Text(" \t ")); root.addContent(new Text(" ")); assertEquals( expect("<root><![CDATA[ ]]> X \n \n \t </root>"), outputString(fraw, root)); assertEquals(expect("<root>X</root>"), outputString(fcompact, root)); assertEquals(expect("<root>X</root>"), outputString(fpretty, root)); assertEquals(expect("<root>X</root>"), outputString(ftso, root)); assertEquals( expect("<root><![CDATA[ ]]> X \n \n \t </root>"), outputString(ftfw, root)); } @Test public void testDocumentSimple() { Document content = new Document(); assertEquals("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n", outputString(fraw, content)); assertEquals("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n", outputString(fcompact, content)); assertEquals("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n", outputString(fpretty, content)); assertEquals("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n", outputString(ftso, content)); assertEquals("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n", outputString(ftfw, content)); } @Test public void testDocumentDocType() { Document content = new Document(); content.setDocType(new DocType("root")); assertEquals( "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE root>\n", outputString(fraw, content)); assertEquals( "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE root>\n", outputString(fcompact, content)); assertEquals( "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE root>\n", outputString(fpretty, content)); assertEquals( "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE root>\n", outputString(ftso, content)); assertEquals( "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE root>\n", outputString(ftfw, content)); } @Test public void testDocumentComment() { Document content = new Document(); content.addContent(new Comment("comment")); assertEquals( "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--comment-->\n", outputString(fraw, content)); assertEquals( "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--comment-->\n", outputString(fcompact, content)); assertEquals( "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--comment-->\n", outputString(fpretty, content)); assertEquals( "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--comment-->\n", outputString(ftso, content)); assertEquals( "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--comment-->\n", outputString(ftfw, content)); } @Test public void testXXX() { Text content = new Text(""); assertEquals("", outputString(fraw, content)); assertEquals("", outputString(fcompact, content)); assertEquals("", outputString(fpretty, content)); assertEquals("", outputString(ftfw, content)); } @Test public void testOutputText() { checkOutput( new Text(" hello there "), " hello there ", "hello there", "hello there", "hello there", " hello there "); } @Test public void testOutputCDATA() { String indata = " hello there bozo ! "; String rawcdata = "<![CDATA[ hello there bozo ! ]]>"; String compdata = "<![CDATA[hello there bozo !]]>"; String prettydata = "<![CDATA[hello there bozo !]]>"; String trimdata = "<![CDATA[ hello there bozo ! ]]>"; checkOutput(new CDATA(indata), rawcdata, compdata, prettydata, prettydata, trimdata); } @Test public void testOutputComment() { String incomment = " hello there bozo ! "; String outcomment = "<!--" + incomment + "-->"; checkOutput(new Comment(incomment), outcomment, outcomment, outcomment, outcomment, outcomment); } @Test public void testOutputProcessingInstructionSimple() { ProcessingInstruction inpi = new ProcessingInstruction("jdomtest", ""); String outpi = "<?jdomtest?>"; checkOutput(inpi, outpi, outpi, outpi, outpi, outpi); } @Test public void testOutputProcessingInstructionData() { String pi = " hello there "; ProcessingInstruction inpi = new ProcessingInstruction("jdomtest", pi); String outpi = "<?jdomtest " + pi + "?>"; checkOutput(inpi, outpi, outpi, outpi, outpi, outpi); } @Test public void testOutputEntityRef() { checkOutput( new EntityRef("name", "publicID", "systemID"), "&name;", "&name;", "&name;", "&name;", "&name;"); } @Test public void testOutputElementSimple() { String txt = "<root/>"; checkOutput(new Element("root"), txt, txt, txt, txt, txt); } @Test public void testOutputElementAttribute() { String txt = "<root att=\"val\" />"; checkOutput(new Element("root").setAttribute("att", "val"), txt, txt, txt, txt, txt); } @Test public void testOutputElementAttributeNotSpecifiedA() { String txt = "<root att=\"val\" />"; final Element root = new Element("root"); final Attribute att = new Attribute("att", "val"); root.setAttribute(att); att.setSpecified(false); checkOutput(root, txt, txt, txt, "<root />", txt); } @Test public void testOutputElementAttributeNotSpecifiedB() { String txt = "<root atta=\"val\" attb=\"attb\" />"; final Element root = new Element("root"); final Attribute atta = new Attribute("atta", "val"); final Attribute attb = new Attribute("attb", "attb"); root.setAttribute(atta); root.setAttribute(attb); atta.setSpecified(false); checkOutput(root, txt, txt, txt, "<root attb=\"attb\" />", txt); } @Test public void testOutputElementCDATA() { String txt = "<root><![CDATA[xx]]></root>"; Element root = new Element("root"); root.addContent(new CDATA("xx")); checkOutput(root, txt, txt, txt, txt, txt); } @Test public void testOutputElementExpandEmpty() { String txt = "<root></root>"; FormatSetup setup = new FormatSetup() { @Override public void setup(Format fmt) { fmt.setExpandEmptyElements(true); } }; checkOutput(new Element("root"), setup, txt, txt, txt, txt, txt); } @Test public void testOutputElementPreserveSpace() { String txt = "<root xml:space=\"preserve\"> <child xml:space=\"default\">abc</child> </root>"; Element root = new Element("root"); root.setAttribute("space", "preserve", Namespace.XML_NAMESPACE); root.addContent(" "); Element child = new Element("child"); child.setAttribute("space", "default", Namespace.XML_NAMESPACE); child.addContent("abc"); root.addContent(child); root.addContent(" "); checkOutput(root, txt, txt, txt, txt, txt); } @Test public void testOutputElementPreserveSpaceComplex() { // the purpose of this test is to ensure that the different // formatting values are used when going down one level, // back up, then in to 'preserve', back up, and then again // down in to normal (not preserve). // this is essentially a test of the FormatStack code.... Element tst = new Element("child"); Comment cmt = new Comment("comment"); tst.addContent(cmt); String spaced = " <child>\n <!--comment-->\n </child>\n"; String compact = "<child><!--comment--></child>"; String preserved = "<child xml:space=\"preserve\"><!--comment--></child>"; Element root = new Element("root"); root.addContent(tst.clone()); root.addContent(tst.clone().setAttribute("space", "preserve", Namespace.XML_NAMESPACE)); root.addContent(tst.clone()); String rawcompact = "<root>" + compact + preserved + compact + "</root>"; String pretty = "<root>\n" + spaced + " " + preserved + "\n" + spaced + "</root>"; checkOutput(root, rawcompact, rawcompact, pretty, pretty, pretty); } @Test public void testOutputElementMultiText() { Element root = new Element("root"); root.addContent(new CDATA(" ")); root.addContent(new Text(" xx ")); root.addContent(new Text("yy")); root.addContent(new Text(" ")); root.addContent(new Text("zz")); root.addContent(new Text(" ww")); root.addContent(new EntityRef("amp")); root.addContent(new Text("vv")); root.addContent(new Text(" ")); checkOutput( root, "<root><![CDATA[ ]]> xx yy zz ww&vv </root>", "<root>xx yy zz ww&vv</root>", "<root>xx yy zz ww&vv</root>", "<root>xx yy zz ww&vv</root>", // This should be changed with issue #31. // The real value should have one additional // space at the beginning and two at the end // for now we leave the broken test here because it // helps with the coverage reports. // the next test is added to be a failing test. "<root><![CDATA[ ]]> xx yy zz ww&vv </root>"); } @Test public void testOutputElementMultiAllWhite() { Element root = new Element("root"); root.addContent(new CDATA(" ")); root.addContent(new Text(" ")); root.addContent(new Text(" ")); root.addContent(new Text("")); root.addContent(new Text(" ")); root.addContent(new Text(" \n \n ")); root.addContent(new Text(" \t ")); root.addContent(new Text(" ")); checkOutput( root, "<root><![CDATA[ ]]> \n \n \t </root>", "<root />", "<root />", "<root />", "<root />"); } @Test public void testOutputElementMultiAllWhiteExpandEmpty() { Element root = new Element("root"); root.addContent(new CDATA(" ")); root.addContent(new Text(" ")); root.addContent(new Text(" ")); root.addContent(new Text("")); root.addContent(new Text(" ")); root.addContent(new Text(" \n \n ")); root.addContent(new Text(" \t ")); root.addContent(new Text(" ")); FormatSetup fs = new FormatSetup() { @Override public void setup(Format fmt) { fmt.setExpandEmptyElements(true); } }; checkOutput( root, fs, "<root><![CDATA[ ]]> \n \n \t </root>", "<root></root>", "<root></root>", "<root></root>", "<root></root>"); } @Test public void testOutputElementMultiMostWhiteExpandEmpty() { // this test has mixed content (text-type and not text type). // and, it has a multi-text-type at the end. Element root = new Element("root"); root.addContent(new CDATA(" ")); root.addContent(new Text(" ")); root.addContent(new Text(" ")); root.addContent(new Text("")); root.addContent(new Text(" ")); root.addContent(new Text(" \n \n ")); root.addContent(new Comment("Boo")); root.addContent(new Text(" \t ")); root.addContent(new Text(" ")); FormatSetup fs = new FormatSetup() { @Override public void setup(Format fmt) { fmt.setExpandEmptyElements(true); } }; checkOutput( root, fs, "<root><![CDATA[ ]]> \n \n <!--Boo--> \t </root>", "<root><!--Boo--></root>", "<root>\n <!--Boo-->\n</root>", "<root>\n <!--Boo-->\n</root>", "<root>\n <!--Boo-->\n</root>"); } @Test public void testOutputElementMixedMultiCDATA() { // this test has mixed content (text-type and not text type). // and, it has a multi-text-type at the end. Element root = new Element("root"); root.addContent(new Comment("Boo")); root.addContent(new Text(" ")); root.addContent(new CDATA("A")); FormatSetup fs = new FormatSetup() { @Override public void setup(Format fmt) { fmt.setExpandEmptyElements(true); } }; checkOutput( root, fs, "<root><!--Boo--> <![CDATA[A]]></root>", "<root><!--Boo--><![CDATA[A]]></root>", "<root>\n <!--Boo-->\n <![CDATA[A]]>\n</root>", "<root>\n <!--Boo-->\n <![CDATA[A]]>\n</root>", "<root>\n <!--Boo-->\n <![CDATA[A]]>\n</root>"); } @Test public void testOutputElementMixedMultiEntityRef() { // this test has mixed content (text-type and not text type). // and, it has a multi-text-type at the end. Element root = new Element("root"); root.addContent(new Comment("Boo")); root.addContent(new Text(" ")); root.addContent(new EntityRef("aer")); FormatSetup fs = new FormatSetup() { @Override public void setup(Format fmt) { fmt.setExpandEmptyElements(true); } }; checkOutput( root, fs, "<root><!--Boo--> &aer;</root>", "<root><!--Boo-->&aer;</root>", "<root>\n <!--Boo-->\n &aer;\n</root>", "<root>\n <!--Boo-->\n &aer;\n</root>", "<root>\n <!--Boo-->\n &aer;\n</root>"); } @Test public void testOutputElementMixedMultiText() { // this test has mixed content (text-type and not text type). // and, it has a multi-text-type at the end. Element root = new Element("root"); root.addContent(new Comment("Boo")); root.addContent(new Text(" ")); root.addContent(new Text("txt")); FormatSetup fs = new FormatSetup() { @Override public void setup(Format fmt) { fmt.setExpandEmptyElements(true); } }; checkOutput( root, fs, "<root><!--Boo--> txt</root>", "<root><!--Boo-->txt</root>", "<root>\n <!--Boo-->\n txt\n</root>", "<root>\n <!--Boo-->\n txt\n</root>", "<root>\n <!--Boo-->\n txt\n</root>"); } @Test public void testOutputElementMixedMultiZeroText() { // this test has mixed content (text-type and not text type). // and, it has a multi-text-type at the end. Element root = new Element("root"); root.addContent(new Comment("Boo")); root.addContent(new Text("")); root.addContent(new Text(" ")); root.addContent(new Text("")); root.addContent(new Text("txt")); root.addContent(new Text("")); FormatSetup fs = new FormatSetup() { @Override public void setup(Format fmt) { fmt.setExpandEmptyElements(true); } }; checkOutput( root, fs, "<root><!--Boo--> txt</root>", "<root><!--Boo-->txt</root>", "<root>\n <!--Boo-->\n txt\n</root>", "<root>\n <!--Boo-->\n txt\n</root>", "<root>\n <!--Boo-->\n txt\n</root>"); } @Test public void testOutputElementInterleavedEmptyText() { // this is to test issue #72 // Compact format only prints first child. // and, it has a multi-text-type at the end. Element root = new Element("root"); root.addContent(new Text(" ")); root.addContent(new Comment("Boo")); root.addContent(new Text(" ")); root.addContent(new Element("child")); root.addContent(new Text(" ")); root.addContent(new ProcessingInstruction("pitarget")); root.addContent(new Text(" ")); checkOutput( root, "<root> <!--Boo--> <child /> <?pitarget?> </root>", "<root><!--Boo--><child /><?pitarget?></root>", "<root>\n <!--Boo-->\n <child />\n <?pitarget?>\n</root>", "<root>\n <!--Boo-->\n <child />\n <?pitarget?>\n</root>", "<root>\n <!--Boo-->\n <child />\n <?pitarget?>\n</root>"); } @Test public void testOutputElementMultiEntityLeftRight() { Element root = new Element("root"); root.addContent(new EntityRef("erl")); root.addContent(new Text(" ")); root.addContent(new Text(" ")); root.addContent(new EntityRef("err")); checkOutput( root, "<root>&erl; &err;</root>", "<root>&erl; &err;</root>", "<root>&erl; &err;</root>", "<root>&erl; &err;</root>", "<root>&erl; &err;</root>"); } @Test public void testOutputElementMultiTrimLeftRight() { Element root = new Element("root"); root.addContent(new Text(" tl ")); root.addContent(new Text(" mid ")); root.addContent(new Text(" tr ")); checkOutput( root, "<root> tl mid tr </root>", "<root>tl mid tr</root>", "<root>tl mid tr</root>", "<root>tl mid tr</root>", "<root> tl mid tr </root>"); } @Test public void testOutputElementMultiCDATALeftRight() { Element root = new Element("root"); root.addContent(new CDATA(" tl ")); root.addContent(new Text(" mid ")); root.addContent(new CDATA(" tr ")); checkOutput( root, "<root><![CDATA[ tl ]]> mid <![CDATA[ tr ]]></root>", "<root><![CDATA[tl]]> mid <![CDATA[tr]]></root>", "<root><![CDATA[tl ]]> mid <![CDATA[ tr]]></root>", "<root><![CDATA[tl ]]> mid <![CDATA[ tr]]></root>", "<root><![CDATA[ tl ]]> mid <![CDATA[ tr ]]></root>"); } @Test public void testOutputElementNamespaces() { String txt = "<ns:root xmlns:ns=\"myns\" xmlns:ans=\"attributens\" xmlns:two=\"two\" ans:att=\"val\"/>"; Element emt = new Element("root", Namespace.getNamespace("ns", "myns")); Namespace ans = Namespace.getNamespace("ans", "attributens"); emt.setAttribute(new Attribute("att", "val", ans)); emt.addNamespaceDeclaration(Namespace.getNamespace("two", "two")); checkOutput(emt, txt, txt, txt, txt, txt); } @Test public void testOutputDocTypeSimple() { checkOutput( new DocType("root"), "<!DOCTYPE root>", "<!DOCTYPE root>", "<!DOCTYPE root>", "<!DOCTYPE root>", "<!DOCTYPE root>"); } @Test public void testOutputDocTypeInternalSubset() { String dec = "<!DOCTYPE root [\ninternal]>"; DocType dt = new DocType("root"); dt.setInternalSubset("internal"); checkOutput(dt, dec, dec, dec, dec, dec); } @Test public void testOutputDocTypeSystem() { String dec = "<!DOCTYPE root SYSTEM \"systemID\">"; checkOutput(new DocType("root", "systemID"), dec, dec, dec, dec, dec); } @Test public void testOutputDocTypePublic() { String dec = "<!DOCTYPE root PUBLIC \"publicID\">"; checkOutput(new DocType("root", "publicID", null), dec, dec, dec, dec, dec); } @Test public void testOutputDocTypePublicSystem() { String dec = "<!DOCTYPE root PUBLIC \"publicID\" \"systemID\">"; checkOutput(new DocType("root", "publicID", "systemID"), dec, dec, dec, dec, dec); } @Test public void testOutputDocumentSimple() { Document doc = new Document(); doc.addContent(new Element("root")); String xmldec = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"; String rtdec = "<root />"; checkOutput( doc, xmldec + "\n" + rtdec + "\n", xmldec + "\n" + rtdec + "\n", xmldec + "\n" + rtdec + "\n", xmldec + "\n" + rtdec + "\n", xmldec + "\n" + rtdec + "\n"); } @Test public void testOutputDocumentOmitEncoding() { Document doc = new Document(); doc.addContent(new Element("root")); String xmldec = "<?xml version=\"1.0\"?>"; FormatSetup setup = new FormatSetup() { @Override public void setup(Format fmt) { fmt.setOmitEncoding(true); } }; String rtdec = "<root />"; checkOutput( doc, setup, xmldec + "\n" + rtdec + "\n", xmldec + "\n" + rtdec + "\n", xmldec + "\n" + rtdec + "\n", xmldec + "\n" + rtdec + "\n", xmldec + "\n" + rtdec + "\n"); } @Test public void testOutputDocumentOmitDeclaration() { Document doc = new Document(); doc.addContent(new Element("root")); FormatSetup setup = new FormatSetup() { @Override public void setup(Format fmt) { fmt.setOmitDeclaration(true); } }; String rtdec = "<root />"; checkOutput(doc, setup, rtdec + "\n", rtdec + "\n", rtdec + "\n", rtdec + "\n", rtdec + "\n"); } @Test public void testOutputDocumentFull() { DocType dt = new DocType("root"); Comment comment = new Comment("comment"); ProcessingInstruction pi = new ProcessingInstruction("jdomtest", ""); Element root = new Element("root"); Document doc = new Document(); doc.addContent(dt); doc.addContent(comment); doc.addContent(pi); doc.addContent(root); String xmldec = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"; String dtdec = "<!DOCTYPE root>"; String commentdec = "<!--comment-->"; String pidec = "<?jdomtest?>"; String rtdec = "<root />"; String lf = "\n"; String dlf = usesrawxmlout ? "" : lf; checkOutput( doc, xmldec + lf + dtdec + commentdec + pidec + rtdec + lf, xmldec + lf + dtdec + commentdec + pidec + rtdec + lf, xmldec + lf + dtdec + dlf + commentdec + dlf + pidec + dlf + rtdec + lf, xmldec + lf + dtdec + dlf + commentdec + dlf + pidec + dlf + rtdec + lf, xmldec + lf + dtdec + dlf + commentdec + dlf + pidec + dlf + rtdec + lf); } @Test public void testDeepNesting() { // need to get beyond 16 levels of XML. DocType dt = new DocType("root"); Element root = new Element("root"); Document doc = new Document(); doc.addContent(dt); doc.addContent(root); String xmldec = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"; String dtdec = "<!DOCTYPE root>"; String lf = "\n"; StringBuilder raw = new StringBuilder(); raw.append(xmldec).append(lf).append(dtdec); StringBuilder pretty = new StringBuilder(); pretty.append(xmldec).append(lf).append(dtdec); if (!usesrawxmlout) { // most test systems use the XMLOutputter in raw mode to output // the results of the conversion. In Raw mode the XMLOutputter will // not make pretty content outside of the root element (but it will // put the XMLDeclaration on it's own line). // so, in the cases where the actual pretty format is used, we add // this newline after the DocType... pretty.append(lf); } raw.append("<root>"); pretty.append("<root>"); pretty.append(lf); final int depth = 40; int cnt = depth; Parent parent = root; StringBuilder indent = new StringBuilder(); while (--cnt > 0) { Element emt = new Element("emt"); parent.getContent().add(emt); parent = emt; raw.append("<emt>"); indent.append(" "); pretty.append(indent.toString()); pretty.append("<emt>"); pretty.append(lf); } parent.getContent().add(new Element("bottom")); raw.append("<bottom />"); pretty.append(indent.toString()); pretty.append(" <bottom />"); pretty.append(lf); cnt = depth; while (--cnt > 0) { raw.append("</emt>"); pretty.append(indent.toString()); pretty.append("</emt>"); indent.setLength(indent.length() - 2); pretty.append(lf); } raw.append("</root>"); raw.append(lf); pretty.append("</root>"); pretty.append(lf); checkOutput( doc, raw.toString(), raw.toString(), pretty.toString(), pretty.toString(), pretty.toString()); } @Test public void testOutputElementContent() { Element root = new Element("root"); root.addContent(new Element("child")); checkOutput( root, "outputElementContent", Element.class, null, "<child />", "<child />", "<child />", "<child />", "<child />"); } @Test public void testOutputList() { List<Object> c = new ArrayList<Object>(); c.add(new Element("root")); checkOutput( c, "output", List.class, null, "<root />", "<root />", "<root />", "<root />", "<root />"); } @Test public void testOutputEscapedMixedMultiText() { // this test has mixed content (text-type and not text type). // and, it has a multi-text-type at the end. Element root = new Element("root"); root.addContent(new Comment("Boo")); root.addContent(new Text(" xx ")); root.addContent(new Text("<emb>")); root.addContent(new Text(" xx ")); FormatSetup fs = new FormatSetup() { @Override public void setup(Format fmt) { fmt.setExpandEmptyElements(true); } }; checkOutput( root, fs, "<root><!--Boo--> xx <emb> xx </root>", "<root><!--Boo-->xx <emb> xx</root>", "<root>\n <!--Boo-->\n xx <emb> xx\n</root>", "<root>\n <!--Boo-->\n xx <emb> xx\n</root>", "<root>\n <!--Boo-->\n xx <emb> xx \n</root>"); } protected void checkOutput( Object content, String raw, String compact, String pretty, String tso, String trimfw) { Class<?> clazz = content.getClass(); checkOutput(content, "output", clazz, null, raw, compact, pretty, tso, trimfw); } protected void checkOutput( Object content, FormatSetup setup, String raw, String compact, String pretty, String tso, String trimfw) { Class<?> clazz = content.getClass(); checkOutput(content, "output", clazz, setup, raw, compact, pretty, tso, trimfw); } /** * The following method will run the output data through each of the three base formatters, raw, * compact, and pretty. It will also run each of those formatters as the outputString(content), * output(content, OutputStream) and output(content, Writer). * * <p>The expectation is that the results of the three output forms (String, OutputStream, and * Writer) will be identical, and that it will match the expected value for the appropriate * formatter. * * @param content The content to output * @param methodprefix What the methods are called * @param clazz The class used as the parameter for the methods. * @param setup A callback mechanism to modify the formatters * @param raw What we expect the content to look like with the RAW format * @param compact What we expect the content to look like with the COMPACT format * @param pretty What we expect the content to look like with the PRETTY format * @param trimfw What we expect the content to look like with the TRIM_FULL_WHITE format */ protected void checkOutput( Object content, String methodprefix, Class<?> clazz, FormatSetup setup, String raw, String compact, String pretty, String tso, String trimfw) { Method meth = getMyMethod(methodprefix + "String", Format.class, clazz); if (meth == null) { return; } String[] descn = new String[] {"Raw", "Compact", "Pretty", "PrettySpecifiedOnly", "TrimFullWhite"}; Format ftrimfw = Format.getPrettyFormat(); ftrimfw.setTextMode(TextMode.TRIM_FULL_WHITE); Format fattspec = Format.getPrettyFormat(); fattspec.setSpecifiedAttributesOnly(true); Format[] formats = new Format[] { getFormat(setup, Format.getRawFormat()), getFormat(setup, Format.getCompactFormat()), getFormat(setup, Format.getPrettyFormat()), getFormat(setup, fattspec), getFormat(setup, ftrimfw) }; String[] result = new String[] {raw, compact, pretty, tso, trimfw}; for (int i = 0; i < 5; i++) { String mstring; try { mstring = (String) meth.invoke(this, formats[i], content); } catch (Exception e) { e.printStackTrace(); throw new IllegalStateException(e); } String msg = "outputString Format " + descn[i]; assertEquals(msg, expect(result[i]), mstring); } } protected Format getFormat(FormatSetup setup, Format input) { if (setup == null) { input.setLineSeparator("\n"); return input; } input.setLineSeparator("\n"); setup.setup(input); return input; } private Method getMyMethod(String name, Class<?>... classes) { try { return this.getClass().getMethod(name, classes); } catch (Exception e) { // ignore. System.out.println( "Can't find " + name + " on " + this.getClass().getName() + ": " + e.getMessage()); } return null; } }