8176447: javax.xml.validation.Validator validates incorrectly on uniqueness constraint
Reviewed-by: lancea
This commit is contained in:
parent
365d1188bf
commit
5dcfefbae0
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||||
*/
|
*/
|
||||||
/*
|
/*
|
||||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
@ -37,6 +37,7 @@ import org.xml.sax.SAXException;
|
|||||||
* @xerces.internal
|
* @xerces.internal
|
||||||
*
|
*
|
||||||
* @author Andy Clark, IBM
|
* @author Andy Clark, IBM
|
||||||
|
* @LastModified: July 2019
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class XPathMatcher {
|
public class XPathMatcher {
|
||||||
@ -88,25 +89,25 @@ public class XPathMatcher {
|
|||||||
//
|
//
|
||||||
|
|
||||||
/** XPath location path. */
|
/** XPath location path. */
|
||||||
private XPath.LocationPath[] fLocationPaths;
|
private final XPath.LocationPath[] fLocationPaths;
|
||||||
|
|
||||||
/** True if XPath has been matched. */
|
/** True if XPath has been matched. */
|
||||||
private int[] fMatched;
|
private final int[] fMatched;
|
||||||
|
|
||||||
/** The matching string. */
|
/** The matching string. */
|
||||||
protected Object fMatchedString;
|
protected Object fMatchedString;
|
||||||
|
|
||||||
/** Integer stack of step indexes. */
|
/** Integer stack of step indexes. */
|
||||||
private IntStack[] fStepIndexes;
|
private final IntStack[] fStepIndexes;
|
||||||
|
|
||||||
/** Current step. */
|
/** Current step. */
|
||||||
private int[] fCurrentStep;
|
private final int[] fCurrentStep;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* No match depth. The value of this field will be zero while
|
* No match depth. The value of this field will be zero while
|
||||||
* matching is successful for the given xpath expression.
|
* matching is successful for the given xpath expression.
|
||||||
*/
|
*/
|
||||||
private int [] fNoMatchDepth;
|
private final int [] fNoMatchDepth;
|
||||||
|
|
||||||
final QName fQName = new QName();
|
final QName fQName = new QName();
|
||||||
|
|
||||||
@ -207,7 +208,7 @@ public class XPathMatcher {
|
|||||||
*
|
*
|
||||||
* @throws SAXException Thrown by handler to signal an error.
|
* @throws SAXException Thrown by handler to signal an error.
|
||||||
*/
|
*/
|
||||||
public void startElement(QName element, XMLAttributes attributes){
|
public void startElement(QName element, XMLAttributes attributes) {
|
||||||
if (DEBUG_METHODS2) {
|
if (DEBUG_METHODS2) {
|
||||||
System.out.println(toString()+"#startElement("+
|
System.out.println(toString()+"#startElement("+
|
||||||
"element={"+element+"},"+
|
"element={"+element+"},"+
|
||||||
@ -215,7 +216,7 @@ public class XPathMatcher {
|
|||||||
")");
|
")");
|
||||||
}
|
}
|
||||||
|
|
||||||
for(int i = 0; i < fLocationPaths.length; i++) {
|
for (int i = 0; i < fLocationPaths.length; i++) {
|
||||||
// push context
|
// push context
|
||||||
int startStep = fCurrentStep[i];
|
int startStep = fCurrentStep[i];
|
||||||
fStepIndexes[i].push(startStep);
|
fStepIndexes[i].push(startStep);
|
||||||
@ -284,9 +285,8 @@ public class XPathMatcher {
|
|||||||
if (DEBUG_MATCH) {
|
if (DEBUG_MATCH) {
|
||||||
System.out.println(toString()+" [CHILD] before");
|
System.out.println(toString()+" [CHILD] before");
|
||||||
}
|
}
|
||||||
if (nodeTest.type == XPath.NodeTest.QNAME) {
|
if (!matches(nodeTest, element)) {
|
||||||
if (!nodeTest.name.equals(element)) {
|
if (fCurrentStep[i] > descendantStep) {
|
||||||
if(fCurrentStep[i] > descendantStep) {
|
|
||||||
fCurrentStep[i] = descendantStep;
|
fCurrentStep[i] = descendantStep;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -296,17 +296,17 @@ public class XPathMatcher {
|
|||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
fCurrentStep[i]++;
|
fCurrentStep[i]++;
|
||||||
if (DEBUG_MATCH) {
|
if (DEBUG_MATCH) {
|
||||||
System.out.println(toString()+" [CHILD] after MATCHED!");
|
System.out.println(toString()+" [CHILD] after MATCHED!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (fCurrentStep[i] == steps.length) {
|
if (fCurrentStep[i] == steps.length) {
|
||||||
if(sawDescendant) {
|
if (sawDescendant) {
|
||||||
fCurrentStep[i] = descendantStep;
|
fCurrentStep[i] = descendantStep;
|
||||||
fMatched[i] = MATCHED_DESCENDANT;
|
fMatched[i] = MATCHED_DESCENDANT;
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
fMatched[i] = MATCHED;
|
fMatched[i] = MATCHED;
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
@ -324,8 +324,7 @@ public class XPathMatcher {
|
|||||||
|
|
||||||
for (int aIndex = 0; aIndex < attrCount; aIndex++) {
|
for (int aIndex = 0; aIndex < attrCount; aIndex++) {
|
||||||
attributes.getName(aIndex, fQName);
|
attributes.getName(aIndex, fQName);
|
||||||
if (nodeTest.type != XPath.NodeTest.QNAME ||
|
if (matches(nodeTest, fQName)) {
|
||||||
nodeTest.name.equals(fQName)) {
|
|
||||||
fCurrentStep[i]++;
|
fCurrentStep[i]++;
|
||||||
if (fCurrentStep[i] == steps.length) {
|
if (fCurrentStep[i] == steps.length) {
|
||||||
fMatched[i] = MATCHED_ATTRIBUTE;
|
fMatched[i] = MATCHED_ATTRIBUTE;
|
||||||
@ -384,7 +383,7 @@ public class XPathMatcher {
|
|||||||
"element={"+element+"},"+
|
"element={"+element+"},"+
|
||||||
")");
|
")");
|
||||||
}
|
}
|
||||||
for(int i = 0; i<fLocationPaths.length; i++) {
|
for (int i = 0; i < fLocationPaths.length; i++) {
|
||||||
// go back a step
|
// go back a step
|
||||||
fCurrentStep[i] = fStepIndexes[i].pop();
|
fCurrentStep[i] = fStepIndexes[i].pop();
|
||||||
|
|
||||||
@ -395,10 +394,13 @@ public class XPathMatcher {
|
|||||||
|
|
||||||
// signal match, if appropriate
|
// signal match, if appropriate
|
||||||
else {
|
else {
|
||||||
int j=0;
|
int j = 0;
|
||||||
for(; j<i && ((fMatched[j] & MATCHED) != MATCHED); j++);
|
for(; j < i && ((fMatched[j] & MATCHED) != MATCHED); j++);
|
||||||
if ((j<i) || (fMatched[j] == 0) ||
|
if ((j < i) || (fMatched[j] == 0)) {
|
||||||
((fMatched[j] & MATCHED_ATTRIBUTE) == MATCHED_ATTRIBUTE)) {
|
continue;
|
||||||
|
}
|
||||||
|
if ((fMatched[j] & MATCHED_ATTRIBUTE) == MATCHED_ATTRIBUTE) {
|
||||||
|
fMatched[i] = 0;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// only certain kinds of matchers actually
|
// only certain kinds of matchers actually
|
||||||
@ -476,6 +478,18 @@ public class XPathMatcher {
|
|||||||
return str.toString();
|
return str.toString();
|
||||||
} // normalize(String):String
|
} // normalize(String):String
|
||||||
|
|
||||||
|
/** Returns true if the given QName matches the node test. **/
|
||||||
|
private static boolean matches(XPath.NodeTest nodeTest, QName value) {
|
||||||
|
if (nodeTest.type == XPath.NodeTest.QNAME) {
|
||||||
|
return nodeTest.name.equals(value);
|
||||||
|
}
|
||||||
|
if (nodeTest.type == XPath.NodeTest.NAMESPACE) {
|
||||||
|
return nodeTest.name.uri == value.uri;
|
||||||
|
}
|
||||||
|
// XPath.NodeTest.WILDCARD
|
||||||
|
return true;
|
||||||
|
} // matches(XPath.NodeTest,QName):boolean
|
||||||
|
|
||||||
//
|
//
|
||||||
// MAIN
|
// MAIN
|
||||||
//
|
//
|
||||||
|
@ -36,11 +36,12 @@ import org.testng.annotations.DataProvider;
|
|||||||
|
|
||||||
import org.testng.annotations.Listeners;
|
import org.testng.annotations.Listeners;
|
||||||
import org.testng.annotations.Test;
|
import org.testng.annotations.Test;
|
||||||
|
import org.xml.sax.SAXException;
|
||||||
import org.xml.sax.SAXParseException;
|
import org.xml.sax.SAXParseException;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* @test
|
* @test
|
||||||
* @bug 8220818
|
* @bug 8220818 8176447
|
||||||
* @library /javax/xml/jaxp/libs /javax/xml/jaxp/unittest
|
* @library /javax/xml/jaxp/libs /javax/xml/jaxp/unittest
|
||||||
* @run testng/othervm validation.ValidationTest
|
* @run testng/othervm validation.ValidationTest
|
||||||
* @summary Runs validations with schemas and sources
|
* @summary Runs validations with schemas and sources
|
||||||
@ -71,6 +72,17 @@ public class ValidationTest {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
DataProvider: uniqueness
|
||||||
|
*/
|
||||||
|
@DataProvider(name = "uniqueness")
|
||||||
|
Object[][] getUniqueData() {
|
||||||
|
return new Object[][]{
|
||||||
|
{"JDK8176447a.xsd", "JDK8176447a.xml"},
|
||||||
|
{"JDK8176447b.xsd", "JDK8176447b.xml"},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
@Test(dataProvider = "invalid", expectedExceptions = SAXParseException.class)
|
@Test(dataProvider = "invalid", expectedExceptions = SAXParseException.class)
|
||||||
public void testValidateRefType(String xsd, String xml) throws Exception {
|
public void testValidateRefType(String xsd, String xml) throws Exception {
|
||||||
validate(xsd, xml);
|
validate(xsd, xml);
|
||||||
@ -81,6 +93,19 @@ public class ValidationTest {
|
|||||||
validate(xsd, xml);
|
validate(xsd, xml);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @bug 8176447
|
||||||
|
* Verifies that the uniqueness constraint is checked.
|
||||||
|
* @param xsd the XSD
|
||||||
|
* @param xml the XML
|
||||||
|
* @throws Exception expected when the uniqueness constraint is validated
|
||||||
|
* correctly.
|
||||||
|
*/
|
||||||
|
@Test(dataProvider = "uniqueness", expectedExceptions = SAXException.class)
|
||||||
|
public void testUnique(String xsd, String xml) throws Exception {
|
||||||
|
validate(xsd, xml);
|
||||||
|
}
|
||||||
|
|
||||||
private void validate(String xsd, String xml) throws Exception {
|
private void validate(String xsd, String xml) throws Exception {
|
||||||
final SchemaFactory schemaFactory = SchemaFactory.newInstance(
|
final SchemaFactory schemaFactory = SchemaFactory.newInstance(
|
||||||
XMLConstants.W3C_XML_SCHEMA_NS_URI);
|
XMLConstants.W3C_XML_SCHEMA_NS_URI);
|
||||||
@ -90,4 +115,5 @@ public class ValidationTest {
|
|||||||
validator.validate(new StreamSource(
|
validator.validate(new StreamSource(
|
||||||
new File(getClass().getResource(FILE_PATH + xml).getFile())));
|
new File(getClass().getResource(FILE_PATH + xml).getFile())));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,8 @@
|
|||||||
|
<test xmlns="http://openjdk_java_net/test.xml"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="JDK8176447a.xsd">
|
||||||
|
<innerObject>
|
||||||
|
<innerInnerObject test-unique-attribute="1" />
|
||||||
|
<innerInnerObject test-unique-attribute="1" />
|
||||||
|
</innerObject>
|
||||||
|
</test>
|
@ -0,0 +1,24 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:h="http://www.w3.org/1999/xhtml"
|
||||||
|
xmlns:sn="http://openjdk_java_net/test.xml"
|
||||||
|
targetNamespace="http://openjdk_java_net/test.xml" elementFormDefault="qualified">
|
||||||
|
<xsd:element name="test" type="sn:object">
|
||||||
|
<xsd:unique name="testunique">
|
||||||
|
<xsd:selector xpath="sn:innerObject"/>
|
||||||
|
<xsd:field xpath="sn:innerInnerObject/@test-unique-attribute"/>
|
||||||
|
</xsd:unique>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:complexType name="object">
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="innerObject" maxOccurs="unbounded" type="sn:testType" />
|
||||||
|
</xsd:sequence>
|
||||||
|
</xsd:complexType>
|
||||||
|
<xsd:complexType name="testType">
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="innerInnerObject" maxOccurs="unbounded" type="sn:testObjectType"/>
|
||||||
|
</xsd:sequence>
|
||||||
|
</xsd:complexType>
|
||||||
|
<xsd:complexType name="testObjectType">
|
||||||
|
<xsd:attribute use="optional" name="test-unique-attribute" type="xsd:int" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:schema>
|
@ -0,0 +1,14 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:noNamespaceSchemaLocation="JDK8176447b.xsd">
|
||||||
|
<e>
|
||||||
|
<e1 a1="a" >
|
||||||
|
<e2 a2="a"/>
|
||||||
|
<e2 a2="a"/>
|
||||||
|
</e1>
|
||||||
|
<e1 a1="a">
|
||||||
|
<e2 a2="b"/>
|
||||||
|
<e2 a2="a"/>
|
||||||
|
</e1>
|
||||||
|
</e>
|
||||||
|
</root>
|
@ -0,0 +1,34 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
|
||||||
|
<xs:element name="root">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="e" maxOccurs="unbounded">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element maxOccurs="unbounded" name="e1">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element maxOccurs="unbounded" name="e2">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:attribute name="a2" use="required" type="xs:NCName"/>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
</xs:sequence>
|
||||||
|
<xs:attribute name="a1" use="required" type="xs:NCName"/>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
|
||||||
|
<xs:key name="checkAttrib">
|
||||||
|
<xs:selector xpath=".//e1"/>
|
||||||
|
<xs:field xpath="@a1"/>
|
||||||
|
<xs:field xpath="e2/@a2"/>
|
||||||
|
</xs:key>
|
||||||
|
|
||||||
|
</xs:element>
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
</xs:schema>
|
Loading…
x
Reference in New Issue
Block a user