From 3700c02693bec14e2d28e361453cd29f89c2c21c Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Tue, 2 May 2023 19:14:37 -0400 Subject: [PATCH 1/4] [JENKINS-71182] Revert to `XML_QUIRKS` --- core/src/main/java/hudson/util/XStream2.java | 3 +- .../test/java/hudson/util/XStream2Test.java | 31 ++++++++++++++++--- 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/core/src/main/java/hudson/util/XStream2.java b/core/src/main/java/hudson/util/XStream2.java index a8dbeaf606fc..8da629f5131e 100644 --- a/core/src/main/java/hudson/util/XStream2.java +++ b/core/src/main/java/hudson/util/XStream2.java @@ -138,7 +138,8 @@ private static class StaxDriver extends StandardStaxDriver { @Override public HierarchicalStreamWriter createWriter(Writer out) { - return new PrettyPrintWriter(out, PrettyPrintWriter.XML_1_1, getNameCoder()); + // TODO JENKINS-71182 StaxWriter will not indent, XML_1_1 does not support emojis, and XML_QUIRKS writes unreadable "�", so 🤷 pick your poison + return new PrettyPrintWriter(out, PrettyPrintWriter.XML_QUIRKS, getNameCoder()); } @Override diff --git a/core/src/test/java/hudson/util/XStream2Test.java b/core/src/test/java/hudson/util/XStream2Test.java index 0352ef394562..ce6ac836d61c 100644 --- a/core/src/test/java/hudson/util/XStream2Test.java +++ b/core/src/test/java/hudson/util/XStream2Test.java @@ -598,6 +598,21 @@ public void testEmojiEscaped() throws Exception { assertEquals("Fox 🦊", bar.s); } + @Issue("JENKINS-71182") + @Test + public void writeEmoji() throws Exception { + Bar b = new Bar(); + String text = "Fox 🦊"; + b.s = text; + StringWriter w = new StringWriter(); + XStream2 xs = new XStream2(); + xs.toXML(b, w); + String xml = w.toString(); + assertThat(xml, is("\n Fox 🦊\n")); + b = (Bar) xs.fromXML(xml); + assertEquals(text, b.s); + } + @Issue("JENKINS-71139") @Test public void nullsWithoutEncodingDeclaration() throws Exception { @@ -615,8 +630,12 @@ public void nullsWithoutEncodingDeclaration() throws Exception { String xml = w.toString(); assertThat(xml, not(containsString("version=\"1.1\""))); System.out.println(xml); - b = (Bar) xs.fromXML(xml); - assertEquals(text, b.s); + try { + b = (Bar) xs.fromXML(xml); + assertEquals(text, b.s); + } catch (RuntimeException x) { + assertThat("cause is XMLStreamException: ParseError at [row,col]:[2,13] Message: The reference to entity \"y\" must end with the ';' delimiter.", Functions.printThrowable(x), containsString("XMLStreamException: ParseError")); + } } @Issue("JENKINS-71139") @@ -636,8 +655,12 @@ public void nullsWithEncodingDeclaration() throws Exception { String xml = baos.toString(StandardCharsets.UTF_8); System.out.println(xml); assertThat(xml, containsString("version=\"1.1\"")); - b = (Bar) xs.fromXML(new ByteArrayInputStream(baos.toByteArray())); - assertEquals(text, b.s); + try { + b = (Bar) xs.fromXML(new ByteArrayInputStream(baos.toByteArray())); + assertEquals(text, b.s); + } catch (RuntimeException x) { + assertThat("cause is XMLStreamException: ParseError at [row,col]:[2,13] Message: The reference to entity \"y\" must end with the ';' delimiter.", Functions.printThrowable(x), containsString("XMLStreamException: ParseError")); + } } } From 229f215ba4140a08e187f998251293a1efa46c01 Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Wed, 3 May 2023 11:36:25 -0400 Subject: [PATCH 2/4] Inlining patched `PrettyPrintWriter` https://github.com/jenkinsci/jenkins/pull/7924#issuecomment-1533167323 --- .../java/hudson/util/PrettyPrintWriter.java | 341 ++++++++++++++++++ core/src/main/java/hudson/util/XStream2.java | 4 +- .../test/java/hudson/util/XStream2Test.java | 40 +- 3 files changed, 349 insertions(+), 36 deletions(-) create mode 100644 core/src/main/java/hudson/util/PrettyPrintWriter.java diff --git a/core/src/main/java/hudson/util/PrettyPrintWriter.java b/core/src/main/java/hudson/util/PrettyPrintWriter.java new file mode 100644 index 000000000000..152f3c519d82 --- /dev/null +++ b/core/src/main/java/hudson/util/PrettyPrintWriter.java @@ -0,0 +1,341 @@ +// TODO adapted from https://github.com/x-stream/xstream/blob/32e52a6519a25366bbb5774bb536b5e290b64a42/xstream/src/java/com/thoughtworks/xstream/io/xml/PrettyPrintWriter.java pending release of https://github.com/jenkinsci/jenkins/pull/7924 + +/* + * Copyright (C) 2004, 2005, 2006 Joe Walnes. + * Copyright (C) 2006, 2007, 2008, 2009, 2011, 2013, 2014, 2015 XStream Committers. + * All rights reserved. + * + * The software in this package is published under the terms of the BSD + * style license a copy of which has been included with this distribution in + * the LICENSE.txt file. + * + * Created on 07. March 2004 by Joe Walnes + */ + +package hudson.util; + +import com.thoughtworks.xstream.core.util.FastStack; +import com.thoughtworks.xstream.core.util.QuickWriter; +import com.thoughtworks.xstream.io.ExtendedHierarchicalStreamWriter; +import com.thoughtworks.xstream.io.StreamException; +import com.thoughtworks.xstream.io.naming.NameCoder; +import com.thoughtworks.xstream.io.xml.AbstractXmlWriter; +import com.thoughtworks.xstream.io.xml.XmlFriendlyNameCoder; +import com.thoughtworks.xstream.io.xml.XmlFriendlyReplacer; +import java.io.Writer; + + +/** + * A simple writer that outputs XML in a pretty-printed indented stream. + *

+ * By default, the chars
+ * & < > " ' \r
+ * are escaped and replaced with a suitable XML entity. To alter this behavior, override the + * {@link #writeText(com.thoughtworks.xstream.core.util.QuickWriter, String)} and + * {@link #writeAttributeValue(com.thoughtworks.xstream.core.util.QuickWriter, String)} methods. + *

+ *

+ * The XML specification requires XML parsers to drop CR characters completely. This implementation will therefore use + * only a LF for line endings, never the platform encoding. You can overwrite the {@link #getNewLine()} method for a + * different behavior. + *

+ *

+ * Note: Depending on the XML version some characters cannot be written. Especially a 0 character is never valid in XML, + * neither directly nor as entity nor within CDATA. However, this writer works by default in a quirks mode, where it + * will write any character at least as character entity (even a null character). You may switch into XML_1_1 mode + * (which supports most characters) or XML_1_0 that does only support a very limited number of control characters. See + * XML specification for version 1.0 or 1.1. If a character is not supported, a + * {@link StreamException} is thrown. Select a proper parser implementation that respects the version in the XML header + * (the Xpp3 parser will also read character entities of normally invalid characters). + *

+ * + * @author Joe Walnes + * @author Jörg Schaible + */ +public class PrettyPrintWriter extends AbstractXmlWriter implements ExtendedHierarchicalStreamWriter { + + public static int XML_QUIRKS = -1; + public static int XML_1_0 = 0; + public static int XML_1_1 = 1; + + private final QuickWriter writer; + private final FastStack elementStack = new FastStack(16); + private final char[] lineIndenter; + private final int mode; + + private boolean tagInProgress; + protected int depth; + private boolean readyForNewLine; + private boolean tagIsEmpty; + + private static final char[] NULL = "�".toCharArray(); + private static final char[] AMP = "&".toCharArray(); + private static final char[] LT = "<".toCharArray(); + private static final char[] GT = ">".toCharArray(); + private static final char[] CR = " ".toCharArray(); + private static final char[] QUOT = """.toCharArray(); + private static final char[] APOS = "'".toCharArray(); + private static final char[] CLOSE = " XML_1_1) { + throw new IllegalArgumentException("Not a valid XML mode"); + } + } + + /** + * @since 1.3 + * @deprecated As of 1.4 use {@link PrettyPrintWriter#PrettyPrintWriter(Writer, int, char[], NameCoder)} instead + */ + @Deprecated + public PrettyPrintWriter( + final Writer writer, final int mode, final char[] lineIndenter, final XmlFriendlyReplacer replacer) { + this(writer, mode, lineIndenter, (NameCoder) replacer); + } + + /** + * @since 1.3 + */ + public PrettyPrintWriter(final Writer writer, final int mode, final char[] lineIndenter) { + this(writer, mode, lineIndenter, new XmlFriendlyNameCoder()); + } + + public PrettyPrintWriter(final Writer writer, final char[] lineIndenter) { + this(writer, XML_QUIRKS, lineIndenter); + } + + /** + * @since 1.3 + */ + public PrettyPrintWriter(final Writer writer, final int mode, final String lineIndenter) { + this(writer, mode, lineIndenter.toCharArray()); + } + + public PrettyPrintWriter(final Writer writer, final String lineIndenter) { + this(writer, lineIndenter.toCharArray()); + } + + /** + * @since 1.4 + */ + public PrettyPrintWriter(final Writer writer, final int mode, final NameCoder nameCoder) { + this(writer, mode, new char[]{' ', ' '}, nameCoder); + } + + /** + * @since 1.3 + * @deprecated As of 1.4 use {@link PrettyPrintWriter#PrettyPrintWriter(Writer, int, NameCoder)} instead + */ + @Deprecated + public PrettyPrintWriter(final Writer writer, final int mode, final XmlFriendlyReplacer replacer) { + this(writer, mode, new char[]{' ', ' '}, replacer); + } + + /** + * @since 1.4 + */ + public PrettyPrintWriter(final Writer writer, final NameCoder nameCoder) { + this(writer, XML_QUIRKS, new char[]{' ', ' '}, nameCoder); + } + + /** + * @deprecated As of 1.4 use {@link PrettyPrintWriter#PrettyPrintWriter(Writer, NameCoder)} instead. + */ + @Deprecated + public PrettyPrintWriter(final Writer writer, final XmlFriendlyReplacer replacer) { + this(writer, XML_QUIRKS, new char[]{' ', ' '}, replacer); + } + + /** + * @since 1.3 + */ + public PrettyPrintWriter(final Writer writer, final int mode) { + this(writer, mode, new char[]{' ', ' '}); + } + + public PrettyPrintWriter(final Writer writer) { + this(writer, new char[]{' ', ' '}); + } + + @Override + public void startNode(final String name) { + final String escapedName = encodeNode(name); + tagIsEmpty = false; + finishTag(); + writer.write('<'); + writer.write(escapedName); + elementStack.push(escapedName); + tagInProgress = true; + depth++; + readyForNewLine = true; + tagIsEmpty = true; + } + + @Override + public void startNode(final String name, final Class clazz) { + startNode(name); + } + + @Override + public void setValue(final String text) { + readyForNewLine = false; + tagIsEmpty = false; + finishTag(); + + writeText(writer, text); + } + + @Override + public void addAttribute(final String key, final String value) { + writer.write(' '); + writer.write(encodeAttribute(key)); + writer.write('='); + writer.write('\"'); + writeAttributeValue(writer, value); + writer.write('\"'); + } + + protected void writeAttributeValue(final QuickWriter writer, final String text) { + writeText(text, true); + } + + protected void writeText(final QuickWriter writer, final String text) { + writeText(text, false); + } + + private void writeText(final String text, final boolean isAttribute) { + text.codePoints().forEach(c -> { + switch (c) { + case '\0': + if (mode == XML_QUIRKS) { + writer.write(NULL); + } else { + throw new StreamException("Invalid character 0x0 in XML stream"); + } + break; + case '&': + writer.write(AMP); + break; + case '<': + writer.write(LT); + break; + case '>': + writer.write(GT); + break; + case '"': + writer.write(QUOT); + break; + case '\'': + writer.write(APOS); + break; + case '\r': + writer.write(CR); + break; + case '\t': + case '\n': + if (!isAttribute) { + writer.write(Character.toChars(c)); + break; + } + //$FALL-THROUGH$ + default: + if (Character.isDefined(c) && !Character.isISOControl(c)) { + if (mode != XML_QUIRKS) { + if (c > '\ud7ff' && c < '\ue000') { + throw new StreamException("Invalid character 0x" + + Integer.toHexString(c) + + " in XML stream"); + } + } + writer.write(Character.toChars(c)); + } else { + if (mode == XML_1_0) { + if (c < 9 || c == '\u000b' || c == '\u000c' || c == '\u000e' || c >= '\u000f' && c <= '\u001f') { + throw new StreamException("Invalid character 0x" + + Integer.toHexString(c) + + " in XML 1.0 stream"); + } + } + if (mode != XML_QUIRKS) { + if (c == '\ufffe' || c == '\uffff') { + throw new StreamException("Invalid character 0x" + + Integer.toHexString(c) + + " in XML stream"); + } + } + writer.write("&#x"); + writer.write(Integer.toHexString(c)); + writer.write(';'); + } + } + }); + } + + @Override + public void endNode() { + depth--; + if (tagIsEmpty) { + writer.write('/'); + readyForNewLine = false; + finishTag(); + elementStack.popSilently(); + } else { + finishTag(); + writer.write(CLOSE); + writer.write((String) elementStack.pop()); + writer.write('>'); + } + readyForNewLine = true; + if (depth == 0) { + writer.flush(); + } + } + + private void finishTag() { + if (tagInProgress) { + writer.write('>'); + } + tagInProgress = false; + if (readyForNewLine) { + endOfLine(); + } + readyForNewLine = false; + tagIsEmpty = false; + } + + protected void endOfLine() { + writer.write(getNewLine()); + for (int i = 0; i < depth; i++) { + writer.write(lineIndenter); + } + } + + @Override + public void flush() { + writer.flush(); + } + + @Override + public void close() { + writer.close(); + } + + /** + * Retrieve the line terminator. This method returns always a line feed, since according the XML specification any + * parser must ignore a carriage return. Overload this method, if you need different behavior. + * + * @return the line terminator + * @since 1.3 + */ + protected String getNewLine() { + return "\n"; + } +} diff --git a/core/src/main/java/hudson/util/XStream2.java b/core/src/main/java/hudson/util/XStream2.java index 8da629f5131e..355c5001545f 100644 --- a/core/src/main/java/hudson/util/XStream2.java +++ b/core/src/main/java/hudson/util/XStream2.java @@ -46,7 +46,6 @@ import com.thoughtworks.xstream.io.HierarchicalStreamReader; import com.thoughtworks.xstream.io.HierarchicalStreamWriter; import com.thoughtworks.xstream.io.ReaderWrapper; -import com.thoughtworks.xstream.io.xml.PrettyPrintWriter; import com.thoughtworks.xstream.io.xml.StandardStaxDriver; import com.thoughtworks.xstream.mapper.CannotResolveClassException; import com.thoughtworks.xstream.mapper.Mapper; @@ -138,8 +137,7 @@ private static class StaxDriver extends StandardStaxDriver { @Override public HierarchicalStreamWriter createWriter(Writer out) { - // TODO JENKINS-71182 StaxWriter will not indent, XML_1_1 does not support emojis, and XML_QUIRKS writes unreadable "�", so 🤷 pick your poison - return new PrettyPrintWriter(out, PrettyPrintWriter.XML_QUIRKS, getNameCoder()); + return new PrettyPrintWriter(out, PrettyPrintWriter.XML_1_1, getNameCoder()); } @Override diff --git a/core/src/test/java/hudson/util/XStream2Test.java b/core/src/test/java/hudson/util/XStream2Test.java index ce6ac836d61c..c1ce9e536a2b 100644 --- a/core/src/test/java/hudson/util/XStream2Test.java +++ b/core/src/test/java/hudson/util/XStream2Test.java @@ -28,12 +28,12 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.instanceOf; -import static org.hamcrest.Matchers.not; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; @@ -43,12 +43,10 @@ import hudson.Functions; import hudson.model.Result; import hudson.model.Run; -import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.InputStream; import java.io.StringReader; import java.io.StringWriter; -import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -617,24 +615,12 @@ public void writeEmoji() throws Exception { @Test public void nullsWithoutEncodingDeclaration() throws Exception { Bar b = new Bar(); - String text = "x\u0000y"; - b.s = text; - StringWriter w = new StringWriter(); - XStream2 xs = new XStream2(); + b.s = "x\u0000y"; try { - xs.toXML(b, w); + new XStream2().toXML(b, new StringWriter()); + fail("expected to fail fast; not supported to read either"); } catch (RuntimeException x) { assertThat("cause is com.thoughtworks.xstream.io.StreamException: Invalid character 0x0 in XML stream", Functions.printThrowable(x), containsString("0x0")); - return; // not supported to read either - } - String xml = w.toString(); - assertThat(xml, not(containsString("version=\"1.1\""))); - System.out.println(xml); - try { - b = (Bar) xs.fromXML(xml); - assertEquals(text, b.s); - } catch (RuntimeException x) { - assertThat("cause is XMLStreamException: ParseError at [row,col]:[2,13] Message: The reference to entity \"y\" must end with the ';' delimiter.", Functions.printThrowable(x), containsString("XMLStreamException: ParseError")); } } @@ -642,24 +628,12 @@ public void nullsWithoutEncodingDeclaration() throws Exception { @Test public void nullsWithEncodingDeclaration() throws Exception { Bar b = new Bar(); - String text = "x\u0000y"; - b.s = text; - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - XStream2 xs = new XStream2(); + b.s = "x\u0000y"; try { - xs.toXMLUTF8(b, baos); + new XStream2().toXMLUTF8(b, new ByteArrayOutputStream()); + fail("expected to fail fast; not supported to read either"); } catch (RuntimeException x) { assertThat("cause is com.thoughtworks.xstream.io.StreamException: Invalid character 0x0 in XML stream", Functions.printThrowable(x), containsString("0x0")); - return; // not supported to read either - } - String xml = baos.toString(StandardCharsets.UTF_8); - System.out.println(xml); - assertThat(xml, containsString("version=\"1.1\"")); - try { - b = (Bar) xs.fromXML(new ByteArrayInputStream(baos.toByteArray())); - assertEquals(text, b.s); - } catch (RuntimeException x) { - assertThat("cause is XMLStreamException: ParseError at [row,col]:[2,13] Message: The reference to entity \"y\" must end with the ';' delimiter.", Functions.printThrowable(x), containsString("XMLStreamException: ParseError")); } } From 6b5728c30fcce4b78af7ef1dba470e57b2191e84 Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Wed, 3 May 2023 11:39:05 -0400 Subject: [PATCH 3/4] `implements ExtendedHierarchicalStreamWriter` did not actually need to be added --- core/src/main/java/hudson/util/PrettyPrintWriter.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/core/src/main/java/hudson/util/PrettyPrintWriter.java b/core/src/main/java/hudson/util/PrettyPrintWriter.java index 152f3c519d82..bad67e0191b9 100644 --- a/core/src/main/java/hudson/util/PrettyPrintWriter.java +++ b/core/src/main/java/hudson/util/PrettyPrintWriter.java @@ -16,7 +16,6 @@ import com.thoughtworks.xstream.core.util.FastStack; import com.thoughtworks.xstream.core.util.QuickWriter; -import com.thoughtworks.xstream.io.ExtendedHierarchicalStreamWriter; import com.thoughtworks.xstream.io.StreamException; import com.thoughtworks.xstream.io.naming.NameCoder; import com.thoughtworks.xstream.io.xml.AbstractXmlWriter; @@ -53,7 +52,7 @@ * @author Joe Walnes * @author Jörg Schaible */ -public class PrettyPrintWriter extends AbstractXmlWriter implements ExtendedHierarchicalStreamWriter { +public class PrettyPrintWriter extends AbstractXmlWriter { public static int XML_QUIRKS = -1; public static int XML_1_0 = 0; From ce785312796a883c329de81324e23070b04057f8 Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Wed, 3 May 2023 13:55:08 -0400 Subject: [PATCH 4/4] Drop `public` from `PrettyPrintWriter` https://github.com/jenkinsci/jenkins/pull/7924#discussion_r1183898639 --- .../java/hudson/util/PrettyPrintWriter.java | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/core/src/main/java/hudson/util/PrettyPrintWriter.java b/core/src/main/java/hudson/util/PrettyPrintWriter.java index bad67e0191b9..4ad71f0f5959 100644 --- a/core/src/main/java/hudson/util/PrettyPrintWriter.java +++ b/core/src/main/java/hudson/util/PrettyPrintWriter.java @@ -52,7 +52,7 @@ * @author Joe Walnes * @author Jörg Schaible */ -public class PrettyPrintWriter extends AbstractXmlWriter { +class PrettyPrintWriter extends AbstractXmlWriter { public static int XML_QUIRKS = -1; public static int XML_1_0 = 0; @@ -80,7 +80,7 @@ public class PrettyPrintWriter extends AbstractXmlWriter { /** * @since 1.4 */ - public PrettyPrintWriter(final Writer writer, final int mode, final char[] lineIndenter, final NameCoder nameCoder) { + PrettyPrintWriter(final Writer writer, final int mode, final char[] lineIndenter, final NameCoder nameCoder) { super(nameCoder); this.writer = new QuickWriter(writer); this.lineIndenter = lineIndenter; @@ -95,7 +95,7 @@ public PrettyPrintWriter(final Writer writer, final int mode, final char[] lineI * @deprecated As of 1.4 use {@link PrettyPrintWriter#PrettyPrintWriter(Writer, int, char[], NameCoder)} instead */ @Deprecated - public PrettyPrintWriter( + PrettyPrintWriter( final Writer writer, final int mode, final char[] lineIndenter, final XmlFriendlyReplacer replacer) { this(writer, mode, lineIndenter, (NameCoder) replacer); } @@ -103,29 +103,29 @@ public PrettyPrintWriter( /** * @since 1.3 */ - public PrettyPrintWriter(final Writer writer, final int mode, final char[] lineIndenter) { + PrettyPrintWriter(final Writer writer, final int mode, final char[] lineIndenter) { this(writer, mode, lineIndenter, new XmlFriendlyNameCoder()); } - public PrettyPrintWriter(final Writer writer, final char[] lineIndenter) { + PrettyPrintWriter(final Writer writer, final char[] lineIndenter) { this(writer, XML_QUIRKS, lineIndenter); } /** * @since 1.3 */ - public PrettyPrintWriter(final Writer writer, final int mode, final String lineIndenter) { + PrettyPrintWriter(final Writer writer, final int mode, final String lineIndenter) { this(writer, mode, lineIndenter.toCharArray()); } - public PrettyPrintWriter(final Writer writer, final String lineIndenter) { + PrettyPrintWriter(final Writer writer, final String lineIndenter) { this(writer, lineIndenter.toCharArray()); } /** * @since 1.4 */ - public PrettyPrintWriter(final Writer writer, final int mode, final NameCoder nameCoder) { + PrettyPrintWriter(final Writer writer, final int mode, final NameCoder nameCoder) { this(writer, mode, new char[]{' ', ' '}, nameCoder); } @@ -134,14 +134,14 @@ public PrettyPrintWriter(final Writer writer, final int mode, final NameCoder na * @deprecated As of 1.4 use {@link PrettyPrintWriter#PrettyPrintWriter(Writer, int, NameCoder)} instead */ @Deprecated - public PrettyPrintWriter(final Writer writer, final int mode, final XmlFriendlyReplacer replacer) { + PrettyPrintWriter(final Writer writer, final int mode, final XmlFriendlyReplacer replacer) { this(writer, mode, new char[]{' ', ' '}, replacer); } /** * @since 1.4 */ - public PrettyPrintWriter(final Writer writer, final NameCoder nameCoder) { + PrettyPrintWriter(final Writer writer, final NameCoder nameCoder) { this(writer, XML_QUIRKS, new char[]{' ', ' '}, nameCoder); } @@ -149,18 +149,18 @@ public PrettyPrintWriter(final Writer writer, final NameCoder nameCoder) { * @deprecated As of 1.4 use {@link PrettyPrintWriter#PrettyPrintWriter(Writer, NameCoder)} instead. */ @Deprecated - public PrettyPrintWriter(final Writer writer, final XmlFriendlyReplacer replacer) { + PrettyPrintWriter(final Writer writer, final XmlFriendlyReplacer replacer) { this(writer, XML_QUIRKS, new char[]{' ', ' '}, replacer); } /** * @since 1.3 */ - public PrettyPrintWriter(final Writer writer, final int mode) { + PrettyPrintWriter(final Writer writer, final int mode) { this(writer, mode, new char[]{' ', ' '}); } - public PrettyPrintWriter(final Writer writer) { + PrettyPrintWriter(final Writer writer) { this(writer, new char[]{' ', ' '}); }