[core] Fix XMLRenderer with UTF-16

When using UTF-16 as encoding, the XMLRenderer produced
invalid XML: When inserting the linebreak encoded with the
given encoding "UTF-16", a BOM was created. This inserted additional
characters U+FEFF in the middle of the file, which is not allowed.

A partial workaround for this issue would be, to use "UTF-16BE" as
encoding instead. This doesn't create a BOM. However, the resulting XML
file is then completely without a BOM.
This commit is contained in:
Andreas Dangel committed 2020-10-26 19:19:57 +01:00
1 parent 40cc666507
commit 91936b000b
2 files changed
+30 -1

No files matched your search

@@ -10,6 +10,8 @@ import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.text.SimpleDateFormat;
import java.util.Date;
@@ -66,7 +68,8 @@ public class XMLRenderer extends AbstractIncrementingRenderer {
@Override
public void start() throws IOException {
String encoding = getProperty(ENCODING);
lineSeparator = PMD.EOL.getBytes(encoding);
String unmarkedEncoding = toUnmarkedEncoding(encoding);
lineSeparator = PMD.EOL.getBytes(unmarkedEncoding);
try {
xmlWriter.writeStartDocument(encoding, "1.0");
@@ -86,6 +89,26 @@ public class XMLRenderer extends AbstractIncrementingRenderer {
}
}
/**
* Return a encoding, which doesn't write a BOM (byte order mark).
* Only UTF-16 encoders might write a BOM, see {@link Charset}.
*
* <p>This is needed, so that we don't accidentally add BOMs whenever
* we insert a newline.
*
* @return
*/
private static String toUnmarkedEncoding(String encoding) {
if (StandardCharsets.UTF_16.name().equalsIgnoreCase(encoding)) {
return StandardCharsets.UTF_16BE.name();
}
// edge case: UTF-16LE with BOM
if ("UTF-16LE_BOM".equalsIgnoreCase(encoding)) {
return StandardCharsets.UTF_16LE.name();
}
return encoding;
}
/**
* Outputs a platform dependent line separator.
*
@@ -123,6 +123,12 @@ public class XMLRendererTest extends AbstractRendererTest {
verifyXmlEscaping(renderer, "\ud801\udc1c", StandardCharsets.UTF_8);
}
@Test
public void testXMLEscapingWithUTF16() throws Exception {
Renderer renderer = getRenderer();
verifyXmlEscaping(renderer, "&#x1041c;", StandardCharsets.UTF_16);
}
@Test
public void testXMLEscapingWithoutUTF8() throws Exception {
Renderer renderer = getRenderer();