TestRelaxNG.java
package wstxtest.vstream;
import java.io.StringWriter;
import javax.xml.stream.*;
import org.codehaus.stax2.*;
import org.codehaus.stax2.validation.*;
import com.ctc.wstx.sw.NonNsStreamWriter;
import com.ctc.wstx.sw.RepairingNsStreamWriter;
import com.ctc.wstx.sw.SimpleNsStreamWriter;
/**
* This is a simple base-line "smoke test" that checks that RelaxNG
* validation works at least minimally.
*/
public class TestRelaxNG
extends BaseValidationTest
{
protected final static String SIMPLE_RNG_SCHEMA =
"<element name='dict' xmlns='http://relaxng.org/ns/structure/1.0'>\n"
+" <oneOrMore>\n"
+" <element name='term'>\n"
+" <attribute name='type' />\n"
+" <optional>\n"
+" <attribute name='extra' />\n"
+" </optional>\n"
+" <element name='word'><text />\n"
+" </element>\n"
+" <element name='description'> <text />\n"
+" </element>\n"
+" </element>\n"
+" </oneOrMore>\n"
+"</element>"
;
/**
* Similar schema, but one that uses namespaces
*/
final static String SIMPLE_RNG_NS_SCHEMA =
"<element xmlns='http://relaxng.org/ns/structure/1.0' name='root'>\n"
+" <zeroOrMore>\n"
+" <element name='ns:leaf' xmlns:ns='http://test'>\n"
+" <optional>\n"
+" <attribute name='attr1' />\n"
+" </optional>\n"
+" <optional>\n"
+" <attribute name='ns:attr2' />\n"
+" </optional>\n"
+" <text />\n"
+" </element>\n"
+" </zeroOrMore>\n"
+"</element>"
;
/**
* Test validation against a simple document valid according
* to a simple RNG schema.
*/
public void testSimpleNonNs()
throws XMLStreamException
{
String XML =
"<?xml version='1.0'?>"
+"<dict>\n"
+" <term type=\"name\">\n"
+" <word>foobar</word>\n"
+" <description>Foo Bar</description>\n"
+" </term>"
+" <term type=\"word\" extra=\"123\">\n"
+" <word>fuzzy</word>\n"
+" <description>adjective</description>\n"
+" </term>"
+"</dict>"
;
XMLValidationSchema schema = parseRngSchema(SIMPLE_RNG_SCHEMA);
for (ValidationMode mode : ValidationMode.values()) {
mode.validate(schema, XML);
}
}
/**
* This unit test checks for couple of simple validity problems
* against the simple rng schema. It does not use namespaces
* (a separate test is needed for ns handling).
*/
public void testInvalidNonNs()
throws XMLStreamException
{
XMLValidationSchema schema = parseRngSchema(SIMPLE_RNG_SCHEMA);
// First, wrong root element:
String XML = "<term type='x'>\n"
+" <word>foobar</word>\n"
+" <description>Foo Bar</description>\n"
+"</term>";
verifyFailure(XML, schema, "wrong root element",
"is not allowed. Possible tag names are");
// Then, wrong child ordering:
XML = "<dict>\n"
+"<term type='x'>\n"
+" <description>Foo Bar</description>\n"
+" <word>foobar</word>\n"
+"</term></dict>";
verifyFailure(XML, schema, "illegal child element ordering",
"tag name \"description\" is not allowed. Possible tag names are");
// Then, missing children:
XML = "<dict>\n"
+"<term type='x'>\n"
+"</term></dict>";
verifyFailure(XML, schema, "missing children",
"uncompleted content model. expecting: <word>");
XML = "<dict>\n"
+"<term type='x'>\n"
+"<word>word</word>"
+"</term></dict>";
verifyFailure(XML, schema, "incomplete children",
"uncompleted content model. expecting: <description>");
// Then illegal text in non-mixed element
XML = "<dict>\n"
+"<term type='x'>No text allowed here"
+" <word>foobar</word>\n"
+" <description>Foo Bar</description>\n"
+"</term></dict>";
verifyFailure(XML, schema, "invalid non-whitespace text",
"Element <term> has non-mixed content specification; can not contain non-white space text");
// missing attribute
XML = "<dict>\n"
+"<term>"
+" <word>foobar</word>\n"
+" <description>Foo Bar</description>\n"
+"</term></dict>";
// Then undeclared attributes
XML = "<dict>\n"
+"<term attr='value' type='x'>"
+" <word>foobar</word>\n"
+" <description>Foo Bar</description>\n"
+"</term></dict>";
verifyFailure(XML, schema, "undeclared attribute",
"unexpected attribute \"attr\"");
XML = "<dict>\n"
+"<term type='x'>"
+" <word type='noun'>foobar</word>\n"
+" <description>Foo Bar</description>\n"
+"</term></dict>";
verifyFailure(XML, schema, "undeclared attribute",
"unexpected attribute \"type\"");
}
public void testSimpleNs()
throws XMLStreamException
{
String XML = "<root>\n"
+" <myns:leaf xmlns:myns=\"http://test\" attr1=\"123\"/>\n"
+" <ns2:leaf xmlns:ns2=\"http://test\" ns2:attr2=\"123\"/>\n"
+"</root>"
;
XMLValidationSchema schema = parseRngSchema(SIMPLE_RNG_NS_SCHEMA);
for (ValidationMode mode : ValidationMode.values()) {
mode.validate(schema, XML);
}
}
/**
* Unit test checks that the namespace matching works as
* expected.
*/
public void testInvalidNs()
throws XMLStreamException
{
XMLValidationSchema schema = parseRngSchema(SIMPLE_RNG_NS_SCHEMA);
// First, wrong root element:
String XML = "<root xmlns='http://test'>\n"
+"<leaf />\n"
+"</root>";
verifyFailure(XML, schema, "wrong root element",
"namespace URI of tag \"root\" is wrong");
// Wrong child namespace
XML = "<root>\n"
+"<leaf xmlns='http://other' />\n"
+"</root>";
verifyFailure(XML, schema, "wrong child element namespace",
"namespace URI of tag \"leaf\" is wrong.");
// Wrong attribute namespace
XML = "<root>\n"
+"<ns:leaf xmlns:ns='http://test' ns:attr1='123' />\n"
+"</root>";
verifyFailure(XML, schema, "wrong attribute namespace",
"unexpected attribute \"attr1\"");
}
/**
* This unit test verifies that the validation can be stopped
* half-way through processing, so that sub-trees (for example)
* can be validated. In this case, we will verify this functionality
* by trying to validate invalid document up to the point where it
* is (or may) still be valid, stop validation, and then continue
* reading. This should not result in an exception.
*/
public void testSimplePartialNonNs()
throws XMLStreamException
{
String XML =
"<?xml version='1.0'?>"
+"<dict>"
+"<term type=\"name\"><invalid/>"
+"</term>"
+"</dict>"
;
XMLValidationSchema schema = parseRngSchema(SIMPLE_RNG_SCHEMA);
for (StopValidatingMethod method : StopValidatingMethod.values()) {
{
StringWriter writer = new StringWriter();
SimpleNsStreamWriter sw = (SimpleNsStreamWriter) constructStreamWriter(writer, true, false);
_testSimplePartialNonNS(XML, schema, sw, writer, method);
}
{
StringWriter writer = new StringWriter();
RepairingNsStreamWriter sw = (RepairingNsStreamWriter) constructStreamWriter(writer, true, true);
_testSimplePartialNonNS(XML, schema, sw, writer, method);
}
{
StringWriter writer = new StringWriter();
NonNsStreamWriter sw = (NonNsStreamWriter) constructStreamWriter(writer, false, false);
_testSimplePartialNonNS(XML, schema, sw, writer, method);
}
}
}
private enum StopValidatingMethod {schema, validator}
private void _testSimplePartialNonNS(String XML, XMLValidationSchema schema, XMLStreamWriter2 sw,
StringWriter writer, StopValidatingMethod method) throws XMLStreamException {
XMLStreamReader2 sr = getReader(XML);
XMLValidator vtor = sr.validateAgainst(schema);
XMLValidator wVtor = sw.validateAgainst(schema);
sw.copyEventFromReader(sr, false);
assertTokenType(START_ELEMENT, sr.next());
assertEquals("dict", sr.getLocalName());
sw.copyEventFromReader(sr, false);
assertTokenType(START_ELEMENT, sr.next());
assertEquals("term", sr.getLocalName());
sw.copyEventFromReader(sr, false);
/* So far so good; but here we'd get an exception... so
* let's stop validating
*/
switch (method) {
case schema:
assertSame(vtor, sr.stopValidatingAgainst(schema));
assertSame(wVtor, sw.stopValidatingAgainst(schema));
break;
case validator:
assertSame(vtor, sr.stopValidatingAgainst(vtor));
assertSame(wVtor, sw.stopValidatingAgainst(wVtor));
break;
default:
throw new IllegalStateException();
}
try {
// And should be good to go
assertTokenType(START_ELEMENT, sr.next());
assertEquals("invalid", sr.getLocalName());
sw.copyEventFromReader(sr, false);
assertTokenType(END_ELEMENT, sr.next());
assertEquals("invalid", sr.getLocalName());
sw.copyEventFromReader(sr, false);
assertTokenType(END_ELEMENT, sr.next());
assertEquals("term", sr.getLocalName());
sw.copyEventFromReader(sr, false);
assertTokenType(END_ELEMENT, sr.next());
assertEquals("dict", sr.getLocalName());
sw.copyEventFromReader(sr, false);
assertTokenType(END_DOCUMENT, sr.next());
sw.copyEventFromReader(sr, false);
} catch (XMLValidationException vex) {
fail("Did not expect validation exception after stopping validation, got: "+vex);
}
sr.close();
sw.close();
assertEquals(XML, writer.toString());
}
public void testSimpleEnumAttr()
throws XMLStreamException
{
final String schemaDef =
"<element xmlns='http://relaxng.org/ns/structure/1.0' name='root'>\n"
+" <attribute name='enumAttr'>\n"
+" <choice>\n"
+" <value>value1</value>\n"
+" <value>another</value>\n"
+" <value>42</value>\n"
+" </choice>\n"
+" </attribute>\n"
+"</element>"
;
XMLValidationSchema schema = parseRngSchema(schemaDef);
// First, simple valid document
String XML = "<root enumAttr=\"another\"/>";
for (ValidationMode mode : ValidationMode.values()) {
mode.validate(schema, XML);
}
// And then invalid one, with unrecognized value
XML = "<root enumAttr='421' />";
verifyFailure(XML, schema, "enum attribute with unknown value",
"attribute \"enumAttr\" has a bad value");
}
/**
* Test case for testing handling ID/IDREF/IDREF attributes, using
* schema datatype library.
*/
public void testSimpleIdAttrs()
throws XMLStreamException
{
final String schemaDef =
"<element xmlns='http://relaxng.org/ns/structure/1.0'"
+" datatypeLibrary='http://www.w3.org/2001/XMLSchema-datatypes' name='root'>\n"
+" <oneOrMore>\n"
+" <element name='leaf'>\n"
+" <attribute name='id'><data type='ID' /></attribute>\n"
+" <optional>\n"
+" <attribute name='ref'><data type='IDREF' /></attribute>\n"
+" </optional>\n"
+" <optional>\n"
+" <attribute name='refs'><data type='IDREFS' /></attribute>\n"
+" </optional>\n"
+" </element>\n"
+" </oneOrMore>\n"
+"</element>"
;
XMLValidationSchema schema = parseRngSchema(schemaDef);
// First, a simple valid document
String XML = "<root>"
+" <leaf id=\"first\" ref=\"second\"/>\n"
+" <leaf id=\"second\" ref=\"third\"/>\n"
+" <leaf id=\"third\" refs=\"first second third\"/>\n"
+"</root>"
;
for (ValidationMode mode : ValidationMode.values()) {
mode.validate(schema, XML);
}
// Then one with malformed id
XML = "<root><leaf id='123invalidid' /></root>";
verifyFailure(XML, schema, "malformed id",
"attribute \"id\" has a bad value");
// Then with malformed IDREF value (would be valid IDREFS)
XML = "<root>"
+" <leaf id='a' ref='a c' />\n"
+" <leaf id='c' />\n"
+"</root>"
;
verifyFailure(XML, schema, "malformed id",
"attribute \"ref\" has a bad value");
// And then invalid one, with dangling ref
XML = "<root>"
+" <leaf id='a' ref='second' />\n"
+"</root>"
;
verifyFailure(XML, schema, "reference to undefined id",
"Undefined ID", true, true, false);
// and another one with some of refs undefined
XML = "<root>"
+" <leaf refs='this other' id='this' />\n"
+"</root>"
;
verifyFailure(XML, schema, "reference to undefined id",
"Undefined ID", true, true, false);
}
public void testSimpleIntAttr()
throws XMLStreamException
{
final String schemaDef =
"<element xmlns='http://relaxng.org/ns/structure/1.0'"
+" datatypeLibrary='http://www.w3.org/2001/XMLSchema-datatypes' name='root'>\n"
+" <element name='leaf'>\n"
+" <attribute name='nr'><data type='integer' /></attribute>\n"
+" </element>\n"
+"</element>"
;
XMLValidationSchema schema = parseRngSchema(schemaDef);
// First, a simple valid document
String XML = "<root><leaf nr=\" 123 \"/></root>";
for (ValidationMode mode : ValidationMode.values()) {
mode.validate(schema, XML);
}
// Then one with invalid element value
verifyFailure("<root><leaf nr='12.03' /></root>",
schema, "invalid integer attribute value",
"does not satisfy the \"integer\" type");
// And missing attribute
verifyFailure("<root><leaf /></root>",
schema, "missing integer attribute value",
"is missing \"nr\" attribute");
// And then two variations of having empty value
verifyFailure("<root><leaf nr=\"\"/></root>",
schema, "missing integer attribute value",
"does not satisfy the \"integer\" type");
verifyFailure("<root><leaf nr='\r\n'/></root>",
schema, "missing integer attribute value",
"does not satisfy the \"integer\" type");
}
/**
* Another test, but one that verifies that empty tags do not
* cause problems with validation
*/
public void testSimpleBooleanElem2()
throws XMLStreamException
{
final String schemaDef =
"<element xmlns='http://relaxng.org/ns/structure/1.0'"
+" datatypeLibrary='http://www.w3.org/2001/XMLSchema-datatypes' name='root'>\n"
+" <element name='leaf1'><text /></element>\n"
+" <element name='leaf2'>\n"
+" <data type='boolean' />\n"
+" </element>\n"
+"</element>"
;
XMLValidationSchema schema = parseRngSchema(schemaDef);
// First, a simple valid document
String XML = "<root><leaf1>abc</leaf1><leaf2>true</leaf2></root>";
for (ValidationMode mode : ValidationMode.values()) {
mode.validate(schema, XML);
}
// Then another valid, but with empty tag for leaf1
XML = "<root><leaf1/><leaf2>false</leaf2></root>";
for (ValidationMode mode : ValidationMode.values()) {
mode.validate(schema, XML);
}
// And then one more invalid case
verifyFailure("<root><leaf1 /><leaf2>true false</leaf2></root>",
schema, "missing boolean element value",
"does not satisfy the \"boolean\" type");
}
/**
* And then a test for validating starting when stream points
* to START_ELEMENT
*/
public void testPartialValidationOk()
throws XMLStreamException
{
/* Hmmh... RelaxNG does define expected root. So need to
* wrap the doc...
*/
String XML =
"<dummy>\n"
+"<dict>\n"
+"<term type=\"name\">\n"
+" <word>foobar</word>\n"
+" <description>Foo Bar</description>\n"
+"</term></dict>\n"
+"</dummy>"
;
XMLValidationSchema schema = parseRngSchema(SIMPLE_RNG_SCHEMA);
{
StringWriter writer = new StringWriter();
SimpleNsStreamWriter sw = (SimpleNsStreamWriter) constructStreamWriter(writer, true, false);
_testPartialValidationOk(XML, schema, sw, writer);
}
{
StringWriter writer = new StringWriter();
NonNsStreamWriter sw = (NonNsStreamWriter) constructStreamWriter(writer, false, false);
_testPartialValidationOk(XML, schema, sw, writer);
}
}
protected void _testPartialValidationOk(String XML, XMLValidationSchema schema, XMLStreamWriter2 sw, StringWriter writer) throws XMLStreamException {
XMLStreamReader2 sr = getReader(XML);
assertTokenType(START_ELEMENT, sr.next());
sw.copyEventFromReader(sr, false);
sr.validateAgainst(schema);
sw.validateAgainst(schema);
while (sr.hasNext()) {
sr.next();
sw.copyEventFromReader(sr, false);
}
sr.close();
sw.close();
assertEquals(XML, writer.toString());
}
/*
//////////////////////////////////////////////////////////////
// Helper methods
//////////////////////////////////////////////////////////////
*/
private XMLStreamReader2 getReader(String contents) throws XMLStreamException
{
XMLInputFactory2 f = getInputFactory();
setValidating(f, false);
return constructStreamReader(f, contents);
}
}