You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@xerces.apache.org by kn...@apache.org on 2008/09/01 17:40:18 UTC
svn commit: r690999 [1/2] - in
/xerces/java/branches/xml-schema-1.1-dev/src/org/apache/xerces: impl/
impl/dv/xs/ impl/xpath/ impl/xs/ impl/xs/alternative/ jaxp/validation/
parsers/
Author: knoaman
Date: Mon Sep 1 08:40:17 2008
New Revision: 690999
URL: http://svn.apache.org/viewvc?rev=690999&view=rev
Log:
Finalize XML Schema Type Alternatives Implementation, by Hiranya Jayathilaka
Modified:
xerces/java/branches/xml-schema-1.1-dev/src/org/apache/xerces/impl/Constants.java
xerces/java/branches/xml-schema-1.1-dev/src/org/apache/xerces/impl/dv/xs/XSSimpleTypeDecl.java
xerces/java/branches/xml-schema-1.1-dev/src/org/apache/xerces/impl/xpath/XPath20.java
xerces/java/branches/xml-schema-1.1-dev/src/org/apache/xerces/impl/xs/XMLSchemaValidator.java
xerces/java/branches/xml-schema-1.1-dev/src/org/apache/xerces/impl/xs/alternative/Test.java
xerces/java/branches/xml-schema-1.1-dev/src/org/apache/xerces/jaxp/validation/XMLSchemaValidatorComponentManager.java
xerces/java/branches/xml-schema-1.1-dev/src/org/apache/xerces/parsers/StandardParserConfiguration.java
xerces/java/branches/xml-schema-1.1-dev/src/org/apache/xerces/parsers/XML11Configuration.java
Modified: xerces/java/branches/xml-schema-1.1-dev/src/org/apache/xerces/impl/Constants.java
URL: http://svn.apache.org/viewvc/xerces/java/branches/xml-schema-1.1-dev/src/org/apache/xerces/impl/Constants.java?rev=690999&r1=690998&r2=690999&view=diff
==============================================================================
--- xerces/java/branches/xml-schema-1.1-dev/src/org/apache/xerces/impl/Constants.java (original)
+++ xerces/java/branches/xml-schema-1.1-dev/src/org/apache/xerces/impl/Constants.java Mon Sep 1 08:40:17 2008
@@ -320,6 +320,9 @@
/** Feature to ignore errors caused by identity constraints ("validation/identity-constraint-checking") */
public static final String IDC_CHECKING_FEATURE = "validation/identity-constraint-checking";
+
+ /** Feature to ignore errors caused by type alternatives */
+ public static final String TYPE_ALTERNATIVES_CHEKING_FEATURE = "validation/type-alternative-checking";
/** Feature to ignore errors caused by unparsed entities ("validation/unparsed-entity-checking") */
public static final String UNPARSED_ENTITY_CHECKING_FEATURE = "validation/unparsed-entity-checking";
@@ -516,6 +519,7 @@
ID_IDREF_CHECKING_FEATURE,
IDC_CHECKING_FEATURE,
UNPARSED_ENTITY_CHECKING_FEATURE,
+ TYPE_ALTERNATIVES_CHEKING_FEATURE,
};
/** Xerces properties. */
Modified: xerces/java/branches/xml-schema-1.1-dev/src/org/apache/xerces/impl/dv/xs/XSSimpleTypeDecl.java
URL: http://svn.apache.org/viewvc/xerces/java/branches/xml-schema-1.1-dev/src/org/apache/xerces/impl/dv/xs/XSSimpleTypeDecl.java?rev=690999&r1=690998&r2=690999&view=diff
==============================================================================
--- xerces/java/branches/xml-schema-1.1-dev/src/org/apache/xerces/impl/dv/xs/XSSimpleTypeDecl.java (original)
+++ xerces/java/branches/xml-schema-1.1-dev/src/org/apache/xerces/impl/dv/xs/XSSimpleTypeDecl.java Mon Sep 1 08:40:17 2008
@@ -591,6 +591,10 @@
return fFinalSet;
}
+ public TypeValidator getTypeValidator() {
+ return fDVs[fValidationDV];
+ }
+
public boolean isFinal(short derivation) {
return (fFinalSet & derivation) != 0;
}
Modified: xerces/java/branches/xml-schema-1.1-dev/src/org/apache/xerces/impl/xpath/XPath20.java
URL: http://svn.apache.org/viewvc/xerces/java/branches/xml-schema-1.1-dev/src/org/apache/xerces/impl/xpath/XPath20.java?rev=690999&r1=690998&r2=690999&view=diff
==============================================================================
--- xerces/java/branches/xml-schema-1.1-dev/src/org/apache/xerces/impl/xpath/XPath20.java (original)
+++ xerces/java/branches/xml-schema-1.1-dev/src/org/apache/xerces/impl/xpath/XPath20.java Mon Sep 1 08:40:17 2008
@@ -5,9 +5,9 @@
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -17,31 +17,1845 @@
package org.apache.xerces.impl.xpath;
+import org.apache.xerces.impl.Constants;
+import org.apache.xerces.impl.dv.InvalidDatatypeValueException;
+import org.apache.xerces.impl.dv.SchemaDVFactory;
+import org.apache.xerces.impl.dv.xs.TypeValidator;
+import org.apache.xerces.impl.dv.xs.XSSimpleTypeDecl;
+import org.apache.xerces.impl.dv.XSSimpleType;
+import org.apache.xerces.impl.xs.AttributePSVImpl;
+import org.apache.xerces.impl.xs.SchemaSymbols;
+import org.apache.xerces.util.IntStack;
import org.apache.xerces.util.SymbolTable;
+import org.apache.xerces.util.XMLChar;
+import org.apache.xerces.util.XMLSymbols;
+import org.apache.xerces.xni.Augmentations;
import org.apache.xerces.xni.NamespaceContext;
+import org.apache.xerces.xni.QName;
+import org.apache.xerces.xs.ShortList;
+import org.apache.xerces.xs.XSConstants;
+import org.apache.xerces.xs.XSSimpleTypeDefinition;
+import org.apache.xerces.xni.XMLAttributes;
/**
* Bare minimal XPath 2.0 implementation for schema
* type alternatives
- *
+ *
* @author Hiranya Jayathilaka, University of Moratuwa
- * @version $Id:$
+ * @version $Id$
*/
public class XPath20 {
protected final String fExpression;
-
protected final SymbolTable fSymbolTable;
+ protected NodeTest fSyntaxTree;
+ protected final NamespaceContext fContext;
- public XPath20(String xpath, SymbolTable symbolTable,
+ public XPath20(String xpath, SymbolTable symbolTable,
NamespaceContext context) throws XPathException {
fExpression = xpath;
fSymbolTable = symbolTable;
- parseExpression(context);
+ fContext = context;
+ fSyntaxTree = null;
+ parseExpression();
+ }
+
+ /**
+ * Traverse the syntax tree recursively and evaluates the tests stored in each of
+ * the nodes. Finally combines the test results and returns the overall result of
+ * the test expression as a boolean value.
+ */
+ public boolean traverseTree(QName element, XMLAttributes attributes) {
+ if (fSyntaxTree == null) {
+ return false;
+ }
+ return visitNode(fSyntaxTree.getChildren()[0], element, attributes);
+ }
+
+ private boolean visitNode(NodeTest node, QName element, XMLAttributes attributes) {
+ int nodeType = node.getType();
+ if (nodeType == SyntaxTreeBuilder.TEST_CONJUNCTION_AND ||
+ node.getType() == SyntaxTreeBuilder.TEST_CONJUNCTION_OR) {
+ NodeTest[] children = node.getChildren();
+ //recursively traverse the left sub tree
+ boolean result1 = visitNode(children[0], element, attributes);
+ //recursively traverse the right sub tree
+ boolean result2 = visitNode(children[1], element, attributes);
+ //combine the results
+ if (nodeType == SyntaxTreeBuilder.TEST_CONJUNCTION_AND) {
+ return result1 && result2;
+ }
+ else {
+ return result1 || result2;
+ }
+ }
+ else {
+ return node.evaluateNodeTest(element, attributes);
+ }
}
-
- private void parseExpression(NamespaceContext context)
+
+ /**
+ * Parses the XPath expression and builds a tree model out of it.
+ */
+ private void parseExpression()
throws XPathException {
- //TODO: Parse the given XPath expression
+ XPath20.Scanner scanner = new Scanner();
+ XPath20.Tokens tokens = new Tokens();
+ try {
+ scanner.scanExpr(fExpression, 0, fExpression.length(), tokens);
+ fSyntaxTree = XPath20.Parser.parse(tokens, fContext, fSymbolTable);
+ }
+ catch (XPathException e) {
+ fSyntaxTree = null; //reset the tree
+ throw e;
+ }
}
+
+ /**
+ * The Scanner implementation for XPath 2.0 expressions used in
+ * the type alternatives.
+ *
+ * @author Hiranya Jayathilaka, University of Moratuwa
+ */
+ private static class Scanner {
+
+ /**
+ * 7-bit ASCII subset
+ *
+ * 0 1 2 3 4 5 6 7 8 9 A B C D E F
+ * 0, 0, 0, 0, 0, 0, 0, 0, 0, HT, LF, 0, 0, CR, 0, 0, // 0
+ * 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1
+ * SP, !, ", #, $, %, &, ', (, ), *, +, ,, -, ., /, // 2
+ * 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, :, ;, <, =, >, ?, // 3
+ * @, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, // 4
+ * P, Q, R, S, T, U, V, W, X, Y, Z, [, \, ], ^, _, // 5
+ * `, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, // 6
+ * p, q, r, s, t, u, v, w, x, y, z, {, |, }, ~, DEL // 7
+ */
+ private static final byte
+ CHARTYPE_INVALID = 0, // invalid XML character
+ CHARTYPE_OTHER = 1, // not special - one of "#%&;?\^`{}~" or DEL
+ CHARTYPE_WHITESPACE = 2, // one of "\t\n\r " (0x09, 0x0A, 0x0D, 0x20)
+ CHARTYPE_EXCLAMATION = 3, // '!' (0x21)
+ CHARTYPE_QUOTE = 4, // '\"' or '\'' (0x22 and 0x27)
+ CHARTYPE_DOLLAR = 5, // '$' (0x24)
+ CHARTYPE_OPEN_PAREN = 6, // '(' (0x28)
+ CHARTYPE_CLOSE_PAREN = 7, // ')' (0x29)
+ CHARTYPE_STAR = 8, // '*' (0x2A)
+ CHARTYPE_PLUS = 9, // '+' (0x2B)
+ CHARTYPE_COMMA = 10, // ',' (0x2C)
+ CHARTYPE_MINUS = 11, // '-' (0x2D)
+ CHARTYPE_PERIOD = 12, // '.' (0x2E)
+ CHARTYPE_SLASH = 13, // '/' (0x2F)
+ CHARTYPE_DIGIT = 14, // '0'-'9' (0x30 to 0x39)
+ CHARTYPE_COLON = 15, // ':' (0x3A)
+ CHARTYPE_LESS = 16, // '<' (0x3C)
+ CHARTYPE_EQUAL = 17, // '=' (0x3D)
+ CHARTYPE_GREATER = 18, // '>' (0x3E)
+ CHARTYPE_ATSIGN = 19, // '@' (0x40)
+ CHARTYPE_LETTER = 20, // 'A'-'Z' or 'a'-'z' (0x41 to 0x5A and 0x61 to 0x7A)
+ CHARTYPE_OPEN_BRACKET = 21, // '[' (0x5B)
+ CHARTYPE_CLOSE_BRACKET = 22, // ']' (0x5D)
+ CHARTYPE_UNDERSCORE = 23, // '_' (0x5F)
+ CHARTYPE_UNION = 24, // '|' (0x7C)
+ CHARTYPE_NONASCII = 25; // Non-ASCII Unicode codepoint (>= 0x80)
+
+ private static final byte[] fASCIICharMap = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0, 2, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 2, 3, 4, 1, 5, 1, 1, 4, 6, 7, 8, 9, 10, 11, 12, 13,
+ 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 1, 16, 17, 18, 1,
+ 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,
+ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 21, 1, 22, 1, 23,
+ 1, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,
+ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 1, 24, 1, 1, 1
+ };
+
+ // symbols
+ private static final String fAndSymbol = "and".intern();
+ private static final String fOrSymbol = "or".intern();
+ private static final String fCastSymbol = "cast".intern();
+ private static final String fAsSymbol = "as".intern();
+
+ /**
+ * Scans the given XPath expression one character at a time and break it
+ * up into a series of tokens.
+ */
+ public void scanExpr(String data, int currentOffset, int endOffset, XPath20.Tokens tokens)
+ throws XPathException {
+
+ int ch, nameOffset;
+ String nameHandle, prefixHandle;
+
+ while (true) {
+ if (currentOffset == endOffset) {
+ break;
+ }
+ ch = data.charAt(currentOffset); //read a character from the expression
+
+ //check for whitespace(s)token
+ while (ch == ' ' || ch == 0x0A || ch == 0x09 || ch == 0x0D) {
+ if (++currentOffset == endOffset) {
+ break;
+ }
+ ch = data.charAt(currentOffset);
+ }
+
+ if (currentOffset == endOffset) {
+ break;
+ }
+
+ byte chartype = (ch >= 0x80) ? CHARTYPE_NONASCII : fASCIICharMap[ch];
+
+ switch (chartype) {
+
+ case CHARTYPE_ATSIGN:
+ tokens.addToken(XPath20.Tokens.EXPRTOKEN_AT_SIGN);
+ if (++currentOffset == endOffset) {
+ //an expression cannot end with an '@' sign
+ throw new XPathException("c-general-xpath");
+ }
+ break;
+
+ case CHARTYPE_EQUAL:
+ tokens.addToken(XPath20.Tokens.EXPRTOKEN_COMPARATOR_EQUAL);
+ if (++currentOffset == endOffset) {
+ //an expression cannot end with '=' sign
+ throw new XPathException("c-general-xpath");
+ }
+ break;
+
+ case CHARTYPE_EXCLAMATION:
+ if (++currentOffset == endOffset) {
+ //an expression cannot end with '!' sign
+ throw new XPathException("c-general-xpath");
+ }
+ ch = data.charAt(currentOffset);
+ if (ch != '=') {
+ throw new XPathException("c-general-xpath");
+ }
+ tokens.addToken(XPath20.Tokens.EXPRTOKEN_COMPARATOR_NOT_EQUAL);
+ if (++currentOffset == endOffset) {
+ //an expression cannot end with '!=' sign
+ throw new XPathException("c-general-xpath");
+ }
+ break;
+
+ case CHARTYPE_LESS:
+ //the token could be '<' or '<='.
+ //we need to read the next character to be sure
+ if (++currentOffset == endOffset) {
+ throw new XPathException("c-general-xpath");
+ }
+ ch = data.charAt(currentOffset);
+ if (ch == '=') {
+ tokens.addToken(XPath20.Tokens.EXPRTOKEN_COMPARATOR_LESS_EQUAL);
+ if (++currentOffset == endOffset) {
+ throw new XPathException("c-general-xpath");
+ }
+ } else {
+ tokens.addToken(XPath20.Tokens.EXPRTOKEN_COMPARATOR_LESS);
+ if (currentOffset + 1 == endOffset) {
+ throw new XPathException("c-general-xpath");
+ }
+ }
+ break;
+
+ case CHARTYPE_GREATER:
+ //the token could be '>' or '>='
+ if (++currentOffset == endOffset) {
+ throw new XPathException("c-general-xpath");
+ }
+ ch = data.charAt(currentOffset);
+ if (ch == '=') {
+ tokens.addToken(XPath20.Tokens.EXPRTOKEN_COMPARATOR_GREATER_EQUAL);
+ if (++currentOffset == endOffset) {
+ throw new XPathException("c-general-xpath");
+ }
+ } else {
+ tokens.addToken(XPath20.Tokens.EXPRTOKEN_COMPARATOR_GREATER);
+ if (currentOffset + 1 == endOffset) {
+ //we have the last read character still
+ //to be processed and so we don't touch
+ //the value of currentOffset
+ throw new XPathException("c-general-xpath");
+ }
+ }
+ break;
+
+ case CHARTYPE_QUOTE:
+ int qchar = ch;
+ if (++currentOffset == endOffset) {
+ throw new XPathException("c-general-xpath");
+ }
+ ch = data.charAt(currentOffset);
+ int litOffset = currentOffset;
+ while (ch != qchar) {
+ if (++currentOffset == endOffset) {
+ //no matching quote
+ throw new XPathException("c-general-xpath");
+ }
+ ch = data.charAt(currentOffset);
+ }
+
+ int litLength = currentOffset - litOffset;
+ tokens.addToken(XPath20.Tokens.EXPRTOKEN_STRING_LITERAL, data.substring
+ (litOffset, litOffset + litLength));
+ ++currentOffset;
+ break;
+
+ case CHARTYPE_OPEN_PAREN:
+ tokens.addToken(XPath20.Tokens.EXPRTOKEN_OPEN_PAREN);
+ if (++currentOffset == endOffset) {
+ throw new XPathException("c-general-xpath");
+ }
+ break;
+
+ case CHARTYPE_CLOSE_PAREN:
+ tokens.addToken(XPath20.Tokens.EXPRTOKEN_CLOSE_PAREN);
+ ++currentOffset;
+ break;
+
+ case CHARTYPE_STAR:
+ //if '*' is encountered treat it as if it was a NameTest
+ if (++currentOffset == endOffset) {
+ break;
+ }
+ tokens.addToken(XPath20.Tokens.EXPRTOKEN_NAMETEST_ANY);
+ break;
+
+ case CHARTYPE_OTHER:
+ if (ch == '?') {
+ tokens.addToken(XPath20.Tokens.EXPRTOKEN_QUESTION_SIGN);
+ }
+ else {
+ throw new XPathException("c-general-xpath");
+ }
+
+ ++currentOffset;
+ break;
+
+ case CHARTYPE_DIGIT:
+ nameOffset = currentOffset;
+ currentOffset = scanNumber(data, currentOffset, endOffset);
+ tokens.addToken(XPath20.Tokens.EXPRTOKEN_NUMERIC_LITERAL, data.
+ substring(nameOffset, currentOffset));
+ break;
+
+ case CHARTYPE_LETTER:
+ nameOffset = currentOffset;
+ currentOffset = scanNCName(data, currentOffset, endOffset);
+ if (currentOffset == nameOffset) {
+ throw new XPathException("c-general-xpath");
+ }
+
+ //read the string of characters
+ nameHandle = data.substring(nameOffset, currentOffset);
+
+ //we read the next character to see whether we have hit a
+ //QName or a NameTest
+ if (currentOffset < endOffset) {
+ ch = data.charAt(currentOffset);
+ }
+ else {
+ ch = -1;
+ }
+ boolean isNameTest = false;
+ boolean isQName = false;
+ prefixHandle = XMLSymbols.EMPTY_STRING;
+ if (ch == ':') {
+ if (++currentOffset == endOffset) {
+ throw new XPathException("c-general-xpath");
+ }
+ ch = data.charAt(currentOffset);
+ if (ch == '*') {
+ //we are at a NameTest
+ if (++currentOffset < endOffset) {
+ ch = data.charAt(currentOffset);
+ }
+ isNameTest = true;
+ }
+ else {
+ prefixHandle = nameHandle;
+ nameOffset = currentOffset;
+ currentOffset = scanNCName(data, currentOffset, endOffset);
+ if (currentOffset == nameOffset) {
+ throw new XPathException("c-general-xpath");
+ }
+ if (currentOffset < endOffset) {
+ ch = data.charAt(currentOffset);
+ }
+ else {
+ ch = -1;
+ }
+ //we have more text, ie we are at a QName
+ nameHandle = data.substring(nameOffset, currentOffset);
+ isQName = true;
+ }
+ }
+ else {
+ //if nothing special was found we treat what we have
+ //read so far as a QName and continue
+ isQName = true;
+ }
+
+ //deal with any whitespace
+ while (ch == ' ' || ch == 0x0A || ch == 0x09 || ch == 0x0D) {
+ if (++currentOffset == endOffset) {
+ break;
+ }
+ ch = data.charAt(currentOffset);
+ }
+
+ if (nameHandle.equals(fAndSymbol)) {
+ tokens.addToken(XPath20.Tokens.EXPRTOKEN_KEYWORD_AND);
+ }
+ else if (nameHandle.equals(fOrSymbol)) {
+ tokens.addToken(XPath20.Tokens.EXPRTOKEN_KEYWORD_OR);
+ }
+ else if (nameHandle.equals(fCastSymbol)) {
+ tokens.addToken(XPath20.Tokens.EXPRTOKEN_KEYWORD_CAST);
+ }
+ else if (nameHandle.equals(fAsSymbol)) {
+ tokens.addToken(XPath20.Tokens.EXPRTOKEN_KEYWORD_AS);
+ }
+ else {
+ if (isNameTest) {
+ tokens.addToken(XPath20.Tokens.EXPRTOKEN_NAMETEST_NS, nameHandle);
+ }
+ else if (isQName) {
+ if (prefixHandle !=XMLSymbols.EMPTY_STRING) {
+ nameHandle = prefixHandle + ":" +nameHandle;
+ }
+ tokens.addToken(XPath20.Tokens.EXPRTOKEN_QNAME, nameHandle);
+ }
+ else {
+ throw new XPathException("c-general-xpath");
+ }
+ }
+ break;
+
+ case CHARTYPE_INVALID:
+ case CHARTYPE_DOLLAR:
+ case CHARTYPE_PLUS:
+ case CHARTYPE_UNION:
+ case CHARTYPE_OPEN_BRACKET:
+ case CHARTYPE_CLOSE_BRACKET:
+ case CHARTYPE_COLON:
+ case CHARTYPE_COMMA:
+ case CHARTYPE_SLASH:
+ throw new XPathException("c-general-xpath");
+
+ default:
+ throw new XPathException("c-general-xpath");
+ }
+ }
+ }
+
+ /*
+ * Reads a numeric literal in a test XPath expression.
+ */
+ private int scanNumber(String data, int currentOffset, int endOffset) throws XPathException {
+ int ch = data.charAt(currentOffset);
+ while (ch >= '0' && ch <= '9') {
+ if (++currentOffset == endOffset) {
+ break;
+ }
+ ch = data.charAt(currentOffset);
+ }
+
+ if (ch == '.') {
+ if (++currentOffset < endOffset) {
+ ch = data.charAt(currentOffset);
+ while (ch >= '0' && ch <= '9') {
+ if (++currentOffset == endOffset) {
+ break;
+ }
+ ch = data.charAt(currentOffset);
+ }
+ }
+ }
+
+ if (ch == 'e' || ch == 'E') {
+ if (++currentOffset < endOffset) {
+ ch = data.charAt(currentOffset);
+ if (ch == '+' || ch == '-') {
+ ch = data.charAt(++currentOffset);
+ }
+
+ if (ch < '0' || ch > '9') {
+ throw new XPathException("c-general-xpath");
+ }
+
+ while (ch >= '0' && ch <= '9') {
+ if (++currentOffset == endOffset) {
+ break;
+ }
+ ch = data.charAt(currentOffset);
+ }
+ }
+ else {
+ throw new XPathException("c-general-xpath");
+ }
+ }
+ return currentOffset;
+ }
+
+ /*
+ * Reads a NCName in a test XPath expression
+ */
+ private int scanNCName(String data, int currentOffset, int endOffset) {
+ int ch = data.charAt(currentOffset);
+ if (ch >= 0x80) {
+ if (!XMLChar.isNameStart(ch)) {
+ return currentOffset;
+ }
+ }
+ else {
+ byte chartype = fASCIICharMap[ch];
+ if (chartype != CHARTYPE_LETTER && chartype != CHARTYPE_UNDERSCORE) {
+ return currentOffset;
+ }
+ }
+
+ while (++currentOffset < endOffset) {
+ ch = data.charAt(currentOffset);
+ if (ch >= 0x80) {
+ if (!XMLChar.isName(ch)) {
+ break;
+ }
+ }
+ else {
+ byte chartype = fASCIICharMap[ch];
+ if (chartype != CHARTYPE_LETTER && chartype != CHARTYPE_DIGIT &&
+ chartype != CHARTYPE_PERIOD && chartype != CHARTYPE_MINUS &&
+ chartype != CHARTYPE_UNDERSCORE) {
+ break;
+ }
+ }
+ }
+ return currentOffset;
+ }
+
+ }
+
+ /**
+ * Used by the XPath20.Tokenizer to store a set of tokens. Generally
+ * only the token type is stored. But in certain situations the textual
+ * value of the tokens can be saved.
+ *
+ * @author Hiranya Jayathilaka, University of Moratuwa
+ */
+ private static class Tokens {
+
+ public static final int
+ EXPRTOKEN_OPEN_PAREN = 0,
+ EXPRTOKEN_CLOSE_PAREN = 1,
+
+ EXPRTOKEN_KEYWORD_OR = 2,
+ EXPRTOKEN_KEYWORD_AND = 3,
+ EXPRTOKEN_KEYWORD_CAST = 4,
+ EXPRTOKEN_KEYWORD_AS = 5,
+
+ EXPRTOKEN_COMPARATOR_EQUAL = 6,
+ EXPRTOKEN_COMPARATOR_NOT_EQUAL = 7,
+ EXPRTOKEN_COMPARATOR_LESS = 8,
+ EXPRTOKEN_COMPARATOR_LESS_EQUAL = 9,
+ EXPRTOKEN_COMPARATOR_GREATER = 10,
+ EXPRTOKEN_COMPARATOR_GREATER_EQUAL = 11,
+
+ EXPRTOKEN_AT_SIGN = 12,
+ EXPRTOKEN_QUESTION_SIGN = 13,
+ EXPRTOKEN_STAR_SIGN = 14,
+ EXPRTOKEN_COLON_SIGN = 15,
+
+ EXPRTOKEN_NUMERIC_LITERAL = 16,
+ EXPRTOKEN_STRING_LITERAL = 17,
+
+ //[7] QName ::= PrefixedName | UnprefixedName
+ EXPRTOKEN_QNAME = 18,
+ EXPRTOKEN_ATTR_NAME = 19,
+
+ //[4] NameTest ::= QName | '*' | NCName ':' '*'
+ EXPRTOKEN_NAMETEST_ANY = 20, //'*' scenario
+ EXPRTOKEN_NAMETEST_NS = 21; //NCName:* scenario
+
+
+ private static final int INITIAL_TOKEN_COUNT = 8;
+
+ //the token list
+ private int[] fTokens = new int[INITIAL_TOKEN_COUNT];
+
+ //stores the textual values of tokens (indexed by the corresponding index in the token list)
+ private String[] fTokenNames = new String[INITIAL_TOKEN_COUNT];
+ //private Hashtable<Integer, String> fTokenNames = new Hashtable<Integer, String>();
+ private int fTokenCount = 0;
+ private int fCurrentTokenIndex = 0;
+
+ /**
+ * Adds the specified token to the list of tokens. Only the token
+ * type is saved.
+ */
+ public void addToken(int token) {
+ addToken(token, null);
+ }
+ /*public void addToken(int token) {
+
+ if (fTokenCount == fTokens.length) {
+ int[] oldArray = fTokens;
+ fTokens = new int[fTokenCount << 1];
+ System.arraycopy(oldArray, 0, fTokens, 0, fTokenCount);
+ }
+ fTokens[fTokenCount] = token;
+ fTokenCount++;
+ }*/
+
+ /**
+ * Adds the specified token to the list of tokens along with its
+ * textual value.
+ */
+ public void addToken(int token, String tokenStr) {
+
+ if (fTokenCount == fTokens.length) {
+ int[] oldArray = fTokens;
+ fTokens = new int[fTokenCount << 1];
+ System.arraycopy(oldArray, 0, fTokens, 0, fTokenCount);
+
+ String[] oldNames = fTokenNames;
+ fTokenNames = new String[fTokenCount << 1];
+ System.arraycopy(oldNames, 0, fTokenNames, 0, fTokenCount);
+ }
+ fTokens[fTokenCount] = token;
+ fTokenNames[fTokenCount] = tokenStr;
+ fTokenCount++;
+ }
+ /*public void addToken(int token, String tokenStr) {
+ addToken(token);
+ fTokenNames.put(fTokenCount - 1, tokenStr);
+ }*/
+
+ /**
+ * Checks whether there are any more tokens in the list
+ */
+ public boolean hasNext() {
+ return fCurrentTokenIndex < fTokenCount;
+ }
+
+ /**
+ * Returns the next token in the token list
+ */
+ public int next() throws XPathException {
+ if (fCurrentTokenIndex == fTokenCount) {
+ throw new XPathException("c-general-xpath");
+ }
+ return fTokens[fCurrentTokenIndex++];
+ }
+
+ public int peek() {
+ if (fCurrentTokenIndex != fTokenCount) {
+ return fTokens[fCurrentTokenIndex];
+ }
+ else {
+ return -1;
+ }
+ }
+
+ /**
+ * Reset the position indicator in the token list to be 0
+ */
+ public void rewind() {
+ fCurrentTokenIndex = 0;
+ }
+
+ /**
+ * Gets the textual value of a token. If the text value for
+ * the specified token is not in the Map will return null.
+ */
+ public String getTokenValue(int tokenIndex) {
+ return fTokenNames[tokenIndex];
+ }
+ }
+
+
+ /**
+ * The parser implementation for the test XPath expressions
+ * used in type alternatives. Processes a set of tokens given
+ * by the XPath20.Tokenizer and checks whether they expression
+ * conforms to the XML Schema 1.1 Structures specification.
+ *
+ * @author Hiranya Jayathilaka, University of Moratuwa
+ */
+ private static class Parser {
+
+ /**
+ * Parses a set of tokens to see whether the test XPath expression
+ * represented by the tokens is valid. Valid tokens are passed to the
+ * SyntaxTreeBuilder in order to construct the abstract syntax tree
+ * representation of the expression.
+ */
+ public static NodeTest parse(XPath20.Tokens tokens, NamespaceContext context,
+ SymbolTable symbolTable) throws XPathException {
+
+ boolean expectingNameTest = false;
+ boolean expectingQName = true;
+ boolean attrAxis = false;
+ boolean inCastExpr = false;
+
+ int i = 0;
+ int openBrackets = 0;
+ int prevToken = -1;
+
+ //init the content model builder
+ SyntaxTreeBuilder treeBuilder = new SyntaxTreeBuilder();
+
+ while (tokens.hasNext()) {
+ int token = tokens.next();
+
+ switch (token) {
+
+ case XPath20.Tokens.EXPRTOKEN_AT_SIGN:
+ //we are at the beginning of an attribute name
+ check(!expectingNameTest);
+ //must be followed by a nametest
+ expectingNameTest = true;
+ expectingQName = false;
+ attrAxis = true;
+ break;
+
+ case XPath20.Tokens.EXPRTOKEN_QNAME:
+ check(expectingNameTest || expectingQName);
+ if (prevToken == XPath20.Tokens.EXPRTOKEN_AT_SIGN) {
+ //if the '@' sign was detected previously
+ //we should let the tree builder know that we
+ //are dealing with an attribute name here
+ treeBuilder.addToken(Tokens.EXPRTOKEN_ATTR_NAME, tokens.getTokenValue(i));
+ }
+ else {
+ treeBuilder.addToken(token, tokens.getTokenValue(i));
+ }
+ expectingNameTest = false;
+ if (inCastExpr) {
+ //any explicit casts must be to built-in types
+ if (!isBuiltInType(tokens.getTokenValue(i), context, symbolTable)) {
+ throw new XPathException("c-general-xpath");
+ }
+
+ //if we are in a cast expression the QName can be followed by
+ //a '?'
+ token = tokens.peek();
+ if (token == XPath20.Tokens.EXPRTOKEN_QUESTION_SIGN) {
+ token = tokens.next();
+ treeBuilder.addToken(token, null);
+ i++;
+ }
+ else {
+ treeBuilder.addToken(XPath20.Tokens.EXPRTOKEN_QUESTION_SIGN, null);
+ }
+ //end of cast expression
+ inCastExpr = false;
+ expectingQName = false;
+ }
+ break;
+
+ //handle comparator signs (=, >, <, <=, >=, !=)
+ case XPath20.Tokens.EXPRTOKEN_COMPARATOR_EQUAL:
+ case XPath20.Tokens.EXPRTOKEN_COMPARATOR_NOT_EQUAL:
+ case XPath20.Tokens.EXPRTOKEN_COMPARATOR_GREATER:
+ case XPath20.Tokens.EXPRTOKEN_COMPARATOR_GREATER_EQUAL:
+ case XPath20.Tokens.EXPRTOKEN_COMPARATOR_LESS:
+ case XPath20.Tokens.EXPRTOKEN_COMPARATOR_LESS_EQUAL:
+ check(!expectingNameTest && !expectingQName);
+ if (!tokens.hasNext()) {
+ //the test must not end here
+ //if it does something's wrong
+ throw new XPathException("c-general-xpath");
+ }
+ else if (prevToken == XPath20.Tokens.EXPRTOKEN_STRING_LITERAL ||
+ prevToken == XPath20.Tokens.EXPRTOKEN_NUMERIC_LITERAL ||
+ prevToken == XPath20.Tokens.EXPRTOKEN_QUESTION_SIGN ||
+ attrAxis) {
+ //the previous token must be a literal or a
+ //cast expression
+ treeBuilder.addToken(token, null);
+ attrAxis = false;
+ }
+ else {
+ throw new XPathException("c-general-xpath");
+ }
+ break;
+
+ case XPath20.Tokens.EXPRTOKEN_NUMERIC_LITERAL:
+ case XPath20.Tokens.EXPRTOKEN_STRING_LITERAL:
+ treeBuilder.addToken(token, tokens.getTokenValue(i));
+ expectingNameTest = false;
+ expectingQName = false;
+ break;
+
+ case XPath20.Tokens.EXPRTOKEN_KEYWORD_CAST:
+ if (prevToken == XPath20.Tokens.EXPRTOKEN_STRING_LITERAL ||
+ prevToken == XPath20.Tokens.EXPRTOKEN_NUMERIC_LITERAL ||
+ prevToken == XPath20.Tokens.EXPRTOKEN_NAMETEST_NS ||
+ prevToken == XPath20.Tokens.EXPRTOKEN_NAMETEST_ANY ||
+ prevToken == XPath20.Tokens.EXPRTOKEN_QNAME) {
+
+ treeBuilder.addToken(token, null);
+ token = tokens.next();
+ if (token == XPath20.Tokens.EXPRTOKEN_KEYWORD_AS) {
+ //keyword 'cast' must be followed by the keyword 'as'
+ treeBuilder.addToken(token, null);
+ //should be followed by a QName
+ expectingQName = true;
+ expectingNameTest = false;
+ inCastExpr = true;
+ i++;
+ }
+ else {
+ throw new XPathException("c-general-xpath");
+ }
+ }
+ else {
+ throw new XPathException("c-general-xpath");
+ }
+ break;
+
+ case XPath20.Tokens.EXPRTOKEN_KEYWORD_OR:
+ case XPath20.Tokens.EXPRTOKEN_KEYWORD_AND:
+ //might be followed by a QName
+ expectingQName = true;
+ treeBuilder.addToken(token, null);
+ break;
+
+ case XPath20.Tokens.EXPRTOKEN_OPEN_PAREN:
+ openBrackets++;
+ expectingQName = true;
+ treeBuilder.addToken(token, null);
+ break;
+
+ case XPath20.Tokens.EXPRTOKEN_CLOSE_PAREN:
+ if (openBrackets > 0) {
+ treeBuilder.addToken(token, null);
+ expectingQName = expectingNameTest = false;
+ openBrackets--;
+ }
+ else {
+ throw new XPathException("c-general-xpath");
+ }
+ break;
+
+ default:
+ throw new XPathException("c-general-xpath");
+ }
+
+ //'cache' the current token for the next iteration
+ prevToken = token;
+ i++;
+ }
+ if (openBrackets > 0) {
+ //check for bracket inconsistency
+ throw new XPathException("c-general-xpath");
+ }
+ return treeBuilder.markEnd();
+ }
+
+ /**
+ * Checks whether a specified boolean condition is satisfied by the current
+ * state of the parser. If the conditions are not satisfied throws an XPath
+ * exception
+ */
+ private static void check( boolean b ) throws XPathException {
+ if(!b) throw new XPathException("c-general-xpath");
+ }
+
+ /**
+ * Checks whether a given QName represents a valid built-in type
+ */
+ private static boolean isBuiltInType(String qname, NamespaceContext context,
+ SymbolTable symbolTable) {
+ boolean builtIn = false;
+ if (qname.indexOf(':') != -1) {
+ String[] qnameElements = qname.split(":");
+ String prefix = symbolTable.addSymbol(qnameElements[0]);
+ String uri = context.getURI(prefix);
+ if (SchemaSymbols.URI_SCHEMAFORSCHEMA == uri) {
+ XSSimpleType type = SchemaDVFactory.getInstance().getBuiltInType(qnameElements[1]);
+ if (type != null) {
+ builtIn = true;
+ }
+ }
+ }
+ return builtIn;
+ }
+ }
+
+
+ /**
+ * The syntax tree builder implementation for the test XPath expressions used in
+ * the type alternatives.
+ *
+ * @author Hiranya Jayathilaka, University of Moratuwa
+ */
+ private static class SyntaxTreeBuilder {
+
+ public static final int
+ TEST_CATEGORY_ROOT = 0,
+
+ TEST_CATEGORY_VALUE = 1,
+ TEST_CATEGORY_VALUE_AS_CAST = 2,
+ TEST_CATEGORY_ATTR = 3,
+ TEST_CATEGORY_ATTR_AS_CAST = 4,
+
+ TEST_CATEGORY_ATTR_VALUE = 5,
+ TEST_CATEGORY_ATTR_VALUE_AS_CAST = 6,
+ TEST_CATEGORY_ATTR_AS_CAST_VALUE = 7,
+ TEST_CATEGORY_ATTR_AS_CAST_VALUE_AS_CAST = 8,
+
+ TEST_CATEGORY_VALUE_VALUE = 9,
+ TEST_CATEGORY_VALUE_VALUE_AS_CAST = 10,
+ TEST_CATEGORY_VALUE_AS_CAST_VALUE = 11,
+ TEST_CATEGORY_VALUE_AS_CAST_VALUE_AS_CAST = 12,
+
+ TEST_CATEGORY_ATTR_ATTR = 13,
+ TEST_CATEGORY_ATTR_ATTR_AS_CAST = 14,
+ TEST_CATEGORY_ATTR_AS_CAST_ATTR = 15,
+ TEST_CATEGORY_ATTR_AS_CAST_ATTR_AS_CAST = 16,
+
+ TEST_CONJUNCTION_OR = 17,
+ TEST_CONJUNCTION_AND = 18,
+ TEST_OPEN_PAREN = 19,
+ TEST_CLOSE_PAREN = 20;
+
+ private static final int INITIAL_TOKEN_COUNT = 8;
+
+ private NodeTest fCurrentNode;
+ private int[] fTokensBuffer = new int[INITIAL_TOKEN_COUNT];
+ private String[] fTokenNames = new String[INITIAL_TOKEN_COUNT];
+ private int fTokenCount = 0;
+ //private ArrayList<Integer> fTokensBuffer;
+ //private ArrayList<String> fTokenNames;
+
+ private boolean fExcessBrackets;
+ private IntStack fBracketStates;
+
+ private String fLHS, fRHS;
+ private int fComparator;
+ private boolean fNumeric;
+
+ public SyntaxTreeBuilder() {
+ fCurrentNode = null;
+ //fTokensBuffer = new ArrayList<Integer>();
+ //fTokenNames = new ArrayList<String>();
+ fBracketStates = new IntStack();
+ fExcessBrackets = false;
+ fNumeric = false;
+ }
+
+ /**
+ * Processes the given token and adds it to the syntax tree
+ */
+ public void addToken(int token, String text) throws XPathException{
+ NodeTest node = null;
+ NodeTest conjunction = null;
+
+ switch (token) {
+
+ case Tokens.EXPRTOKEN_KEYWORD_AND:
+ node = getNodeTest();
+ conjunction = new NodeTest(TEST_CONJUNCTION_AND);
+ if (fExcessBrackets) {
+ fExcessBrackets = false;
+ }
+ break;
+
+ case Tokens.EXPRTOKEN_KEYWORD_OR:
+ node = getNodeTest();
+ conjunction = new NodeTest(TEST_CONJUNCTION_OR);
+ if (fExcessBrackets) {
+ fExcessBrackets = false;
+ }
+ break;
+
+ case Tokens.EXPRTOKEN_OPEN_PAREN:
+ fBracketStates.push(fExcessBrackets ? 1 : 0);
+ fExcessBrackets = true;
+ break;
+
+ case Tokens.EXPRTOKEN_CLOSE_PAREN:
+ if (!fExcessBrackets) {
+ node = getNodeTest();
+ }
+ fExcessBrackets = fBracketStates.pop() == 1;
+ break;
+
+ default:
+ {
+ if (fTokenCount == fTokensBuffer.length) {
+ int[] oldArray = fTokensBuffer;
+ fTokensBuffer = new int[fTokenCount << 1];
+ System.arraycopy(oldArray, 0, fTokensBuffer, 0, fTokenCount);
+
+ String[] oldNames = fTokenNames;
+ fTokenNames = new String[fTokenCount << 1];
+ System.arraycopy(oldNames, 0, fTokenNames, 0, fTokenCount);
+ }
+ fTokensBuffer[fTokenCount] = token;
+ fTokenNames[fTokenCount] = text;
+ fTokenCount++;
+ //fTokensBuffer.add(token);
+ //fTokenNames.add(text);
+ }
+ break;
+ }
+ connectToTree(conjunction, node);
+ }
+
+ /**
+ * Attach the two given nodes to the syntax tree
+ */
+ private void connectToTree(NodeTest conjunction, NodeTest node) {
+ if (fCurrentNode == null && node != null) {
+ if (conjunction != null) {
+ fCurrentNode = conjunction;
+ fCurrentNode.setFirstChild(node);
+ }
+ else {
+ fCurrentNode = node;
+ }
+ }
+ else if (fCurrentNode != null && !fCurrentNode.isComplete()) {
+ if (node != null) {
+ if (conjunction != null) {
+ fCurrentNode.setSecondChild(conjunction);
+ conjunction.setFirstChild(node);
+ fCurrentNode = conjunction;
+ }
+ else {
+ fCurrentNode.setSecondChild(node);
+ while (fCurrentNode.getParent() != null &&
+ fCurrentNode.isComplete()) {
+ fCurrentNode = fCurrentNode.getParent();
+ }
+ }
+ }
+ }
+ else if (fCurrentNode != null && fCurrentNode.isComplete()) {
+ if (conjunction != null && node == null) {
+ conjunction.setFirstChild(fCurrentNode);
+ fCurrentNode = conjunction;
+ }
+ }
+ }
+
+ /**
+ * Call this method to complete building the syntax tree. Once called
+ * this method will create the root node of the tree and attach the tree
+ * constructed so far to the tree.
+ */
+ public NodeTest markEnd() throws XPathException {
+ connectToTree(null, getNodeTest());
+ NodeTest root = new NodeTest(TEST_CATEGORY_ROOT);
+ root.setFirstChild(fCurrentNode);
+ return root;
+ }
+
+ private NodeTest getNodeTest() throws XPathException {
+ int testType = getTestCategory();
+ if (testType < 0) {
+ return null;
+ }
+ NodeTest node = new NodeTest(testType);
+ node.setLHS(fLHS);
+ if (testType != TEST_CATEGORY_ATTR &&
+ testType != TEST_CATEGORY_VALUE) {
+ node.setRHS(fRHS);
+ node.setComparator(fComparator);
+ }
+
+ node.setNumeric(fNumeric);
+ fTokenCount = 0;
+ //fTokensBuffer.clear();
+ //fTokenNames.clear();
+ return node;
+ }
+
+ /**
+ * Analyzes the content currently in the tokens buffer and tries to
+ * classify the expression into one of the test categories. Looks at
+ * the number of tokens in the buffer, their order and types. While
+ * doing so extracts the LHS, RHS and comparator components from the
+ * test expression.
+ */
+ private int getTestCategory() throws XPathException {
+ int testCategory;
+ final int size = fTokenCount;
+
+ switch (size) {
+ case 0:
+ return -1;
+ case 1:
+ //Only one token in the test
+ if (fTokensBuffer[0] == Tokens.EXPRTOKEN_ATTR_NAME) {
+ testCategory = TEST_CATEGORY_ATTR;
+ }
+ else {
+ testCategory = TEST_CATEGORY_VALUE;
+ if (fTokensBuffer[0] == Tokens.EXPRTOKEN_NUMERIC_LITERAL) {
+ fNumeric = true;
+ }
+ }
+ fLHS = fTokenNames[0];
+ break;
+
+ case 3:
+ //3 tokens in the test
+ if (fTokensBuffer[0] == Tokens.EXPRTOKEN_ATTR_NAME &&
+ fTokensBuffer[2] == Tokens.EXPRTOKEN_ATTR_NAME) {
+ testCategory = TEST_CATEGORY_ATTR_ATTR;
+ }
+ else if (fTokensBuffer[0] == Tokens.EXPRTOKEN_ATTR_NAME &&
+ fTokensBuffer[2] != Tokens.EXPRTOKEN_ATTR_NAME) {
+ testCategory = TEST_CATEGORY_ATTR_VALUE;
+ }
+ else if ((fTokensBuffer[0] == Tokens.EXPRTOKEN_NUMERIC_LITERAL &&
+ fTokensBuffer[2] == Tokens.EXPRTOKEN_NUMERIC_LITERAL) ||
+ (fTokensBuffer[0] == Tokens.EXPRTOKEN_STRING_LITERAL &&
+ fTokensBuffer[2] == Tokens.EXPRTOKEN_STRING_LITERAL)) {
+ testCategory = TEST_CATEGORY_VALUE_VALUE;
+ }
+ else {
+ throw new XPathException("c-general-xpath");
+ }
+ fLHS = fTokenNames[0];
+ fRHS = fTokenNames[2];
+ fComparator = fTokensBuffer[1];
+ break;
+
+ case 5:
+ //5 tokens in the test
+ if (fTokensBuffer[0] == Tokens.EXPRTOKEN_ATTR_NAME &&
+ fTokensBuffer[1] == Tokens.EXPRTOKEN_KEYWORD_CAST) {
+ testCategory = TEST_CATEGORY_ATTR_AS_CAST;
+ fLHS = fTokenNames[0] + " cast as " + fTokenNames[3] + " ?";
+ }
+ else {
+ testCategory = TEST_CATEGORY_VALUE_AS_CAST;
+ fLHS = fTokenNames[0] + " cast as " + fTokenNames[3] + " ?";
+ }
+ break;
+
+ case 7:
+ //7 tokens in the test (one explicit cast is in the test)
+ if (fTokensBuffer[0] == Tokens.EXPRTOKEN_ATTR_NAME &&
+ fTokensBuffer[1] == Tokens.EXPRTOKEN_KEYWORD_CAST &&
+ fTokensBuffer[6] == Tokens.EXPRTOKEN_ATTR_NAME) {
+ testCategory = TEST_CATEGORY_ATTR_AS_CAST_ATTR;
+ fLHS = fTokenNames[0] + " cast as " + fTokenNames[3] + " ?";
+ fRHS = fTokenNames[6];
+ fComparator = fTokensBuffer[5];
+ }
+ else if (fTokensBuffer[0] == Tokens.EXPRTOKEN_ATTR_NAME &&
+ fTokensBuffer[3] == Tokens.EXPRTOKEN_KEYWORD_CAST &&
+ fTokensBuffer[2] == Tokens.EXPRTOKEN_ATTR_NAME) {
+ testCategory = TEST_CATEGORY_ATTR_ATTR_AS_CAST;
+ fLHS = fTokenNames[0];
+ fRHS = fTokenNames[2] + " cast as " + fTokenNames[5] + " ?";
+ fComparator = fTokensBuffer[1];
+ }
+ else if (fTokensBuffer[0] == Tokens.EXPRTOKEN_ATTR_NAME &&
+ fTokensBuffer[1] == Tokens.EXPRTOKEN_KEYWORD_CAST &&
+ fTokensBuffer[6] != Tokens.EXPRTOKEN_ATTR_NAME) {
+ testCategory = TEST_CATEGORY_ATTR_AS_CAST_VALUE;
+ fLHS = fTokenNames[0] + " cast as " + fTokenNames[3] + " ?";
+ fRHS = fTokenNames[6];
+ fComparator = fTokensBuffer[5];
+ }
+ else if (fTokensBuffer[0] == Tokens.EXPRTOKEN_ATTR_NAME &&
+ fTokensBuffer[3] == Tokens.EXPRTOKEN_KEYWORD_CAST &&
+ fTokensBuffer[2] != Tokens.EXPRTOKEN_ATTR_NAME) {
+ testCategory = TEST_CATEGORY_ATTR_VALUE_AS_CAST;
+ fLHS = fTokenNames[0];
+ fRHS = fTokenNames[2] + " cast as " + fTokenNames[5] + " ?";
+ fComparator = fTokensBuffer[1];
+ }
+ else if (fTokensBuffer[0] != Tokens.EXPRTOKEN_ATTR_NAME &&
+ fTokensBuffer[1] == Tokens.EXPRTOKEN_KEYWORD_CAST &&
+ fTokensBuffer[6] != Tokens.EXPRTOKEN_ATTR_NAME) {
+ testCategory = TEST_CATEGORY_VALUE_AS_CAST_VALUE;
+ fLHS = fTokenNames[0] + " cast as " + fTokenNames[3] + " ?";
+ fRHS = fTokenNames[6];
+ fComparator = fTokensBuffer[5];
+ }
+ else if (fTokensBuffer[0] != Tokens.EXPRTOKEN_ATTR_NAME &&
+ fTokensBuffer[3] == Tokens.EXPRTOKEN_KEYWORD_CAST &&
+ fTokensBuffer[2] != Tokens.EXPRTOKEN_ATTR_NAME) {
+ testCategory = TEST_CATEGORY_VALUE_VALUE_AS_CAST;
+ fLHS = fTokenNames[0];
+ fRHS = fTokenNames[2] + " cast as " + fTokenNames[5] + " ?";
+ fComparator = fTokensBuffer[1];
+ }
+ else {
+ throw new XPathException("c-general-xpath");
+ }
+ break;
+
+ case 11:
+ //11 tokens in the test (two explicit casts in the test)
+ if (fTokensBuffer[0] == Tokens.EXPRTOKEN_ATTR_NAME &&
+ fTokensBuffer[6] == Tokens.EXPRTOKEN_ATTR_NAME) {
+ testCategory = TEST_CATEGORY_ATTR_AS_CAST_ATTR_AS_CAST;
+ }
+ else if (fTokensBuffer[0] == Tokens.EXPRTOKEN_ATTR_NAME &&
+ fTokensBuffer[6] != Tokens.EXPRTOKEN_ATTR_NAME) {
+ testCategory = TEST_CATEGORY_ATTR_AS_CAST_VALUE_AS_CAST;
+ }
+ else {
+ testCategory = TEST_CATEGORY_VALUE_AS_CAST_VALUE_AS_CAST;
+ }
+ fLHS = fTokenNames[0] + " cast as " + fTokenNames[3] + " ?";
+ fRHS = fTokenNames[6] + " cast as " + fTokenNames[9] + " ?";
+ fComparator = fTokensBuffer[5];
+ break;
+
+ default:
+ throw new XPathException("c-general-xpath");
+
+ }
+
+ if (fTokensBuffer[0] == Tokens.EXPRTOKEN_NUMERIC_LITERAL) {
+ fNumeric = true;
+ }
+ return testCategory;
+ }
+ }
+
+ /**
+ * The syntax tree for test XPath expressions is composed of NodeTests.
+ * A NodeTest encapsulates all the necessary information regarding a
+ * simple test expressed in XPath 2.0 language.
+ *
+ * @author Hiranya Jayathilaka, University of Moratuwa
+ */
+ private static class NodeTest {
+
+ private int fNodeType;
+ private NodeTest fParentNode = null;
+ private NodeTest[] fChildNodes = null;
+
+ private String fLHS = null;
+ private String fRHS = null;
+ private int fComparator;
+ private boolean fNumeric = false;
+
+ public NodeTest(int nodeType) {
+ this.fNodeType = nodeType;
+ if (nodeType == SyntaxTreeBuilder.TEST_CONJUNCTION_OR ||
+ nodeType == SyntaxTreeBuilder.TEST_CONJUNCTION_AND) {
+ fChildNodes = new NodeTest[2];
+ }
+ else if (nodeType == SyntaxTreeBuilder.TEST_CATEGORY_ROOT) {
+ fChildNodes = new NodeTest[1];
+ }
+ }
+
+ public int getType() {
+ return fNodeType;
+ }
+
+ public NodeTest[] getChildren() {
+ return fChildNodes;
+ }
+
+ public void setLHS(String text) {
+ fLHS = text;
+ }
+
+ public String getLHS() {
+ return fLHS;
+ }
+
+ public void setRHS(String text) {
+ fRHS = text;
+ }
+
+ public String getRHS() {
+ return fRHS;
+ }
+
+ public void setComparator(int comp) {
+ fComparator = comp;
+ }
+
+ public NodeTest getParent() {
+ return fParentNode;
+ }
+
+ private void setParent(NodeTest parentNode) {
+ this.fParentNode = parentNode;
+ }
+
+ public void setFirstChild(NodeTest test1) {
+ if (fChildNodes != null) {
+ fChildNodes[0] = test1;
+ fChildNodes[0].setParent(this);
+ }
+ }
+
+ public void setSecondChild(NodeTest test2) {
+ if (fChildNodes != null) {
+ fChildNodes[1] = test2;
+ fChildNodes[1].setParent(this);
+ }
+ }
+
+ public boolean getNumeric() {
+ return fNumeric;
+ }
+
+ public void setNumeric(boolean numeric) {
+ fNumeric = numeric;
+ }
+
+ /**
+ * Checks whether the NodeTest is complete. A NodeTest is complete if it has both
+ * an LHS and an RHS.
+ */
+ public boolean isComplete() {
+ if (fNodeType != SyntaxTreeBuilder.TEST_CONJUNCTION_AND &&
+ fNodeType != SyntaxTreeBuilder.TEST_CONJUNCTION_OR) {
+ return true;
+ }
+ else {
+ if (fChildNodes[0] != null && fChildNodes[1] != null) {
+ return true;
+ }
+ return false;
+ }
+ }
+
+ /**
+ * Evaluates the test expression stored in the node and returns a boolean
+ * value.
+ */
+ public boolean evaluateNodeTest(QName element, XMLAttributes attributes) {
+ Object actualVal1, actualVal2;
+ Augmentations aug1, aug2;
+ AttributePSVImpl attr1, attr2;
+ short variety;
+ XSSimpleTypeDecl type1, type2;
+
+ switch (fNodeType) {
+
+ case SyntaxTreeBuilder.TEST_CATEGORY_ATTR_VALUE:
+ aug1 = attributes.getAugmentations(fLHS);
+ if (aug1 == null) {
+ return false;
+ }
+
+ attr1 = (AttributePSVImpl) aug1.getItem(Constants.ATTRIBUTE_PSVI);
+ type1 = (XSSimpleTypeDecl) attr1.getTypeDefinition();
+ variety = type1.getVariety();
+ if (variety == XSSimpleTypeDefinition.VARIETY_UNION) {
+ type1 = (XSSimpleTypeDecl) attr1.getMemberTypeDefinition();
+ }
+
+ actualVal1 = attr1.getActualNormalizedValue();
+ try {
+ actualVal2 = type1.validate(fRHS, null, null); //convert the value to the type of the attribute
+ return DataMatcher.compareActualValues(actualVal1, actualVal2, fComparator, type1);
+ }
+ catch (InvalidDatatypeValueException e) {
+ return false;
+ }
+
+
+ case SyntaxTreeBuilder.TEST_CATEGORY_ATTR_VALUE_AS_CAST:
+ aug1 = attributes.getAugmentations(fLHS);
+ if (aug1 == null) {
+ return false;
+ }
+
+ attr1 = (AttributePSVImpl) aug1.getItem(Constants.ATTRIBUTE_PSVI);
+ type1 = (XSSimpleTypeDecl) attr1.getTypeDefinition();
+ variety = type1.getVariety();
+ if (variety == XSSimpleTypeDefinition.VARIETY_UNION) {
+ type1 = (XSSimpleTypeDecl) attr1.getMemberTypeDefinition();
+ }
+
+ actualVal1 = attr1.getActualNormalizedValue();
+ type2 = getCastedType(fRHS); //get the casted type
+ try {
+ //cast the value into the specified type
+ actualVal2 = type2.validate(getCastedValue(fRHS), null, null);
+ //check whether the casted type and the attribute type are comparable
+ if (DataMatcher.isComparable(
+ attr1.getActualNormalizedValueType(),
+ type2.getBuiltInKind(),
+ attr1.getItemValueTypes(),
+ null)) {
+
+ return DataMatcher.compareActualValues(actualVal1, actualVal2, fComparator, type1);
+ }
+ return false;
+ }
+ catch (InvalidDatatypeValueException e) {
+ return false;
+ }
+
+
+ case SyntaxTreeBuilder.TEST_CATEGORY_ATTR_AS_CAST_VALUE:
+ aug1 = attributes.getAugmentations(getCastedValue(fLHS));
+ if (aug1 == null) {
+ return false;
+ }
+
+ attr1 = (AttributePSVImpl) aug1.getItem(Constants.ATTRIBUTE_PSVI);
+ type1 = (XSSimpleTypeDecl) attr1.getTypeDefinition();
+ variety = type1.getVariety();
+ if (variety == XSSimpleTypeDefinition.VARIETY_UNION) {
+ type1 = (XSSimpleTypeDecl) attr1.getMemberTypeDefinition();
+ }
+
+ type2 = getCastedType(fLHS);
+ try {
+ //perform the specified cast on the attribute value
+ actualVal1 = type2.validate(attributes.getValue(getCastedValue(fLHS)), null, null);
+ //convert the given value into the attribute's casted type
+ actualVal2 = type2.validate(fRHS, null, null);
+ return DataMatcher.compareActualValues(actualVal1, actualVal2, fComparator, type2);
+ }
+ catch (InvalidDatatypeValueException e) {
+ return false;
+ }
+
+
+ case SyntaxTreeBuilder.TEST_CATEGORY_ATTR_AS_CAST_VALUE_AS_CAST:
+ aug1 = attributes.getAugmentations(getCastedValue(fLHS));
+ if (aug1 == null) {
+ return false;
+ }
+
+ type1 = getCastedType(fLHS);
+ type2 = getCastedType(fRHS);
+
+ //check whether the two casted types are comparable
+ if (DataMatcher.isComparable(
+ type1.getBuiltInKind(),
+ type2.getBuiltInKind(),
+ null,
+ null)) {
+
+ try {
+ //perform the two casts
+ actualVal1 = type1.validate(attributes.getValue(getCastedValue(fLHS)), null, null);
+ actualVal2 = type2.validate(getCastedValue(fRHS), null, null);
+ return DataMatcher.compareActualValues(actualVal1, actualVal2, fComparator, type1);
+ }
+ catch (InvalidDatatypeValueException e) {
+ return false;
+ }
+ }
+
+
+ case SyntaxTreeBuilder.TEST_CATEGORY_ATTR_ATTR:
+ aug1 = attributes.getAugmentations(fLHS);
+ aug2 = attributes.getAugmentations(fRHS);
+ if (aug1 == null || aug2 == null) {
+ return false;
+ }
+
+ attr1 = (AttributePSVImpl) aug1.getItem(Constants.ATTRIBUTE_PSVI);
+ attr2 = (AttributePSVImpl) aug2.getItem(Constants.ATTRIBUTE_PSVI);
+ type1 = (XSSimpleTypeDecl) attr1.getTypeDefinition();
+ type2 = (XSSimpleTypeDecl) attr2.getTypeDefinition();
+
+ //check whether the two attribute types are comparable
+ if (DataMatcher.isComparable(
+ attr1.getActualNormalizedValueType(),
+ attr2.getActualNormalizedValueType(),
+ attr1.getItemValueTypes(),
+ attr2.getItemValueTypes())) {
+
+ actualVal1 = attr1.getActualNormalizedValue();
+ actualVal2 = attr2.getActualNormalizedValue();
+ return DataMatcher.compareActualValues(actualVal1, actualVal2, fComparator, type1);
+ }
+ return false;
+
+
+ case SyntaxTreeBuilder.TEST_CATEGORY_ATTR_ATTR_AS_CAST:
+ aug1 = attributes.getAugmentations(fLHS);
+ aug2 = attributes.getAugmentations(fRHS);
+ if (aug1 == null || aug2 == null) {
+ return false;
+ }
+
+ attr1 = (AttributePSVImpl) aug1.getItem(Constants.ATTRIBUTE_PSVI);
+ type1 = (XSSimpleTypeDecl) attr1.getTypeDefinition();
+ type2 = getCastedType(fRHS);
+
+ //check whether the two attribute types are comparable
+ if (DataMatcher.isComparable(
+ attr1.getActualNormalizedValueType(),
+ type2.getBuiltInKind(),
+ attr1.getItemValueTypes(),
+ null)) {
+
+ actualVal1 = attr1.getActualNormalizedValue();
+ try {
+ //perform the cast
+ actualVal2 = type2.validate(attributes.getValue(getCastedValue(fRHS)), null, null);
+ return DataMatcher.compareActualValues(actualVal1, actualVal2, fComparator, type1);
+ }
+ catch (InvalidDatatypeValueException e) {
+ return false;
+ }
+ }
+ return false;
+
+
+ case SyntaxTreeBuilder.TEST_CATEGORY_ATTR_AS_CAST_ATTR:
+ aug1 = attributes.getAugmentations(fLHS);
+ aug2 = attributes.getAugmentations(fRHS);
+ if (aug1 == null || aug2 == null) {
+ return false;
+ }
+
+ attr2 = (AttributePSVImpl) aug2.getItem(Constants.ATTRIBUTE_PSVI);
+ type2 = (XSSimpleTypeDecl) attr2.getTypeDefinition();
+ type1 = getCastedType(fLHS);
+
+ //check whether the two attribute types are comparable
+ if (DataMatcher.isComparable(
+ type1.getBuiltInKind(),
+ attr2.getActualNormalizedValueType(),
+ null,
+ attr2.getItemValueTypes())) {
+
+ actualVal2 = attr2.getActualNormalizedValue();
+ try {
+ //perform the cast
+ actualVal1 = type1.validate(attributes.getValue(getCastedValue(fRHS)), null, null);
+ return DataMatcher.compareActualValues(actualVal1, actualVal2, fComparator, type1);
+ }
+ catch (InvalidDatatypeValueException e) {
+ return false;
+ }
+ }
+ return false;
+
+
+ case SyntaxTreeBuilder.TEST_CATEGORY_ATTR_AS_CAST_ATTR_AS_CAST:
+ aug1 = attributes.getAugmentations(fLHS);
+ aug2 = attributes.getAugmentations(fRHS);
+ if (aug1 == null || aug2 == null) {
+ return false;
+ }
+
+ attr1 = (AttributePSVImpl) aug1.getItem(Constants.ATTRIBUTE_PSVI);
+ type1 = getCastedType(fLHS);
+ type2 = getCastedType(fRHS);
+
+ //check whether the two casted types are comparable
+ if (DataMatcher.isComparable(
+ type1.getBuiltInKind(),
+ type2.getBuiltInKind(),
+ null,
+ null)) {
+
+ try {
+ //perform the two casts
+ actualVal1 = type1.validate(attributes.getValue(getCastedValue(fLHS)), null, null);
+ actualVal2 = type2.validate(attributes.getValue(getCastedValue(fRHS)), null, null);
+ return DataMatcher.compareActualValues(actualVal1, actualVal2, fComparator, type1);
+ }
+ catch (InvalidDatatypeValueException e) {
+ return false;
+ }
+ }
+ return false;
+
+
+ case SyntaxTreeBuilder.TEST_CATEGORY_VALUE_VALUE:
+ //treat both values as strings and compare
+ if (!fNumeric) {
+ return DataMatcher.compareActualValues(fLHS, fRHS, fComparator,
+ (XSSimpleTypeDecl) SchemaDVFactory.getInstance().getBuiltInType("string"));
+ }
+ else {
+ type1 = (XSSimpleTypeDecl) SchemaDVFactory.getInstance().
+ getBuiltInType("decimal");
+ try {
+ actualVal1 = type1.validate(fLHS, null, null);
+ actualVal2 = type1.validate(fRHS, null, null);
+ return DataMatcher.compareActualValues(actualVal1, actualVal2, fComparator, type1);
+ }
+ catch (InvalidDatatypeValueException e) {
+ return false;
+ }
+ }
+
+
+
+ case SyntaxTreeBuilder.TEST_CATEGORY_VALUE_VALUE_AS_CAST:
+ type2 = getCastedType(fRHS);
+ try {
+ //convert both values to the casted type
+ actualVal1 = type2.validate(getCastedValue(fLHS), null, null);
+ actualVal2 = type2.validate(getCastedValue(fRHS), null, null);
+ return DataMatcher.compareActualValues(actualVal1, actualVal2, fComparator, type2);
+ }
+ catch (InvalidDatatypeValueException e) {
+ return false;
+ }
+
+
+ case SyntaxTreeBuilder.TEST_CATEGORY_VALUE_AS_CAST_VALUE:
+ type1 = getCastedType(fLHS);
+ try {
+ //convert both values to the casted type
+ actualVal1 = type1.validate(getCastedValue(fLHS), null, null);
+ actualVal2 = type1.validate(getCastedValue(fRHS), null, null);
+ return DataMatcher.compareActualValues(actualVal1, actualVal2, fComparator, type1);
+ }
+ catch (InvalidDatatypeValueException e) {
+ return false;
+ }
+
+
+ case SyntaxTreeBuilder.TEST_CATEGORY_VALUE_AS_CAST_VALUE_AS_CAST:
+ type1 = getCastedType(fLHS);
+ type2 = getCastedType(fRHS);
+
+ //check whether the two casted types are comparable
+ if (DataMatcher.isComparable(
+ type1.getBuiltInKind(),
+ type2.getBuiltInKind(),
+ null,
+ null)) {
+ try {
+ actualVal1 = type1.validate(getCastedValue(fLHS), null, null);
+ actualVal2 = type1.validate(getCastedValue(fRHS), null, null);
+ return DataMatcher.compareActualValues(actualVal1, actualVal2, fComparator, type1);
+ }
+ catch (InvalidDatatypeValueException e) {
+ return false;
+ }
+ }
+ return false;
+
+ case SyntaxTreeBuilder.TEST_CATEGORY_VALUE:
+ if (fNumeric) {
+ //if numeric treat as decimal
+ type1 = (XSSimpleTypeDecl) SchemaDVFactory.getInstance().getBuiltInType("decimal");
+ try {
+ //see whether the value is numerically equal to 0
+ actualVal1 = type1.validate(fLHS, null, null);
+ actualVal2 = type1.validate("0", null, null);
+ return !DataMatcher.compareActualValues(actualVal1, actualVal2,
+ Tokens.EXPRTOKEN_COMPARATOR_EQUAL, type1);
+ }
+ catch (InvalidDatatypeValueException e) {
+ return false;
+ }
+ }
+ return true;
+
+ case SyntaxTreeBuilder.TEST_CATEGORY_VALUE_AS_CAST:
+ type1 = getCastedType(fLHS);
+ try {
+ //perform the cast
+ actualVal1 = type1.validate(getCastedValue(fLHS), null, null);
+ actualVal2 = type1.validate("0", null, null);
+ }
+ catch (InvalidDatatypeValueException e) {
+ return false;
+ }
+
+ if (actualVal1 == null) {
+ return false;
+ }
+ else if (type1.getNumeric()) {
+ return !DataMatcher.compareActualValues(actualVal1, actualVal2,
+ Tokens.EXPRTOKEN_COMPARATOR_EQUAL, type1);
+ }
+
+
+ case SyntaxTreeBuilder.TEST_CATEGORY_ATTR:
+ aug1 = attributes.getAugmentations(fLHS);
+ if (aug1 == null) {
+ return false;
+ }
+ attr1 = (AttributePSVImpl) aug1.getItem(Constants.ATTRIBUTE_PSVI);
+ type1 = (XSSimpleTypeDecl) attr1.getTypeDefinition();
+ actualVal1 = attr1.getActualNormalizedValue();
+ if (actualVal1 == null) {
+ return false;
+ }
+
+ if (type1.getNumeric()) {
+ try {
+ actualVal2 = type1.validate("0", null, null);
+ return !DataMatcher.compareActualValues(actualVal1, actualVal2,
+ Tokens.EXPRTOKEN_COMPARATOR_EQUAL, type1);
+ }
+ catch (InvalidDatatypeValueException e) {
+ return false;
+ }
+ }
+ return true;
+
+
+ case SyntaxTreeBuilder.TEST_CATEGORY_ATTR_AS_CAST:
+ aug1 = attributes.getAugmentations(getCastedValue(fLHS));
+ if (aug1 == null) {
+ return false;
+ }
+ attr1 = (AttributePSVImpl) aug1.getItem(Constants.ATTRIBUTE_PSVI);
+ type1 = getCastedType(fLHS);
+ actualVal1 = attr1.getActualNormalizedValue();
+ if (actualVal1 == null) {
+ return false;
+ }
+
+ if (type1.getNumeric()) {
+ try {
+ actualVal1 = type1.validate(attributes.getValue(getCastedValue(fLHS)), null, null);
+ actualVal2 = type1.validate("0", null, null);
+ return !DataMatcher.compareActualValues(actualVal1, actualVal2,
+ Tokens.EXPRTOKEN_COMPARATOR_EQUAL, type1);
+ }
+ catch (InvalidDatatypeValueException e) {
+ return false;
+ }
+ }
+ return true;
+ }
+ return false;
+ }
+
+ private XSSimpleTypeDecl getCastedType(String castExpr) {
+ int start = castExpr.indexOf(" cast as ", 0) + 9;
+ int end = castExpr.indexOf(" ?", start);
+ String qname = castExpr.substring(start, end);
+ String[] qnameElements = qname.split(":");
+ return (XSSimpleTypeDecl) SchemaDVFactory.getInstance().getBuiltInType(qnameElements[1]);
+ }
+
+ private String getCastedValue(String castExpr) {
+ int end = castExpr.indexOf(" cast as ", 0);
+ return castExpr.substring(0, end);
+ }
+
+ }
+
+ /**
+ * This class provides the necessary means to compare actual values. This
+ * functionality is used during the XPath evaluation.
+ *
+ * @author Hiranya Jayathilaka, University of Moratuwa
+ */
+ private static class DataMatcher {
+
+ public static boolean compareActualValues(Object value1, Object value2, int comparator,
+ XSSimpleTypeDecl type) {
+
+ TypeValidator typeValidator = type.getTypeValidator();
+ short ordered = type.getOrdered();
+
+ if (ordered == XSSimpleTypeDecl.ORDERED_FALSE) {
+ //if the type is not ordered then only equality can be tested
+ //delegate the test to the type
+ if (comparator == Tokens.EXPRTOKEN_COMPARATOR_EQUAL) {
+ return type.isEqual(value1, value2);
+ }
+ else if (comparator == Tokens.EXPRTOKEN_COMPARATOR_NOT_EQUAL) {
+ return !type.isEqual(value1, value2);
+ }
+ else {
+ //only equality can be tested upon unordered types
+ return false;
+ }
+ }
+
+ //if the type is ordered then the corresponding TypeValidator should
+ //know how to compare the values
+ switch (comparator) {
+
+ case Tokens.EXPRTOKEN_COMPARATOR_EQUAL:
+ return typeValidator.compare(value1, value2) == 0;
+
+ case Tokens.EXPRTOKEN_COMPARATOR_NOT_EQUAL:
+ return typeValidator.compare(value1, value2) != 0;
+
+ case Tokens.EXPRTOKEN_COMPARATOR_GREATER:
+ return typeValidator.compare(value1, value2) > 0;
+
+ case Tokens.EXPRTOKEN_COMPARATOR_GREATER_EQUAL:
+ return typeValidator.compare(value1, value2) >= 0;
+
+ case Tokens.EXPRTOKEN_COMPARATOR_LESS:
+ return typeValidator.compare(value1, value2) < 0;
+
+ case Tokens.EXPRTOKEN_COMPARATOR_LESS_EQUAL:
+ return typeValidator.compare(value1, value2) <= 0;
+ }
+ return false;
+ }
+
+ public static boolean compareLists(XSSimpleTypeDecl type) {
+ return false;
+ }
+
+ /**
+ * Checks whether two specified data types are comparable. The types passed
+ * into this method should be defined in XSConstants as *_DT values.
+ */
+ public static boolean isComparable(short type1, short type2,
+ ShortList typeList1, ShortList typeList2) {
+
+ short primitiveType1 = convertToPrimitiveKind(type1);
+ short primitiveType2 = convertToPrimitiveKind(type2);
+
+ if (primitiveType1 != primitiveType2) {
+ return (primitiveType1 == XSConstants.ANYSIMPLETYPE_DT && primitiveType2 == XSConstants.STRING_DT ||
+ primitiveType1 == XSConstants.STRING_DT && primitiveType2 == XSConstants.ANYSIMPLETYPE_DT);
+ }
+ else if (primitiveType1 == XSConstants.LIST_DT || primitiveType1 == XSConstants.LISTOFUNION_DT) {
+ final int typeList1Length = typeList1 != null ? typeList1.getLength() : 0;
+ final int typeList2Length = typeList2 != null ? typeList2.getLength() : 0;
+ if (typeList1Length != typeList2Length) {
+ return false;
+ }
+ for (int i = 0; i < typeList1Length; ++i) {
+ final short primitiveItem1 = convertToPrimitiveKind(typeList1.item(i));
+ final short primitiveItem2 = convertToPrimitiveKind(typeList2.item(i));
+ if (primitiveItem1 != primitiveItem2) {
+ if (primitiveItem1 == XSConstants.ANYSIMPLETYPE_DT && primitiveItem2 == XSConstants.STRING_DT ||
+ primitiveItem1 == XSConstants.STRING_DT && primitiveItem2 == XSConstants.ANYSIMPLETYPE_DT) {
+ continue;
+ }
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ private static short convertToPrimitiveKind(short valueType) {
+ //Primitive data types
+ if (valueType <= XSConstants.NOTATION_DT) {
+ return valueType;
+ }
+ // Types derived from string
+ if (valueType <= XSConstants.ENTITY_DT) {
+ return XSConstants.STRING_DT;
+ }
+ // Types derived from decimal
+ if (valueType <= XSConstants.POSITIVEINTEGER_DT) {
+ return XSConstants.DECIMAL_DT;
+ }
+ // Other types
+ return valueType;
+ }
+ }
+
}
Modified: xerces/java/branches/xml-schema-1.1-dev/src/org/apache/xerces/impl/xs/XMLSchemaValidator.java
URL: http://svn.apache.org/viewvc/xerces/java/branches/xml-schema-1.1-dev/src/org/apache/xerces/impl/xs/XMLSchemaValidator.java?rev=690999&r1=690998&r2=690999&view=diff
==============================================================================
--- xerces/java/branches/xml-schema-1.1-dev/src/org/apache/xerces/impl/xs/XMLSchemaValidator.java (original)
+++ xerces/java/branches/xml-schema-1.1-dev/src/org/apache/xerces/impl/xs/XMLSchemaValidator.java Mon Sep 1 08:40:17 2008
@@ -36,6 +36,8 @@
import org.apache.xerces.impl.validation.ConfigurableValidationState;
import org.apache.xerces.impl.validation.ValidationManager;
import org.apache.xerces.impl.validation.ValidationState;
+import org.apache.xerces.impl.xs.alternative.Test;
+import org.apache.xerces.impl.xs.alternative.XSTypeAlternativeImpl;
import org.apache.xerces.impl.xs.identity.Field;
import org.apache.xerces.impl.xs.identity.FieldActivator;
import org.apache.xerces.impl.xs.identity.IdentityConstraint;
@@ -187,6 +189,10 @@
protected static final String IDENTITY_CONSTRAINT_CHECKING =
Constants.XERCES_FEATURE_PREFIX + Constants.IDC_CHECKING_FEATURE;
+ /** Feature identifier: whether to ignore type alternatives */
+ protected static final String TYPE_ALTERNATIVES_CHECKING =
+ Constants.XERCES_FEATURE_PREFIX + Constants.TYPE_ALTERNATIVES_CHEKING_FEATURE;
+
// property identifiers
/** Property identifier: symbol table. */
@@ -255,6 +261,7 @@
ID_IDREF_CHECKING,
IDENTITY_CONSTRAINT_CHECKING,
UNPARSED_ENTITY_CHECKING,
+ TYPE_ALTERNATIVES_CHECKING,
};
@@ -284,6 +291,7 @@
null,
null,
null,
+ null,
};
/** Recognized properties. */
@@ -1224,6 +1232,8 @@
private boolean fIDCChecking;
+ private boolean fTypeAlternativesChecking;
+
/** temporary validated info */
private ValidatedInfo fValidatedInfo = new ValidatedInfo();
@@ -1450,6 +1460,13 @@
catch (XMLConfigurationException e) {
fValidationState.setUnparsedEntityChecking(true);
}
+
+ try {
+ fTypeAlternativesChecking = componentManager.getFeature(TYPE_ALTERNATIVES_CHECKING);
+ }
+ catch (XMLConfigurationException e) {
+ fTypeAlternativesChecking = true;
+ }
// get schema location properties
try {
@@ -2159,6 +2176,29 @@
matcher.startElement( element, attributes);
}
+ //process type alternatives
+ if (fTypeAlternativesChecking) {
+ boolean typeSelected = false;
+ XSTypeAlternativeImpl[] alternatives = fCurrentElemDecl.getTypeAlternatives();
+ if (alternatives != null) {
+ for (int i = 0; i < alternatives.length; i++) {
+ Test test = alternatives[i].getTest();
+ if (test != null && test.evaluateTest(element, attributes)) {
+ fCurrentType = alternatives[i].getTypeDefinition();
+ typeSelected = true;
+ break;
+ }
+ }
+ //if a type is not selected try to assign the default type
+ if (!typeSelected) {
+ XSTypeAlternativeImpl defType = fCurrentElemDecl.getDefaultTypeDefinition();
+ if (defType != null) {
+ fCurrentType = defType.getTypeDefinition();
+ }
+ }
+ }
+ }
+
if (fAugPSVI) {
augs = getEmptyAugs(augs);
Modified: xerces/java/branches/xml-schema-1.1-dev/src/org/apache/xerces/impl/xs/alternative/Test.java
URL: http://svn.apache.org/viewvc/xerces/java/branches/xml-schema-1.1-dev/src/org/apache/xerces/impl/xs/alternative/Test.java?rev=690999&r1=690998&r2=690999&view=diff
==============================================================================
--- xerces/java/branches/xml-schema-1.1-dev/src/org/apache/xerces/impl/xs/alternative/Test.java (original)
+++ xerces/java/branches/xml-schema-1.1-dev/src/org/apache/xerces/impl/xs/alternative/Test.java Mon Sep 1 08:40:17 2008
@@ -18,12 +18,14 @@
package org.apache.xerces.impl.xs.alternative;
import org.apache.xerces.impl.xpath.XPath20;
+import org.apache.xerces.xni.QName;
+import org.apache.xerces.xni.XMLAttributes;
/**
* XML schema type alternative test attribute
*
* @author Hiranya Jayathilaka, University of Moratuwa
- * @version $Id:$
+ * @version $Id$
*/
public class Test {
@@ -48,6 +50,16 @@
return fXPath;
}
+ /** Evaluate the test expression with respect to the specified element and its attributes */
+ public boolean evaluateTest(QName element, XMLAttributes attributes) {
+ if (fXPath != null) {
+ return fXPath.traverseTree(element, attributes);
+ }
+ else {
+ return false;
+ }
+ }
+
public String toString() {
return fXPath.toString();
}
Modified: xerces/java/branches/xml-schema-1.1-dev/src/org/apache/xerces/jaxp/validation/XMLSchemaValidatorComponentManager.java
URL: http://svn.apache.org/viewvc/xerces/java/branches/xml-schema-1.1-dev/src/org/apache/xerces/jaxp/validation/XMLSchemaValidatorComponentManager.java?rev=690999&r1=690998&r2=690999&view=diff
==============================================================================
--- xerces/java/branches/xml-schema-1.1-dev/src/org/apache/xerces/jaxp/validation/XMLSchemaValidatorComponentManager.java (original)
+++ xerces/java/branches/xml-schema-1.1-dev/src/org/apache/xerces/jaxp/validation/XMLSchemaValidatorComponentManager.java Mon Sep 1 08:40:17 2008
@@ -81,6 +81,10 @@
/** Feature identifier: whether to ignore identity constraint errors */
protected static final String IDENTITY_CONSTRAINT_CHECKING =
Constants.XERCES_FEATURE_PREFIX + Constants.IDC_CHECKING_FEATURE;
+
+ /** Feature identifier: whether to ignore type alternatives errors */
+ protected static final String TYPE_ALTERNATIVES_CHECKING =
+ Constants.XERCES_FEATURE_PREFIX + Constants.TYPE_ALTERNATIVES_CHEKING_FEATURE;
// property identifiers
@@ -243,6 +247,7 @@
fFeatures.put(ID_IDREF_CHECKING, Boolean.TRUE);
fFeatures.put(IDENTITY_CONSTRAINT_CHECKING, Boolean.TRUE);
fFeatures.put(UNPARSED_ENTITY_CHECKING, Boolean.TRUE);
+ fFeatures.put(TYPE_ALTERNATIVES_CHECKING, Boolean.TRUE);
}
/**
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@xerces.apache.org
For additional commands, e-mail: commits-help@xerces.apache.org