
import net.sf.saxon.Configuration;
import net.sf.saxon.Controller;
import net.sf.saxon.trans.XPathException;
import net.sf.saxon.instruct.UserFunction;
import net.sf.saxon.om.*;
import net.sf.saxon.query.DynamicQueryContext;
import net.sf.saxon.query.StaticQueryContext;
import net.sf.saxon.query.XQueryExpression;
import net.sf.saxon.query.QueryResult;
import net.sf.saxon.value.IntegerValue;
import net.sf.saxon.value.Value;
import net.sf.saxon.xpath.XPathEvaluator;
import net.sf.saxon.trans.XPathException;

import javax.xml.transform.OutputKeys;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.Properties;


/**
 * Some examples to show how the Saxon XQuery API should be used
 */
public class QueryAPIExamples {

    /**
     * Class is not instantiated, so give it a private constructor
     */
    private QueryAPIExamples() {
    }

    /**
     * Method main
     */
    public static void main(String argv[]) {

        String test = "all";
        if (argv.length > 0) {
            test = argv[0];
        }

        if (test.equals("all") || test.equals("toStreamResult")) {
            System.out.println("\n\n==== toStreamResult ====");

            try {
                exampleToStreamResult();
            } catch (XPathException ex) {
                handleException(ex);
            }
        }

        if (test.equals("all") || test.equals("toSingleton")) {
            System.out.println("\n\n==== toSingleton ====");

            try {
                exampleToSingleton();
            } catch (XPathException ex) {
                handleException(ex);
            }
        }

        if (test.equals("all") || test.equals("toSequence")) {
            System.out.println("\n\n==== toSequence ====");

            try {
                exampleToSequence();
            } catch (XPathException ex) {
                handleException(ex);
            }
        }

        if (test.equals("all") || test.equals("toSerializedSequence")) {
            System.out.println("\n\n==== toSerializedSequence ====");

            try {
                exampleToSerializedSequence();
            } catch (XPathException ex) {
                handleException(ex);
            }
        }

        if (test.equals("all") || test.equals("toWrappedSequence")) {
            System.out.println("\n\n==== toWrappedSequence ====");

            try {
                exampleToWrappedSequence();
            } catch (XPathException ex) {
                handleException(ex);
            }
        }

        if (test.equals("all") || test.equals("toHTMLFile")) {
            System.out.println("\n\n==== toHTMLFile ====");

            try {
                exampleToHTMLFile();
            } catch (XPathException ex) {
                handleException(ex);
            } catch (IOException ex) {
                System.err.println(
                        "Problem reading/writing files. Check that the current directory is the 'samples' directory");
                handleException(ex);
            }
        }

        if (test.equals("all") || test.equals("withParam")) {
            System.out.println("\n\n==== withParam ====");

            try {
                exampleWithParam();
            } catch (XPathException ex) {
                handleException(ex);
            }
        }

        if (test.equals("all") || test.equals("pipeline")) {
            System.out.println("\n\n==== pipeline ====");

            try {
                examplePipeline();
            } catch (XPathException ex) {
                handleException(ex);
            }
        }

        if (test.equals("all") || test.equals("directFunction")) {
            System.out.println("\n\n==== directFunction ====");

            try {
                exampleDirectFunction();
            } catch (XPathException ex) {
                handleException(ex);
            }
        }


    }

    /**
     * Show a query producing a document as its result and serializing this
     * directly to System.out
     */

    public static void exampleToStreamResult() throws XPathException {
        final Configuration config = new Configuration();
        final StaticQueryContext sqc = new StaticQueryContext(config);
        final XQueryExpression exp = sqc.compileQuery("<a b='c'>{5+2}</a>");

        final DynamicQueryContext dynamicContext = new DynamicQueryContext(config);
        final Properties props = new Properties();
        props.setProperty(OutputKeys.METHOD, "xml");
        props.setProperty(OutputKeys.INDENT, "yes");
        exp.run(dynamicContext, new StreamResult(System.out), props);
    }

    /**
     * Show a query producing a single atomic value as its result and returning the value
     * to the Java application
     */

    public static void exampleToSingleton() throws XPathException {
        final Configuration config = new Configuration();
        final StaticQueryContext sqc = new StaticQueryContext(config);
        final XQueryExpression exp = sqc.compileQuery("avg(for $i in 1 to 10 return $i*$i)");

        final DynamicQueryContext dynamicContext = new DynamicQueryContext(config);
        final BigDecimal result = (BigDecimal)exp.evaluateSingle(dynamicContext);
        System.out.println(result);

    }

    /**
     * Show a query producing a sequence as its result and returning the sequence
     * to the Java application in the form of an iterator. For each item in the
     * result, its string value is output.
     */

    public static void exampleToSequence() throws XPathException {
        final Configuration config = new Configuration();
        final StaticQueryContext sqc = new StaticQueryContext(config);
        final XQueryExpression exp = sqc.compileQuery("for $i in 1 to 10 return ($i * $i)");

        final DynamicQueryContext dynamicContext = new DynamicQueryContext(config);
        final SequenceIterator iter = exp.iterator(dynamicContext);
        while (true) {
            Item item = iter.next();
            if (item == null) {
                break;
            }
            System.out.println(item.getStringValue());
        }

    }

    /**
     * Show a query producing a sequence as its result and returning the sequence
     * to the Java application in the form of an iterator. The sequence is then
     * output by serializing each item individually, with each item on a new line.
     */

    public static void exampleToSerializedSequence() throws XPathException {
        final Configuration config = new Configuration();
        final StaticQueryContext sqc = new StaticQueryContext(config);
        final XQueryExpression exp = sqc.compileQuery(
                "<doc><chap><a>3</a></chap></doc>//a, <b>4</b>, attribute c {5}, 19");
        Properties props = new Properties();
        props.setProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");

        final DynamicQueryContext dynamicContext = new DynamicQueryContext(config);
        final SequenceIterator iter = exp.iterator(dynamicContext);
        QueryResult.serializeSequence(iter, config, System.out, props);
    }

    /**
     * Show a query producing a sequence as its result and returning the sequence
     * to the Java application in the form of an iterator. The sequence is then
     * output by wrapping the items in a document, with wrapping elements indicating
     * the type of each item, and serializing the resulting document.
     */

    public static void exampleToWrappedSequence() throws XPathException {
        final Configuration config = new Configuration();
        final StaticQueryContext sqc = new StaticQueryContext(config);
        final XQueryExpression exp = sqc.compileQuery(
                "<doc><chap><a>3</a></chap></doc>//a, <b>4</b>, attribute c {5}, 19");
        Properties props = new Properties();
        props.setProperty(OutputKeys.OMIT_XML_DECLARATION, "no");
        props.setProperty(OutputKeys.INDENT, "yes");

        final DynamicQueryContext dynamicContext = new DynamicQueryContext(config);
        final SequenceIterator iter = exp.iterator(dynamicContext);
        final DocumentInfo doc = QueryResult.wrap(iter, config);
        QueryResult.serialize(doc, new StreamResult(System.out), props, config);
    }


    /**
     * Show how to run a query that is read from a file and that serializes its output
     * as HTML to another file. The input to the query (the initial value of the context
     * node) is supplied as the content of another file.
     */

    public static void exampleToHTMLFile() throws XPathException, IOException {
        final Configuration config = new Configuration();
        final StaticQueryContext sqc = new StaticQueryContext(config);
        final XQueryExpression exp = sqc.compileQuery(new FileReader("query/books-to-html.xq"));
        final DynamicQueryContext dynamicContext = new DynamicQueryContext(config);
        dynamicContext.setContextNode(sqc.buildDocument(new StreamSource("data/books.xml")));
        final Properties props = new Properties();
        props.setProperty(OutputKeys.METHOD, "html");
        props.setProperty(OutputKeys.DOCTYPE_PUBLIC, "-//W3C//DTD HTML 4.01 Transitional//EN");
        exp.run(dynamicContext, new StreamResult(new File("booklist.html")), props);
    }

    /**
     * Show a query that takes a parameter (external variable) as input.
     * The query produces a single atomic value as its result and returns the value
     * to the Java application. For the types of value that may be returned, and
     * their mapping to XPath data types, see {@link XPathEvaluator#evaluate}
     */

    public static void exampleWithParam() throws XPathException {
        final Configuration config = new Configuration();
        final StaticQueryContext sqc = new StaticQueryContext(config);
        final XQueryExpression exp = sqc.compileQuery("declare variable $in as xs:integer external;" +
                "$in * $in");

        final DynamicQueryContext dynamicContext = new DynamicQueryContext(config);
        dynamicContext.setParameter("in", new Long(17));
        final Long result = (Long)exp.evaluateSingle(dynamicContext);
        System.out.println("17 * 17 = " + result);

    }

    /**
     * Show how to run two queries in tandem. The second query is applied to the
     * results of the first.
     */

    public static void examplePipeline() throws XPathException {
        final Configuration config = new Configuration();

        // Compile the first query
        final StaticQueryContext sqc1 = new StaticQueryContext(config);
        final XQueryExpression exp1 = sqc1.compileQuery(
                "declare variable $in as xs:integer external;" +
                "document{ <a>{$in * $in}</a> }"
        );

        // Compile the second query (each query should have its own static context)
        final StaticQueryContext sqc2 = new StaticQueryContext(config);
        final XQueryExpression exp2 = sqc2.compileQuery(
                "/a + 5"
        );

        // Run the first query
        final DynamicQueryContext dynamicContext = new DynamicQueryContext(config);
        dynamicContext.setParameter("in", new Long(3));
        final NodeInfo doc = (NodeInfo)exp1.evaluateSingle(dynamicContext);

        // Run the second query
        dynamicContext.clearParameters();
        dynamicContext.setContextNode(doc);
        final Object result = exp2.evaluateSingle(dynamicContext);
        System.out.println("3*3 + 5 = " + result);
        // The result is actually a java.lang.Double
    }

    /**
     * Show a direct call from the Java application to a function defined in the
     * Query. This is a very efficient way of invoking a query, but it does minimal
     * checking of the supplied arguments
     */

    public static void exampleDirectFunction() throws XPathException {
        final StaticQueryContext sqc = new StaticQueryContext(new Configuration());
        final XQueryExpression exp1 = sqc.compileQuery("declare namespace f='f.ns';" +
                "declare function f:t1($v1 as xs:integer, $v2 as xdt:untypedAtomic*) { " +
                "   $v1 div $v2" +
                "};" +
                "10");

        final UserFunction fn1 = sqc.getUserDefinedFunction("f.ns", "t1", 2);
        final Controller controller = exp1.getController();
        final Value[] arglist = new Value[2];
        arglist[0] = new IntegerValue(10);
        for (int i=3; i<10; i++) {
            arglist[1] = new IntegerValue(i);
            final ValueRepresentation result = fn1.call(arglist, controller);
            System.out.println(arglist[0] + " div " + arglist[1] + " = " + result);
        }
    }

    /**
     * Handle an exception thrown while running one of the examples
     *
     * @param ex the exception
     */
    private static void handleException(Exception ex) {
        System.out.println("EXCEPTION: " + ex);
        ex.printStackTrace();
    }

}

//
// The contents of this file are subject to the Mozilla Public License Version 1.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.mozilla.org/MPL/
//
// Software distributed under the License is distributed on an "AS IS" basis,
// WITHOUT WARRANTY OF ANY KIND, either express or implied.
// See the License for the specific language governing rights and limitations under the License.
//
// The Original Code is: all this file.
//
// The Initial Developer of the Original Code is Michael H. Kay.
//
// Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
//
// Contributor(s): none.
//
