Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions release-notes/CREDITS
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ Konrad Windszus (kwin@github)

* Contributed fix for #96: woodstox-core.jar 6.0.2 uses JDK-11 removed internal APIs
(6.1.0)
* Reported #95: `BaseStreamWriter.writeRaw()` should not close open element
(7.2.0)

Daniel Kulp (dkulp@github)

Expand Down
5 changes: 5 additions & 0 deletions release-notes/VERSION
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ Project: woodstox

7.2.0 (not yet released)

#95: `BaseStreamWriter.writeRaw()` no longer closes an open start element
(NOTE: behavior change; existing callers that relied on the auto-close
must now emit an empty `writeCharacters("")` or other element-body call
before writing post-attribute content with `writeRaw()`)
(reported by Konrad W)
#113: `IllegalStateException` on closing tags; cannot recover from
unbalanced close tag when it is not preceded by whitespace
(reported by Joshua E)
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/com/ctc/wstx/api/ReaderConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -778,7 +778,7 @@ public Map<String,EntityDecl> getCustomInternalEntities()
}
// Better be defensive and just return a copy...
int len = custEnt.size();
HashMap<String,EntityDecl> m = new HashMap<String,EntityDecl>(len + (len >> 2), 0.81f);
HashMap<String,EntityDecl> m = new HashMap<>(len + (len >> 2), 0.81f);
for (Map.Entry<String,EntityDecl> me : custEnt.entrySet()) {
m.put(me.getKey(), me.getValue());
}
Expand Down
9 changes: 6 additions & 3 deletions src/main/java/com/ctc/wstx/api/WstxInputProperties.java
Original file line number Diff line number Diff line change
Expand Up @@ -245,16 +245,19 @@ public final class WstxInputProperties
public final static String P_MAX_ENTITY_COUNT = "com.ctc.wstx.maxEntityCount";

/**
* Maximum depth of nested (general parsed) entity expansions.
*
* Property of type {@link java.lang.Integer}:
* defines maximum allowed nesting of (general parsed) entity expansions.
*
* @since 4.3
*/
public final static String P_MAX_ENTITY_DEPTH = "com.ctc.wstx.maxEntityDepth";

// and yet more size constraints (4.3+)

/**
* Maximum level of nesting of XML elements, starting with root element.
* Property of type {@link java.lang.Integer}:
* defines maximum allowed level of nesting of DTD subsets (starting with the internal
* DTD subset).
*
* @since 5.4 / 6.4
*/
Expand Down
12 changes: 3 additions & 9 deletions src/main/java/com/ctc/wstx/sw/BaseStreamWriter.java
Original file line number Diff line number Diff line change
Expand Up @@ -1161,14 +1161,14 @@ public void writeStartDocument(String version, String encoding,
doWriteStartDocument(version, encoding, standAlone ? "yes" : "no");
}

// 13-May-2026, tatu: [woodstox-core#95] writeRaw() must NOT close an
// open start element, so that callers can use it to emit content
// (typically whitespace) between attributes within a start tag.
@Override
public void writeRaw(String text)
throws XMLStreamException
{
mAnyOutput = true;
if (mStartElementOpen) {
closeStartElement(mEmptyElement);
}
try {
mWriter.writeRaw(text, 0, text.length());
} catch (IOException ioe) {
Expand All @@ -1181,9 +1181,6 @@ public void writeRaw(String text, int start, int offset)
throws XMLStreamException
{
mAnyOutput = true;
if (mStartElementOpen) {
closeStartElement(mEmptyElement);
}
try {
mWriter.writeRaw(text, start, offset);
} catch (IOException ioe) {
Expand All @@ -1196,9 +1193,6 @@ public void writeRaw(char[] text, int start, int offset)
throws XMLStreamException
{
mAnyOutput = true;
if (mStartElementOpen) {
closeStartElement(mEmptyElement);
}
try {
mWriter.writeRaw(text, start, offset);
} catch (IOException ioe) {
Expand Down
56 changes: 56 additions & 0 deletions src/test/java/stax2/wstream/TestStreamWriter.java
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,10 @@ public void testRaw()
w.writeStartDocument();
w.writeStartElement("test");
w.writeAttribute("attr", "value");
// 13-May-2026, tatu: As of [woodstox-core#95], writeRaw() no longer
// closes an open start element; an empty writeCharacters() is the
// idiomatic way to commit the start tag before raw content.
w.writeCharacters("");
w.writeRaw("this or &apos;that&apos;");
char[] cbuf = new char[RAW2.length() + 10];
RAW2.getChars(0, RAW2.length(), cbuf, 3);
Expand Down Expand Up @@ -279,6 +283,58 @@ public void testRaw()
}
}

/**
* Unit test for [woodstox-core#95]: writeRaw() must not close an open
* start element, so it can be used to emit content (typically whitespace)
* between attributes within the start tag, e.g. for indenting attributes.
*/
public void testRawBetweenAttributes()
throws XMLStreamException
{
for (int i = 0; i < 3; ++i) {
boolean ns = (i > 0);
StringWriter strw = new StringWriter();
XMLStreamWriter2 w = (i == 2) ? getRepairingWriter(strw)
: getNonRepairingWriter(strw, ns);
w.writeStartDocument();
w.writeStartElement("test");
w.writeAttribute("a", "1");
// Raw whitespace between attributes must be allowed and must
// not close the start tag.
w.writeRaw("\n ");
w.writeAttribute("b", "2");
w.writeRaw("\n ");
w.writeAttribute("c", "3");
w.writeEndElement();
w.writeEndDocument();
w.close();

String xml = strw.toString();
// Verify the literal raw whitespace made it into the start tag,
// between the attributes. (The writer also prepends its own ' '
// before each attribute name, so the exact number of spaces after
// the newline is implementation detail; we just check the newline
// and indentation we asked for is present.)
assertTrue("Expected raw newline+indent between attributes, got: " + xml,
xml.contains("a=\"1\"\n ")
&& xml.contains("b=\"2\"\n "));

// And the result must still parse as well-formed XML with all
// three attributes present.
XMLStreamReader sr = constructNsStreamReader(xml, true);
assertTokenType(START_DOCUMENT, sr.getEventType());
assertTokenType(START_ELEMENT, sr.next());
assertEquals("test", sr.getLocalName());
assertEquals(3, sr.getAttributeCount());
assertEquals("1", sr.getAttributeValue(null, "a"));
assertEquals("2", sr.getAttributeValue(null, "b"));
assertEquals("3", sr.getAttributeValue(null, "c"));
assertTokenType(END_ELEMENT, sr.next());
assertTokenType(END_DOCUMENT, sr.next());
sr.close();
}
}

/*
//////////////////////////////////////////////////////////
// Then custom quoting/escaping writers
Expand Down