Package org.apache.xerces.impl.xs

Source Code of org.apache.xerces.impl.xs.XMLAssertPsychopathImpl

/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements.  See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* 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.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.apache.xerces.impl.xs;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Stack;
import java.util.StringTokenizer;
import java.util.Vector;

import org.apache.xerces.dom.CoreDocumentImpl;
import org.apache.xerces.dom.PSVIAttrNSImpl;
import org.apache.xerces.dom.PSVIDocumentImpl;
import org.apache.xerces.dom.PSVIElementNSImpl;
import org.apache.xerces.impl.Constants;
import org.apache.xerces.impl.dv.XSSimpleType;
import org.apache.xerces.impl.xs.assertion.XMLAssertAdapter;
import org.apache.xerces.impl.xs.assertion.XSAssertImpl;
import org.apache.xerces.impl.xs.util.XSTypeHelper;
import org.apache.xerces.util.XMLChar;
import org.apache.xerces.xni.Augmentations;
import org.apache.xerces.xni.QName;
import org.apache.xerces.xni.XMLAttributes;
import org.apache.xerces.xni.XMLString;
import org.apache.xerces.xs.ElementPSVI;
import org.apache.xerces.xs.XSComplexTypeDefinition;
import org.apache.xerces.xs.XSConstants;
import org.apache.xerces.xs.XSElementDeclaration;
import org.apache.xerces.xs.XSModel;
import org.apache.xerces.xs.XSMultiValueFacet;
import org.apache.xerces.xs.XSObjectList;
import org.apache.xerces.xs.XSSimpleTypeDefinition;
import org.apache.xerces.xs.XSTypeDefinition;
import org.eclipse.wst.xml.xpath2.processor.DynamicContext;
import org.eclipse.wst.xml.xpath2.processor.DynamicError;
import org.eclipse.wst.xml.xpath2.processor.PsychoPathTypeHelper;
import org.eclipse.wst.xml.xpath2.processor.ResultSequence;
import org.eclipse.wst.xml.xpath2.processor.ResultSequenceFactory;
import org.eclipse.wst.xml.xpath2.processor.StaticError;
import org.eclipse.wst.xml.xpath2.processor.ast.XPath;
import org.eclipse.wst.xml.xpath2.processor.internal.types.AnyAtomicType;
import org.eclipse.wst.xml.xpath2.processor.internal.types.AnyType;
import org.eclipse.wst.xml.xpath2.processor.internal.types.SchemaTypeValueFactory;
import org.w3c.dom.DOMException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

/**
* Class implementing an XPath interface for XML Schema 1.1 "assertions" evaluation.
* This class interfaces with the "Eclipse/PsychoPath XPath 2.0" engine for XPath
* expression evaluations for XML Schema assertions.
*
* The class here constructs Xerces PSVI enabled DOM trees -- "on which
* PsychoPath XPath engine operates" (for typed XDM instance support) from
* XNI event calls. XML Schema assertions are evaluated on these XPath tree
* instances in a bottom up fashion.
*
* @xerces.internal
*
* @author Mukul Gandhi, IBM
* @author Ken Cai, IBM
*
* @version $Id: XMLAssertPsychopathImpl.java 1035484 2010-11-15 22:54:30Z mukulg $
*/
public class XMLAssertPsychopathImpl extends XMLAssertAdapter {

    // class variable declarations
    private DynamicContext fDynamicContext;
    private XSModel fSchema = null;
    private AbstractPsychoPathImpl fAbstrPsychopathImpl = null;

    // the DOM root of assertions tree
    private Document fAssertDocument = null;

    // an element to track construction of assertion DOM tree. Value of this object changes as per
    // the XNI document events.
    private Element fCurrentAssertDomNode = null;

    // a stack holding the DOM roots for assertions evaluation
    private Stack fAssertRootStack = null;

    // a stack parallel to 'assertRootStack' storing all assertions for a single XDM tree.
    private Stack fAssertListStack = null;

    // XMLSchemaValidator reference. set from the XMLSchemaValidator object itself.
    private XMLSchemaValidator fValidator = null;
   
    // parameters to pass to PsychoPath engine (like, the XML namespace bindings).
    private Map fAssertParams = null;
   
    // an instance variable to track the name of an attribute currently been processed for assertions.
    private String fAttrName = null;
   
    // a placeholder definition used for assertions error messages.
    private final String ERROR_PLACEHOLDER_REGEX = "\\{\\$value\\}";

   
    /*
     * Class constructor.
     */
    public XMLAssertPsychopathImpl(Map assertParams) {       
        // initializing the class variables
        this.fAssertDocument = new PSVIDocumentImpl();       
        this.fAssertRootStack = new Stack();
        this.fAssertListStack = new Stack();
        this.fAssertParams = assertParams;
    }
   

    /*
     * Initialize the PsychoPath XPath processor.
     */
    private void initXPathProcessor() throws Exception {       
        fValidator = (XMLSchemaValidator) getProperty("http://apache.org/xml/properties/assert/validator");       
        fAbstrPsychopathImpl = new AbstractPsychoPathImpl();
        fDynamicContext = fAbstrPsychopathImpl.initDynamicContext(fSchema, fAssertDocument, fAssertParams);       
    } // initXPathProcessor
   

    /*
     * (non-Javadoc)
     * @see org.apache.xerces.xni.parser.XMLAssertAdapter#startElement
     *      (org.apache.xerces.xni.QName, org.apache.xerces.xni.XMLAttributes,
     *       org.apache.xerces.xni.Augmentations)
     */
    public void startElement(QName element, XMLAttributes attributes, Augmentations augs) {
       
        if (fCurrentAssertDomNode == null) {
           fCurrentAssertDomNode = new PSVIElementNSImpl((CoreDocumentImpl) fAssertDocument, element.uri, element.rawname);
           fAssertDocument.appendChild(fCurrentAssertDomNode);
        } else {
            Element elem = new PSVIElementNSImpl((CoreDocumentImpl) fAssertDocument, element.uri, element.rawname);
            fCurrentAssertDomNode.appendChild(elem);
            fCurrentAssertDomNode = elem;
        }

        // add attribute nodes to DOM element node
        final int attributesLength = attributes.getLength();
        for (int attIndex = 0; attIndex < attributesLength; attIndex++) {
            String attrUri = attributes.getURI(attIndex);
            String attQName = attributes.getQName(attIndex);
            String attrLocalName = attributes.getLocalName(attIndex);
            String attValue = attributes.getValue(attIndex);            
           
            PSVIAttrNSImpl attrNode = new PSVIAttrNSImpl((PSVIDocumentImpl)fAssertDocument, attrUri, attQName, attrLocalName);
            attrNode.setNodeValue(attValue);
           
            // set PSVI information for the attribute
            Augmentations attrAugs = attributes.getAugmentations(attIndex);
            AttributePSVImpl attrPSVI = (AttributePSVImpl) attrAugs.getItem(Constants.ATTRIBUTE_PSVI);
            attrNode.setPSVI(attrPSVI);
           
            fCurrentAssertDomNode.setAttributeNode(attrNode);
        }

        List assertionList = (List) augs.getItem("ASSERT");
       
        // if we have assertions applicable to this element, store the element reference and the assertions on it,
        // on the runtime stacks.
        if (assertionList != null) {
            fAssertRootStack.push(fCurrentAssertDomNode);
            fAssertListStack.push(assertionList);
        }
       
    } // startElement
   

    /*
     * (non-Javadoc)
     * @see org.apache.xerces.xni.parser.XMLAssertAdapter#endElement(org.apache.xerces.xni.QName,
     *      org.apache.xerces.xni.Augmentations)
     */
    public void endElement(QName element, Augmentations augs) throws Exception {
       
        if (fCurrentAssertDomNode != null) {
            // set PSVI information on the element
            ElementPSVI elemPSVI = (ElementPSVI) augs.getItem(Constants.ELEMENT_PSVI);
            ((PSVIElementNSImpl) fCurrentAssertDomNode).setPSVI(elemPSVI);
      
            // itemType for xs:list
            XSSimpleTypeDefinition itemType = null;
           
            // memberTypes for xs:union
            XSObjectList memberTypes = null;
           
            if (elemPSVI.getTypeDefinition().getTypeCategory() == XSTypeDefinition.SIMPLE_TYPE) {
                XSSimpleTypeDefinition simpleTypeDefn = (XSSimpleTypeDefinition) elemPSVI.getTypeDefinition();
                itemType = simpleTypeDefn.getItemType();
                if (itemType == null) {
                    memberTypes = simpleTypeDefn.getMemberTypes();   
                }
            }
           
            if (!fAssertRootStack.empty() && (fCurrentAssertDomNode == fAssertRootStack.peek())) {              
                 // get XSModel instance               
                 fSchema =  ((PSVIElementNSImpl) fCurrentAssertDomNode).getSchemaInformation();
                
                 // pop the stack, to go one level up
                 fAssertRootStack.pop();
                 // get assertions, and go one level up on the stack
                 List assertions = (List) fAssertListStack.pop();
                 Boolean atomicValueValidity = (Boolean) augs.getItem("ATOMIC_VALUE_VALIDITY");
                 if (atomicValueValidity.booleanValue()) {                   
                    // depending on simple content's validity status from XMLSchemaValidator, process
                    // XML schema assertions.
                    processAllAssertionsOnElement(element, itemType, memberTypes, assertions, elemPSVI);
                 }
            }

            if (fCurrentAssertDomNode.getParentNode() instanceof Element) {
                fCurrentAssertDomNode = (Element)fCurrentAssertDomNode.getParentNode();
            }
        }
       
    } // endElement
   

    /*
     * Method to evaluate all of XML schema 1.1 assertions for an element tree. This is the root method
     * which evaluates all XML schema assertions, in a single XML instance validation episode.
     */
    private void processAllAssertionsOnElement(QName element, XSSimpleTypeDefinition itemType, XSObjectList memberTypes,
                                               List assertions, ElementPSVI elemPSVI)
                                               throws Exception {
        
         // initialize the XPath engine
         initXPathProcessor();
        
         // determine "string value" of XPath2 context variable, $value
         String value = getStringValueOf$Value(elemPSVI);

         // evaluate assertions
         if (assertions instanceof XSObjectList) {
            // assertions from a "complex type" definition            
            evaluateAssertionsFromAComplexType(element, assertions, value);           
         }
         else if (assertions instanceof Vector) {
            // assertions from a "simple type" definition
            evaluateAssertionsFromASimpleType(element, itemType, memberTypes, assertions, value);           
         }
        
    } // processAllAssertionsOnElement


    /*
     * Determine "string value" of XPath2 context variable $value.
     */
    private String getStringValueOf$Value(ElementPSVI pElemPSVI) throws DOMException {
       
        int textChildCount = 0;       
        // we are only interested in text & element nodes. Store count of them in this variable.
        int effectiveChildCount = 0;
       
        // there could be adjacent text nodes in a DOM tree. merge them to get the value.
        NodeList childList = fCurrentAssertDomNode.getChildNodes();
        StringBuffer textValueContents = new StringBuffer();
        final int childListLength = childList.getLength();
        for (int childNodeIndex = 0; childNodeIndex < childListLength; childNodeIndex++) {
            Node node = childList.item(childNodeIndex);
            short nodeType = node.getNodeType();
            if (nodeType == Node.TEXT_NODE) {
                textChildCount++;
                effectiveChildCount++;
                textValueContents.append(node.getNodeValue());
            }
            else if (nodeType == Node.ELEMENT_NODE) {
                effectiveChildCount++; 
            }
        }
       
        String value = "";
       
        if (textChildCount == effectiveChildCount) {
            // the DOM tree we are inspecting has simple content. therefore we can find the desired string value.
            XSElementDeclaration elemDecl = pElemPSVI.getElementDeclaration();
            if ((elemDecl.getTypeDefinition()).derivedFrom(SchemaSymbols.URI_SCHEMAFORSCHEMA,
                                                           SchemaSymbols.ATTVAL_STRING,
                                                           XSConstants.DERIVATION_RESTRICTION)) {
                // if element's schema type is derived by restriction from xs:string, white-space normalization is
                // not needed for the string value for context variable $value.
                value = textValueContents.toString()
            }
            else {
                // white-space normalization is needed for the string value of $value in case of derivation from
                // non xs:string atomic types.
                value = XMLChar.trim(textValueContents.toString());
            }   
        }
        else {
            // the DOM tree we are inspecting, has 'mixed/element only' content.
            value = null;
        }
       
        return value;
       
    } // getStringValueOf$Value


    /*
     * Evaluate assertions on a "complex type".
     */
    private void evaluateAssertionsFromAComplexType(QName element, List assertions, String value)
                                                    throws Exception {
        if (value != null) {
            // complex type with simple content
            setTypedValueFor$value(value, null, null);
        } else {
            // complex type with complex content. assign an empty XPath2 sequence to xpath context variable $value.
            fDynamicContext.set_variable(new org.eclipse.wst.xml.xpath2.processor.internal.types.QName(
                                             "value"), getXPath2ResultSequence(new ArrayList()));
        }
       
        XSObjectList assertList = (XSObjectList) assertions;
        XSObjectList attrMemberTypes = null;
        final int assertListSize = assertList.size();
        for (int i = 0; i < assertListSize; i++) {
            XSAssertImpl assertImpl = (XSAssertImpl) assertList.get(i);              
            boolean xpathContextExists = false;
            if (assertImpl.getType() == XSConstants.ASSERTION) {
                // not an assertion facet
                xpathContextExists = true;  
            }
            // check if this is an assertion, from an attribute
            if (assertImpl.getAttrName() != null) {
                value = assertImpl.getAttrValue();
                XSSimpleTypeDefinition attrType = (XSSimpleTypeDefinition) assertImpl.getTypeDefinition();
                attrMemberTypes = attrType.getMemberTypes();
                if (assertImpl.getVariety() == XSSimpleTypeDefinition.VARIETY_LIST) {
                    // this assertion belongs to a type, that is an item type of a "simpleType -> list".
                    // tokenize the list value by the longest sequence of white-spaces.
                    StringTokenizer values = new StringTokenizer(value, " \n\t\r");
                   
                    // evaluate assertion on all of list items
                    while (values.hasMoreTokens()) {
                        String itemValue = values.nextToken();
                        setValueOf$valueForAListItem(attrType, itemValue);                       
                        AssertionError assertError = evaluateAssertion(element, assertImpl, itemValue,
                                                                       xpathContextExists, true);
                        if (assertError != null) {
                            reportAssertionsError(assertError);   
                        }
                    }
                }
                else if (assertImpl.getVariety() == XSSimpleTypeDefinition.VARIETY_ATOMIC) {
                    // evaluating assertions for "simpleType -> restriction"
                    setTypedValueFor$value(value, null, attrType);
                    AssertionError assertError = evaluateAssertion(element, assertImpl, value,
                                                                   xpathContextExists, false);
                    if (assertError != null) {
                        reportAssertionsError(assertError);   
                    }
                }               
            }
            else {
                AssertionError assertError = evaluateAssertion(element, assertImpl, value,
                                                               xpathContextExists, false);
                if (assertError != null) {
                    reportAssertionsError(assertError);   
               
            }
        }

        // evaluate assertions on "simpleType -> union" on an attribute
        if (attrMemberTypes != null && attrMemberTypes.getLength() > 0) {               
            boolean isValidationFailedForUnion = isValidationFailedForUnion(attrMemberTypes, element, value, true);

            if (isValidationFailedForUnion) {
                // none of the member types of union (the assertions in them) can successfully validate an atomic value.
                // this results in an overall validation failure. report an error message.
                fValidator.reportSchemaError("cvc-assertion.attr.union.3.13.4.1", new Object[] {
                                             element.rawname, fAttrName, value } );  
            }

            fAttrName = null;           
        }
       
    } // evaluateAssertionsFromAComplexType
   
   
    /*
     * Evaluate assertions on a "simple type".
     */
    private void evaluateAssertionsFromASimpleType(QName element, XSSimpleTypeDefinition itemType,
                                                   XSObjectList memberTypes, List assertions, String value)                                            
                                                   throws Exception {
       
        // assertions from a simple type definition          
        Vector assertList = (Vector) assertions;
        final int assertListLength = assertList.size();
        for (int i = 0; i < assertListLength; i++) {
            XSAssertImpl assertImpl = (XSAssertImpl) assertList.get(i);
            if (itemType != null) {
               // evaluating assertions for "simpleType -> list". tokenize the list value by the longest sequence of
               // white-spaces.
               StringTokenizer values = new StringTokenizer(value, " \n\t\r");
              
               // evaluate assertion on all of list items
               while (values.hasMoreTokens()) {
                   String itemValue = values.nextToken();
                   setValueOf$valueForAListItem(itemType, itemValue);
                   AssertionError assertError = evaluateAssertion(element, assertImpl, itemValue, false, true);
                   if (assertError != null) {
                       reportAssertionsError(assertError);   
                   }
               }
            }
            else if (memberTypes != null && memberTypes.getLength() == 0) {
                // evaluating assertions for "simpleType -> restriction"
                setTypedValueFor$value(value, null, null);
                AssertionError assertError = evaluateAssertion(element, assertImpl, value, false, false);
                if (assertError != null) {
                    reportAssertionsError(assertError);   
                }   
            }               
        }

        if (memberTypes != null && memberTypes.getLength() > 0) {
             // evaluate assertions for "simpleType -> union"
             boolean isValidationFailedForUnion = isValidationFailedForUnion(memberTypes, element, value, false);
            // only 1 error message is reported for assertion failures on "simpleType -> union",
            // since it is hard (perhaps impossible?) to determine statically that what all assertions
            // can cause validation failure, when participating in an XML schema union.
            if (isValidationFailedForUnion) {
                 fValidator.reportSchemaError("cvc-assertion.union.3.13.4.1", new Object[] { element.rawname, value } );  
            }
        }
       
    } // evaluateAssertionsFromASimpleType
   
   
    /*
     * Set a typed value of XPath2 context variable $value if an atomic value on which assertion is been evaluated,
     * is an item of a schema component xs:list.
     */
    private void setValueOf$valueForAListItem(XSSimpleTypeDefinition simpType, String value)
                                              throws Exception {
       
        XSObjectList memberTypes = simpType.getMemberTypes();
        if (memberTypes.getLength() > 0) {
            // the list item's type has variety 'union'.
            XSSimpleTypeDefinition actualListItemType = getActualListItemTypeForVarietyUnion(memberTypes, value);
            // set a schema 'typed value' to variable $value
            setTypedValueFor$value(value, actualListItemType, null);
        }
        else {
            // the list item's type has variety 'atomic'.
            setTypedValueFor$value(value, simpType, null);
        }

    } // setValueOf$valueForAListItem
   
   
    /*
     * Determine if an validation must fail due to assertions evaluation for "simpleType -> union" member types.
     */
    private boolean isValidationFailedForUnion(XSObjectList memberTypes, QName element, String value, boolean isAttribute) {
       
        boolean validationFailedForUnion = true;
        final int memberTypesLength = memberTypes.getLength();
       
        for (int memberTypeIdx = 0; memberTypeIdx < memberTypesLength; memberTypeIdx++) {
            XSSimpleTypeDefinition memType = (XSSimpleTypeDefinition) memberTypes.item(memberTypeIdx);
           
            // check for assertions on types in an non-schema namespace
            if (!SchemaSymbols.URI_SCHEMAFORSCHEMA.equals(memType.getNamespace()) && simpleTypeHasAsserts(memType)) {
                XSObjectList memberTypeFacets = memType.getMultiValueFacets();
                final int memberTypeFacetsLength = memberTypeFacets.getLength();
                for (int memberTypeFacetIdx = 0; memberTypeFacetIdx < memberTypeFacetsLength; memberTypeFacetIdx++) {
                    XSMultiValueFacet facet = (XSMultiValueFacet) memberTypeFacets.item(memberTypeFacetIdx);
                    if (facet.getFacetKind() == XSSimpleTypeDefinition.FACET_ASSERT) {
                        Vector assertFacets = facet.getAsserts();
                        int assertsSucceeded = 0;
                        for (Iterator iter = assertFacets.iterator(); iter.hasNext(); ) {
                            XSAssertImpl assertImpl = (XSAssertImpl) iter.next();
                            try {
                               setTypedValueFor$value(value, memType, null);
                               AssertionError assertError = evaluateAssertion(element, assertImpl, value, false, false);
                               if (assertError == null) {
                                   assertsSucceeded++; 
                               }
                               else if (isAttribute && fAttrName == null) {
                                   fAttrName = assertImpl.getAttrName();  
                               }
                            }
                            catch(Exception ex) {
                               // An exception may occur if for example, a typed value cannot be constructed by PsychoPath
                               // engine for a given "string value" (say a value '5' was attempted to be formed as a typed
                               // value xs:date).
                               // it's useful to report warning ... TO DO
                            }
                        }
                        if (assertsSucceeded == assertFacets.size()) {
                            // all assertions on a 'union' member type have evaluated to 'true', therefore validation with
                            // union has succeeded wrt assertions.
                            return false
                        }
                    }
                }
            }
        }
       
        return validationFailedForUnion;
       
    } // isValidationFailedForUnion
   
   
    /*
     * Check if a simple type has assertion facets.
     */
    private boolean simpleTypeHasAsserts(XSSimpleTypeDefinition simpleType) {
       
        boolean hasAssertions = false;
       
        XSObjectList simpleTypeFacets = simpleType.getMultiValueFacets();
        final int simpleTypeFacetsLength = simpleTypeFacets.getLength();
        for (int facetIdx = 0; facetIdx < simpleTypeFacetsLength; facetIdx++) {
            XSMultiValueFacet facet = (XSMultiValueFacet) simpleTypeFacets.item(facetIdx);
            if (facet.getFacetKind() == XSSimpleTypeDefinition.FACET_ASSERT && facet.getAsserts().size() > 0) {
                hasAssertions = true;
                break;
            }
        }
       
        return hasAssertions;

    } // simpleTypeHasAsserts
   

    /*
     * (non-Javadoc)
     * @see org.apache.xerces.xni.parser.XMLAssertAdapter#characters
     *      (org.apache.xerces.xni.XMLString)
     */
    public void characters(XMLString text) {       
        // add a child text node to the assertions, DOM tree
        if (fCurrentAssertDomNode != null) {
            fCurrentAssertDomNode.appendChild(fAssertDocument.createTextNode(new String(text.ch,
                                                                        text.offset, text.length)));
        }       
    }
   

    /*
     * Method to evaluate an assertion object.
     */
    private AssertionError evaluateAssertion(QName element, XSAssertImpl assertImpl, String value,
                                             boolean xPathContextExists, boolean isList) {
       
        AssertionError assertionError = null;
       
        try
            XPath xp = assertImpl.getCompiledXPath();
           
            boolean result;           
            if ((value == null) ||
                (xPathContextExists == true)) {
                result = fAbstrPsychopathImpl.evaluatePsychoPathExpr(xp, assertImpl.getXPathDefaultNamespace(),
                                                                     fCurrentAssertDomNode)
            }
            else {
                // XPath context is "undefined"
                result = fAbstrPsychopathImpl.evaluatePsychoPathExpr(xp, assertImpl.getXPathDefaultNamespace(),
                                                                     null);
            }
           
            if (!result) {
               // assertion evaluation is false
               assertionError = new AssertionError("cvc-assertion.3.13.4.1", element, assertImpl, value, isList);
            }
        }
        catch (DynamicError ex) {
            if (ex.code().equals("XPDY0002")) {
               // ref: http://www.w3.org/TR/xpath20/#eval_context
               assertionError = new AssertionError("cvc-assertion.4.3.15.3", element, assertImpl, value, isList);
            }
            else {
               assertionError = new AssertionError("cvc-assertion.3.13.4.1", element, assertImpl, value, isList);
            }
        }
        catch (StaticError ex) {
            assertionError = new AssertionError("cvc-assertion.3.13.4.1", element, assertImpl, value, isList);
        }
        catch(Exception ex) {
            assertionError = new AssertionError("cvc-assertion.3.13.4.1", element, assertImpl, value, isList);  
        }
       
        return assertionError;
       
    } // evaluateAssertion
   
   
    /*
     * Find a "schema typed value" (of kind xs:anyAtomicType*) to assign to XPath2 context variable $value.
     */
    private void setTypedValueFor$value(String value, XSSimpleTypeDefinition listOrUnionType,
                                        XSTypeDefinition attrType) throws Exception {
       
        // dummy short code initializer
        short xsdTypecode = -100;
       
        if (listOrUnionType != null) {
            xsdTypecode = getXercesXSDTypeCodeFor$Value(listOrUnionType);
            setValueOf$ValueForSTVarietyAtomic(value, xsdTypecode);
        }
        else {
           if (attrType != null) {
              // is value of an attribute
              xsdTypecode = getXercesXSDTypeCodeFor$Value(attrType);
              setValueOf$ValueForSTVarietyAtomic(value, xsdTypecode);
           }
           else {
              // is "simple type" value of an element
              PSVIElementNSImpl currentAssertPSVINode = (PSVIElementNSImpl) fCurrentAssertDomNode;
              XSTypeDefinition typeDef = currentAssertPSVINode.getTypeDefinition();
              if (typeDef instanceof XSComplexTypeDefinition && ((XSComplexTypeDefinition) typeDef).getSimpleType()
                                                                     != null) {
                  setValueOf$ValueForCTWithSimpleContent(value,
                                            (XSComplexTypeDefinition) typeDef);
              }
              else if (typeDef instanceof XSComplexTypeDefinition &&
                      ((XSComplexTypeDefinition) typeDef).getSimpleType() == null) {
                  // assign an empty XPath2 sequence to xpath context variable
                  // $value.
                  fDynamicContext.set_variable(new org.eclipse.wst.xml.xpath2.processor.internal.types.QName(
                                                   "value"), getXPath2ResultSequence(new ArrayList()));
              }
              else {
                  xsdTypecode = getXercesXSDTypeCodeFor$Value(typeDef);
                  setValueOf$ValueForSTVarietyAtomic(value, xsdTypecode);
              }
           }
        }
       
    } // setTypedValueFor$value
   
   
    /*
     * Set value of XPath2 context variable $value, when variety of it's schema type is "simpleType -> atomic". 
     */
    private void setValueOf$ValueForSTVarietyAtomic(String value, short xsdTypecode) {
        AnyType psychoPathType = SchemaTypeValueFactory.newSchemaTypeValue(xsdTypecode, value);
        fDynamicContext.set_variable(new org.eclipse.wst.xml.xpath2.processor.internal.types.QName(
                                         "value"), (AnyAtomicType) psychoPathType);
    } // setValueOf$ValueForSTVarietyAtomic


    /*
     * Set value of XPath2 context variable $value, if element has a complex type with simple content.
     */
    private void setValueOf$ValueForCTWithSimpleContent(String value, XSComplexTypeDefinition typeDef) {
       
        XSComplexTypeDefinition cmplxTypeDef = (XSComplexTypeDefinition)typeDef;
        XSSimpleTypeDefinition complexTypeSimplContentType = cmplxTypeDef.getSimpleType();
        if (complexTypeSimplContentType.getVariety() == XSSimpleTypeDefinition.VARIETY_LIST) {
            // simple content type has variety xs:list
            XSSimpleTypeDefinition listItemType = complexTypeSimplContentType.getItemType();
            // split the "string value" of list contents, into non white-space tokens.
            StringTokenizer values = new StringTokenizer(value, " \n\t\r");
           
            // construct a list of atomic XDM items, to assign to XPath2 context variable $value.
            List xdmItemList = new ArrayList();
            final XSObjectList memberTypes = listItemType.getMemberTypes();
            if (memberTypes.getLength() > 0) {
               // itemType of xs:list has variety 'union'. here list items may have different types, which are determined below.
               while (values.hasMoreTokens()) {
                   String itemValue = values.nextToken();
                   XSSimpleTypeDefinition listItemTypeForUnion = getActualListItemTypeForVarietyUnion(memberTypes, itemValue);
                   xdmItemList.add(SchemaTypeValueFactory.newSchemaTypeValue(listItemTypeForUnion.getBuiltInKind(), itemValue));
               }                                 
            }
            else {
               // every list item has a same type (the itemType of xs:list).
               while (values.hasMoreTokens()) {
                   String itemValue = values.nextToken();
                   xdmItemList.add(SchemaTypeValueFactory.newSchemaTypeValue(listItemType.getBuiltInKind(), itemValue));
               }                                 
            }

            // assign an XPath2 sequence to xpath context variable $value
            fDynamicContext.set_variable(new org.eclipse.wst.xml.xpath2.processor.internal.types.QName(
                                           "value"), getXPath2ResultSequence(xdmItemList));
        }
        else if (complexTypeSimplContentType.getVariety() == XSSimpleTypeDefinition.VARIETY_UNION) {
            // simple content type has variety xs:union
            XSSimpleTypeDefinition simpleContentTypeForUnion = getActualListItemTypeForVarietyUnion
                                                                     (complexTypeSimplContentType.getMemberTypes(), value);
            fDynamicContext.set_variable(new org.eclipse.wst.xml.xpath2.processor.internal.types.QName("value"),
                                             SchemaTypeValueFactory.newSchemaTypeValue(simpleContentTypeForUnion.getBuiltInKind(),
                                             value));
        }
        else {
            // simple content type has variety atomic
            setValueOf$ValueForSTVarietyAtomic(value, getXercesXSDTypeCodeFor$Value(cmplxTypeDef.getSimpleType()));
        }
         
    } // setValueOf$ValueForCTWithSimpleContent
   
   
    /*
       Find the built-in Xerces schema 'type code' for XPath2 variable, $value. This function recursively
       searches the XML schema type hierarchy navigating up the base types, to find the needed built-in type.
    */
    private short getXercesXSDTypeCodeFor$Value(XSTypeDefinition elementType) {
           
      if (Constants.NS_XMLSCHEMA.equals(elementType.getNamespace())) {
         short typeCode = -100; // dummy initializer
        
         boolean isxsd11Type = false;
        
         // the below 'if else' clauses are written to process few special cases handling few of schema types,
         // within PsychoPath XPath engine.
         final String elementTypeName = elementType.getName();
         if ("dayTimeDuration".equals(elementTypeName)) {
             typeCode = PsychoPathTypeHelper.DAYTIMEDURATION_DT;
             isxsd11Type = true;
         }
         else if ("yearMonthDuration".equals(elementTypeName)) {
             typeCode = PsychoPathTypeHelper.YEARMONTHDURATION_DT;
             isxsd11Type = true;
         }
        
         return (isxsd11Type) ? typeCode : ((XSSimpleTypeDefinition) elementType).getBuiltInKind();   
      }
      else {
         return getXercesXSDTypeCodeFor$Value(elementType.getBaseType());
      }
     
    } // getXercesXSDTypeCodeFor$Value
   
   
    /*
     * Construct an PsychoPath XPath2 "result sequence", given a list of XDM items as input.
     */
    private ResultSequence getXPath2ResultSequence(List xdmItems) {
       
        ResultSequence xpath2Seq = ResultSequenceFactory.create_new();
       
        for (Iterator iter = xdmItems.iterator(); iter.hasNext(); ) {
            xpath2Seq.add((AnyType) iter.next());
        }
       
        return xpath2Seq;
       
    } // getXPath2ResultSequence
   
   
    /*
     * Method to report assertions error messages.
     */
    private void reportAssertionsError(AssertionError assertError) {
       
        String key = assertError.getErrorCode();
        QName element = assertError.getElement();
        XSAssertImpl assertImpl = assertError.getAssertion();
        boolean isList = assertError.isList();
        String value = assertError.getValue();
       
        XSTypeDefinition typeDef = assertImpl.getTypeDefinition();       
        String typeString = "";
       
        if (typeDef != null) {
            typeString = (typeDef.getName() != null) ? typeDef.getName() : "#anonymous";  
        }
        else {
            typeString = "#anonymous";
        }
       
        String elemErrorAnnotation = element.rawname;
        if (assertImpl.getAttrName() != null) {
            elemErrorAnnotation = element.rawname + " (attribute => " + assertImpl.getAttrName()+ ")";   
        }               
       
        String listAssertErrMessage = "";       
        if (isList) {
           listAssertErrMessage =  "Assertion failed for an xs:list member value '" + assertError.getValue() + "'.";
        }
           
        String message = assertImpl.getMessage();
        if (message != null) {
           // substitute all placeholder macro instances of "{$value}" with atomic value stored in variable "value".
           if (value != null && !"".equals(value)) {
              message = message.replaceAll(ERROR_PLACEHOLDER_REGEX, value);
           }
          
           if (!message.endsWith(".")) {
              message = message + ".";   
           }
           if (key.equals("cvc-assertion.4.3.15.3")) {
              message = "Assertion failed (undefined context) for schema type '" + typeString + "'. " + message;  
           }
           else {
              message = "Assertion failed for schema type '" + typeString + "'. " + message;
           }          
           fValidator.reportSchemaError("cvc-assertion.failure", new Object[] { message, listAssertErrMessage } );   
        }
        else {
           fValidator.reportSchemaError(key, new Object[] { elemErrorAnnotation, assertImpl.getTest().getXPath().toString(),
                                                            typeString, listAssertErrMessage} );
        }
       
    } // reportAssertionsError
   
   
    /*
     * Find the actual schema type of "list item" instance, if the "item type" of list has variety union.
     */
    private XSSimpleTypeDefinition getActualListItemTypeForVarietyUnion(XSObjectList memberTypes, String value) {

        XSSimpleTypeDefinition simpleTypeDefn = null;
       
        // iterate the member types of union in order, to find that which schema type can successfully validate an
        // atomic value first.
        final int memberTypesLength = memberTypes.getLength();
        for (int memTypeIdx = 0; memTypeIdx < memberTypesLength; memTypeIdx++) {
           XSSimpleType memSimpleType = (XSSimpleType) memberTypes.item(memTypeIdx);
           if (XSTypeHelper.isValueValidForASimpleType(value, memSimpleType)) {
              // no more memberTypes need to be checked
              simpleTypeDefn = memSimpleType;
              break;
           }
        }
       
        return simpleTypeDefn;
       
    } // getActualListItemTypeForVarietyUnion
   
   
    /*
     * Class to store "assertion evaluation" error details.
     */
    final class AssertionError {
       
        // instance variables       
        private final String errorCode;
        private final QName element;
        private final XSAssertImpl assertImpl;
        private final String value;
        // does this error concerns "simpleType -> list"
        private final boolean isList;
       
        // class constructor
        public AssertionError(String errorCode, QName element, XSAssertImpl assertImpl,
                              String value, boolean isList) {
           this.errorCode = errorCode;
           this.element = element;
           this.assertImpl = assertImpl;
           this.value = value;
           this.isList = isList;
        }
       
        public String getErrorCode() {
           return errorCode;   
        }
       
        public QName getElement() {
           return element;      
        }
       
        public XSAssertImpl getAssertion() {
           return assertImpl;   
        }
       
        public String getValue() {
           return value;
        }
       
        public boolean isList() {
           return isList;   
        }
       
    } // class AssertionError
   
} // class XMLAssertPsychopathImpl
TOP

Related Classes of org.apache.xerces.impl.xs.XMLAssertPsychopathImpl

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.