/**
   * Try to precompile the arguments to the function. This method is shared by the implementations
   * of the three XPath functions matches(), replace(), and tokenize().
   *
   * @param args the supplied arguments to the function, as an array
   * @param patternArg the position of the argument containing the regular expression
   * @param flagsArg the position of the argument containing the flags
   * @return the compiled regular expression, or null indicating that the information is not
   *     available statically so it cannot be precompiled
   * @throws XPathException if any failure occurs, in particular, if the regular expression is
   *     invalid
   */
  public static RegularExpression tryToCompile(
      Expression[] args, int patternArg, int flagsArg, StaticContext env) throws XPathException {
    if (patternArg > args.length - 1) {
      // too few arguments were supplied; the error will be reported in due course
      return null;
    }
    CharSequence flagstr = null;
    if (args.length - 1 < flagsArg) {
      flagstr = "";
    } else if (args[flagsArg] instanceof StringValue) {
      flagstr = ((StringValue) args[flagsArg]).getStringValueCS();
    }

    if (args[patternArg] instanceof StringValue && flagstr != null) {
      try {
        Platform platform = env.getConfiguration().getPlatform();
        CharSequence in = ((StringValue) args[patternArg]).getStringValueCS();
        RegularExpression regexp = platform.compileRegularExpression(in, true, flagstr);
        return regexp;
      } catch (XPathException err) {
        StaticError e2 = new StaticError(err.getMessage());
        e2.setErrorCode("FORX0002");
        throw e2;
      }
    } else {
      return null;
    }
  }
  /**
   * Set the expression defining the value of the attribute. If this is a constant, and if
   * validation against a schema type was requested, the validation is done immediately.
   *
   * @param select The expression defining the content of the attribute
   * @param config
   * @throws StaticError if the expression is a constant, and validation is requested, and the
   *     constant doesn't match the required type.
   */
  public void setSelect(Expression select, Configuration config) throws StaticError {
    super.setSelect(select, config);

    // Attempt early validation if possible
    if (select instanceof AtomicValue && schemaType != null && !schemaType.isNamespaceSensitive()) {
      CharSequence value = ((AtomicValue) select).getStringValueCS();
      XPathException err =
          schemaType.validateContent(
              value, DummyNamespaceResolver.getInstance(), config.getNameChecker());
      if (err != null) {
        StaticError se =
            new StaticError(
                "Attribute value "
                    + Err.wrap(value, Err.VALUE)
                    + " does not the match the required type "
                    + schemaType.getDescription()
                    + ". "
                    + err.getMessage());
        se.setErrorCode("XTTE1540");
        throw se;
      }
    }

    // If value is fixed, test whether there are any special characters that might need to be
    // escaped when the time comes for serialization
    if (select instanceof StringValue) {
      boolean special = false;
      CharSequence val = ((StringValue) select).getStringValueCS();
      for (int k = 0; k < val.length(); k++) {
        char c = val.charAt(k);
        if ((int) c < 33 || (int) c > 126 || c == '<' || c == '>' || c == '&' || c == '\"') {
          special = true;
          break;
        }
      }
      if (!special) {
        this.options |= ReceiverOptions.NO_SPECIAL_CHARS;
      }
    }

    // If attribute name is xml:id, add whitespace normalization
    if ((nameCode & NamePool.FP_MASK) == StandardNames.XML_ID) {
      Expression[] args = {select};
      FunctionCall fn =
          SystemFunction.makeSystemFunction("normalize-space", 1, config.getNamePool());
      fn.setArguments(args);
      select = fn;
      super.setSelect(select, config);
    }
  }
  /**
   * Simplify and validate. This is a pure function so it can be simplified in advance if the
   * arguments are known
   *
   * @return the simplified expression
   * @throws net.sf.saxon.trans.StaticError if any error is found (e.g. invalid regular expression)
   */
  public Expression simplify(StaticContext env) throws XPathException {
    Expression e = simplifyArguments(env);

    // compile the regular expression once if possible
    if (regexp == null && !(e instanceof Value)) {
      try {
        regexp = tryToCompile(argument, 1, 2, env);
      } catch (StaticError err) {
        err.setLocator(this);
        throw err;
      }
    }

    return e;
  }
 /** Type-check the expression. */
 public Expression analyze(StaticContext env, ItemType contextItemType) throws XPathException {
   if (contextItemType == null) {
     StaticError err = new StaticError("Cannot select a node here: the context item is undefined");
     err.setIsTypeError(true);
     err.setLocator(this);
     throw err;
   }
   if (contextItemType instanceof AtomicType) {
     StaticError err =
         new StaticError("Cannot select a node here: the context item is an atomic value");
     err.setIsTypeError(true);
     err.setLocator(this);
     throw err;
   }
   return this;
 }
 public void checkArguments(StaticContext env) throws XPathException {
   if (checked) return;
   checked = true;
   super.checkArguments(env);
   Optimizer opt = env.getConfiguration().getOptimizer();
   argument[1] = ExpressionTool.unsorted(opt, argument[1], false);
   if (argument[0] instanceof StringValue) {
     // common case, key name is supplied as a constant
     try {
       keyFingerprint =
           ((ExpressionContext) env)
               .getFingerprint(((StringValue) argument[0]).getStringValue(), false);
     } catch (XPathException e) {
       StaticError err =
           new StaticError(
               "Error in key name "
                   + ((StringValue) argument[0]).getStringValue()
                   + ": "
                   + e.getMessage());
       err.setLocator(this);
       err.setErrorCode("XTDE1260");
       throw err;
     }
     if (keyFingerprint == -1) {
       StaticError err =
           new StaticError(
               "Key " + ((StringValue) argument[0]).getStringValue() + " has not been defined");
       err.setLocator(this);
       err.setErrorCode("XTDE1260");
       throw err;
     }
   } else {
     // we need to save the namespace context
     nsContext = env.getNamespaceResolver();
   }
 }
  /**
   * Check that any elements and attributes constructed or returned by this expression are
   * acceptable in the content model of a given complex type. It's always OK to say yes, since the
   * check will be repeated at run-time. The process of checking element and attribute constructors
   * against the content model of a complex type also registers the type of content expected of
   * those constructors, so the static validation can continue recursively.
   */
  public void checkPermittedContents(SchemaType parentType, StaticContext env, boolean whole)
      throws XPathException {
    int fp = nameCode & NamePool.FP_MASK;
    if (fp == StandardNames.XSI_TYPE
        || fp == StandardNames.XSI_SCHEMA_LOCATION
        || fp == StandardNames.XSI_NIL
        || fp == StandardNames.XSI_NO_NAMESPACE_SCHEMA_LOCATION) {
      return;
    }
    if (parentType instanceof SimpleType) {
      StaticError err =
          new StaticError(
              "Attribute "
                  + env.getNamePool().getDisplayName(nameCode)
                  + " is not permitted in the content model of the simple type "
                  + parentType.getDescription());
      err.setIsTypeError(true);
      err.setLocator(this);
      if (getHostLanguage() == Configuration.XSLT) {
        err.setErrorCode("XTTE1510");
      } else {
        err.setErrorCode("XQDY0027");
      }
      throw err;
    }
    SchemaType type;
    try {
      type = ((ComplexType) parentType).getAttributeUseType(fp);
    } catch (SchemaException e) {
      throw new StaticError(e);
    }
    if (type == null) {
      StaticError err =
          new StaticError(
              "Attribute "
                  + env.getNamePool().getDisplayName(nameCode)
                  + " is not permitted in the content model of the complex type "
                  + parentType.getDescription());
      err.setIsTypeError(true);
      err.setLocator(this);
      if (getHostLanguage() == Configuration.XSLT) {
        err.setErrorCode("XTTE1510");
      } else {
        err.setErrorCode("XQDY0027");
      }
      throw err;
    }
    if (type instanceof AnyType) {
      return;
    }

    try {
      select.checkPermittedContents(type, env, true);
      // TODO: does this allow for the fact that the content will be atomized?
    } catch (XPathException e) {
      if (e.getLocator() == null || e.getLocator() == e) {
        e.setLocator(this);
      }
      throw e;
    }
  }