Author: fanningpj
Date: Wed Jun 24 12:49:22 2026
New Revision: 1935617
Log:
escape ]]> in element content in OptimizedForSpeedSaver. Thanks to aizu-m. This
closes #71
Modified:
xmlbeans/trunk/src/main/java/org/apache/xmlbeans/impl/store/Saver.java
xmlbeans/trunk/src/test/java/misc/checkin/SaveOptimizeForSpeedTest.java
Modified: xmlbeans/trunk/src/main/java/org/apache/xmlbeans/impl/store/Saver.java
==============================================================================
--- xmlbeans/trunk/src/main/java/org/apache/xmlbeans/impl/store/Saver.java
Wed Jun 24 12:13:28 2026 (r1935616)
+++ xmlbeans/trunk/src/main/java/org/apache/xmlbeans/impl/store/Saver.java
Wed Jun 24 12:49:22 2026 (r1935617)
@@ -2074,10 +2074,11 @@ abstract class Saver {
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 @@ abstract class Saver {
}
}
- 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 @@ abstract class Saver {
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) {
Modified:
xmlbeans/trunk/src/test/java/misc/checkin/SaveOptimizeForSpeedTest.java
==============================================================================
--- xmlbeans/trunk/src/test/java/misc/checkin/SaveOptimizeForSpeedTest.java
Wed Jun 24 12:13:28 2026 (r1935616)
+++ xmlbeans/trunk/src/test/java/misc/checkin/SaveOptimizeForSpeedTest.java
Wed Jun 24 12:49:22 2026 (r1935617)
@@ -67,6 +67,38 @@ public class SaveOptimizeForSpeedTest {
}
@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
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]