TestNmTokenAttrRead.java
package org.codehaus.stax.test.vstream;
import javax.xml.stream.*;
import org.junit.jupiter.api.Test;
/**
* Unit test suite that tests handling of attributes that are declared
* by DTD to be of type NMTOKEN or NMTOKENS; such information is only
* guranteed to be available in validation mode.
*/
public class TestNmTokenAttrRead
extends BaseVStreamTest
{
/*
///////////////////////////////////////
// Test cases
///////////////////////////////////////
*/
/**
* Test case that verifies behaviour of valid NMTOKEN/NMTOKENS
* attribute declarations.
*/
@Test
public void testValidNmTokenAttrDecl()
throws XMLStreamException
{
// Following should be ok
String XML = "<!DOCTYPE root [\n"
+"<!ELEMENT root EMPTY>\n"
+"<!ATTLIST root name NMTOKEN #IMPLIED\n"
+" names NMTOKENS #IMPLIED>\n"
+"]>\n<root />";
streamThrough(getValidatingReader(XML));
}
/**
* Test case that verifies behaviour of invalid NMTOKEN/NMTOKENS
* attribute declarations.
*/
@Test
public void testInvalidNmTokenAttrDecl()
throws XMLStreamException
{
// A default value whose FIRST character is not a valid NameChar must be
// rejected: an Nmtoken is (NameChar)+, with no start-char exception.
String XML = "<!DOCTYPE elem [\n"
+"<!ELEMENT elem EMPTY>\n"
+"<!ATTLIST elem name NMTOKEN '?foo'>\n"
+"]>\n<elem />";
streamThroughFailing(getValidatingReader(XML),
"invalid NMTOKEN default with illegal first char ('?')");
// Same for NMTOKENS: first token's leading char illegal
XML = "<!DOCTYPE elem [\n"
+"<!ELEMENT elem EMPTY>\n"
+"<!ATTLIST elem names NMTOKENS '/foo bar'>\n"
+"]>\n<elem />";
streamThroughFailing(getValidatingReader(XML),
"invalid NMTOKENS default with illegal first char ('/')");
}
@Test
public void testValidNmTokenAttrUse()
throws XMLStreamException
{
String XML = "<!DOCTYPE elem [\n"
+"<!ELEMENT elem (elem*)>\n"
+"<!ATTLIST elem name NMTOKEN #IMPLIED>\n"
+"<!ATTLIST elem names NMTOKENS #IMPLIED>\n"
+"]>\n<elem name='some-Name'> <elem names='a few names1' /> </elem>";
streamThrough(getValidatingReader(XML));
}
@Test
public void testInvalidNmTokenAttrUse()
throws XMLStreamException
{
// Error: invalid NMTOKEN, ? not valid
String XML = "<!DOCTYPE elem [\n"
+"<!ELEMENT elem (elem*)>\n"
+"<!ATTLIST elem name NMTOKEN #IMPLIED>\n"
+"]>\n<elem name='?foo'/>";
streamThroughFailing(getValidatingReader(XML),
"invalid char ('?') in NMTOKEN");
// Error: invalid NMTOKENS, / not valid
XML = "<!DOCTYPE elem [\n"
+"<!ELEMENT elem (elem*)>\n"
+"<!ATTLIST elem name NMTOKEN #IMPLIED>\n"
+"]>\n<elem name='foo foo/bar'/>";
streamThroughFailing(getValidatingReader(XML),
"invalid char ('/') in NMTOKENS");
}
/**
* Unit test for a declared NMTOKENS default value whose tokens are a
* single character long. The token-scanning loop in the NMTOKENS default
* validator used to skip the char right after a token start and could let
* its index run past the value length, throwing a raw
* StringIndexOutOfBoundsException (e.g. for default "x") instead of
* accepting the perfectly valid value -- and falsely rejecting values
* like "a b" whose first token is a single char.
*/
@Test
public void testNmTokensDefaultWithSingleCharTokens()
throws XMLStreamException
{
// Default with a single-char token (last token single char), plus
// extra spaces, to also exercise normalization/tokenization:
String XML = "<!DOCTYPE root [\n"
+"<!ELEMENT root (child)>\n"
+"<!ELEMENT child EMPTY>\n"
+"<!ATTLIST child a NMTOKENS 'x y z'>\n"
+"]>\n<root><child/></root>";
XMLStreamReader sr = getValidatingReader(XML);
assertTokenType(DTD, sr.next());
assertTokenType(START_ELEMENT, sr.next()); // root
assertTokenType(START_ELEMENT, sr.next()); // child (gets default)
assertEquals("child", sr.getLocalName());
assertEquals(1, sr.getAttributeCount());
assertEquals("a", sr.getAttributeLocalName(0));
// Single-char tokens must be preserved and space-normalized:
assertEquals("x y z", sr.getAttributeValue(0));
sr.close();
// A single-char-only default must be accepted (used to throw):
streamThrough(getValidatingReader("<!DOCTYPE root [\n"
+"<!ELEMENT root EMPTY>\n"
+"<!ATTLIST root a NMTOKENS 'x'>\n"
+"]>\n<root/>"));
// ...but an invalid token (illegal char, non-first position) is still
// rejected cleanly rather than crashing or being accepted:
streamThroughFailing(getValidatingReader("<!DOCTYPE root [\n"
+"<!ELEMENT root EMPTY>\n"
+"<!ATTLIST root a NMTOKENS 'ok bad/tok'>\n"
+"]>\n<root/>"),
"invalid NMTOKENS default value (illegal char '/')");
}
/**
* Unit test that verifies that values of attributes of type NMTOKEN and
* NMTOKENS will get properly normalized.
*/
@Test
public void testNmTokenAttrNormalization()
throws XMLStreamException
{
String XML = "<!DOCTYPE elem [\n"
+"<!ELEMENT elem (elem*, elem2?, elem3?)>\n"
+"<!ATTLIST elem name NMTOKEN #IMPLIED>\n"
+"<!ATTLIST elem names NMTOKENS #IMPLIED>\n"
+"<!ELEMENT elem2 EMPTY>\n"
+"<!ATTLIST elem2 name NMTOKEN 'somename '>\n"
+"<!ELEMENT elem3 EMPTY>\n"
+"<!ATTLIST elem3 names NMTOKENS 'name1\tname2 name3 '>\n"
+"]>"
+"<elem name='nmToken '>"
+"<elem name=' name' />"
+"<elem names='first_name \tsecond last' />"
+"<elem2 /><elem3 />"
+"</elem>";
;
XMLStreamReader sr = getValidatingReader(XML);
assertTokenType(DTD, sr.next());
assertTokenType(START_ELEMENT, sr.next());
assertEquals("elem", sr.getLocalName());
assertEquals(1, sr.getAttributeCount());
assertEquals("nmToken", sr.getAttributeValue(0));
assertTokenType(START_ELEMENT, sr.next());
assertEquals(1, sr.getAttributeCount());
assertEquals("name", sr.getAttributeValue(0));
assertTokenType(END_ELEMENT, sr.next());
assertTokenType(START_ELEMENT, sr.next());
assertEquals(1, sr.getAttributeCount());
assertEquals("first_name second last", sr.getAttributeValue(0));
assertTokenType(END_ELEMENT, sr.next());
// then the defaults
assertTokenType(START_ELEMENT, sr.next());
assertEquals("elem2", sr.getLocalName());
assertEquals(1, sr.getAttributeCount());
assertEquals("name", sr.getAttributeLocalName(0));
assertEquals("somename", sr.getAttributeValue(0));
assertTokenType(END_ELEMENT, sr.next());
assertTokenType(START_ELEMENT, sr.next());
assertEquals("elem3", sr.getLocalName());
assertEquals(1, sr.getAttributeCount());
assertEquals("names", sr.getAttributeLocalName(0));
assertEquals("name1 name2 name3", sr.getAttributeValue(0));
assertTokenType(END_ELEMENT, sr.next());
assertTokenType(END_ELEMENT, sr.next());
}
/*
////////////////////////////////////////
// Private methods
////////////////////////////////////////
*/
}