escape ]]> in element content in OptimizedForSpeedSaver. Thanks to aizu-m. This closes #71 git-svn-id: https://svn.apache.org/repos/asf/xmlbeans/trunk@1935617 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/src/main/java/org/apache/xmlbeans/impl/store/Saver.java b/src/main/java/org/apache/xmlbeans/impl/store/Saver.java index 2a16750..2c1c5c7 100755 --- a/src/main/java/org/apache/xmlbeans/impl/store/Saver.java +++ b/src/main/java/org/apache/xmlbeans/impl/store/Saver.java
@@ -2074,10 +2074,11 @@ int cch = c._cchSrc; int off = c._offSrc; int index = 0; + int trailingBrackets = 0; while (index < cch) { int indexLimit = Math.min(index + 512, cch); CharUtil.getChars(_buf, 0, src, off + index, indexLimit - index); - entitizeAndWriteText(indexLimit - index); + trailingBrackets = entitizeAndWriteText(indexLimit - index, trailingBrackets); index = indexLimit; } } @@ -2113,7 +2114,7 @@ } } - private void entitizeAndWriteText(int bufLimit) { + private int entitizeAndWriteText(int bufLimit, int trailingBrackets) { int index = 0; for (int i = 0; i < bufLimit; i++) { char c = _buf[i]; @@ -2122,15 +2123,33 @@ emit(_buf, index, i - index); emit("<"); index = i + 1; + trailingBrackets = 0; break; case '&': emit(_buf, index, i - index); emit("&"); index = i + 1; + trailingBrackets = 0; + break; + case '>': + // ']]>' is not allowed in content, so escape the '>' that closes it + if (trailingBrackets >= 2) { + emit(_buf, index, i - index); + emit(">"); + index = i + 1; + } + trailingBrackets = 0; + break; + case ']': + trailingBrackets++; + break; + default: + trailingBrackets = 0; break; } } emit(_buf, index, bufLimit - index); + return trailingBrackets; } private void entitizeAndWriteCommentText(int bufLimit) {
diff --git a/src/test/java/misc/checkin/SaveOptimizeForSpeedTest.java b/src/test/java/misc/checkin/SaveOptimizeForSpeedTest.java index 7005ce7..2cb5d7f 100644 --- a/src/test/java/misc/checkin/SaveOptimizeForSpeedTest.java +++ b/src/test/java/misc/checkin/SaveOptimizeForSpeedTest.java
@@ -67,6 +67,38 @@ } @Test + void testCDataEndInText() throws Exception { + XmlObject o = XmlObject.Factory.parse("<root/>"); + try (XmlCursor cur = o.newCursor()) { + cur.toFirstChild(); + cur.toFirstContentToken(); + cur.insertChars("a]]>b"); + } + String out = saveForSpeed(o); + // ']]>' is forbidden in element content and must be escaped to ']]>' + assertFalse(out.contains("]]>")); + // before the fix the literal ']]>' makes this a fatal parse error + XmlObject.Factory.parse(out); + } + + @Test + void testCDataEndAcrossChunkBoundary() throws Exception { + // a ']]>' that straddles the 512-char chunk boundary: ']]' end the first + // chunk, '>' is the first char of the second. The trailing-bracket state + // has to carry across chunks or the '>' is emitted unescaped. + String data = repeat('x', 510) + "]]>" + repeat('y', 600); + XmlObject o = XmlObject.Factory.parse("<root/>"); + try (XmlCursor cur = o.newCursor()) { + cur.toFirstChild(); + cur.toFirstContentToken(); + cur.insertChars(data); + } + String out = saveForSpeed(o); + assertFalse(out.contains("]]>")); + XmlObject.Factory.parse(out); + } + + @Test void testProcInstTerminatorAcrossChunkBoundary() throws Exception { // a '?>' that straddles the 512-char chunk boundary: '?' is the last char // of the first chunk, '>' the first char of the second. The per-chunk