content: /*
   * 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 mf.org.apache.xerces.impl.xs.identity;
  
  import mf.org.apache.xerces.impl.xpath.XPathException;
  import mf.org.apache.xerces.impl.xs.util.ShortListImpl;
  import mf.org.apache.xerces.util.SymbolTable;
  import mf.org.apache.xerces.util.XMLChar;
  import mf.org.apache.xerces.xni.NamespaceContext;
  import mf.org.apache.xerces.xs.ShortList;
  import mf.org.apache.xerces.xs.XSComplexTypeDefinition;
  import mf.org.apache.xerces.xs.XSConstants;
  import mf.org.apache.xerces.xs.XSIDCDefinition;
  import mf.org.apache.xerces.xs.XSTypeDefinition;
  
  
  /**
   * Schema identity constraint field.
   *
   * @author Andy Clark, IBM
   * @version $Id: Field.java 572110 2007-09-02 19:04:44Z mrglavas $
   * @xerces.internal
   */
  public class Field {
  
      //
      // Data
      //
  
      /**
       * Field XPath.
       */
      protected final Field.XPath fXPath;
  
  
      /**
       * Identity constraint.
       */
      protected final IdentityConstraint fIdentityConstraint;
  
      //
      // Constructors
      //
  
      /**
       * Constructs a field.
       */
      public Field(Field.XPath xpath,
                   IdentityConstraint identityConstraint) {
          fXPath = xpath;
          fIdentityConstraint = identityConstraint;
      } // <init>(Field.XPath,IdentityConstraint)
  
      //
      // Public methods
      //
  
      /**
       * Returns the field XPath.
       */
      public mf.org.apache.xerces.impl.xpath.XPath getXPath() {
          return fXPath;
      } // getXPath():org.apache.xerces.impl.v1.schema.identity.XPath
  
      /**
       * Returns the identity constraint.
       */
      public IdentityConstraint getIdentityConstraint() {
          return fIdentityConstraint;
      } // getIdentityConstraint():IdentityConstraint
  
      // factory method
  
      /**
       * Creates a field matcher.
       */
      public XPathMatcher createMatcher(ValueStore store) {
          return new Field.Matcher(fXPath, store);
      } // createMatcher(ValueStore):XPathMatcher
  
      //
      // Object methods
      //
  
      /**
       * Returns a string representation of this object.
       */
      @Override
      public String toString() {
          return fXPath.toString();
      } // toString():String
  
      //
      // Classes
      //
  
      /**
       * Field XPath.
       *
       * @author Andy Clark, IBM
       */
      public static class XPath
              extends mf.org.apache.xerces.impl.xpath.XPath {
  
          //
          // Constructors
          //
  
          /**
           * Constructs a field XPath expression.
           */
          public XPath(String xpath,
                       SymbolTable symbolTable,
                       NamespaceContext context) throws XPathException {
              super(fixupXPath(xpath), symbolTable, context);
  
              // verify that only one attribute is selected per branch
              for (int i = 0; i < fLocationPaths.length; i++) {
                  for (int j = 0; j < fLocationPaths[i].steps.length; j++) {
                      mf.org.apache.xerces.impl.xpath.XPath.Axis axis =
                              fLocationPaths[i].steps[j].axis;
                      if (axis.type == XPath.Axis.ATTRIBUTE &&
                              (j < fLocationPaths[i].steps.length - 1)) {
                          throw new XPathException("c-fields-xpaths");
                      }
                  }
              }
          } // <init>(String,SymbolTable,NamespacesContext)
  
          /**
           * Fixup XPath expression. Avoid creating a new String if possible.
           */
          private static String fixupXPath(String xpath) {
  
              final int end = xpath.length();
              int offset = 0;
              boolean whitespace = true;
              char c;
  
              // NOTE: We have to prefix the field XPath with "./" in
              //       order to handle selectors such as "@attr" that 
              //       select the attribute because the fields could be
              //       relative to the selector element. -Ac
              //       Unless xpath starts with a descendant node -Achille Fokoue
              //      ... or a / or a . - NG
              for (; offset < end; ++offset) {
                  c = xpath.charAt(offset);
                  if (whitespace) {
                      if (!XMLChar.isSpace(c)) {
                          if (c == '.' || c == '/') {
                              whitespace = false;
                          } else if (c != '|') {
                              return fixupXPath2(xpath, offset, end);
                          }
                      }
                  } else if (c == '|') {
                      whitespace = true;
                  }
              }
              return xpath;
  
          } // fixupXPath(String):String
  
          private static String fixupXPath2(String xpath, int offset, final int end) {
  
              StringBuffer buffer = new StringBuffer(end + 2);
              for (int i = 0; i < offset; ++i) {
                  buffer.append(xpath.charAt(i));
              }
              buffer.append("./");
  
              boolean whitespace = false;
              char c;
  
              for (; offset < end; ++offset) {
                  c = xpath.charAt(offset);
                  if (whitespace) {
                      if (!XMLChar.isSpace(c)) {
                          if (c == '.' || c == '/') {
                              whitespace = false;
                          } else if (c != '|') {
                              buffer.append("./");
                              whitespace = false;
                          }
                      }
                  } else if (c == '|') {
                      whitespace = true;
                  }
                  buffer.append(c);
              }
              return buffer.toString();
  
          } // fixupXPath2(String, int, int):String
  
      } // class XPath
  
      /**
       * Field matcher.
       *
       * @author Andy Clark, IBM
       */
      protected class Matcher
              extends XPathMatcher {
  
          //
          // Data
          //
  
          /**
           * Value store for data values.
           */
          protected final ValueStore fStore;
  
          /**
           * A flag indicating whether the field is allowed to match a value.
           */
          protected boolean fMayMatch = true;
  
          //
          // Constructors
          //
  
          /**
           * Constructs a field matcher.
           */
          public Matcher(Field.XPath xpath, ValueStore store) {
              super(xpath);
              fStore = store;
          } // <init>(Field.XPath,ValueStore)
  
          //
          // XPathHandler methods
          //
  
          /**
           * This method is called when the XPath handler matches the
           * XPath expression.
           */
          @Override
          protected void matched(Object actualValue, short valueType, ShortList itemValueType, boolean isNil) {
              super.matched(actualValue, valueType, itemValueType, isNil);
              if (isNil && (fIdentityConstraint.getCategory() == XSIDCDefinition.IC_KEY)) {
                  String code = "KeyMatchesNillable";
                  fStore.reportError(code,
                          new Object[]{fIdentityConstraint.getElementName(), fIdentityConstraint.getIdentityConstraintName()});
              }
              fStore.addValue(Field.this, fMayMatch, actualValue, convertToPrimitiveKind(valueType), convertToPrimitiveKind(itemValueType));
              // once we've stored the value for this field, we set the mayMatch
              // member to false so that in the same scope, we don't match any more
              // values (and throw an error instead).
              fMayMatch = false;
          } // matched(String)
  
          private short convertToPrimitiveKind(short valueType) {
              /** Primitive datatypes. */
              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;
          }
  
          private ShortList convertToPrimitiveKind(ShortList itemValueType) {
              if (itemValueType != null) {
                  int i;
                  final int length = itemValueType.getLength();
                  for (i = 0; i < length; ++i) {
                      short type = itemValueType.item(i);
                      if (type != convertToPrimitiveKind(type)) {
                          break;
                      }
                  }
                  if (i != length) {
                      final short[] arr = new short[length];
                      for (int j = 0; j < i; ++j) {
                          arr[j] = itemValueType.item(j);
                      }
                      for (; i < length; ++i) {
                          arr[i] = convertToPrimitiveKind(itemValueType.item(i));
                      }
                      return new ShortListImpl(arr, arr.length);
                  }
              }
              return itemValueType;
          }
  
          @Override
          protected void handleContent(XSTypeDefinition type, boolean nillable, Object actualValue, short valueType, ShortList itemValueType) {
              if (type == null ||
                      type.getTypeCategory() == XSTypeDefinition.COMPLEX_TYPE &&
                              ((XSComplexTypeDefinition) type).getContentType()
                                      != XSComplexTypeDefinition.CONTENTTYPE_SIMPLE) {
  
                  // the content must be simpleType content
                  fStore.reportError("cvc-id.3", new Object[]{
                          fIdentityConstraint.getName(),
                          fIdentityConstraint.getElementName()});
  
              }
              fMatchedString = actualValue;
              matched(fMatchedString, valueType, itemValueType, nillable);
          } // handleContent(XSElementDecl, String)
  
      } // class Matcher
  
  } // class Field
  