diff --git a/src/main/java/org/codehaus/plexus/util/xml/pull/MXSerializer.java b/src/main/java/org/codehaus/plexus/util/xml/pull/MXSerializer.java index e69d28f..973c076 100644 --- a/src/main/java/org/codehaus/plexus/util/xml/pull/MXSerializer.java +++ b/src/main/java/org/codehaus/plexus/util/xml/pull/MXSerializer.java @@ -853,95 +853,36 @@ public void flush() throws IOException { // --- utility methods protected void writeAttributeValue(String value, Writer out) throws IOException { - // .[apostrophe and <, & escaped], - final char quot = attributeUseApostrophe ? '\'' : '"'; - final String quotEntity = attributeUseApostrophe ? "'" : """; - - int pos = 0; - for (int i = 0; i < value.length(); i++) { - char ch = value.charAt(i); - if (ch == '&') { - if (i > pos) out.write(value.substring(pos, i)); - out.write("&"); - pos = i + 1; - } - if (ch == '<') { - if (i > pos) out.write(value.substring(pos, i)); - out.write("<"); - pos = i + 1; - } else if (ch == quot) { - if (i > pos) out.write(value.substring(pos, i)); - out.write(quotEntity); - pos = i + 1; - } else if (ch < 32) { - // in XML 1.0 only legal character are #x9 | #xA | #xD - // and they must be escaped otherwise in attribute value they are normalized to spaces - if (ch == 13 || ch == 10 || ch == 9) { - if (i > pos) out.write(value.substring(pos, i)); - out.write("&#"); - out.write(Integer.toString(ch)); - out.write(';'); - pos = i + 1; - } else { - throw new IllegalStateException( - "character " + Integer.toString(ch) + " is not allowed in output" + getLocation()); - // in XML 1.1 legal are [#x1-#xD7FF] - // if(ch > 0) { - // if(i > pos) out.write(text.substring(pos, i)); - // out.write("&#"); - // out.write(Integer.toString(ch)); - // out.write(';'); - // pos = i + 1; - // } else { - // throw new IllegalStateException( - // "character zero is not allowed in XML 1.1 output"+getLocation()); - // } - } - } - } - if (pos > 0) { - out.write(value.substring(pos)); - } else { - out.write(value); // this is shortcut to the most common case - } - } - - protected void writeElementContent(String text, Writer out) throws IOException { - // escape '<', '&', ']]>', <32 if necessary - int pos = 0; - for (int i = 0; i < text.length(); i++) { - // TODO: check if doing char[] text.getChars() would be faster than getCharAt(i) ... - char ch = text.charAt(i); - if (ch == ']') { - if (seenBracket) { - seenBracketBracket = true; - } else { - seenBracket = true; - } - } else { + if (value != null) { + // .[apostrophe and <, & escaped], + final char quot = attributeUseApostrophe ? '\'' : '"'; + final String quotEntity = attributeUseApostrophe ? "'" : """; + + int pos = 0; + for (int i = 0; i < value.length(); i++) { + char ch = value.charAt(i); if (ch == '&') { - if (i > pos) out.write(text.substring(pos, i)); + if (i > pos) out.write(value.substring(pos, i)); out.write("&"); pos = i + 1; - } else if (ch == '<') { - if (i > pos) out.write(text.substring(pos, i)); + } + if (ch == '<') { + if (i > pos) out.write(value.substring(pos, i)); out.write("<"); pos = i + 1; - } else if (seenBracketBracket && ch == '>') { - if (i > pos) out.write(text.substring(pos, i)); - out.write(">"); + } else if (ch == quot) { + if (i > pos) out.write(value.substring(pos, i)); + out.write(quotEntity); pos = i + 1; } else if (ch < 32) { // in XML 1.0 only legal character are #x9 | #xA | #xD - if (ch == 9 || ch == 10 || ch == 13) { - // pass through - - // } else if(ch == 13) { //escape - // if(i > pos) out.write(text.substring(pos, i)); - // out.write("&#"); - // out.write(Integer.toString(ch)); - // out.write(';'); - // pos = i + 1; + // and they must be escaped otherwise in attribute value they are normalized to spaces + if (ch == 13 || ch == 10 || ch == 9) { + if (i > pos) out.write(value.substring(pos, i)); + out.write("&#"); + out.write(Integer.toString(ch)); + out.write(';'); + pos = i + 1; } else { throw new IllegalStateException( "character " + Integer.toString(ch) + " is not allowed in output" + getLocation()); @@ -958,15 +899,78 @@ protected void writeElementContent(String text, Writer out) throws IOException { // } } } - if (seenBracket) { - seenBracketBracket = seenBracket = false; - } + } + if (pos > 0) { + out.write(value.substring(pos)); + } else { + out.write(value); // this is shortcut to the most common case } } - if (pos > 0) { - out.write(text.substring(pos)); - } else { - out.write(text); // this is shortcut to the most common case + } + + protected void writeElementContent(String text, Writer out) throws IOException { + if (text != null) { + // escape '<', '&', ']]>', <32 if necessary + int pos = 0; + for (int i = 0; i < text.length(); i++) { + // TODO: check if doing char[] text.getChars() would be faster than getCharAt(i) ... + char ch = text.charAt(i); + if (ch == ']') { + if (seenBracket) { + seenBracketBracket = true; + } else { + if (ch == '&') { + if (i > pos) + out.write(text.substring(pos, i)); + out.write("&"); + pos = i + 1; + } else if (ch == '<') { + if (i > pos) + out.write(text.substring(pos, i)); + out.write("<"); + pos = i + 1; + } else if (seenBracketBracket && ch == '>') { + if (i > pos) + out.write(text.substring(pos, i)); + out.write(">"); + pos = i + 1; + } else if (ch < 32) { + // in XML 1.0 only legal character are #x9 | #xA | #xD + if (ch == 9 || ch == 10 || ch == 13) { + // pass through + + // } else if(ch == 13) { //escape + // if(i > pos) out.write(text.substring(pos, i)); + // out.write("&#"); + // out.write(Integer.toString(ch)); + // out.write(';'); + // pos = i + 1; + } else { + throw new IllegalStateException("character " + Integer.toString(ch) + " is not allowed in output" + getLocation()); + // in XML 1.1 legal are [#x1-#xD7FF] + // if(ch > 0) { + // if(i > pos) out.write(text.substring(pos, i)); + // out.write("&#"); + // out.write(Integer.toString(ch)); + // out.write(';'); + // pos = i + 1; + // } else { + // throw new IllegalStateException( + // "character zero is not allowed in XML 1.1 output"+getLocation()); + // } + } + } + if (seenBracket) { + seenBracketBracket = seenBracket = false; + } + } + } + if (pos > 0) { + out.write(text.substring(pos)); + } else { + out.write(text); // this is shortcut to the most common case + } + } } } diff --git a/src/test/java/org/codehaus/plexus/util/xml/pull/MXSerializerTest.java b/src/test/java/org/codehaus/plexus/util/xml/pull/MXSerializerTest.java new file mode 100644 index 0000000..fb2e40e --- /dev/null +++ b/src/test/java/org/codehaus/plexus/util/xml/pull/MXSerializerTest.java @@ -0,0 +1,43 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.codehaus.plexus.util.xml.pull; + +import java.io.IOException; +import java.io.StringWriter; + +import org.junit.jupiter.api.Test; + +/** + * Tests {@link MXSerializer}. + */ +public class MXSerializerTest { + + /** + * Tests MJAVADOC-793. + */ + @Test + public void testNpe() throws IOException { + // should be no-ops + new MXSerializer().writeElementContent(null, null); + new MXSerializer().writeAttributeValue(null, null); + final StringWriter stringWriter = new StringWriter(); + new MXSerializer().writeElementContent(null, stringWriter); + new MXSerializer().writeAttributeValue(null, stringWriter); + } +}