diff --git a/release-notes/VERSION b/release-notes/VERSION
index 10bcd42d..27008b36 100644
--- a/release-notes/VERSION
+++ b/release-notes/VERSION
@@ -10,6 +10,9 @@ Project: woodstox
5.3.1 (not yet released)
+#104: `NullPointerException` in `DTDValidator.validateElementEnd()`
+ for element undefined in DTD
+ (reported by ChrisTrenkamp@github)
#105: W3CSchemaFactory constructor incorrectly references relaxng
5.3.0 (15-Jul-2019)
diff --git a/src/main/java/com/ctc/wstx/dtd/DTDValidator.java b/src/main/java/com/ctc/wstx/dtd/DTDValidator.java
index d750320c..18862913 100644
--- a/src/main/java/com/ctc/wstx/dtd/DTDValidator.java
+++ b/src/main/java/com/ctc/wstx/dtd/DTDValidator.java
@@ -331,10 +331,10 @@ public int validateElementEnd(String localName, String uri, String prefix)
throws XMLStreamException
{
// First, let's remove the top:
- int ix = mElemCount-1;
- /* [WSTX-200]: need to avoid problems when doing sub-tree
- * validation...
- */
+ final int ix = mElemCount-1;
+
+ // [WSTX-200]: need to avoid problems when doing sub-tree
+ // validation...
if (ix < 0) {
return XMLValidator.CONTENT_ALLOW_WS;
}
@@ -359,7 +359,13 @@ public int validateElementEnd(String localName, String uri, String prefix)
// doesn't really matter; epilog/prolog differently handled:
return XMLValidator.CONTENT_ALLOW_WS;
}
- return mElems[ix-1].getAllowedContent();
+
+ final DTDElement element = mElems[ix-1];
+
+ // 22-Apr-2020, tatu: `null` validator added for unknown elements
+ return (element == null)
+ ? XMLValidator.CONTENT_ALLOW_ANY_TEXT
+ : element.getAllowedContent();
}
//public void validateText(String text, boolean lastTextSegment) ;
diff --git a/src/test/java/wstxtest/vstream/TestDTDErrorCollection104Test.java b/src/test/java/wstxtest/vstream/TestDTDErrorCollection104Test.java
new file mode 100644
index 00000000..739f42bf
--- /dev/null
+++ b/src/test/java/wstxtest/vstream/TestDTDErrorCollection104Test.java
@@ -0,0 +1,103 @@
+package wstxtest.vstream;
+
+import stax2.BaseStax2Test;
+
+import java.io.StringReader;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.xml.stream.*;
+
+import org.codehaus.stax2.XMLStreamReader2;
+import org.codehaus.stax2.validation.ValidationProblemHandler;
+import org.codehaus.stax2.validation.XMLValidationException;
+import org.codehaus.stax2.validation.XMLValidationProblem;
+import org.codehaus.stax2.validation.XMLValidationSchema;
+import org.codehaus.stax2.validation.XMLValidationSchemaFactory;
+
+public class TestDTDErrorCollection104Test
+ extends BaseStax2Test
+{
+ // [woodstox-core#103]
+ public void testValidationBeyondUnknownElement() throws Exception
+ {
+ final String DOC =
+ "\n";
+
+ final String INPUT_DTD =
+"\n"
++"\n"
+;
+
+ XMLInputFactory f = getInputFactory();
+ setCoalescing(f, true);
+
+ XMLValidationSchemaFactory schemaFactory =
+ XMLValidationSchemaFactory.newInstance(XMLValidationSchema.SCHEMA_ID_DTD);
+ XMLValidationSchema schema = schemaFactory.createSchema(new StringReader(INPUT_DTD));
+ XMLStreamReader2 sr = (XMLStreamReader2)f.createXMLStreamReader(
+ new StringReader(DOC));
+
+ final List probs = new ArrayList();
+
+ sr.validateAgainst(schema);
+ sr.setValidationProblemHandler(new ValidationProblemHandler() {
+ @Override
+ public void reportProblem(XMLValidationProblem problem)
+ throws XMLValidationException {
+ probs.add(problem);
+ }
+ });
+
+ assertTokenType(START_ELEMENT, sr.next());
+ assertEquals("map", sr.getLocalName());
+
+ sr.next(); // SPACE or CHARACTERS
+ assertTokenType(START_ELEMENT, sr.next());
+ assertEquals("val", sr.getLocalName());
+ assertEquals(1, probs.size());
+ assertEquals("Undefined element encountered",
+ probs.get(0).getMessage());
+
+ sr.next(); // SPACE or CHARACTERS
+ assertEquals(1, probs.size());
+
+ // From this point on, however, behavior bit unclear except
+ // that for DTD I guess we can at least check for undefined
+ // cases....
+
+ assertTokenType(START_ELEMENT, sr.next());
+ assertEquals("prop", sr.getLocalName());
+ // not defined either so:
+ assertEquals(2, probs.size());
+ assertEquals("Undefined element encountered",
+ probs.get(1).getMessage());
+
+ assertTokenType(END_ELEMENT, sr.next());
+ assertEquals("prop", sr.getLocalName());
+ assertEquals(2, probs.size());
+
+ sr.next(); // SPACE or CHARACTERS
+ assertEquals(2, probs.size());
+ assertTokenType(END_ELEMENT, sr.next());
+ assertEquals("val", sr.getLocalName());
+ assertEquals(2, probs.size());
+
+ sr.next(); // SPACE or CHARACTERS
+ assertTokenType(END_ELEMENT, sr.next());
+ assertEquals("map", sr.getLocalName());
+ assertEquals(3, probs.size());
+ assertEquals("Validation error, element : Expected at least one element ",
+ probs.get(2).getMessage());
+
+ // Plus content model now missing (s)
+ assertTokenType(END_DOCUMENT, sr.next());
+ assertEquals(3, probs.size());
+
+ sr.close();
+ }
+}