/**
   * Evaluate expression and return a node
   *
   * @param cexp cexp
   * @param ctx ctx
   * @return type
   * @throws FaultException FaultException
   * @throws EvaluationException EvaluationException
   */
  public Node evaluateNode(OExpression cexp, EvaluationContext ctx)
      throws FaultException, EvaluationException {
    List retVal = evaluate(cexp, ctx);

    if (retVal.size() == 0) {
      throw new FaultException(
          cexp.getOwner().getConstants().getQnSelectionFailure(),
          "No results for expression: " + cexp);
    }

    if (retVal.size() > 1) {
      throw new FaultException(
          cexp.getOwner().getConstants().getQnSelectionFailure(),
          "Multiple results for expression: " + cexp);
    }

    return (Node) retVal.get(0);
  }
  /**
   * Evaluate expression and return a date
   *
   * @param cexp cexp
   * @param context context
   * @return type
   * @throws FaultException FaultException
   * @throws EvaluationException EvaluationException
   */
  public Calendar evaluateAsDate(OExpression cexp, EvaluationContext context)
      throws FaultException, EvaluationException {
    List literal = DOMUtils.toList(evaluate(cexp, context, XPathConstants.NODESET));

    if (literal.size() == 0) {
      throw new FaultException(
          cexp.getOwner().getConstants().getQnSelectionFailure(),
          "No results for expression: " + cexp);
    }

    if (literal.size() > 1) {
      throw new FaultException(
          cexp.getOwner().getConstants().getQnSelectionFailure(),
          "Multiple results for expression: " + cexp);
    }

    Object date = literal.get(0);

    if (date instanceof Calendar) {
      return (Calendar) date;
    }

    if (date instanceof Date) {
      Calendar cal = Calendar.getInstance();
      cal.setTime((Date) date);

      return cal;
    }

    if (date instanceof Element) {
      date = ((Element) date).getTextContent();
    }

    try {
      return ISO8601DateParser.parseCal(date.toString());
    } catch (Exception ex) {
      String errmsg = "Invalid date: " + literal;
      __log.error(errmsg, ex);
      throw new FaultException(
          cexp.getOwner().getConstants().getQnInvalidExpressionValue(), errmsg);
    }
  }
  /**
   * Evaluate expression and return duration
   *
   * @param cexp cexp
   * @param context context
   * @return type
   * @throws FaultException FaultException
   * @throws EvaluationException EvaluationException
   */
  public Duration evaluateAsDuration(OExpression cexp, EvaluationContext context)
      throws FaultException, EvaluationException {
    String literal = this.evaluateAsString(cexp, context);

    try {
      return new Duration(literal);
    } catch (Exception ex) {
      String errmsg = "Invalid duration: " + literal;
      __log.error(errmsg, ex);
      throw new FaultException(
          cexp.getOwner().getConstants().getQnInvalidExpressionValue(), errmsg);
    }
  }
  /**
   * Evaluate expression and return opaque type
   *
   * @param cexp cexp
   * @param ctx ctx
   * @param type type
   * @return type
   * @throws FaultException FaultException
   * @throws EvaluationException EvaluationException
   */
  private Object evaluate(OExpression cexp, EvaluationContext ctx, QName type)
      throws FaultException, EvaluationException {
    try {
      OXQuery10ExpressionBPEL20 oxquery10 = ((OXQuery10ExpressionBPEL20) cexp);

      XQDataSource xqds = new SaxonXQDataSource();
      XQConnection xqconn = xqds.getConnection();

      Configuration configuration = ((SaxonXQConnection) xqconn).getConfiguration();
      configuration.setAllNodesUntyped(true);
      configuration.setHostLanguage(Configuration.XQUERY);

      XQStaticContext staticEnv = xqconn.getStaticContext();

      NSContext nsContext = oxquery10.getNamespaceCtx();
      Set<String> prefixes = nsContext.getPrefixes();
      for (String prefix : prefixes) {
        String uri = nsContext.getNamespaceURI(prefix);
        staticEnv.declareNamespace(prefix, uri);
      }

      configuration.setSchemaValidationMode(Validation.SKIP);
      xqconn.setStaticContext(staticEnv);

      // Prepare expression, for starters
      String xquery =
          oxquery10
              .getXquery()
              .replaceFirst(
                  Constants.XQUERY_FUNCTION_HANDLER_COMPILER,
                  Constants.XQUERY_FUNCTION_HANDLER_RUNTIME);
      XQPreparedExpression exp = xqconn.prepareExpression(xquery);

      JaxpFunctionResolver funcResolver = new JaxpFunctionResolver(ctx, oxquery10);
      JaxpVariableResolver variableResolver =
          new JaxpVariableResolver(ctx, oxquery10, configuration);
      // Bind external variables to runtime values
      for (QName variable : exp.getAllUnboundExternalVariables()) {
        // Evaluate referenced variable
        Object value = variableResolver.resolveVariable(variable);

        if (value instanceof Value) {
          SaxonXQConnection saxonConn = (SaxonXQConnection) xqconn;
          try {
            Item item = ((Value) value).asItem();
            if (item == null) {
              exp.bindSequence(variable, xqconn.createSequence(Collections.EMPTY_LIST.iterator()));
            } else {
              XQItem item2 = new SaxonXQItem(item, saxonConn);
              exp.bindItem(variable, item2);
            }
          } catch (XPathException e) {
            __log.warn("", e);
          }
        } else {

          if (value instanceof Date) {
            Date d = (Date) value;
            value = org.apache.ode.utils.ISO8601DateParser.format(d);
          }

          // Figure out type of variable
          XQSequenceType xqType = getItemType(xqconn, value);

          // Saxon doesn't like binding sequences to variables
          if (value instanceof Node) {
            // a node is a node-list, but the inverse isn't true.
            // so, if the value is truly a node, leave it alone.
          } else if (value instanceof NodeList) {
            // So extract the first item from the node list
            NodeList nodeList = (NodeList) value;
            ArrayList nodeArray = new ArrayList();
            for (int i = 0; i < nodeList.getLength(); i++) {
              nodeArray.add(nodeList.item(i));
            }
            value = xqconn.createSequence(nodeArray.iterator());
          }

          // Bind value with external variable
          if (value != null && xqType != null) {
            if (value instanceof XQSequence) {
              exp.bindSequence(variable, (XQSequence) value);
            } else {
              if (xqType instanceof XQItemType) {
                exp.bindObject(variable, value, (XQItemType) xqType);
              }
            }
          }
        }
      }

      // Set context node
      Node contextNode = (ctx.getRootNode() == null) ? DOMUtils.newDocument() : ctx.getRootNode();
      contextNode.setUserData(
          XQuery10BpelFunctions.USER_DATA_KEY_FUNCTION_RESOLVER, funcResolver, null);
      exp.bindItem(
          XQConstants.CONTEXT_ITEM,
          xqconn.createItemFromNode(contextNode, xqconn.createNodeType()));

      // Execute query
      XQResultSequence result = exp.executeQuery();

      // Cast Saxon result to Java result
      Object evalResult = getResultValue(type, result);

      if ((evalResult != null) && __log.isDebugEnabled()) {
        __log.debug(
            "Expression "
                + cexp.toString()
                + " generated result "
                + evalResult
                + " - type="
                + evalResult.getClass().getName());

        if (ctx.getRootNode() != null) {
          __log.debug("Was using context node " + DOMUtils.domToString(ctx.getRootNode()));
        }
      }

      return evalResult;
    } catch (XQException xqe) {
      // Extracting the real cause from all this wrapping isn't a simple task
      Throwable cause = (xqe.getCause() != null) ? xqe.getCause() : xqe;

      if (cause instanceof DynamicError) {
        Throwable th = ((DynamicError) cause).getException();

        if (th != null) {
          cause = th;

          if (cause.getCause() != null) {
            cause = cause.getCause();
          }
        }
      }

      throw new EvaluationException(
          "Error while executing an XQuery expression: " + cause.toString(), cause);
    } catch (WrappedResolverException wre) {
      __log.debug("Could not evaluate expression because of ", wre);
      throw (FaultException) wre.getCause();
    }
  }