/* CVS: $Id: KnowledgeCatalog.java,v 1.12 2001/03/16 16:15:39 gvijf Exp $ */

package evolution;

import java.net.URL;
import java.lang.*;
import java.util.*;
import java.io.*;

/**
 * Abstract class of knowledgecatalogs.
 */
abstract public class KnowledgeCatalog {

    /**
     * Create a new KnowledgeCatalog from the given file.
     */
    protected KnowledgeCatalog(String fileName) throws FileNotFoundException, IOException {
        if(isInitialized())
            System.err.println("Warning this KnowledgeCatalog is already " +
            	"initialized (" + this + " from " + getFileName() + ")");
        URL url = ClassLoader.getSystemResource(getResourcePath() + fileName);
        if(url == null)
            throw new FileNotFoundException("Could not find " + fileName);
        prop = new Properties();
        prop.load(url.openStream());
        isInitialized = true;
        this.fileName = url;
        setResourcePath(fileName);
        SystemMessage.message("Loaded " + url);
    }

    /**
     * Set the resource path from a fileName of a KnowledgeCatalog.
     * If the resource path is already set, this method does nothing.
     * @param fileName A relative path + filename
     * (e.g. resources/evolution.prop). The directory separator is
     * the one used in URL's and UNIX paths (a '/' thus).
     */
    protected void setResourcePath(String fileName) {
        if(resourcePath != null) return;
        int i = fileName.lastIndexOf("/");
        if(i == -1) {
            resourcePath = "";
        } else {
        	resourcePath = fileName.substring(0, i + 1);
        }
    }

    /**
     * The resource path.
     * If not set returns an empty string "".
     */
    public static String getResourcePath() {
        return (resourcePath != null) ? resourcePath : "";
    }

    /**
     * Is this catalog initialized.
     */
    public boolean isInitialized() {
        return isInitialized;
    }

    /**
     * Return the filename from which this catalog was loaded.
     */
    public String getFileName() {
        return fileName.toString();
    }

    /**
     * Strip the path from a string representation of a class name.
     * e.g. evolution.lands.Water => Water
     */
    public static String stripPathFromClassName(String name) {
        int i = name.lastIndexOf('.');
        if(i == -1) return name;
        return name.substring(i+1, name.length());
    }

    /**
     * Strip the path from the class name of this class.
     */
    public static String stripPathFromClassName(Class c) {
        return stripPathFromClassName(c.getName());
    }

    /**
     * Strip the path from the class name of this object.
     */
    public static String stripPathFromClassName(Object o) {
        return stripPathFromClassName(o.getClass());
    }

    /**
     * Dynamically load a class by name.
     * The class will first be searched in the evolution package and if not
     * found in the resources package.
     * @param packageName The subpackage name, e.g. actions
     * @param className The class name, e.g. Fishing
     */
    protected static Class dlClassForName(String packageName, String className) throws ClassNotFoundException {
        try {
            return Class.forName("evolution." + packageName + "." + className);
        } catch(ClassNotFoundException e) {
            return Class.forName("resources." + packageName + "." + className);
        }
    }

    /**
     * Parse a string of name(value) pairs into a map from the property list.
     * @param propertyName The string to parse, e.g. "Bananas(50) Cocos(13.4)"
     * @param def If the property is not found, use def as string to parse.
     * @result For the example: {"Bananas" => Double(50),
     * "Cocos" => Double(13.4)}
     */
    public Map makeStringDoubleMap(String propertyName, String def) {
        Map m = new HashMap();
        List l = split(" ", getProperty(propertyName, def));
        Iterator it = l.iterator();
        while(it.hasNext()) {
            String e = (String) it.next();
            m.put(extractName(e), extractValue(e));
        }
        return m;
    }

    /**
     * Split a given string in parts.
     */
    public static List split(String delim, String str) {
        ArrayList result = new ArrayList();
        StringTokenizer tokenizer = new StringTokenizer(str, delim);
        while(tokenizer.hasMoreTokens()) {
            result.add(tokenizer.nextElement());
        }
        return result;
    }

    /**
     * Extract the name of a name(value) or name(argument) list.
     * e.g. Cherries(50) => Cherries
     */
    public static String extractName(String spec) {
        List s = split("()", spec);
        return (String) s.get(0);
    }

    /**
     * Extract the argument of a name(argument) list.
     * e.g. Cherries(BlueBerries) => BlueBerries
     */
    public static String extractArgument(String spec) {
        List s = split("()", spec);
        if(s.size() != 2)
            return null;
        return (String) s.get(1);
    }

    /**
     * Extract the value of a name(value) list.
     * e.g. Cherries(50) => 50
     */
    public static Double extractValue(String spec) {
        List s = split("()", spec);
        if(s.size() != 2)
            return null;
        return new Double((String) s.get(1));
    }

    /**
     * Get a property.
     * If not found returns null.
     */
    public String getProperty(String name) {
        String result = prop.getProperty(name);
        return result;
    }

    /**
     * Get a property.
     * If the property is not found returns def.
     */
    public String getProperty(String name, String def) {
        return prop.getProperty(name, def);
    }

    /**
     * Defines the behavior. 
     */
    private Properties prop;

    /**
     * The url from where this catalog is loaded.
     */
    private URL fileName;

    /**
     * Is this singleton initialized.
     * Kind of a hack because there is no decent support in java to
     * make a inheritance tree for singletons.
     */
    private boolean isInitialized = false;

    /**
     * The resource path.
     * This is stripped from the first filename used to load a
     * KnowledgeCatalog.
     */
    private static String resourcePath = null;

}
