/*
 * Copyright (c) 2004-2008 Pixware. 
 *
 * Author: Hussein Shafie
 *
 * This file is part of the XMLmind XML Editor project.
 * For conditions of distribution and use, see the accompanying legal.txt file.
 */

import java.io.File;

import java.util.Map;
import java.util.ArrayList;

import com.xmlmind.xml.name.Name;

import com.xmlmind.xml.doc.XNode;
import com.xmlmind.xml.doc.Document;

import com.xmlmind.xml.load.LoadDocument;

import com.xmlmind.xml.xpath.ParseException;
import com.xmlmind.xml.xpath.EvalException;
import com.xmlmind.xml.xpath.NodeIterator;
import com.xmlmind.xml.xpath.Variant;
import com.xmlmind.xml.xpath.VariantBase;
import com.xmlmind.xml.xpath.StringExpr;
import com.xmlmind.xml.xpath.NumberExpr;
import com.xmlmind.xml.xpath.BooleanExpr;
import com.xmlmind.xml.xpath.NodeSetExpr;
import com.xmlmind.xml.xpath.VariantExpr;
import com.xmlmind.xml.xpath.Pattern;
import com.xmlmind.xml.xpath.ExprContext;
import com.xmlmind.xml.xpath.ExprContextBase;
import com.xmlmind.xml.xpath.ExprParser;

public final class XPathUtil {
    public static String evalAsString(String expr, XNode node) 
        throws ParseException, EvalException {
        return evalAsString(expr, node, null);
    }

    public static String evalAsString(String expr, XNode node,
                                      Map<String, Object> variables) 
        throws ParseException, EvalException {
        StringExpr parsed = 
            ExprParser.parseStringExpr(expr, node.namespacePrefixMap(),
                                       /*acceptUnknownPrefix*/ false);
        return parsed.eval(node, createExprContext(variables));
    }

    private static 
    ExprContext createExprContext(final Map<String, Object> variables) {
        if (variables == null) {
            return new ExprContextBase();
        } else {
            return new ExprContextBase() {
                public Variant getVariableValue(Name name, XNode node) 
                    throws EvalException {
                    Object value = variables.get(name.localPart);
                    if (value == null)
                        return null;
                    else
                        return VariantBase.create(value);
                }
            };
        }
    }

    public static double evalAsNumber(String expr, XNode node) 
        throws ParseException, EvalException {
        return evalAsNumber(expr, node, null);
    }

    public static double evalAsNumber(String expr, XNode node,
                                      Map<String, Object> variables) 
        throws ParseException, EvalException {
        NumberExpr parsed = 
            ExprParser.parseNumberExpr(expr, node.namespacePrefixMap(),
                                       /*acceptUnknownPrefix*/ false);
        return parsed.eval(node, createExprContext(variables));
    }

    public static boolean evalAsBoolean(String expr, XNode node) 
        throws ParseException, EvalException {
        return evalAsBoolean(expr, node, null);
    }

    public static boolean evalAsBoolean(String expr, XNode node,
                                        Map<String, Object> variables) 
        throws ParseException, EvalException {
        BooleanExpr parsed = 
            ExprParser.parseBooleanExpr(expr, node.namespacePrefixMap(),
                                        /*acceptUnknownPrefix*/ false);
        return parsed.eval(node, createExprContext(variables));
    }

    public static XNode[] evalAsNodeSet(String expr, XNode node) 
        throws ParseException, EvalException {
        return evalAsNodeSet(expr, node, null);
    }

    public static XNode[] evalAsNodeSet(String expr, XNode node,
                                        Map<String, Object> variables) 
        throws ParseException, EvalException {
        NodeSetExpr parsed = 
            ExprParser.parseNodeSetExpr(expr, node.namespacePrefixMap(),
                                        /*acceptUnknownPrefix*/ false);
        NodeIterator iter = parsed.eval(node, createExprContext(variables));

        ArrayList<XNode> list = new ArrayList<XNode>();
        for (;;) {
            XNode n = iter.next();
            if (n == null)
                break;

            list.add(n);
        }

        XNode[] nodes = new XNode[list.size()];
        list.toArray(nodes);
        return nodes;
    }

    public static Variant evalAsVariant(String expr, XNode node) 
        throws ParseException, EvalException {
        return evalAsVariant(expr, node, null);
    }

    public static Variant evalAsVariant(String expr, XNode node,
                                        Map<String, Object> variables) 
        throws ParseException, EvalException {
        VariantExpr parsed = 
            ExprParser.parseVariantExpr(expr, node.namespacePrefixMap(),
                                        /*acceptUnknownPrefix*/ false);
        return parsed.eval(node, createExprContext(variables));
    }

    public static boolean matches(XNode node, String pattern) 
        throws ParseException, EvalException {
        return matches(node, pattern, null);
    }

    public static boolean matches(XNode node, String pattern,
                                  Map<String, Object> variables) 
        throws ParseException, EvalException {
        Pattern parsed = 
            ExprParser.parsePattern(pattern, node.namespacePrefixMap(),
                                    /*acceptUnknownPrefix*/ false);
        return parsed.matches(node, createExprContext(variables));
    }

    // -----------------------------------------------------------------------

    public static void main(String[] args)
        throws Exception {
        if (args.length != 2) {
            System.err.println("usage: java XPathUtil xml_doc" +
                               " xpath_expression_which_is_also_a_pattern");
            System.exit(1);
        }
        String fileName = args[0];
        String expr = args[1];

        Document doc = LoadDocument.load(new File(fileName));

        XNode[] nodes = XPathUtil.evalAsNodeSet(expr, doc);
        if (nodes.length == 0) {
            System.out.println("no nodes in '" + fileName + "' match '" + 
                               expr + "'");
            return;
        }

        for (int i = 0; i < nodes.length; ++i) {
            XNode node = nodes[i];

            boolean match = false;
            try {
                match = XPathUtil.matches(node, expr);
            } catch (ParseException e) {
                System.err.println("'" + expr + 
                                   "' is not a pattern: skipping " + node);
                continue;
            }

            System.out.println(node + 
                               (match? " matches " : " DOES NOT MATCH ") + 
                               "'" + expr + "'");
        }
    }
}

