/**
  * The following function is called when it is found that the output is not a well-formed
  * document. Unless the ContentHandler accepts "balanced content", this is a fatal error.
  */
 protected void notifyNotWellFormed() throws XPathException {
   DynamicError err =
       new DynamicError(
           "The result tree cannot be supplied to the ContentHandler because it is not well-formed XML");
   err.setErrorCode(SaxonErrorCode.SXCH0002);
   throw err;
 }
  /**
   * Evaluate the matches() function to give a Boolean value.
   *
   * @param c The dynamic evaluation context
   * @return the result as a BooleanValue, or null to indicate the empty sequence
   * @throws XPathException on an error
   */
  public Item evaluateItem(XPathContext c) throws XPathException {
    AtomicValue sv0 = (AtomicValue) argument[0].evaluateItem(c);
    if (sv0 == null) {
      sv0 = StringValue.EMPTY_STRING;
    }
    ;

    RegularExpression re = regexp;

    if (re == null) {
      AtomicValue pat = (AtomicValue) argument[1].evaluateItem(c);
      if (pat == null) return null;

      CharSequence flags;
      if (argument.length == 2) {
        flags = "";
      } else {
        AtomicValue sv2 = (AtomicValue) argument[2].evaluateItem(c);
        if (sv2 == null) return null;
        flags = sv2.getStringValueCS();
      }

      try {
        final Platform platform = c.getConfiguration().getPlatform();
        re = platform.compileRegularExpression(pat.getStringValueCS(), true, flags);
      } catch (XPathException err) {
        DynamicError de = new DynamicError(err);
        de.setErrorCode("FORX0002");
        de.setXPathContext(c);
        throw de;
      }
    }
    return BooleanValue.get(re.containsMatch(sv0.getStringValueCS()));
  }
 /**
  * Set the Java flags from the supplied XPath flags.
  *
  * @param inFlags the flags as a string, e.g. "im"
  * @return the flags as a bit-significant integer
  * @throws DynamicError if the supplied value is invalid
  */
 public static int setFlags(CharSequence inFlags) throws DynamicError {
   int flags = Pattern.UNIX_LINES;
   for (int i = 0; i < inFlags.length(); i++) {
     char c = inFlags.charAt(i);
     switch (c) {
       case 'm':
         flags |= Pattern.MULTILINE;
         break;
       case 'i':
         flags |= Pattern.CASE_INSENSITIVE;
         flags |= Pattern.UNICODE_CASE;
         break;
       case 's':
         flags |= Pattern.DOTALL;
         break;
       case 'x':
         flags |= Pattern.COMMENTS; // note, this enables comments as well as whitespace
         break;
       default:
         DynamicError err =
             new DynamicError("Invalid character '" + c + "' in regular expression flags");
         err.setErrorCode("FORX0001");
         throw err;
     }
   }
   return flags;
 }
  /**
   * Process this instruction
   *
   * @param context the dynamic context of the transformation
   * @return a TailCall to be executed by the caller, always null for this instruction
   */
  public TailCall processLeavingTail(XPathContext context) throws XPathException {
    Controller controller = context.getController();
    SequenceReceiver out = context.getReceiver();
    int opt = options;
    int ann = annotation;

    // we may need to change the namespace prefix if the one we chose is
    // already in use with a different namespace URI: this is done behind the scenes
    // by the Outputter

    CharSequence value = expandChildren(context);
    if (schemaType != null) {
      // test whether the value actually conforms to the given type
      XPathException err =
          schemaType.validateContent(
              value,
              DummyNamespaceResolver.getInstance(),
              context.getConfiguration().getNameChecker());
      if (err != null) {
        ValidationException verr =
            new ValidationException(
                "Attribute value "
                    + Err.wrap(value, Err.VALUE)
                    + " does not the match the required type "
                    + schemaType.getDescription()
                    + ". "
                    + err.getMessage());
        verr.setErrorCode("XTTE1540");
        verr.setLocator(this);
        throw verr;
      }
    } else if (validationAction == Validation.STRICT || validationAction == Validation.LAX) {
      try {
        ann = controller.getConfiguration().validateAttribute(nameCode, value, validationAction);
      } catch (ValidationException e) {
        DynamicError err = DynamicError.makeDynamicError(e);
        String errorCode = e.getErrorCodeLocalPart();
        if (errorCode == null) {
          errorCode = (validationAction == Validation.STRICT ? "XTTE1510" : "XTTE1515");
        }
        err.setErrorCode(errorCode);
        err.setXPathContext(context);
        err.setLocator(this);
        err.setIsTypeError(true);
        throw err;
      }
    }
    try {
      out.attribute(nameCode, ann, value, locationId, opt);
    } catch (XPathException err) {
      throw dynamicError(this, err, context);
    }

    return null;
  }
 /** Handle a SAXException thrown by the ContentHandler */
 public void handleSAXException(SAXException err) throws XPathException {
   Exception nested = err.getException();
   if (nested instanceof XPathException) {
     throw (XPathException) nested;
   } else if (nested instanceof SchemaException) {
     throw new DynamicError(nested);
   } else {
     DynamicError de = new DynamicError(err);
     de.setErrorCode(SaxonErrorCode.SXCH0003);
     throw de;
   }
 }
 /**
  * Type-check the expression. This also calls preEvaluate() to evaluate the function if all the
  * arguments are constant; functions that do not require this behavior can override the
  * preEvaluate method.
  */
 public Expression typeCheck(StaticContext env, ItemType contextItemType) throws XPathException {
   try {
     return super.typeCheck(env, contextItemType);
   } catch (XPathException err) {
     if ("XPDY0002".equals(err.getErrorCodeLocalPart())) {
       DynamicError e =
           new DynamicError("Cannot call the key() function when there is no context node");
       e.setErrorCode("XTDE1270");
       throw e;
     }
     throw err;
   }
 }
  /** Evaluate in a general context */
  public Item evaluateItem(XPathContext c) throws XPathException {
    StringValue sv = (StringValue) argument[0].evaluateItem(c);
    if (sv == null) {
      return StringValue.EMPTY_STRING;
    }

    byte fb = Normalizer.C;
    if (argument.length == 2) {
      String form = argument[1].evaluateAsString(c).trim();
      if (form.equalsIgnoreCase("NFC")) {
        fb = Normalizer.C;
      } else if (form.equalsIgnoreCase("NFD")) {
        fb = Normalizer.D;
      } else if (form.equalsIgnoreCase("NFKC")) {
        fb = Normalizer.KC;
      } else if (form.equalsIgnoreCase("NFKD")) {
        fb = Normalizer.KD;
      } else if (form.equalsIgnoreCase("")) {
        return sv;
      } else {
        String msg = "Normalization form " + form + " is not supported";
        DynamicError err = new DynamicError(msg);
        err.setErrorCode("FOCH0003");
        err.setXPathContext(c);
        err.setLocator(this);
        throw err;
      }
    }

    // fast path for ASCII strings: normalization is a no-op
    boolean allASCII = true;
    CharSequence chars = sv.getStringValueCS();
    if (chars instanceof CompressedWhitespace) {
      return sv;
    }
    for (int i = chars.length() - 1; i >= 0; i--) {
      if (chars.charAt(i) > 127) {
        allASCII = false;
        break;
      }
    }
    if (allASCII) {
      return sv;
    }

    Normalizer norm = new Normalizer(fb);
    CharSequence result = norm.normalize(sv.getStringValueCS());
    return StringValue.makeStringValue(result);
  }
  public Item evaluateItem(XPathContext context) throws XPathException {
    Orphan o = (Orphan) super.evaluateItem(context);
    if (schemaType != null) {
      XPathException err =
          schemaType.validateContent(
              o.getStringValueCS(),
              DummyNamespaceResolver.getInstance(),
              context.getConfiguration().getNameChecker());
      if (err != null) {
        throw new ValidationException(
            "Attribute value "
                + Err.wrap(o.getStringValueCS(), Err.VALUE)
                + " does not the match the required type "
                + schemaType.getDescription()
                + ". "
                + err.getMessage());
      }
      o.setTypeAnnotation(schemaType.getFingerprint());
      if (schemaType.isNamespaceSensitive()) {
        throw new DynamicError(
            "Cannot validate a parentless attribute whose content is namespace-sensitive");
      }
    } else if (validationAction == Validation.STRICT || validationAction == Validation.LAX) {
      try {
        int ann =
            context
                .getController()
                .getConfiguration()
                .validateAttribute(nameCode, o.getStringValueCS(), validationAction);
        o.setTypeAnnotation(ann);
      } catch (ValidationException e) {
        DynamicError err = DynamicError.makeDynamicError(e);
        err.setErrorCode(e.getErrorCodeLocalPart());
        err.setXPathContext(context);
        err.setLocator(this);
        err.setIsTypeError(true);
        throw err;
      }
    }

    return o;
  }
  /**
   * Get a Receiver that wraps a given Result object. Saxon calls this method to construct a
   * serialization pipeline. The method can be overridden in a subclass; alternatively, the subclass
   * can override the various methods used to instantiate components of the serialization pipeline.
   *
   * @param result The final destination of the serialized output. Usually a StreamResult, but other
   *     kinds of Result are possible.
   * @param pipe The PipelineConfiguration.
   * @param props The serialization properties
   */
  public Receiver getReceiver(Result result, PipelineConfiguration pipe, Properties props)
      throws XPathException {
    if (result instanceof Emitter) {
      ((Emitter) result).setOutputProperties(props);
      return (Emitter) result;
    } else if (result instanceof Receiver) {
      Receiver receiver = (Receiver) result;
      receiver.setSystemId(result.getSystemId());
      receiver.setPipelineConfiguration(pipe);
      return receiver;
    } else if (result instanceof SAXResult) {
      ContentHandlerProxy proxy = newContentHandlerProxy();
      proxy.setUnderlyingContentHandler(((SAXResult) result).getHandler());
      proxy.setPipelineConfiguration(pipe);
      proxy.setOutputProperties(props);
      if ("yes".equals(props.getProperty(SaxonOutputKeys.SUPPLY_SOURCE_LOCATOR))) {
        if (pipe.getConfiguration().isCompileWithTracing()) {
          pipe.getController().addTraceListener(proxy.getTraceListener());
        } else {
          DynamicError de =
              new DynamicError(
                  "Cannot use saxon:supply-source-locator unless tracing was enabled at compile time");
          de.setErrorCode(SaxonErrorCode.SXSE0002);
          throw de;
        }
      }
      proxy.open();
      return proxy;
    } else if (result instanceof StreamResult) {

      // The "target" is the start of the output pipeline, the Receiver that
      // instructions will actually write to (except that other things like a
      // NamespaceReducer may get added in front of it). The "emitter" is the
      // last thing in the output pipeline, the Receiver that actually generates
      // characters or bytes that are written to the StreamResult.

      Receiver target;
      String method = props.getProperty(OutputKeys.METHOD);
      if (method == null) {
        target = newUncommittedSerializer(result, props);
        target.setPipelineConfiguration(pipe);
        return target;
      }

      Emitter emitter;

      CharacterMapExpander characterMapExpander = null;
      String useMaps = props.getProperty(SaxonOutputKeys.USE_CHARACTER_MAPS);
      if (useMaps != null) {
        Controller controller = (pipe == null ? null : pipe.getController());
        if (controller == null) {
          DynamicError de =
              new DynamicError("Cannot use character maps in an environment with no Controller");
          de.setErrorCode(SaxonErrorCode.SXSE0001);
          throw de;
        }
        characterMapExpander = controller.makeCharacterMapExpander(useMaps, this);
        characterMapExpander.setPipelineConfiguration(pipe);
      }

      ProxyReceiver normalizer = null;
      String normForm = props.getProperty(SaxonOutputKeys.NORMALIZATION_FORM);
      if (normForm != null && !normForm.equals("none")) {
        normalizer = newUnicodeNormalizer(pipe, props);
      }

      if ("html".equals(method)) {
        emitter = newHTMLEmitter();
        emitter.setPipelineConfiguration(pipe);
        target = createHTMLSerializer(emitter, props, pipe, characterMapExpander, normalizer);

      } else if ("xml".equals(method)) {
        emitter = newXMLEmitter();
        emitter.setPipelineConfiguration(pipe);
        target = createXMLSerializer(emitter, props, pipe, normalizer, characterMapExpander);

      } else if ("xhtml".equals(method)) {
        emitter = newXHTMLEmitter();
        emitter.setPipelineConfiguration(pipe);
        target = createXHTMLSerializer(emitter, props, pipe, normalizer, characterMapExpander);

      } else if ("text".equals(method)) {
        emitter = newTEXTEmitter();
        emitter.setPipelineConfiguration(pipe);
        target = createTextSerializer(emitter, characterMapExpander, normalizer);

      } else {
        Receiver userReceiver;
        if (pipe == null) {
          throw new DynamicError("Unsupported serialization method " + method);
        } else {
          // See if this output method is recognized by the Configuration
          userReceiver = pipe.getConfiguration().makeEmitter(method, pipe.getController());
          userReceiver.setPipelineConfiguration(pipe);
          if (userReceiver instanceof ContentHandlerProxy
              && "yes".equals(props.getProperty(SaxonOutputKeys.SUPPLY_SOURCE_LOCATOR))) {
            if (pipe.getConfiguration().isCompileWithTracing()) {
              pipe.getController()
                  .addTraceListener(((ContentHandlerProxy) userReceiver).getTraceListener());
            } else {
              DynamicError de =
                  new DynamicError(
                      "Cannot use saxon:supply-source-locator unless tracing was enabled at compile time");
              de.setErrorCode(SaxonErrorCode.SXSE0002);
              throw de;
            }
          }
          target = userReceiver;
          if (userReceiver instanceof Emitter) {
            emitter = (Emitter) userReceiver;
          } else {
            return userReceiver;
          }
        }
      }
      emitter.setOutputProperties(props);
      StreamResult sr = (StreamResult) result;
      emitter.setStreamResult(sr);
      return target;

    } else {
      if (pipe != null) {
        // try to find an external object model that knows this kind of Result
        List externalObjectModels = pipe.getConfiguration().getExternalObjectModels();
        for (int m = 0; m < externalObjectModels.size(); m++) {
          ExternalObjectModel model = (ExternalObjectModel) externalObjectModels.get(m);
          Receiver builder = model.getDocumentBuilder(result);
          if (builder != null) {
            builder.setSystemId(result.getSystemId());
            builder.setPipelineConfiguration(pipe);
            return builder;
          }
        }
      }
    }

    throw new IllegalArgumentException("Unknown type of result: " + result.getClass());
  }
  /** Allocate a Comparator to perform the comparisons described by this sort key component */
  public Comparator makeComparator(XPathContext context) throws XPathException {

    String orderX = order.evaluateAsString(context);

    final Configuration config = context.getConfiguration();
    final TypeHierarchy th = config.getTypeHierarchy();

    Comparator comp;
    if (collation != null) {
      comp = collation;
    } else if (collationName != null) {
      String cname = collationName.evaluateAsString(context);
      URI collationURI;
      try {
        collationURI = new URI(cname);
        if (!collationURI.isAbsolute()) {
          if (baseURI == null) {
            throw new DynamicError("Collation URI is relative, and base URI is unknown");
          } else {
            URI base = new URI(baseURI);
            collationURI = base.resolve(collationURI);
          }
        }
      } catch (URISyntaxException err) {
        throw new DynamicError("Collation name " + cname + " is not a valid URI: " + err);
      }
      try {
        comp = context.getCollation(collationURI.toString());
      } catch (XPathException e) {
        if ("FOCH0002".equals(e.getErrorCodeLocalPart())) {
          e.setErrorCode("XTDE1035");
        }
        throw e;
      }
    } else {
      String caseOrderX = caseOrder.evaluateAsString(context);
      String languageX = language.evaluateAsString(context);
      Properties props = new Properties();
      if (!languageX.equals("")) {
        props.setProperty("lang", languageX);
      }
      if (!caseOrderX.equals("#default")) {
        props.setProperty("case-order", caseOrderX);
      }
      comp = config.getPlatform().makeCollation(config, props);
    }

    if (dataTypeExpression == null) {
      int type = sortKey.getItemType(th).getAtomizedItemType().getPrimitiveType();
      comp = AtomicSortComparer.makeSortComparer(comp, type, context);
      if (!emptyLeast) {
        comp = new EmptyGreatestComparer((AtomicComparer) comp);
      }
    } else {
      String dataType = dataTypeExpression.evaluateAsString(context);
      if (dataType.equals("text")) {
        comp = new TextComparer(comp);
      } else if (dataType.equals("number")) {
        comp = NumericComparer.getInstance();
      } else {
        DynamicError err = new DynamicError("data-type on xsl:sort must be 'text' or 'number'");
        err.setErrorCode("XTDE0030");
        throw err;
      }
    }

    if (stable != null) {
      StringValue stableVal = (StringValue) stable.evaluateItem(context);
      String s = stableVal.getStringValue().trim();
      if (s.equals("yes") || s.equals("no")) {
        // no action
      } else {
        DynamicError err = new DynamicError("Value of 'stable' on xsl:sort must be 'yes' or 'no'");
        err.setErrorCode("XTDE0030");
        throw err;
      }
    }

    if (orderX.equals("ascending")) {
      return comp;
    } else if (orderX.equals("descending")) {
      return new DescendingComparer(comp);
    } else {
      DynamicError err1 = new DynamicError("order must be 'ascending' or 'descending'");
      err1.setErrorCode("XTDE0030");
      throw err1;
    }
  }