/*
  Copyright 2006 by Sean Luke
  Licensed under the Academic Free License version 3.0
  See the file "LICENSE" for more information
*/


package ec.util;

import java.io.*;
import java.util.*;
import javax.swing.tree.*;
import java.net.*;

/* 

 * ParameterDatabase.java
 * Created: Sat Aug  7 12:09:19 1999
 */

/**
 * 
 * 

* This extension of the Properties class allows you to set, get, and delete * Parameters in a hierarchical tree-like database. The database consists of a * list of Parameters, plus an array of "parent databases" which it falls back * on when it can't find the Parameter you're looking for. Parents may also have * arrays of parents, and so on.. * *

* The parameters are loaded from a Java property-list file, which is basically * a collection of parameter=value pairs, one per line. Empty lines and lines * beginning with # are ignored. These parameters and their values are * case-sensitive , and whitespace is trimmed I believe. * *

* An optional set of parameters, "parent. n ", where n are * consecutive integers starting at 0, define the filenames of the database's * parents. * *

* An optional set of parameters, "print-params", specifies whether or not * parameters should be printed as they are used (through one of the get(...) * methods). If print-params is unset, or set to false or FALSE, nothing is * printed. If set to non-false, then the parameters are printed prepended with a "P:" * when their values are requested, "E:" when their existence is tested. Prior to the * "P:" or "E:" you may see a "!" (meaning that the parameter isn't in the database), * or a "<" (meaning that the parameter was a default parameter which was never * looked up because the primary parameter contained the value). * *

*

* When you create a ParameterDatabase using new ParameterDatabase(), it is * created thus: * *

*

* * * * * * * * *
DATABASE: database
FROM: (empty)
* * *

* When you create a ParameterDatabase using new ParameterDatabase( file * ), it is created by loading the database file, and its parent file tree, * thus: * *

*

* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
DATABASE: database -> parent0 +-> parent0 +-> parent0 +-> ....
FROM: (empty)  (file) | (parent.0) | (parent.0)  ....
     |  +-> parent1 +-> ....
     |  | (parent.1)  
     |  ....   
     |     
     +-> parent1 +-> ....  
     | (parent.1)    
     ....     
* * *

* When you create a ParameterDatabase using new ParameterDatabase( file,argv * ), the preferred way, it is created thus: * * *

*

* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
DATABASE: database -> parent0 +-> parent0 +-> parent0 +-> parent0 +-> ....
FROM: (empty) (argv)  (file) | (parent.0) | (parent.0)  ....
       |  +-> parent1 +-> ....
       |  | (parent.1)  
       |  ....   
       |     
       +-> parent1 +-> ....  
       | (parent.1)    
       ....     
* * *

* ...that is, the actual top database is empty, and stores parameters added * programmatically; its parent is a database formed from arguments passed in on * the command line; its parent is the parameter database which actually * loads from foo. This allows you to programmatically add parameters which * override those in foo, then delete them, thus bringing foo's parameters back * in view. * *

* Once a parameter database is loaded, you query it with the get * methods. The database, then its parents, are searched until a match is found * for your parameter. The search rules are thus: (1) the root database is * searched first. (2) If a database being searched doesn't contain the data, it * searches its parents recursively, starting with parent 0, then moving up, * until all searches are exhausted or something was found. (3) No database is * searched twice. * *

The various get methods all take two parameters. The first * parameter is fetched and retrieved first. If that fails, the second one * (known as the default parameter) is fetched and retrieved. You * can pass in null for the default parameter if you don't have one. * *

You can test a parameter for existence with the exists methods. * *

* You can set a parameter (in the topmost database only with the * set command. The remove command removes a parameter * from the topmost database only. The removeDeeply command removes * that parameter from every database. * *

* The values stored in a parameter database must not contain "#", "=", * non-ascii values, or whitespace. * *

* Note for JDK 1.1 . Finally recovering from stupendous idiocy, JDK 1.2 * included parseDouble() and parseFloat() commands; now you can READ A FLOAT * FROM A STRING without having to create a Float object first! Anyway, you will * need to modify the getFloat() method below if you're running on JDK 1.1, but * understand that large numbers of calls to the method may be inefficient. * Sample JDK 1.1 code is given with those methods, but is commented out. * * * @author Sean Luke * @version 1.0 */ public class ParameterDatabase extends Properties implements Serializable { public static final String C_HERE = "$"; public static final String C_CLASS = "@"; public static final String UNKNOWN_VALUE = ""; public static final String PRINT_PARAMS = "print-params"; public static final int PS_UNKNOWN = -1; public static final int PS_NONE = 0; public static final int PS_PRINT_PARAMS = 1; public int printState = PS_UNKNOWN; // A descriptive name of the parameter database String label; // the parents of this database Vector parents; // If the database was loaded via a file, this holds the directory of the database File directory; // a checkbox (unchecked by uncheck()) for not hitting the same database twice in a graph search boolean checked; // List of parameters which were requested and ones which furthermore were fulfilled Hashtable gotten; Hashtable accessed; // If the database was loaded via getResource(), this holds the class and relative path // used in that load Class relativeClass; String relativePath; /** * Searches down through databases to find a given parameter, whose value * must be a full Class name, and the class must be a descendent of but not * equal to mustCastTosuperclass . Loads the class and returns an * instance (constructed with the default constructor), or throws a * ParamClassLoadException if there is no such Class. If the parameter is * not found, the defaultParameter is used. The parameter chosen is marked * "used". */ public Object getInstanceForParameter(Parameter parameter, Parameter defaultParameter, Class mustCastTosuperclass) throws ParamClassLoadException { printGotten(parameter, defaultParameter, false); Parameter p; if (_exists(parameter)) p = parameter; else if (_exists(defaultParameter)) p = defaultParameter; else throw new ParamClassLoadException( "No class name provided.\nPARAMETER: " + parameter + (defaultParameter == null ? "" : "\n ALSO: " + defaultParameter)); try { Class c = Class.forName(get(p), true, Thread.currentThread().getContextClassLoader()); if (!mustCastTosuperclass.isAssignableFrom(c)) throw new ParamClassLoadException("The class " + c.getName() + "\ndoes not cast into the superclass " + mustCastTosuperclass.getName() + "\nPARAMETER: " + parameter + (defaultParameter == null ? "" : "\n ALSO: " + defaultParameter)); if (mustCastTosuperclass == c) throw new ParamClassLoadException("The class " + c.getName() + "\nmust not be the same as the required superclass " + mustCastTosuperclass.getName() + "\nPARAMETER: " + parameter + (defaultParameter == null ? "" : "\n ALSO: " + defaultParameter)); return c.newInstance(); } catch (ClassNotFoundException e) { throw new ParamClassLoadException("Class not found: " + get(p) + "\nPARAMETER: " + parameter + (defaultParameter == null ? "" : "\n ALSO: " + defaultParameter) + "\nEXCEPTION: \n\n" + e); } catch (IllegalArgumentException e) { throw new ParamClassLoadException("Could not load class: " + get(p) + "\nPARAMETER: " + parameter + (defaultParameter == null ? "" : "\n ALSO: " + defaultParameter) + "\nEXCEPTION: \n\n" + e); } catch (InstantiationException e) { throw new ParamClassLoadException( "The requested class is an interface or an abstract class: " + get(p) + "\nPARAMETER: " + parameter + (defaultParameter == null ? "" : "\n ALSO: " + defaultParameter) + "\nEXCEPTION: \n\n" + e); } catch (IllegalAccessException e) { throw new ParamClassLoadException( "The requested class cannot be initialized with the default initializer: " + get(p) + "\nPARAMETER: " + parameter + (defaultParameter == null ? "" : "\n ALSO: " + defaultParameter) + "\nEXCEPTION: \n\n" + e); } } /** * Searches down through databases to find a given parameter, whose value * must be a full Class name, and the class must be a descendent, or equal * to, mustCastTosuperclass . Loads the class and returns an instance * (constructed with the default constructor), or throws a * ParamClassLoadException if there is no such Class. The parameter chosen * is marked "used". */ public Object getInstanceForParameterEq(Parameter parameter, Parameter defaultParameter, Class mustCastTosuperclass) throws ParamClassLoadException { printGotten(parameter, defaultParameter, false); Parameter p; if (_exists(parameter)) p = parameter; else if (_exists(defaultParameter)) p = defaultParameter; else throw new ParamClassLoadException( "No class name provided.\nPARAMETER: " + parameter + "\n ALSO: " + (defaultParameter == null ? "" : "\n ALSO: " + defaultParameter)); try { Class c = Class.forName(get(p), true, Thread.currentThread().getContextClassLoader()); if (!mustCastTosuperclass.isAssignableFrom(c)) throw new ParamClassLoadException("The class " + c.getName() + "\ndoes not cast into the superclass " + mustCastTosuperclass.getName() + "\nPARAMETER: " + parameter + "\n ALSO: " + (defaultParameter == null ? "" : "\n ALSO: " + defaultParameter)); return c.newInstance(); } catch (ClassNotFoundException e) { throw new ParamClassLoadException("Class not found: " + get(p) + "\nPARAMETER: " + parameter + "\n ALSO: " + (defaultParameter == null ? "" : "\n ALSO: " + defaultParameter) + "\nEXCEPTION: \n\n" + e); } catch (IllegalArgumentException e) { throw new ParamClassLoadException("Could not load class: " + get(p) + "\nPARAMETER: " + parameter + "\n ALSO: " + (defaultParameter == null ? "" : "\n ALSO: " + defaultParameter) + "\nEXCEPTION: \n\n" + e); } catch (InstantiationException e) { throw new ParamClassLoadException( "The requested class is an interface or an abstract class: " + get(p) + "\nPARAMETER: " + parameter + "\n ALSO: " + (defaultParameter == null ? "" : "\n ALSO: " + defaultParameter) + "\nEXCEPTION: \n\n" + e); } catch (IllegalAccessException e) { throw new ParamClassLoadException( "The requested class cannot be initialized with the default initializer: " + get(p) + "\nPARAMETER: " + parameter + "\n ALSO: " + (defaultParameter == null ? "" : "\n ALSO: " + defaultParameter) + "\nEXCEPTION: \n\n" + e); } } /** * Searches down through databases to find a given parameter. The value * associated with this parameter must be a full Class name, and the class * must be a descendent of but not equal to mustCastTosuperclass . * Loads and returns the associated Class, or throws a * ParamClassLoadException if there is no such Class. If the parameter is * not found, the defaultParameter is used. The parameter chosen is marked * "used". */ public Class getClassForParameter(Parameter parameter, Parameter defaultParameter, Class mustCastTosuperclass) throws ParamClassLoadException { printGotten(parameter, defaultParameter, false); Parameter p; if (_exists(parameter)) p = parameter; else if (_exists(defaultParameter)) p = defaultParameter; else throw new ParamClassLoadException( "No class name provided.\nPARAMETER: " + parameter + "\n ALSO: " + (defaultParameter == null ? "" : "\n ALSO: " + defaultParameter)); try { Class c = Class.forName(get(p), true, Thread.currentThread().getContextClassLoader()); if (!mustCastTosuperclass.isAssignableFrom(c)) throw new ParamClassLoadException("The class " + c.getName() + "\ndoes not cast into the superclass " + mustCastTosuperclass.getName() + "\nPARAMETER: " + parameter + "\n ALSO: " + (defaultParameter == null ? "" : "\n ALSO: " + defaultParameter)); return c; } catch (ClassNotFoundException e) { throw new ParamClassLoadException("Class not found: " + get(p) + "\nPARAMETER: " + parameter + "\n ALSO: " + (defaultParameter == null ? "" : "\n ALSO: " + defaultParameter) + "\nEXCEPTION: \n\n" + e); } catch (IllegalArgumentException e) { throw new ParamClassLoadException("Could not load class: " + get(p) + "\nPARAMETER: " + parameter + "\n ALSO: " + (defaultParameter == null ? "" : "\n ALSO: " + defaultParameter) + "\nEXCEPTION: \n\n" + e); } } /** * Searches down through databases to find a given parameter; If the * parameter does not exist, defaultValue is returned. If the parameter * exists, and it is set to "false" (case insensitive), false is returned. * Else true is returned. The parameter chosen is marked "used" if it * exists. */ public boolean getBoolean(Parameter parameter, Parameter defaultParameter, boolean defaultValue) { printGotten(parameter, defaultParameter, false); if (_exists(parameter)) return getBoolean(parameter, defaultValue); else return getBoolean(defaultParameter, defaultValue); } /** * Searches down through databases to find a given parameter; If the * parameter does not exist, defaultValue is returned. If the parameter * exists, and it is set to "false" (case insensitive), false is returned. * Else true is returned. The parameter chosen is marked "used" if it * exists. */ boolean getBoolean(Parameter parameter, boolean defaultValue) { if (!_exists(parameter)) return defaultValue; return (!get(parameter).equalsIgnoreCase("false")); } /** * Parses an integer from a string, either in decimal or (if starting with * an x) in hex */ // we assume that the string has been trimmed already int parseInt(String string) throws NumberFormatException { char c; if (string != null && string.length() > 0 && ((string.charAt(0) == (c = 'x')) || c == 'X')) { // it's a hex int, load it as hex return Integer.parseInt(string.substring(1), 16); } else { try { // it's decimal return Integer.parseInt(string); } catch (NumberFormatException e) { // maybe it's a double ending in .0, which should be okay try { double d = Double.parseDouble(string); if (d == (int) d) return (int) d; // looking fine else throw e; } catch (NumberFormatException e2) { throw e; } } } } /** * Parses a long from a string, either in decimal or (if starting with an x) * in hex */ // we assume that the string has been trimmed already /*protected*/ long parseLong(String string) throws NumberFormatException { char c; if (string != null && string.length() > 0 && ((string.charAt(0) == (c = 'x')) || c == 'X')) { // it's a hex int, load it as hex return Long.parseLong(string.substring(1), 16); } else { try { // it's decimal return Long.parseLong(string); } catch (NumberFormatException e) { // maybe it's a double ending in .0, which should be okay try { double d = Double.parseDouble(string); if (d == (long) d) return (long) d; // looking fine else throw e; } catch (NumberFormatException e2) { throw e; } } } } /** * Searches down through databases to find a given parameter, whose value * must be an integer. It returns the value, else throws a * NumberFormatException exception if there is an error in parsing the * parameter. The parameter chosen is marked "used" if it exists. Integers * may be in decimal or (if preceded with an X or x) in hexadecimal. */ /*protected*/ int getInt(Parameter parameter) throws NumberFormatException { if (_exists(parameter)) { try { return parseInt(get(parameter)); } catch (NumberFormatException e) { throw new NumberFormatException("Bad integer (" + get(parameter) + " ) for parameter " + parameter); } } else throw new NumberFormatException( "Integer does not exist for parameter " + parameter); } /** * Searches down through databases to find a given parameter, whose value * must be an integer. It returns the value, else throws a * NumberFormatException exception if there is an error in parsing the * parameter. The parameter chosen is marked "used" if it exists. Integers * may be in decimal or (if preceded with an X or x) in hexadecimal. */ public int getInt(Parameter parameter, Parameter defaultParameter) throws NumberFormatException { printGotten(parameter, defaultParameter, false); if (_exists(parameter)) return getInt(parameter); else if (_exists(defaultParameter)) return getInt(defaultParameter); else throw new NumberFormatException( "Integer does not exist for either parameter " + parameter + "\nor\n" + defaultParameter); } /** * Searches down through databases to find a given parameter, whose value * must be an integer >= minValue. It returns the value, or minValue-1 if * the value is out of range or if there is an error in parsing the * parameter. The parameter chosen is marked "used" if it exists. Integers * may be in decimal or (if preceded with an X or x) in hexadecimal. */ public int getInt(Parameter parameter, Parameter defaultParameter, int minValue) { printGotten(parameter, defaultParameter, false); if (_exists(parameter)) return getInt(parameter, minValue); else return getInt(defaultParameter, minValue); } /** * Searches down through databases to find a given parameter, whose value * must be an integer >= minValue. It returns the value, or minValue-1 if * the value is out of range or if there is an error in parsing the * parameter. The parameter chosen is marked "used" if it exists. Integers * may be in decimal or (if preceded with an X or x) in hexadecimal. */ /*protected*/ int getInt(Parameter parameter, int minValue) { if (_exists(parameter)) { try { int i = parseInt(get(parameter)); if (i < minValue) return minValue - 1; return i; } catch (NumberFormatException e) { return minValue - 1; } } else return minValue - 1; } /** * Searches down through databases to find a given parameter, which must be * an integer. If there is an error in parsing the parameter, then default * is returned. The parameter chosen is marked "used" if it exists. Integers * may be in decimal or (if preceded with an X or x) in hexadecimal. */ public int getIntWithDefault(Parameter parameter, Parameter defaultParameter, int defaultValue) { printGotten(parameter, defaultParameter, false); if (_exists(parameter)) return getIntWithDefault(parameter, defaultValue); else return getIntWithDefault(defaultParameter, defaultValue); } /** * Searches down through databases to find a given parameter, which must be * an integer. If there is an error in parsing the parameter, then default * is returned. The parameter chosen is marked "used" if it exists. Integers * may be in decimal or (if preceded with an X or x) in hexadecimal. */ int getIntWithDefault(Parameter parameter, int defaultValue) { if (_exists(parameter)) { try { return parseInt(get(parameter)); } catch (NumberFormatException e) { return defaultValue; } } else return defaultValue; } /** * Searches down through databases to find a given parameter, whose value * must be an integer >= minValue and <= maxValue. It returns the value, or * minValue-1 if the value is out of range or if there is an error in * parsing the parameter. The parameter chosen is marked "used" if it * exists. Integers may be in decimal or (if preceded with an X or x) in * hexadecimal. */ public int getIntWithMax(Parameter parameter, Parameter defaultParameter, int minValue, int maxValue) { printGotten(parameter, defaultParameter, false); if (_exists(parameter)) return getIntWithMax(parameter, minValue, maxValue); else return getIntWithMax(defaultParameter, minValue, maxValue); } /** * Searches down through databases to find a given parameter, whose value * must be an integer >= minValue and <= maxValue. It returns the value, or * minValue-1 if the value is out of range or if there is an error in * parsing the parameter. The parameter chosen is marked "used" if it * exists. Integers may be in decimal or (if preceded with an X or x) in * hexadecimal. */ int getIntWithMax(Parameter parameter, int minValue, int maxValue) { if (_exists(parameter)) { try { int i = parseInt(get(parameter)); if (i < minValue) return minValue - 1; if (i > maxValue) return minValue - 1; return i; } catch (NumberFormatException e) { return minValue - 1; } } else return minValue - 1; } float getFloat(Parameter parameter) throws NumberFormatException { if (_exists(parameter)) { try { // For JDK 1.2 and later, this is more efficient... // float i = Float.parseFloat(get(parameter)); // ...but we can't use it and still be compatible with JDK 1.1 return Float.valueOf(get(parameter)).floatValue(); // what stupidity... } catch (NumberFormatException e) { throw new NumberFormatException("Bad float (" + get(parameter) + " ) for parameter " + parameter); } } else throw new NumberFormatException( "Float does not exist for parameter " + parameter); } /* * Searches down through databases to find a given parameter, whose value * must be a float. It returns the value, else throws a * NumberFormatException exception if there is an error in parsing the * parameter. The parameter chosen is marked "used" if it exists. */ public float getFloat(Parameter parameter, Parameter defaultParameter) throws NumberFormatException { printGotten(parameter, defaultParameter, false); if (_exists(parameter)) return getFloat(parameter); else if (_exists(defaultParameter)) return getFloat(defaultParameter); else throw new NumberFormatException( "Float does not exist for either parameter " + parameter + "\nor\n" + defaultParameter); } /** * Searches down through databases to find a given parameter, whose value * must be a float >= minValue. If not, this method returns minvalue-1, else * it returns the parameter value. The parameter chosen is marked "used" if * it exists. */ public float getFloat(Parameter parameter, Parameter defaultParameter, double minValue) { printGotten(parameter, defaultParameter, false); if (_exists(parameter)) return getFloat(parameter, minValue); else return getFloat(defaultParameter, minValue); } /** * Searches down through databases to find a given parameter, whose value * must be a float >= minValue. If not, this method returns minvalue-1, else * it returns the parameter value. The parameter chosen is marked "used" if * it exists. */ float getFloat(Parameter parameter, double minValue) { if (_exists(parameter)) { try { float i = Float.valueOf(get(parameter)).floatValue(); // what stupidity... // For JDK 1.2 and later, this is more efficient... // float i = Float.parseFloat(get(parameter)); // ...but we can't use it and still be compatible with JDK 1.1 if (i < minValue) return (float) (minValue - 1); return i; } catch (NumberFormatException e) { return (float) (minValue - 1); } } else return (float) (minValue - 1); } /** * Searches down through databases to find a given parameter, which must be * a float. If there is an error in parsing the parameter, then default is * returned. The parameter chosen is marked "used" if it exists. */ public float getFloatWithDefault(Parameter parameter, Parameter defaultParameter, double defaultValue) { printGotten(parameter, defaultParameter, false); if (_exists(parameter)) return getFloatWithDefault(parameter, defaultValue); else return getFloatWithDefault(defaultParameter, defaultValue); } /** * Searches down through databases to find a given parameter, which must be * a float. If there is an error in parsing the parameter, then default is * returned. The parameter chosen is marked "used" if it exists. */ float getFloatWithDefault(Parameter parameter, double defaultValue) { if (_exists(parameter)) { try { // For JDK 1.2 and later, this is more efficient... // return Float.parseFloat(get(parameter)); // ...but we can't use it and still be compatible with JDK 1.1 return Float.valueOf(get(parameter)).floatValue(); // what stupidity... } catch (NumberFormatException e) { return (float) (defaultValue); } } else return (float) (defaultValue); } /** * Searches down through databases to find a given parameter, whose value * must be a float >= minValue and <= maxValue. If not, this method returns * minvalue-1, else it returns the parameter value. The parameter chosen is * marked "used" if it exists. */ public float getFloatWithMax(Parameter parameter, Parameter defaultParameter, double minValue, double maxValue) { printGotten(parameter, defaultParameter, false); if (_exists(parameter)) return getFloat(parameter, minValue, maxValue); else return getFloat(defaultParameter, minValue, maxValue); } /** * Searches down through databases to find a given parameter, whose value * must be a float >= minValue and <= maxValue. If not, this method returns * minvalue-1, else it returns the parameter value. The parameter chosen is * marked "used" if it exists. * * @deprecated Use getFloatWithMax instead */ public float getFloat(Parameter parameter, Parameter defaultParameter, double minValue, double maxValue) { return getFloatWithMax(parameter, defaultParameter, minValue, maxValue); } /** * Searches down through databases to find a given parameter, whose value * must be a float >= minValue and <= maxValue. If not, this method returns * minvalue-1, else it returns the parameter value. The parameter chosen is * marked "used" if it exists. */ float getFloat(Parameter parameter, double minValue, double maxValue) { if (_exists(parameter)) { try { float i = Float.valueOf(get(parameter)).floatValue(); // what stupidity... // For JDK 1.2 and later, this is more efficient... // float i = Float.parseFloat(get(parameter)); // ...but we can't use it and still be compatible with JDK 1.1 if (i < minValue) return (float) (minValue - 1); if (i > maxValue) return (float) (minValue - 1); return i; } catch (NumberFormatException e) { return (float) (minValue - 1); } } else return (float) (minValue - 1); } double getDouble(Parameter parameter) throws NumberFormatException { if (_exists(parameter)) { try { // For JDK 1.2 and later, this is more efficient... // double i = Double.parseDouble(get(parameter)); // ...but we can't use it and still be compatible with JDK 1.1 return Double.valueOf(get(parameter)).doubleValue(); // what stupidity... } catch (NumberFormatException e) { throw new NumberFormatException("Bad double (" + get(parameter) + " ) for parameter " + parameter); } } else throw new NumberFormatException( "Double does not exist for parameter " + parameter); } /* * Searches down through databases to find a given parameter, whose value * must be an double. It returns the value, else throws a * NumberFormatException exception if there is an error in parsing the * parameter. The parameter chosen is marked "used" if it exists. */ public double getDouble(Parameter parameter, Parameter defaultParameter) throws NumberFormatException { printGotten(parameter, defaultParameter, false); if (_exists(parameter)) return getDouble(parameter); else if (_exists(defaultParameter)) return getDouble(defaultParameter); else throw new NumberFormatException( "Double does not exist for either parameter " + parameter + "\nor\n" + defaultParameter); } /** * Searches down through databases to find a given parameter, whose value * must be a double >= minValue. If not, this method returns minvalue-1, * else it returns the parameter value. The parameter chosen is marked * "used" if it exists. */ public double getDouble(Parameter parameter, Parameter defaultParameter, double minValue) { printGotten(parameter, defaultParameter, false); if (_exists(parameter)) return getDouble(parameter, minValue); else return getDouble(defaultParameter, minValue); } /** * Searches down through databases to find a given parameter, whose value * must be a double >= minValue. If not, this method returns minvalue-1, * else it returns the parameter value. The parameter chosen is marked * "used" if it exists. */ double getDouble(Parameter parameter, double minValue) { if (_exists(parameter)) { try { double i = Double.valueOf(get(parameter)).doubleValue(); // what stupidity... // For JDK 1.2 and later, this is more efficient... // double i = Double.parseDouble(get(parameter)); // ...but we can't use it and still be compatible with JDK 1.1 if (i < minValue) return (double) (minValue - 1); return i; } catch (NumberFormatException e) { return (double) (minValue - 1); } } else return (double) (minValue - 1); } /** * Searches down through databases to find a given parameter, whose value * must be a double >= minValue and <= maxValue. If not, this method returns * minvalue-1, else it returns the parameter value. The parameter chosen is * marked "used" if it exists. */ public double getDoubleWithMax(Parameter parameter, Parameter defaultParameter, double minValue, double maxValue) { printGotten(parameter, defaultParameter, false); if (_exists(parameter)) return getDouble(parameter, minValue, maxValue); else return getDouble(defaultParameter, minValue, maxValue); } /** * Searches down through databases to find a given parameter, whose value * must be a double >= minValue and <= maxValue. If not, this method returns * minvalue-1, else it returns the parameter value. The parameter chosen is * marked "used" if it exists. * * @deprecated use getDoubleWithMax instead */ public double getDouble(Parameter parameter, Parameter defaultParameter, double minValue, double maxValue) { return getDoubleWithMax(parameter, defaultParameter, minValue, maxValue); } /** * Searches down through databases to find a given parameter, whose value * must be a double >= minValue and <= maxValue. If not, this method returns * minvalue-1, else it returns the parameter value. The parameter chosen is * marked "used" if it exists. */ double getDouble(Parameter parameter, double minValue, double maxValue) { if (_exists(parameter)) { try { double i = Double.valueOf(get(parameter)).doubleValue(); // what stupidity... // For JDK 1.2 and later, this is more efficient... // double i = Double.parseDouble(get(parameter)); // ...but we can't use it and still be compatible with JDK 1.1 if (i < minValue) return (double) (minValue - 1); if (i > maxValue) return (double) (minValue - 1); return i; } catch (NumberFormatException e) { return (double) (minValue - 1); } } else return (double) (minValue - 1); } /** * Searches down through databases to find a given parameter, which must be * a float. If there is an error in parsing the parameter, then default is * returned. The parameter chosen is marked "used" if it exists. */ public double getDoubleWithDefault(Parameter parameter, Parameter defaultParameter, double defaultValue) { printGotten(parameter, defaultParameter, false); if (_exists(parameter)) return getDoubleWithDefault(parameter, defaultValue); else return getDoubleWithDefault(defaultParameter, defaultValue); } /** * Searches down through databases to find a given parameter, which must be * a float. If there is an error in parsing the parameter, then default is * returned. The parameter chosen is marked "used" if it exists. */ double getDoubleWithDefault(Parameter parameter, double defaultValue) { if (_exists(parameter)) { try { // For JDK 1.2 and later, this is more efficient... // return Double.parseDouble(get(parameter)); // ...but we can't use it and still be compatible with JDK 1.1 return Double.valueOf(get(parameter)).doubleValue(); // what stupidity... } catch (NumberFormatException e) { return defaultValue; } } else return defaultValue; } /** * Searches down through databases to find a given parameter, whose value * must be a long. It returns the value, else throws a NumberFormatException * exception if there is an error in parsing the parameter. The parameter * chosen is marked "used" if it exists. Longs may be in decimal or (if * preceded with an X or x) in hexadecimal. */ /*protected*/ long getLong(Parameter parameter) throws NumberFormatException { if (_exists(parameter)) { try { return parseLong(get(parameter)); } catch (NumberFormatException e) { throw new NumberFormatException("Bad long (" + get(parameter) + " ) for parameter " + parameter); } } else throw new NumberFormatException( "Long does not exist for parameter " + parameter); } /** * Searches down through databases to find a given parameter, whose value * must be a long. It returns the value, else throws a NumberFormatException * exception if there is an error in parsing the parameter. The parameter * chosen is marked "used" if it exists. Longs may be in decimal or (if * preceded with an X or x) in hexadecimal. */ public long getLong(Parameter parameter, Parameter defaultParameter) throws NumberFormatException { printGotten(parameter, defaultParameter, false); if (_exists(parameter)) return getLong(parameter); else if (_exists(defaultParameter)) return getLong(defaultParameter); else throw new NumberFormatException( "Long does not exist for either parameter " + parameter + "\nor\n" + defaultParameter); } /** * Searches down through databases to find a given parameter, whose value * must be a long >= minValue. If not, this method returns errValue, else it * returns the parameter value. The parameter chosen is marked "used" if it * exists. Longs may be in decimal or (if preceded with an X or x) in * hexadecimal. */ public long getLong(Parameter parameter, Parameter defaultParameter, long minValue) { printGotten(parameter, defaultParameter, false); if (_exists(parameter)) return getLong(parameter, minValue); else return getLong(defaultParameter, minValue); } /** * Searches down through databases to find a given parameter, whose value * must be a long >= minValue. If not, this method returns errValue, else it * returns the parameter value. The parameter chosen is marked "used" if it * exists. Longs may be in decimal or (if preceded with an X or x) in * hexadecimal. */ long getLong(Parameter parameter, long minValue) { if (_exists(parameter)) { try { long i = parseLong(get(parameter)); if (i < minValue) return minValue - 1; return i; } catch (NumberFormatException e) { return minValue - 1; } } else return (minValue - 1); } /** * Searches down through databases to find a given parameter, which must be * a long. If there is an error in parsing the parameter, then default is * returned. The parameter chosen is marked "used" if it exists. Longs may * be in decimal or (if preceded with an X or x) in hexadecimal. */ public long getLongWithDefault(Parameter parameter, Parameter defaultParameter, long defaultValue) { printGotten(parameter, defaultParameter, false); if (_exists(parameter)) return getLongWithDefault(parameter, defaultValue); else return getLongWithDefault(defaultParameter, defaultValue); } /** * Searches down through databases to find a given parameter, which must be * a long. If there is an error in parsing the parameter, then default is * returned. The parameter chosen is marked "used" if it exists. Longs may * be in decimal or (if preceded with an X or x) in hexadecimal. */ long getLongWithDefault(Parameter parameter, long defaultValue) { if (_exists(parameter)) { try { return parseLong(get(parameter)); } catch (NumberFormatException e) { return defaultValue; } } else return defaultValue; } /** * Searches down through databases to find a given parameter, whose value * must be a long >= minValue and = < maxValue. If not, this method returns * errValue, else it returns the parameter value. The parameter chosen is * marked "used" if it exists. Longs may be in decimal or (if preceded with * an X or x) in hexadecimal. */ public long getLongWithMax(Parameter parameter, Parameter defaultParameter, long minValue, long maxValue) { printGotten(parameter, defaultParameter, false); if (_exists(parameter)) return getLong(parameter, minValue, maxValue); else return getLong(defaultParameter, minValue, maxValue); } /** * Use getLongWithMax(...) instead. Searches down through databases to find * a given parameter, whose value must be a long >= minValue and = < * maxValue. If not, this method returns errValue, else it returns the * parameter value. The parameter chosen is marked "used" if it exists. * Longs may be in decimal or (if preceded with an X or x) in hexadecimal. */ long getLongWithMax(Parameter parameter, long minValue, long maxValue) { if (_exists(parameter)) { try { long i = parseLong(get(parameter)); if (i < minValue) return minValue - 1; if (i > maxValue) return minValue - 1; return i; } catch (NumberFormatException e) { return minValue - 1; } } else return (minValue - 1); } /** * Use getLongWithMax(...) instead. Searches down through databases to find * a given parameter, whose value must be a long >= minValue and = < * maxValue. If not, this method returns errValue, else it returns the * parameter value. The parameter chosen is marked "used" if it exists. * Longs may be in decimal or (if preceded with an X or x) in hexadecimal. * * @deprecated */ public long getLong(Parameter parameter, Parameter defaultParameter, long minValue, long maxValue) { printGotten(parameter, defaultParameter, false); return getLongWithMax(parameter, defaultParameter, minValue, maxValue); } /** * Use getLongWithMax(...) instead. Searches down through databases to find * a given parameter, whose value must be a long >= minValue and = < * maxValue. If not, this method returns errValue, else it returns the * parameter value. The parameter chosen is marked "used" if it exists. * * @deprecated */ long getLong(Parameter parameter, long minValue, long maxValue) { return getLongWithMax(parameter, minValue, maxValue); } /** * Searches down through the databases to find a given parameter, whose * value must be an absolute or relative path name. If it is absolute, a * File is made based on the path name. If it is relative, a file is made by * resolving the path name with respect to the directory in which the file * was which defined this ParameterDatabase in the ParameterDatabase * hierarchy. If the parameter is not found, this returns null. The File is * not checked for validity. The parameter chosen is marked "used" if it * exists. */ public File getFile(Parameter parameter, Parameter defaultParameter) { printGotten(parameter, defaultParameter, false); if (_exists(parameter)) return getFile(parameter); else return getFile(defaultParameter); } /** * Searches down through the databases to find a given parameter, whose * value must be an absolute or relative path name. If the parameter begins * with a "$", a file is made based on the relative path name and returned * directly. Otherwise, if it is absolute, a File is made based on the path * name, or if it is relative, a file is made by resolving the path name * with respect to the directory in which the file was which defined this * ParameterDatabase in the ParameterDatabase hierarchy. If the parameter is * not found, this returns null. The File is not checked for validity. The * parameter chosen is marked "used" if it exists. */ File getFile(Parameter parameter) { if (_exists(parameter)) { String p = get(parameter); if (p == null) return null; if (p.startsWith(C_HERE)) return new File(p.substring(C_HERE.length())); else if (p.startsWith(C_CLASS)) return null; // can't start with that. else { File f = new File(p); if (f.isAbsolute()) return f; else return new File(directoryFor(parameter), p); } } else return null; } /** * Searches down through the databases to find a given parameter, whose * value must be an absolute or relative path name. If it is absolute, a * file is made based on the path name, and an InputStream is opened on * the file and returned. If the path name begins with "$", then an * InputStream is opened on a file relative to the directory where the * system was started. Otherwise if the path name is relative, an InputStream is made by * resolving the path name with respect to the directory in which the file * was which defined this ParameterDatabase in the ParameterDatabase * hierarchy, be it in the file system or in a jar file. If the parameter is not found, * this returns null. If no such file exists, null is also returned. * The parameter chosen is marked "used" if it exists. */ public InputStream getResource(Parameter parameter, Parameter defaultParameter) { printGotten(parameter, defaultParameter, false); if (_exists(parameter)) return getResource(parameter); else return getResource(defaultParameter); } int indexOfFirstWhitespace(String s) { int len = s.length(); for(int i =0; i < len; i++) if (Character.isWhitespace(s.charAt(i))) return i; return -1; } InputStream getResource(Parameter parameter) { try { if (_exists(parameter)) { String p = get(parameter); if (p == null) return null; if (p.startsWith(C_HERE)) return new FileInputStream(getFile(parameter)); else if (p.startsWith(C_CLASS)) { int i = indexOfFirstWhitespace(p); if (i == -1) return null; String classname = p.substring(C_CLASS.length(),i); String filename = p.substring(i).trim(); return Class.forName(classname, true, Thread.currentThread().getContextClassLoader()).getResourceAsStream(filename); } else { File f = new File(p); if (f.isAbsolute()) return new FileInputStream(f); Class c = getLocation(parameter.param).relativeClass; String rp = getLocation(parameter.param).relativePath; if (c != null) { return c.getResourceAsStream(new File(new File(rp).getParent(), p).getPath()); } else return new FileInputStream(new File(directoryFor(parameter), p)); } } else return null; } catch (FileNotFoundException ex1) { return null; } catch (ClassNotFoundException ex2) { return null; } } /** * Searches down through databases to find a given parameter. Returns the * parameter's value (trimmed) or null if not found or if the trimmed result * is empty. The parameter chosen is marked "used" if it exists. */ public synchronized String getString(Parameter parameter, Parameter defaultParameter) { printGotten(parameter, defaultParameter, false); if (_exists(parameter)) return getString(parameter); else return getString(defaultParameter); } /** * Searches down through databases to find a given parameter. Returns the * parameter's value (trimmed) or null if not found or if the trimmed result * is empty. The parameter chosen is marked "used" if it exists. */ /*protected*/ synchronized String getString(Parameter parameter) { if (_exists(parameter)) return get(parameter); else return null; } /** * Searches down through databases to find a given parameter. Returns the * parameter's value trimmed of whitespace, or defaultValue.trim() if the * result is not found or the trimmed result is empty. */ public String getStringWithDefault(Parameter parameter, Parameter defaultParameter, String defaultValue) { printGotten(parameter, defaultParameter, false); if (_exists(parameter)) return getStringWithDefault(parameter, defaultValue); else return getStringWithDefault(defaultParameter, defaultValue); } /** * Searches down through databases to find a given parameter. Returns the * parameter's value trimmed of whitespace, or defaultValue.trim() if the * result is not found or the trimmed result is empty. */ /*protected*/ String getStringWithDefault(Parameter parameter, String defaultValue) { if (_exists(parameter)) { String result = get(parameter); if (result == null) { if (defaultValue == null) return null; else result = defaultValue.trim(); } else { result = result.trim(); if (result.length() == 0) { if (defaultValue == null) return null; else result = defaultValue.trim(); } } return result; } else { if (defaultValue == null) return null; else return defaultValue.trim(); } } /** Clears the checked flag */ /*protected*/ synchronized void uncheck() { if (!checked) return; // we already unchecked this path -- this is dangerous if // parents are used without children checked = false; int size = parents.size(); for (int x = 0; x < size; x++) ((ParameterDatabase) (parents.elementAt(x))).uncheck(); } /** * Sets a parameter in the topmost database to a given value, trimmed of * whitespace. */ public synchronized void set(Parameter parameter, String value) { String tmp = value.trim(); put(parameter.param, tmp); // fireParameterSet(parameter, tmp); } /** * Prints out all the parameters marked as used, plus their values. If a * parameter was listed as "used" but not's actually in the database, the * value printed is UNKNOWN_VALUE (set to "?????") */ public synchronized void listGotten(PrintWriter p) { Vector vec = new Vector(); Enumeration e = gotten.keys(); while (e.hasMoreElements()) vec.addElement(e.nextElement()); // sort the keys Object[] array = new Object[vec.size()]; vec.copyInto(array); java.util.Collections.sort(vec); // Uncheck and print each item for (int x = 0; x < array.length; x++) { String s = (String) (array[x]); String v = null; if (s != null) { v = (String) (_get(s)); uncheck(); } if (v == null) v = UNKNOWN_VALUE; p.println(s + " = " + v); } p.flush(); } /** Prints out all the parameters NOT marked as used, plus their values. */ public synchronized void listNotGotten(PrintWriter p) { Vector vec = new Vector(); Hashtable all = new Hashtable(); _list(null, false, null, all); // grab all the nonshadowed keys Enumeration e = gotten.keys(); while (e.hasMoreElements()) all.remove(e.nextElement()); e = all.keys(); while (e.hasMoreElements()) vec.addElement(e.nextElement()); // sort the keys Object[] array = new Object[vec.size()]; vec.copyInto(array); java.util.Collections.sort(vec); // Uncheck and print each item for (int x = 0; x < array.length; x++) { String s = (String) (array[x]); String v = null; if (s != null) { v = (String) (_get(s)); uncheck(); } if (v == null) v = UNKNOWN_VALUE; p.println(s + " = " + v); } p.flush(); } /** Prints out all the parameters NOT marked as used, plus their values. */ public synchronized void listNotAccessed(PrintWriter p) { Vector vec = new Vector(); Hashtable all = new Hashtable(); _list(null, false, null, all); // grab all the nonshadowed keys Enumeration e = accessed.keys(); while (e.hasMoreElements()) all.remove(e.nextElement()); e = all.keys(); while (e.hasMoreElements()) vec.addElement(e.nextElement()); // sort the keys Object[] array = new Object[vec.size()]; vec.copyInto(array); java.util.Collections.sort(vec); // Uncheck and print each item for (int x = 0; x < array.length; x++) { String s = (String) (array[x]); String v = null; if (s != null) { v = (String) (_get(s)); uncheck(); } if (v == null) v = UNKNOWN_VALUE; p.println(s + " = " + v); } p.flush(); } /** * Prints out all the parameters marked as accessed ("gotten" by some * getFoo(...) method), plus their values. If this method ever prints * UNKNOWN_VALUE ("?????"), that's a bug. */ public synchronized void listAccessed(PrintWriter p) { Vector vec = new Vector(); Enumeration e = accessed.keys(); while (e.hasMoreElements()) vec.addElement(e.nextElement()); // sort the keys Object[] array = new Object[vec.size()]; vec.copyInto(array); java.util.Collections.sort(vec); // Uncheck and print each item for (int x = 0; x < array.length; x++) { String s = (String) (array[x]); String v = null; if (s != null) { v = (String) (_get(s)); uncheck(); } if (v == null) v = UNKNOWN_VALUE; p.println(s + " = " + v); } p.flush(); } /** Returns true if parameter exist in the database @deprecated use exists(Parameter, null) */ public synchronized boolean exists(Parameter parameter) { printGotten(parameter, null, true); return _exists(parameter); } /*protected*/ synchronized boolean _exists(Parameter parameter) { if (parameter == null) return false; String result = _get(parameter.param); uncheck(); accessed.put(parameter.param, Boolean.TRUE); return (result != null); } /** * Returns true if either parameter or defaultParameter exists in the * database */ public synchronized boolean exists(Parameter parameter, Parameter defaultParameter) { printGotten(parameter, defaultParameter, true); if (exists(parameter)) return true; if (exists(defaultParameter)) return true; return false; } /* P: Successfully retrieved parameter !P: Unsuccessfully retrieved parameter = 0; i--) if (url.charAt(i) == '!' && url.charAt(i+1) == '/') // PROBABLY it { break; } if (i < 0) // uh oh return null; String originalPath = url.substring(i+1); // don't include the ! if (path.startsWith("/")) // it's absolute { // 2. If the replacement path is absolute, just use that. originalPath = path; // just replace it } else { // 3. Else remove the file // /ec/app/ant/ int j; for(j = originalPath.length() - 1; j >= 0; j--) if (originalPath.charAt(j) == '/') // PROBABLY is it { break; } if (j < 0) // uh oh return null; originalPath = originalPath.substring(0, j+1); // include the slash // 4. Tack on the replacement path // /ec/app/ant/../../gp/koza.params originalPath += path; } // 5. Simplify // /ec/gp/koza.params return simplifyPath(originalPath); } // Given a URL referring to something in a Jar file, removes the final filename from // the end of the existing internal path inside the Jar file, then revises the // path to point to the provided path relative to that path. Returns the resultant // URL. For example, if the URL was jar:file:/private/tmp/ecj.jar!/ec/app/ant/Ant.class // and the relative path was ../../gp/koza.params then the function would return // the URL jar:file:/private/tmp/ecj.jar!/ec/gp/koza.params static URL concatenatedJarResource(URL original, String path) { // A Jar URL looks like this: jar:URLtoJarFile!/path/to/resource/in/jar // For example: jar:file:/private/tmp/ecj.jar!/ec/app/ant/Ant.class // Given another path to tack on, say ../../gp/koza.params // We need to edit this as follows: // 0. Identify the path start and extract the path // /ec/app/ant/Ant.class String url = original.toString(); int i; for(i = url.length() - 2; i >= 0; i--) if (url.charAt(i) == '!' && url.charAt(i+1) == '/') // PROBABLY it { break; } if (i < 0) // uh oh return null; // see concatenatedJarPath for further procedures... String revisedPath = concatenatedJarPath(original, path); // 6. Put back in URL // jar:file:/private/tmp/ecj.jar!/ec/gp/koza.params try { return new URL(url.substring(0, i + 1) + revisedPath); } catch (MalformedURLException e) { return null; } } // Eliminates .. and . from a relative path without converting it // according to the file system. For example, // "hello/there/../how/./are/you/yo/../../hey" becomes // "hello/how/are/hey". This is useful for cleaning up path names for // URLs. static String simplifyPath(String pathname) { File path = new File(pathname); ArrayList a = new ArrayList(); while(path != null && path.getName() != null) { String n = path.getName(); a.add(n); path = path.getParentFile(); } ArrayList b = new ArrayList(); for(int i = a.size() - 1; i >= 0; i--) { String n = (String)(a.get(i)); if (n.equals(".")) { } // do nothing else if (n.equals("..") && b.size() != 0 && !b.get(0).equals("..")) b.remove(b.size() - 1); else b.add(n); } if (b.size() == 0) return ""; path = new File((String)(b.get(0))); for(int i = 1; i < b.size(); i++) { path = new File(path, (String)(b.get(i))); } return path.getPath(); } /** * Creates a new parameter database from a given database file and argv * list. The top-level database is completely empty, pointing to a second * database which contains the parameter entries stored in args, which * points to a tree of databases constructed using * ParameterDatabase(filename). */ public ParameterDatabase(String pathNameRelativeToClassFile, Class cls, String[] args) throws FileNotFoundException, IOException { this(); label = "" + cls + " : " + pathNameRelativeToClassFile; ParameterDatabase files = new ParameterDatabase(pathNameRelativeToClassFile, cls); // Create the Parameter Database for the arguments ParameterDatabase a = new ParameterDatabase(); a.relativeClass = cls; a.relativePath = files.relativePath; a.parents.addElement(files); boolean hasArgs = false; for (int x = 0; x < args.length - 1; x++) { if (args[x].equals("-p")) { String s = args[x+1].trim(); if (s.length() == 0) continue; // failure int eq = s.indexOf('='); // look for the '=' if (eq <= 0) continue; // '=' isn't there, or it's the first char: failure put(s.substring(0,eq), s.substring(eq+1)); // add the parameter if (!hasArgs) { label = label + " Args: "; hasArgs = true; } label = label + s + " "; } } // Set me up relativeClass = cls; relativePath = files.relativePath; parents.addElement(a); } /** Creates a new parameter database loaded from a parameter file located relative to a class file, wherever the class file may be (such as in a jar). This approach uses resourceLocation.getResourceAsStream() to load the parameter file. If parent.n are defined, parents will be attempted to be loaded -- that's the reason for the FileNotFoundException and IOException. */ public ParameterDatabase(String pathNameRelativeToClassFile, Class cls) throws FileNotFoundException, IOException { this(); label = "" + cls + " : " + pathNameRelativeToClassFile; URL def = defaultResourceURL(cls); relativeClass = cls; try { if (isJarFile(def)) { // loading from jar file, handle it specially. This is because // file URLs can handle ../ etc but jar urls CANNOT, stupid Java relativePath = concatenatedJarPath(def, pathNameRelativeToClassFile); load(concatenatedJarResource(def, pathNameRelativeToClassFile).openStream()); } else { relativePath = simplifyPath(pathNameRelativeToClassFile); InputStream f = cls.getResourceAsStream(relativePath); load(f); try { f.close(); } catch (IOException e) { } } } catch (NullPointerException e) { throw new IOException("Could not load database from resource file " + relativePath + " relative to the class " + cls, e); } catch (IOException e) { throw new IOException("Could not load database from resource file " + relativePath + " relative to the class " + cls, e); } // load parents for (int x = 0 ; ; x++) { String s = getProperty("parent." + x); if (s == null) return; // we're done if (new File(s).isAbsolute()) // it's an absolute file definition parents.addElement(new ParameterDatabase(new File(s))); else if (s.startsWith(C_CLASS)) { int i = indexOfFirstWhitespace(s); if (i == -1) throw new FileNotFoundException("Could not parse file into filename and classname:\n\tparent." + x + " = " + s); String classname = s.substring(C_CLASS.length(),i); String filename = s.substring(i).trim(); try { parents.addElement(new ParameterDatabase(filename, Class.forName(classname, true, Thread.currentThread().getContextClassLoader()))); } catch (ClassNotFoundException ex) { throw new FileNotFoundException("Could not parse file into filename and classname:\n\tparent." + x + " = " + s); } } else { String path = new File(new File(pathNameRelativeToClassFile).getParent(), s).toString(); parents.addElement(new ParameterDatabase(path, cls)); } } } /** Creates a new parameter database loaded from the given stream. Non-relative parents are not permitted. If parent.n are defined, parents will be attempted to be loaded -- that's the reason for the FileNotFoundException and IOException. */ public ParameterDatabase(java.io.InputStream stream) throws FileNotFoundException, IOException { this(); label = "Stream: " + System.identityHashCode(stream); load(stream); // load parents for (int x = 0;; x++) { String s = getProperty("parent." + x); if (s == null) return; // we're done if (new File(s).isAbsolute()) // it's an absolute file definition parents.addElement(new ParameterDatabase(new File(s))); else if (s.startsWith(C_CLASS)) { int i = indexOfFirstWhitespace(s); if (i == -1) throw new FileNotFoundException("Could not parse file into filename and classname:\n\tparent." + x + " = " + s); String classname = s.substring(C_CLASS.length(),i); String filename = s.substring(i).trim(); try { parents.addElement(new ParameterDatabase(filename, Class.forName(classname, true, Thread.currentThread().getContextClassLoader()))); } catch (ClassNotFoundException ex) { throw new FileNotFoundException("Could not parse file into filename and classname:\n\tparent." + x + " = " + s); } } else throw new FileNotFoundException("Attempt to load a relative file, but there's no parent file: " + s); } } /** * Creates a new parameter database tree from a given database file and its * parent files. */ public ParameterDatabase(File file) throws FileNotFoundException, IOException { this(); label = "File: " + file.getPath(); //this.file = file.getName(); directory = new File(file.getParent()); // get the directory // file is in FileInputStream f = new FileInputStream(file); load(f); try { f.close(); } catch (IOException e) { } // load parents for (int x = 0;; x++) { String s = getProperty("parent." + x); if (s == null) return; // we're done if (new File(s).isAbsolute()) // it's an absolute file definition parents.addElement(new ParameterDatabase(new File(s))); else if (s.startsWith(C_CLASS)) { int i = indexOfFirstWhitespace(s); if (i == -1) throw new FileNotFoundException("Could not parse file into filename and classname:\n\tparent." + x + " = " + s); String classname = s.substring(C_CLASS.length(),i); String fname = s.substring(i).trim(); try { parents.addElement(new ParameterDatabase(fname, Class.forName(classname, true, Thread.currentThread().getContextClassLoader()))); } catch (ClassNotFoundException ex) { throw new FileNotFoundException("Could not parse file into filename and classname:\n\tparent." + x + " = " + s); } } else // it's relative to my path parents.addElement(new ParameterDatabase(new File(file.getParent(), s))); } } /** * Creates a new parameter database from a given database file and argv * list. The top-level database is completely empty, pointing to a second * database which contains the parameter entries stored in args, which * points to a tree of databases constructed using * ParameterDatabase(file). */ public ParameterDatabase(File file, String[] args) throws FileNotFoundException, IOException { this(); label = "File: " + file.getPath(); //this.file = file.getName(); directory = new File(file.getParent()); // get the directory // file is in // Create the Parameter Database tree for the files ParameterDatabase files = new ParameterDatabase(file); // Create the Parameter Database for the arguments ParameterDatabase a = new ParameterDatabase(); a.parents.addElement(files); boolean hasArgs = false; for (int x = 0; x < args.length - 1; x++) { if (args[x].equals("-p")) { String s = args[x+1].trim(); if (s.length() == 0) continue; // failure int eq = s.indexOf('='); // look for the '=' if (eq <= 0) continue; // '=' isn't there, or it's the first char: failure put(s.substring(0,eq), s.substring(eq+1)); // add the parameter if (!hasArgs) { label = label + " Args: "; hasArgs = true; } label = label + s + " "; } } // Set me up parents.addElement(a); } /** * Prints out all the parameters in the database, but not shadowed * parameters. */ public void list(PrintWriter p) { list(p, false); } /** * Prints out all the parameters in the database. Useful for debugging. If * listShadowed is true, each parameter is printed with the parameter * database it's located in. If listShadowed is false, only active * parameters are listed, and they're all given in one big chunk. */ public void list(PrintWriter p, boolean listShadowed) { if (listShadowed) _list(p, listShadowed, "root", null); else { Hashtable gather = new Hashtable(); _list(null, listShadowed, "root", gather); Vector vec = new Vector(); Enumeration e = gather.keys(); while (e.hasMoreElements()) vec.addElement(e.nextElement()); java.util.Collections.sort(vec); // Uncheck and print each item for (int x = 0; x < vec.size(); x++) { String s = (String) vec.get(x); String v = null; if (s != null) v = (String) gather.get(s); if (v == null) v = UNKNOWN_VALUE; if (p!=null) p.println(s + " = " + v); } } if (p!=null) p.flush(); } /** Private helper function. */ void _list(PrintWriter p, boolean listShadowed, String prefix, Hashtable gather) { if (listShadowed) { // Print out my header if (p!=null) { p.println("\n########" + prefix); super.list(p); } int size = parents.size(); for (int x = 0; x < size; x++) ((ParameterDatabase) (parents.elementAt(x)))._list(p, listShadowed, prefix + "." + x, gather); } else { // load in reverse order so things get properly overwritten int size = parents.size(); for (int x = size - 1; x >= 0; x--) ((ParameterDatabase) (parents.elementAt(x)))._list(p, listShadowed, prefix, gather); Enumeration e = keys(); while (e.hasMoreElements()) { String key = (String) (e.nextElement()); gather.put(key, get(key)); } } if (p!=null) p.flush(); } public String toString() { String s = super.toString(); if (parents.size() > 0) { s += " : ("; for (int x = 0; x < parents.size(); x++) { if (x > 0) s += ", "; s += parents.elementAt(x); } s += ")"; } return s; } /** * Builds a TreeModel from the available property keys. */ public TreeModel buildTreeModel() { //String sep = System.getProperty("file.separator"); ParameterDatabaseTreeNode root = new ParameterDatabaseTreeNode( //this.directory.getAbsolutePath() + sep + this.filename); label); ParameterDatabaseTreeModel model = new ParameterDatabaseTreeModel(root); _buildTreeModel(model, root); model.sort(root, new Comparator() { public int compare(Object o1, Object o2) { ParameterDatabaseTreeNode t1 = (ParameterDatabaseTreeNode)o1; ParameterDatabaseTreeNode t2 = (ParameterDatabaseTreeNode)o2; return ((Comparable)t1.getUserObject()).compareTo(t2.getUserObject()); } }); // In order to add elements to the tree model, the leaves need to be // visible. This is because some properties have values *and* sub- // properties. Otherwise, if the nodes representing these properties did // not yet have children, then they would be invisible and the tree model // would be unable to add child nodes to them. model.setVisibleLeaves(false); return model; } void _buildTreeModel(DefaultTreeModel model, DefaultMutableTreeNode root) { Enumeration e = keys(); while (e.hasMoreElements()) { _addNodeForParameter(model, root, (String)e.nextElement()); } int size = parents.size(); for (int i = 0; i < size; ++i) { ParameterDatabase parentDB = (ParameterDatabase) parents .elementAt(i); parentDB._buildTreeModel(model, root); } } /** * @param model * @param root * @param e */ void _addNodeForParameter(DefaultTreeModel model, DefaultMutableTreeNode root, String key) { if (key.indexOf("parent.") == -1) { /* * TODO split is new to 1.4. To maintain 1.2 compatability we need * to use a different approach. Just use a string tokenizer. */ StringTokenizer tok = new StringTokenizer(key,"."); String[] path = new String[tok.countTokens()]; int t = 0; while(tok.hasMoreTokens()) { path[t++] = tok.nextToken(); } DefaultMutableTreeNode parent = root; for (int i = 0; i < path.length; ++i) { int children = model.getChildCount(parent); if (children > 0) { int c = 0; for (; c < children; ++c) { DefaultMutableTreeNode child = (DefaultMutableTreeNode) parent.getChildAt(c); if (child.getUserObject().equals(path[i])) { parent = child; break; } } if (c == children) { DefaultMutableTreeNode child = new ParameterDatabaseTreeNode(path[i]); model.insertNodeInto(child, parent, parent.getChildCount()); parent = child; } } // If the parent has no children, just add the node. else { DefaultMutableTreeNode child = new ParameterDatabaseTreeNode(path[i]); model.insertNodeInto(child, parent, 0); parent = child; } } } } /** Test the ParameterDatabase */ public static void main(String[] args) throws FileNotFoundException, IOException { ParameterDatabase pd = new ParameterDatabase(new File(args[0]), args); pd.set(new Parameter("Hi there"), "Whatever"); pd.set(new Parameter(new String[] { "1", "2", "3" }), " Whatever "); pd.set(new Parameter(new String[] { "a", "b", "c" }).pop().push("d"), "Whatever"); System.err.println("\n\n PRINTING ALL PARAMETERS \n\n"); pd.list(new PrintWriter(System.err, true), true); System.err.println("\n\n PRINTING ONLY VALID PARAMETERS \n\n"); pd.list(new PrintWriter(System.err, true), false); } }

Ads help maintain this website.