package com.protek.abs.apps.basis.xdata;

import org.w3c.dom.*;
import org.xml.sax.InputSource;

import javax.xml.parsers.*;
import javax.xml.transform.TransformerException;

// this must be moved to a wrapper to be independend of Apache
import org.apache.xpath.*;
import org.apache.xpath.objects.*;

/**
 * Title:        ABS - advanced billing system
 * Description:
 * Copyright:    Copyright (c) 2001
 * Company:      Protek Flagship LLC
 * @author Konstantin Piroumian
 * @version 1.0
 */

public class XData implements XDataInterface {

    Document doc;
    Node root;
    Node contextNode;

    public XData() throws XDataException {
        initialize();
    }

    public XData(InputSource is) throws XDataException {
        initialize(is);
    }

    protected void initialize() throws XDataException {
        doc = newDocument();
        root = doc.createDocumentFragment();
        doc.appendChild(root);
        contextNode = root;
    }

    protected void initialize(InputSource is) throws XDataException {
        doc = getDocument(is);
        root = doc.getDocumentElement();
        contextNode = root;
    }

    // XDataInterface implementation
    public String getValue(String xpath) throws XDataException{
        try {
            return XPathAPI.eval(contextNode, xpath).toString();
        } catch (TransformerException te) {
            throw new XDataException(te);
        }
    }

    public void setValue(String xpath, String value) throws XDataException {
        try {
            Node node = XPathAPI.selectSingleNode(contextNode, xpath);
            if (node != null) {
                switch (node.getNodeType()) {
                    case Node.TEXT_NODE:
                    case Node.ATTRIBUTE_NODE: {
                        node.setNodeValue(value);
                        break;
                    }
                    case Node.ELEMENT_NODE: {
                        Node child = XPathAPI.selectSingleNode(node, "text()");
                        if (child == null) {
                            child = doc.createTextNode(value);
                            node.appendChild(child);
                        }
                        else {
                            child.setNodeValue(value);
                        }
                        break;
                    }
                }
            }
        } catch (TransformerException te) {
            throw new XDataException(te);
        }
    }

    public void addValue(String xpath, String value) throws XDataException {
//        try {
//            // FIXME How to implement this?
//        } catch (TransformerException te) {
//            throw new XDataException(te);
//        }
    }

    public String removeValue(String xpath) throws XDataException {
        try {
            Node node = XPathAPI.selectSingleNode(contextNode, xpath);
            if (node != null) {
                return node.getParentNode().removeChild(node).toString();
            }
            else {
                throw new XDataException("Cannot remove value by expression: " + xpath);
            }

        } catch (TransformerException te) {
            throw new XDataException(te);
        }
    }

    public Node setContextNode(String xpath) throws XDataException {
        try {
            Node node = XPathAPI.selectSingleNode(doc, xpath);
            if (node != null) {
                contextNode = node;
                return contextNode;
            }
            else {
                throw new XDataException("Could not set context node to: " + xpath);
            }
        } catch (TransformerException te) {
            throw new XDataException(te);
        }
    }

    public static Document newDocument()
    throws XDataException
    {
        try {
            DocumentBuilder db = DocumentBuilderFactory.newInstance().newDocumentBuilder();
            return db.newDocument();
        } catch (ParserConfigurationException pce) {
            throw new XDataException(pce);
        }
    }

    public static Document getDocument(InputSource is)
    throws XDataException
    {
        try {
            DocumentBuilder db = DocumentBuilderFactory.newInstance().newDocumentBuilder();
            return db.parse(is);
        } catch (ParserConfigurationException pce) {
            throw new XDataException(pce);
        }
        catch (java.io.IOException ioe) {
            throw new XDataException(ioe);
        }
        catch (org.xml.sax.SAXException se) {
            throw new XDataException(se);
        }
    }


    /**
     *
     */
    public static void main(String[] args) {
        try {
            java.io.StringReader sr = new java.io.StringReader(
                "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
                + "<person>"
                + "<first_name />"
                + "<first_name id=\"1\"/>"
                + "</person>"
            );
            InputSource is = new InputSource(sr);
            XDataInterface xd = new XData(is);

            String setPath = "/person/first_name[@id='1']";
            String getPath = "/person/first_name[2]";

            long time = System.currentTimeMillis();
            xd.setValue(setPath, "Konstantin");
            time = System.currentTimeMillis() - time;
            System.out.println("SET value to '" + setPath + "' processed in: " + time + " ms");

            time = System.currentTimeMillis();
            String value = xd.getValue(getPath);
            time = System.currentTimeMillis() - time;
            System.out.println("Value of '"+ getPath +"': " + value);
            System.out.println("GET processed in: " + time + " ms");
        } catch (Exception e) {
            System.out.println("Error!!!");
            e.printStackTrace();
        }
    }
}




