/*
 * Scone - The Web Enhancement Framework
 * Copyright (C) 2009 Harald Weinreich, Volkert Buchmann, Frank Wollenweber, Torsten Haß
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */
package  scone.util;


import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;

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


/**
 * Properties which are defined and stored in an XML document.
 * <p>
 * Properties files must contain a properties element which may contain property elements.
 * There are three types of properties: boolean, choice and text.<br>
 * Text properties are defined like this:<br>
 * <pre>&lt;property name="example1" type="text" value="example value"/&gt;</pre><br>
 * Boolean properties are defined like this (the value may be true or false):<br>
 * <pre>&lt;property name="example2" type="boolean" value="true"&gt;</pre><br>
 * Choice properties are defined like this:<br>
 * <pre>&lt;property name="example3" type="choice" value="harald"&gt;
 *    &lt;value&gt;volkert&lt;/value&gt;
 *    &lt;value&gt;harald&lt;/value&gt;
 *    &lt;value&gt;matthias&lt;/value&gt;
 * &lt;/property&gt;</pre><br>
 * Where the value elements contain possible choices.  <p>
 * Every Scone plugin has a properties file named [sconepath]/config/properties/[fullclassname].xml 
 * The class PersistentPropertiesGui can generate a dialog from such a properties file.
 */  
public class PersistentProperties {
    private Document dom = null;
    private String fileName = null;
    private Hashtable properties = new Hashtable();
    private Vector propertiesOrder = new Vector();

    /**
     * creates a new instance of PersitentProperties.
     * @param fileName the file name of the XML definitions.
     */
    public PersistentProperties(String fileName) {
        this.fileName = fileName;
        try {
            // read in xml document
            dom = XMLHelper.parseReader(new FileReader(fileName));
            NodeEnumeration nodes = new NodeEnumeration(dom.getFirstChild(), "property");
            Element el = null;
            Property prop = null;

            // and parse in the properties
            while (nodes.hasMoreNodes()) {
                try {
                    el = (Element) nodes.nextNode();
                    prop = new Property(el);
                    properties.put(prop.getName(), prop);
                    propertiesOrder.add(prop.getName());
                } catch (Exception exc) {
                    ErrorLog.log(this, "<init>", "", exc);
                }
            }
        } catch (IOException ioe) {
            ErrorLog.log(this, "<init>", "Could not read " + fileName, ioe);
            System.out.println("The properties file " + fileName + " could not be found.\nPlease create one and try again!");
            // we are trying to create an empty dom
            dom = new org.apache.xerces.dom.DocumentImpl();
            dom.appendChild(dom.createElement("properties"));
        } catch (Exception ex) {
            ErrorLog.log(this, "<init>", "Error occurred while reading " + fileName,
                    ex);
        }
    }

    /**
     * returns an enumeration of the property names in the order in 
     * which they are defined in the xml document
     * @return the enumeration of the property names
     */
    public Enumeration getKeys() {
        return propertiesOrder.elements();
    }
    
    /**
     * returns the value of the specified property as a String
     * @param name the name of the property
     * @return the value of the property as a String or null if the property does not exist
     */
    public String get(String name) {
        if (properties.get(name) != null) {
            return  ((Property) properties.get(name)).getValue();
        } else {
            return  null;
        }
    }

    /**
     * returns the possible values of a choice property 
     * or null if the property does not exist or if it is no choice property.
     * @param name the name
     * @return a Vector of the possible values
     */
    protected Vector getValues(String name) {
        if (properties.get(name) != null) {
            return  ((Property) properties.get(name)).getValues();
        } else {
            return  null;
        }
    }

    /**
     * returns the type of this property
     * @param name the name of the property
     * @return the type of the property or null if it does not exist
     */
    protected String getType(String name) {
        if (properties.get(name) != null) {
            return  ((Property) properties.get(name)).getType();
        } else {
            return  null;
        }
    }

    /**
     * sets a property value. 
     * If the property does not exist, a new text property will be created.
     * @param name the name of the property
     * @param value the value of the property
     */
    public void set(String name, String value) {
        if (properties.get(name) != null) {
            ((Property) properties.get(name)).setValue(value);
        } else {
            Element node = dom.createElement("property");

            node.setAttribute("type", "text");
            node.setAttribute("name", name);
            node.setAttribute("value", value);
            dom.getFirstChild().appendChild(node);
            properties.put(name, new Property(node));
        }
    }

    /**
     * stores the current property values to the xml document.
     */
    public void store() {
        try {
            DOMWriter writer = new DOMWriter(new FileWriter(fileName), false);

            writer.print(dom);
        } catch (Exception exc) {
            ErrorLog.log(this, "store()", "Could not store " + fileName, exc);
        }
    }

    /**
     * returns the file name of the xml document
     * @return the file name of the xml document
     */
    public String getName() {
        return  fileName;
    }

    // a persistent property
    class Property {
        // the xml element representing the property
        Element element = null;

        // creates a new property
        Property(Element element) {
            this.element = element;
        }

        // returns the property name
        String getName() {
            return  element.getAttribute("name");
        }

        // returns the property value
        String getValue() {
            return  element.getAttribute("value");
        }

        // returns the property type
        String getType() {
            return  element.getAttribute("type");
        }

        // returns the possible values if it is a choice property
        Vector getValues() {
            Vector vec = new Vector();
            String value = null;
            NodeEnumeration nodes = new NodeEnumeration(element, "value");

            while (nodes.hasMoreNodes()) {
                try {
                    vec.addElement(nodes.nextNode().getFirstChild().getNodeValue());
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            return  vec;
        }

        // sets the value of the property
        void setValue(String value) {
            element.setAttribute("value", value);
        }
    }
}

