class XPathExpression implements Expression {

    @Override
    public ExpressionResult apply(ExpressionContext expressionContext, ExpressionResult input) {
        Object doc = document.apply(expressionContext, input).asXml();
        String xpathValue = xpath.apply(expressionContext, input).getValue().toString();
        XPath xpathEvaluator = xpathFactory.newXPath();
        Object result = null;
        ExpressionResult outputTypeValue = null;
        try {
            if( outputType != null) {
                outputTypeValue = outputType.apply(expressionContext, input);
                if( doc instanceof InputSource) {
                    result = xpathEvaluator.compile(xpathValue).evaluate(
                            (InputSource)doc, outputType(outputTypeValue));
                } else {
                    result = xpathEvaluator.compile(xpathValue).evaluate(
                            doc, outputType(outputTypeValue));
                }
            } else {
                if( doc instanceof InputSource) {
                    result = xpathEvaluator.compile(xpathValue).evaluate((InputSource) doc );
                } else {
                    result = xpathEvaluator.compile(xpathValue).evaluate(doc);
                }
            }
        } catch (XPathExpressionException e) {
            throw new RuntimeException(e.getMessage(), e);
        }
        List<ExpressionResult> values = new ArrayList<>();
        if( result instanceof Element) {
            return toExpressionResult(result, outputTypeValue);
        }

        if( result instanceof NodeList) {
            NodeList resultArray = (NodeList)result;
            for( int i = 0; i < resultArray.getLength(); i++) {
                values.add(toExpressionResult(resultArray.item(i), outputTypeValue));
            }
            return new ListExpressionResult(values);
        }

        return toExpressionResult(result, outputTypeValue);
    }

    public  XPathExpression(String alias, Expression[] expressions);

}