// Copyright 2008-2014 severally by the contributors
//
// Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package net.sf.practicalxml.example;

import java.util.List;

import javax.xml.namespace.QName;
import javax.xml.xpath.XPathFunction;
import javax.xml.xpath.XPathFunctionException;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

import net.sf.practicalxml.ParseUtil;
import net.sf.practicalxml.xpath.XPathWrapper;


/**
 *  Example of using XPathFactory, including namespace and function bindings.
 */
public class XPathExample
{
    /**
     *  Some sample XML. Note that all elements are namespaced, but do not use qualified names.
     */
    private final static String xml = "<root xmlns='urn:X-EXAMPLE'>"
                                    +     "<entry id='1' code='A'>foo</entry>"
                                    +     "<entry id='2' code='A'>bar</entry>"
                                    +     "<entry id='3' code='C'>baz</entry>"
                                    +     "<entry id='4' code='E'>argle</entry>"
                                    +     "<entry id='4' code='E'>bargle</entry>"
                                    + "</root>";

    /**
     *  An XPath function that tests a single node's value against an inclusive string-valued
     *  range (useful because XPath 1.0 does not support relational operations against strings).
     *  <p>
     *  This implementation uses the standard XPathFunction API, and makes assumptions about the
     *  types of its argument. For real-world functions, I recommend starting from
     *  <code>net.sf.practicalxml.xpath.AbstractFunction</code>, which uses a Template Method
     *  approach to the boilerplate of argument processing.
     */
    private static class XPathRange implements XPathFunction
    {
        @SuppressWarnings("rawtypes")
        public Object evaluate(List args) throws XPathFunctionException
        {
            try
            {
                java.util.Iterator argItx = args.iterator();
                NodeList selection = (NodeList)argItx.next();
                String lowerBound = (String)argItx.next();
                String upperBound = (String)argItx.next();

                String value = (selection.getLength() > 0) ? selection.item(0).getNodeValue() : "";
                return (value.compareTo(lowerBound) >= 0) && (value.compareTo(upperBound) <= 0);
            }
            catch (Exception ex)
            {
                throw new XPathFunctionException(ex);
            }
        }
    }


    public static void main(String[] argv) throws Exception
    {
        Document dom = ParseUtil.parse(xml);

        // selects a single element using an attribute test
        List<Element> selection1 = new XPathWrapper("//ns:entry[@code='C']")
                                   .bindNamespace("ns", "urn:X-EXAMPLE")
                                   .evaluate(dom, Element.class);
        System.out.println("nodes selected by simple test = " + selection1.size());

        // selects the same element using a function -- note the second namespace binding
        List<Element> selection2 = new XPathWrapper("//ns:entry[fn:inRange(@code, 'B', 'D')]")
                                   .bindNamespace("ns", "urn:X-EXAMPLE")
                                   .bindNamespace("fn", "urn:X-EXAMPLE-FN")
                                   .bindFunction(new QName("urn:X-EXAMPLE-FN", "inRange"), new XPathRange(), 3)
                                   .evaluate(dom, Element.class);
        System.out.println("nodes selected by function    = " + selection2.size());
    }
}
