Commit in lcsim-contrib/src/main/java/org/lcsim/contrib/Mbussonn/kf/ToyConfig on MAIN
ToyConfig.java+461added 1.1
.cvsignore+1added 1.1
ToyConfigException.java+23added 1.1
ToyConfigRecord.java+696added 1.1
+1181
4 added files
add forgotten dependencies Toyconfig

lcsim-contrib/src/main/java/org/lcsim/contrib/Mbussonn/kf/ToyConfig
ToyConfig.java added at 1.1
diff -N ToyConfig.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ ToyConfig.java	23 Jun 2009 19:44:44 -0000	1.1
@@ -0,0 +1,461 @@
+package org.lcsim.contrib.Mbussonn.kf.ToyConfig;
+
+import java.io.*;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.io.PrintStream;
+
+/**
+ *
+ * Main class in a primitive runtime configuration utility.
+ * Parses the input file into records, holds a collection of
+ * records and provides access by parameter name.  Provides
+ * error checking of the global structure of the file and
+ * for attempts to access non-existent parameters.
+ *
+ *@author $Author: mbussonn $
+ *@version $Id: ToyConfig.java,v 1.1 2009/06/23 19:44:44 mbussonn Exp $
+ *
+ * Date $Date: 2009/06/23 19:44:44 $
+ *
+ */
+
+//
+// Work list:
+// 1) Should I make the copy c'tor and assignment operators private?
+//    or is this even a sensible question in java?
+// 2) The following record will fail to parse correctly.
+//    String name = "//This is not a comment";
+// 3) Catch exceptions??
+// 4) Rename toString so that it can throw??
+
+public class ToyConfig{
+
+    /**
+     * Manage creation of the singleton instance and access to it.
+     *
+     * @return The singleton instance of this class.
+     */
+    static public ToyConfig getInstance() throws ToyConfigException{
+	if ( instance == null ){
+	    instance = new ToyConfig();
+	}
+	return instance;
+    }
+
+
+    /**
+     * Read the input file to populate this class.
+     *
+     * @return The singleton instance of this class.
+     */
+    private ToyConfig( ) throws ToyConfigException{
+	ReadFile();
+    }
+
+
+    /**
+     * Change the configuration file to be read. Gives a warning if
+     * a configuration file has already been read.
+     *
+     */
+    public static void setConfigFile( String file ){
+	if ( instance == null ){
+	    configfile = file;
+	} else {
+	    System.err.println("Configuration file already read;" +
+			       " setConfigFile() will be ignored." );
+	}
+    }
+
+    /**
+     * Return a Set<String> containing all variable names found in
+     * the configuration file.
+     *
+     * @return a Set<String> containing all variable names.
+     */
+    public Set<String> getAllNames(){
+	return rmap.keySet();
+    }
+
+    // Accessors to named parameters, separated by data type.
+    // All checking is done in the ToyConfigRecord class.
+
+
+    /**
+     * Get a specified parameter as a string.  Works for all record types.
+     *
+     * @return the value of the parameter.
+     */
+    public String getString ( String name ) throws ToyConfigException{
+	ToyConfigRecord r = getRecord(name);
+	return r.getString();
+    }
+
+    /**
+     * Get a specified parameter as a String, if not present in the file
+     * return the value specified by the second argument.
+     *
+     * @return the value of the parameter as an String.
+     */
+    public String getString ( String name, String def ){
+	String i=def;
+	try{
+	    ToyConfigRecord r = getRecord(name);
+	    i = r.getString();
+	} catch ( ToyConfigException e ){
+	}
+	return i;
+    }
+
+    /**
+     * Get a specified parameter as a int.
+     *
+     * @return the value of the parameter as an int.
+     */
+    public int getInt ( String name ) throws ToyConfigException{
+	ToyConfigRecord r = getRecord(name);
+	return r.getInt();
+    }
+
+    /**
+     * Get a specified parameter as a int, if not present in the file
+     * return the value specified by the second argument.
+     *
+     * @return the value of the parameter as an int.
+     */
+    public int getInt ( String name, int def ){
+	int i=def;
+	try{
+	    ToyConfigRecord r = getRecord(name);
+	    i = r.getInt();
+	} catch ( ToyConfigException e ){
+	}
+
+	return i;
+    }
+
+    /**
+     * Get a specified parameter as a double.
+     *
+     * @return the value of the parameter as an double.
+     */
+    public double getDouble ( String name ) throws ToyConfigException{
+	ToyConfigRecord r = getRecord(name);
+	return r.getDouble();
+    }
+
+    /**
+     * Get a specified parameter as a double, if not present in the file
+     * return the value specified by the second argument.
+     *
+     * @return the value of the parameter as an double.
+     */
+    public double getDouble ( String name, double def ){
+	double d=def;
+	try{
+	    ToyConfigRecord r = getRecord(name);
+	    d = r.getDouble();
+	} catch ( ToyConfigException e ){
+	}
+
+	return d;
+    }
+
+
+    /**
+     * Get a specified parameter as a boolean.
+     *
+     * @return the value of the parameter as an boolean.
+     */
+    public boolean getBoolean ( String name ) throws ToyConfigException{
+	ToyConfigRecord r = getRecord(name);
+	return r.getBoolean();
+    }
+
+    /**
+     * Get a specified parameter as a boolean, if not present in the file
+     * return the value specified by the second argument.
+     *
+     * @return the value of the parameter as an boolean.
+     */
+    public boolean getBoolean ( String name, boolean def ){
+	boolean b=def;
+	try{
+	    ToyConfigRecord r = getRecord(name);
+	    b = r.getBoolean();
+	} catch ( ToyConfigException e ){
+	}
+
+	return b;
+    }
+
+
+    /**
+     * Get a specified parameter as a List<String>. Works for all parameter types.
+     *
+     * @return the value of the parameter as a List<String>.
+     */
+    public List<String> getListString ( String name ) throws ToyConfigException{
+	ToyConfigRecord r = getRecord(name);
+	return r.getListString();
+    }
+
+
+    /**
+     * Get a specified parameter as a List<Integer>.
+     *
+     * @return the value of the parameter as a List<Integer>.
+     */
+    public List<Integer> getListInteger ( String name ) throws ToyConfigException{
+	ToyConfigRecord r = getRecord(name);
+	return r.getListInteger();
+    }
+
+    /**
+     * Get a specified parameter as an array int[].
+     *
+     * @return the value of the parameter as an array int[].
+     */
+    public int[] getArrayInt ( String name ) throws ToyConfigException{
+	ToyConfigRecord r = getRecord(name);
+	List<Integer> l = r.getListInteger();
+
+	// Copy to array format.
+	int[] a = new int[l.size()];
+	for ( int i=0; i<l.size(); ++i ){
+	    a[i]=(l.get(i)).intValue();
+	}
+	return a;
+    }
+
+
+    /**
+     * Get a specified parameter as a List<Double>.
+     *
+     * @return the value of the parameter as a List<Double>.
+     */
+    public List<Double> getListDouble ( String name ) throws ToyConfigException{
+	ToyConfigRecord r = getRecord(name);
+	return r.getListDouble();
+    }
+
+    /**
+     * Get a specified parameter as an array double[].
+     *
+     * @return the value of the parameter as an array double[].
+     */
+    public double[] getArrayDouble ( String name ) throws ToyConfigException{
+	ToyConfigRecord r = getRecord(name);
+	List<Double> l = r.getListDouble();
+
+	// Copy to array format.
+	double[] a = new double[l.size()];
+	for ( int i=0; i<l.size(); ++i ){
+	    a[i]=(l.get(i)).doubleValue();
+	}
+	return a;
+    }
+
+    /**
+     * Get a specified parameter as a List<Boolean>.
+     *
+     * @return the value of the parameter as a List<Boolean>.
+     */
+    public List<Boolean> getListBoolean ( String name ) throws ToyConfigException{
+	ToyConfigRecord r = getRecord(name);
+	return r.getListBoolean();
+    }
+
+    /**
+     * Get a specified parameter as an array boolean[].
+     *
+     * @return the value of the parameter as an array boolean[].
+     */
+    public boolean[] getArrayBoolean ( String name ) throws ToyConfigException{
+	ToyConfigRecord r = getRecord(name);
+	List<Boolean> l = r.getListBoolean();
+
+	// Copy to array format.
+	boolean[] a = new boolean[l.size()];
+	for ( int i=0; i<l.size(); ++i ){
+	    a[i]=(l.get(i)).booleanValue();
+	}
+	return a;
+    }
+
+
+    /**
+     * Return a the parameter as a formatted string.
+     *
+     * @return a formatted copy of the requested parameter.
+     */
+    public String toString ( String name ){
+	try { 
+	    ToyConfigRecord r = getRecord(name);
+	    return r.toString();
+	} catch ( ToyConfigException e ){
+	    return e.getMessage();
+	}
+    }
+
+
+    /**
+     * Return the complete record as s formatted string.
+     *
+     * @return a formatted copy of the record for the requested parameter.
+     */
+    public ToyConfigRecord getRecord ( String name ) throws ToyConfigException{
+	ToyConfigRecord r = rmap.get(name);
+	if ( r == null ) {
+	    throw new ToyConfigException ("No such parameter: " + name 
+				       + " in file: " + configfile );
+	}
+	return r;
+    }
+
+    /**
+     * Return the name of the input file.
+     *
+     * @return name of the input file.
+     */
+    public String ToyConfigfile(){
+	return configfile;
+    }
+
+    /**
+     * Print a formatted copy of the full configuration file
+     * to the specified output stream.
+     *
+     */
+    public void printAll( PrintStream out){
+	for ( ToyConfigRecord r : image ){
+	    out.println( r.toString() );
+	}
+	out.println("");
+    }
+
+    /**
+     * Print a formatted copy of the full configuration file
+     * to System.out.
+     *
+     */
+    public void printAll( ){
+	printAll(System.out);
+    }
+
+    // Private instance data.
+
+    // Access to the configuration data, keyed by parameter name.
+    Map<String,ToyConfigRecord> rmap = null;
+
+    // Access to the info in rmap in the order in which the records were present
+    // in the input file.
+    List<ToyConfigRecord> image = null;
+
+    // Name of the configuration file, initialized to its default value.
+    private static String configfile = "runtime.conf";
+
+    // Singleton instance.
+    private static ToyConfig instance = null;
+
+    // Private methods, other than the constructor.
+
+
+    /**
+     * Read the input file, break it into records.
+     * Keep a copy of the input file in the orginal record order.
+     * Create a map to access records by parameter name.
+     *
+     */
+    private void ReadFile() throws ToyConfigException{
+
+	rmap  = new HashMap<String,ToyConfigRecord>();
+	image = new ArrayList<ToyConfigRecord>();
+
+	try {
+	    FileReader fr = new FileReader(configfile);
+	    BufferedReader file = new BufferedReader(fr);
+
+	    // Can make this a single loop.
+
+	    String line;
+	    while ( (line = file.readLine()) != null ){
+
+		// Remove comments.
+		line = StripComment(line);
+
+		// Add extension lines if needed.
+		if ( WantsExtension(line) ){
+
+		    StringBuffer all = new StringBuffer(line.substring(0,line.length()));
+		    String nextline;
+		    while ( (nextline = file.readLine()) != null ){
+			nextline = StripComment(nextline);
+			if ( WantsExtension(nextline) ){
+			    if ( nextline.length() != 0 ) 
+			    all.append(nextline.substring(0,nextline.length()).trim());
+			    line = all.toString();
+			} else{
+			    all.append(nextline.trim());
+			    line = all.toString();
+			    break;
+			}
+		    }
+		}
+
+		ToyConfigRecord r = new ToyConfigRecord(line);
+		image.add(r);
+		if ( !r.isCommentOrBlank() ) {
+		    rmap.put(r.getName(),r);
+		}
+	    }
+	    
+	} catch (IOException e){
+	    throw new ToyConfigException( "Error reading configuration file: "  
+					  + configfile 
+					  );
+	}
+    }
+
+    /**
+     * Test to see if this record is complete.
+     * 
+     * A valid end of line indicator is a semi-colon as the last non-blank character
+     * before any comments.  Otherwise this record needs an extension.
+     *
+     * @return true if this record is incomplete and false if it is complete.
+     */
+    private boolean WantsExtension( String s){
+	int icomment = s.indexOf("//");
+	String line = ( icomment == -1 ) ? s.trim() : s.substring(0,icomment).trim();
+	if ( line.length() == 0 ) return false;
+	return ( !line.endsWith(";")) ;
+    }
+
+    /**
+     * Remove, comments, trailing white space and leading whitespace input string.
+     *  - a comment begins with // and continues for the rest of the line.
+     *
+     * This will give a wrong result on a line like:
+     * String name = "//This is not supposed to be a comment"; 
+     *
+     * @return a copy of the input with comments and insignificant whitespace removed.
+     */
+    private String StripComment( String s){
+
+	// Find comment delimiter if present.
+	int islash = s.indexOf("//");
+
+	if ( islash < 0 ){
+	    return s.trim();
+	}
+
+	return s.substring(0,islash).trim();
+
+    }
+
+}

lcsim-contrib/src/main/java/org/lcsim/contrib/Mbussonn/kf/ToyConfig
.cvsignore added at 1.1
diff -N .cvsignore
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ .cvsignore	23 Jun 2009 19:44:44 -0000	1.1
@@ -0,0 +1 @@
+.DS_Store

lcsim-contrib/src/main/java/org/lcsim/contrib/Mbussonn/kf/ToyConfig
ToyConfigException.java added at 1.1
diff -N ToyConfigException.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ ToyConfigException.java	23 Jun 2009 19:44:44 -0000	1.1
@@ -0,0 +1,23 @@
+package org.lcsim.contrib.Mbussonn.kf.ToyConfig;
+
+import java.lang.Exception;
+
+/**
+ *
+ * Exception class for primitive Run Time configuration utility.
+ *
+ *
+ *@author $Author: mbussonn $
+ *@version $Id: ToyConfigException.java,v 1.1 2009/06/23 19:44:44 mbussonn Exp $
+ *
+ * Date $Date: 2009/06/23 19:44:44 $
+ *
+ */
+
+
+public class ToyConfigException extends Exception{
+    
+    public ToyConfigException ( String s){
+	super(s);
+    }
+}

lcsim-contrib/src/main/java/org/lcsim/contrib/Mbussonn/kf/ToyConfig
ToyConfigRecord.java added at 1.1
diff -N ToyConfigRecord.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ ToyConfigRecord.java	23 Jun 2009 19:44:44 -0000	1.1
@@ -0,0 +1,696 @@
+package org.lcsim.contrib.Mbussonn.kf.ToyConfig;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ *
+ * A class to hold one record within the primitive 
+ * RunTimeConfiguration utility.
+ *
+ *@author $Author: mbussonn $
+ *@version $Id: ToyConfigRecord.java,v 1.1 2009/06/23 19:44:44 mbussonn Exp $
+ *
+ * Date $Date: 2009/06/23 19:44:44 $
+ *
+ */
+
+//
+// This class holds a single record from the configuration file.
+// It parses the record, checks for internal consistency and provides
+// accesors for information in the record.
+// 
+// Notes:
+// 1) Supported types:
+//    String, int, double, boolean.
+//    List<String>, List<Integer>, List<Double>, List<Boolean>.
+//    Note that scalar types are primitives but List types are objects.
+//
+// 2) Supports empty lists and empty strings.
+//
+//
+// Work list:
+// 1) Is List the right interface for the return values?
+// 2) Is it aproblem that I return int for scalars and Integer for integer lists?
+//    Similarly for boolean and double? Should I make it symmetric?
+// 3) Should I make copy and assignment c'tors private or non-existant?
+//    Needed in C++ what about Java?
+// 4) Rename toString so that it can throw?
+// 5) Do the type conversion in c'tor so that we fail immediately
+//    and not later on when someone tries to read the value.
+// 6) Add extra accessors to return other arrays as an option to Lists.
+// 7) Add accessor to return a scalar as a list of length 1??
+// 8) 
+//
+
+public class ToyConfigRecord {
+    
+    // Constructor.
+    public ToyConfigRecord( String record ) throws ToyConfigException{
+	this.record = record;
+	Parse();
+    }
+
+
+    /**
+     * Returns a copy of the input record as it was found in the input file
+     * but with line breaks removed.
+     *
+     * @return A copy of the raw record.
+     */
+    public String getRecord(){
+	return record;
+    }
+
+    /**
+     * Returns the type field of this record.
+     * @return The type.
+     */
+    public String getType (){
+	return Type;
+    }
+
+    /**
+     * Returns the variable name field of this record.
+     * @return The variable name.
+     */
+    public String getName (){
+	return Name;
+    }
+
+    /**
+     * Returns the comment field, if any, of this record.
+     * @return The comment, if any, that is part of this record.
+     */
+    public String getComment(){
+	return comment;
+    }
+
+    /**
+     * Returns true if the record contains nothing more than a comment or if the
+     * the record is blank.
+     *
+     * @return true if the record is a pure comment or is blank; false otherwise.
+     */
+    public boolean isCommentOrBlank(){
+	return isCommentOrBlank;
+    }
+
+    // Accessors to return supported data types.
+
+    /**
+     * Return the value as string.  This will work for any data type.
+     * @return The value as a string.
+     */
+    public String getString (){
+	return Values.get(0);
+    }
+
+    /**
+     * Return the value as an int.  Only works for recrods that are of type int.
+     *
+     * @return The value of an int record.
+     */
+    public int getInt () throws ToyConfigException{
+	CheckType("int");
+	try{
+	    return Integer.valueOf(Values.get(0));
+	} catch(NumberFormatException e){
+	    throw new ToyConfigException("Cannot parse this value as an integer: "
+				      + record );
+	}
+    }
+
+    /**
+     * Return the value as an double.  Only works for records that are of type double.
+     * 
+     * @return The value of a double record.
+     */
+    public double getDouble () throws ToyConfigException{
+	CheckType("double");
+	double x=0.;
+	try{
+	    x = Double.valueOf(Values.get(0));
+	} catch(NumberFormatException e){
+	    throw new ToyConfigException("Cannot parse this value as a double: "
+				      + record );
+	}
+	return x;
+    }
+
+    /**
+     * Return the value as a boolean.  Only works for records that are of type boolean.
+     * 
+     * @return The value of a boolean record.
+     */
+    public boolean getBoolean() throws ToyConfigException {
+	CheckType("boolean");
+	boolean b=false;
+	try{
+	    b = Boolean.valueOf(Values.get(0));
+	} catch(NumberFormatException e){
+	    throw new ToyConfigException("Cannot parse this value as a boolean: "
+				      + record );
+	}
+	return b;
+    }
+
+    /**
+     * Return the value as a list of strings.  Works for all record types.
+     *
+     * @return The value of a the record.
+     */
+    // Can return any type of list as a list of strings.
+    public List<String> getListString() throws ToyConfigException{
+	AnyList();
+	return Values;
+    }
+
+    /**
+     * Return the value as a List<Integer>.  Only works for records that are of type List<Integer>.
+     * 
+     * @return The value of a List<int> record.
+     */
+    public List<Integer> getListInteger() throws ToyConfigException {
+	CheckType("List<int>");
+	List<Integer> l = new ArrayList<Integer>();
+	for ( String v : Values ){
+	    Integer I =0;
+	    try{
+		I = Integer.valueOf(v);
+	    } catch(NumberFormatException e){
+		throw new ToyConfigException("Cannot parse this value as a List<int>: "
+					  + record );
+	    }
+	    l.add(I);
+	}
+	return l;
+    }
+
+    /**
+     * Return the value as a List<Double>.  Only works for records that are of type List<Double>.
+     * 
+     * @return return the value of a List<double> record.
+     */
+    public List<Double> getListDouble() throws ToyConfigException {
+	CheckType("List<double>");
+	List<Double> l = new ArrayList<Double>();
+	for ( String v : Values ){
+	    Double D = 0.;
+	    try{
+		D = Double.valueOf(v);
+	    } catch(NumberFormatException e){
+		throw new ToyConfigException("Cannot parse this value as a List<double>: "
+					  + record );
+	    }
+	    l.add(D);
+	}
+	return l;
+    }
+
+    /**
+     * Return the value as a List<Boolean>.  Only works for records that are of type List<Boolean>.
+     * 
+     * @return the value of a List<boolean> record.
+     */
+    public List<Boolean> getListBoolean() throws ToyConfigException {
+	CheckType("List<boolean>");
+	List<Boolean> l = new ArrayList<Boolean>();
+	for ( String v : Values ){
+	    Boolean D = false;
+	    try{
+		D = Boolean.valueOf(v);
+	    } catch(NumberFormatException e){
+		throw new ToyConfigException("Cannot parse this value as a List<boolean>: "
+					  + record );
+	    }
+	    l.add(D);
+	}
+	return l;
+    }
+
+    /**
+     * 
+     * Format the record as a string with standard spacing, ignoring the spacing
+     * on the input line. If the data are strings, then enclose each string in 
+     * quotes, even if it has no embedded spaces.
+     *
+     * 
+     * @return A formatted copy of the record.
+     */
+    // I would like this to throw but it cannot since it overrides a method of the base 
+    // class "Object" that does not throw an exception.
+    public String toString() {
+	if ( isCommentOrBlank ){
+	    return comment;
+	}
+	StringBuffer s = new StringBuffer(Type);
+	s.append(" ");
+	s.append(Name);
+	s.append(" = ");
+	try{
+	    if ( isList ) {
+		s.append("{ ");
+		if ( Type.equals("List<int>") ){
+		    boolean first = true;
+		    for ( Integer I : getListInteger() ){
+			if( !first ){
+			    s.append(", ");
+			} else{
+			    first = false;
+			}
+			s.append(I.toString());
+		    }
+		} else if ( Type.equals("List<double>")){
+		    boolean first = true;
+		    for ( Double D : getListDouble() ){
+			if( !first ){
+			    s.append(", ");
+			} else{
+			    first = false;
+			}
+			s.append(D.toString());
+		    }
+		} else if ( Type.equals("List<boolean>")){
+		    boolean first = true;
+		    for ( Boolean B : getListBoolean() ){
+			if( !first ){
+			    s.append(", ");
+			} else{
+			    first = false;
+			}
+			s.append(B.toString());
+		    }
+		} else {
+		    boolean first = true;
+		    for ( String B : getListString() ){
+			if( !first ){
+			    s.append(", \"");
+			} else{
+			    s.append("\"");
+			    first = false;
+			}
+			s.append(B.toString());
+			s.append( "\"");		}
+		}
+		s.append(" }");
+	    }else{
+		if ( Type.equals("int") ){
+		    try{
+			Integer I = getInt();
+			s.append(I.toString());
+		    }catch(ToyConfigException e){
+			s.append("???");
+		    }
+		} else if ( Type.equals("double")){
+		    try{
+			Double D = getDouble();
+			s.append(D.toString());
+		    } catch (ToyConfigException e){
+			s.append("???");
+		    }
+		} else if ( Type.equals("boolean")){
+		    Boolean B = getBoolean();
+		    s.append( B.toString());
+		} else {
+		    s.append( "\"");
+		    s.append( getString() );
+		    s.append( "\"");
+		}
+	    }
+	}catch (ToyConfigException e) {
+	    System.out.println (e.getMessage() );
+	    s.append(" [Error formating this item], ");
+	}
+	s.append(";");
+	return s.toString();
+    }
+
+    // Private instance data.
+
+    // A copy of the record as it came in.
+    // An external class does the concatenation of multiple line records into a single string.
+    // Present implementation also strips comments - but that could change in the future.
+    private String record;
+
+    // Record with comments and enclosing white space stripped out.
+    private String barerecord;
+
+    // Comment field, including the // delimiter.
+    private String comment = "";
+
+    // Data type.
+    private String Type = null;
+
+    // Name of the datum.
+    private String Name = null;
+
+    // The value field - not yet parsed into components.
+    private String Value = null;
+
+    // The value field, parsed into components.
+    private List<String> Values = null;
+
+    // State data.
+    private boolean isCommentOrBlank = false;
+    private boolean isList           = false;
+
+    // Private methods.
+
+    /**
+     * 
+     * Parse this record, starting from its input string.
+     * 
+     */
+    private void Parse () throws ToyConfigException{
+
+	// Find comment delimiter if present.
+	int islash = record.indexOf("//");
+
+	// Extract comment, if any.
+	if ( islash >= 0 ) comment = record.substring(islash);
+
+	// Extract the part of the record that preceeds the comment.
+	// Trim leading and trailing whitespace.
+	String tmp = (islash < 0 ) ? record.trim() : record.substring(0,islash).trim();
+
+	// Line is blank or contains only a comment.
+	if ( tmp.length() == 0 ){
+	    isCommentOrBlank = true;
+	    return;
+	}
+
+	// Check for syntax of a complete record.
+	if ( tmp.charAt(tmp.length()-1) != ';' ){
+	    throw new ToyConfigException ("Not terminated by Semicolon: " + record);
+	}
+
+	// Strip the trailing semicolon and the leading and trailing whitespace.
+	barerecord = tmp.substring(0,tmp.length()-1).trim();
+	
+	// Split the line into: type name = value;
+	SplitLine();
+
+	// Parse the value part of the record.
+	ParseValue();
+
+    }
+
+
+    /**
+     * 
+     * Split this record into 3 fields: Type Name = Value; 
+     * 
+     */
+    private void SplitLine() throws ToyConfigException{
+
+	// Is there an equals sign with enough space before and after it?
+	// The minimal line is:
+	// t n=v
+	// where,
+	// t = type
+	// n = name
+	// v = value
+	// the space between t and n is significant.
+	// No embedded whitespace allowed within t or n.
+	// So the first legal spot for the equals sign is:
+	//   - must be at index 3 or greater
+	//   - the first equals sign in the line must not be the last non-whitespace character in the line.
+	// Remember that barerecord has leading and trailing spaces trimmed.
+	int iequal = barerecord.indexOf("=");
+	if ( iequal < 3 || iequal >= barerecord.length()-1){
+	    throw new ToyConfigException( "Misplaced equals sign in record: " + record );
+	}
+
+	// The value part of the field, to be parsed elsewhere.
+	Value = barerecord.substring(iequal+1).trim();
+	
+	// Extract type and name fields.
+	String first = barerecord.substring(0,iequal);
+	String [] tmp = first.split("[ \t]+");
+	if ( tmp.length != 2 || Value.length() < 1 ){
+	    throw new ToyConfigException( "Too many files in record: " + record );
+	}
+	Type = tmp[0];
+	Name = tmp[1];
+
+    }
+
+
+    /**
+     * 
+     * Parse the Value part of the record.
+     * 
+     */
+    private void ParseValue() throws ToyConfigException{
+
+	// Check for a record that is a list.
+	isList = ( Type.indexOf("List<") > -1 ) ? true: false;
+
+	// Part of the string to parse.
+	// Default is for non-lists.
+	int iopen  = 0;
+	int iclose = Value.length();
+
+	// If this is a list, strip the enclosing {}.
+	if ( isList ){
+	    iopen  = Value.indexOf("{");
+	    iclose = Value.lastIndexOf("}");
+	    if ( ( iopen  < 0 ) ||
+		 ( iclose < 0 ) ||
+		 ( iclose < (iopen+1) ) ){
+		throw new ToyConfigException( "Cannot parse record as a list: " +  record );
+	    }
+	    iopen = iopen + 1;
+	} 
+
+	// Remove {}, if present, and any leading and trailing whitespace.
+	String listpart = Value.substring(iopen,iclose).trim();
+
+	// Output of the parsing: one entry for each value in the list.
+	Values = new ArrayList<String>();
+
+	// Accumulate the next value in this variable.
+	StringBuffer next = new StringBuffer();
+
+	// Some predefined characters that hve special meaning.
+	Character quote = Character.valueOf('\"');
+	Character slash = Character.valueOf('\\');
+	Character comma = Character.valueOf(',');
+
+	// States:
+	// 0: not within any value
+	// 1: within a value that is not started by a quotation mark.
+	// 2: within a value that is started by a quotation mark.
+	// 3: into white space delimitation but have not yet found comma.
+	// 10: last character was an escape, otherwise in state 0
+	// 11: last character was an escape, otherwise in state 1
+	// 12: last character was an escape, otherwise in state 2
+	// 13: last character was an escape, otherwise in state 3
+
+	int state=0;
+	int i=-1;
+	while (++i<listpart.length()){
+	    
+	    // Next Character, need both primitive and object representations.
+	    char c = listpart.charAt(i);
+	    Character C = c;
+
+	    // If this character was escaped, add it to the next field
+	    // and drop out of escape mode.
+	    if ( state > 9 ){
+		next.append(c);
+		state = state - 10;
+		continue;
+
+	    } 
+	    // Not within any list value.
+	    else if ( state == 0 ){
+
+		// Skip white space between tokens.
+		if ( Character.isWhitespace(c) ) {
+		    continue;
+
+		} 
+
+		// Starting a new item with a quote, strip the quote.
+		else if ( C.compareTo(quote) == 0 ){
+		    state = 2;
+		    continue;
+
+		} 
+
+		// Starting a new item with a escape.
+		else if ( C.compareTo(slash) == 0 ){
+		    next.append(c);
+		    state = 11;
+		    continue;
+
+		} 
+
+		// Consecutive commas add an empty item to the list.
+		// State stays at 0.
+		else if ( C.compareTo(comma) == 0 && next.length() == 0 ){
+		    Values.add("");
+		    continue;
+
+		} 
+		// Starting a new item with neither escape nor quote.
+		else {
+		    next.append(c);
+		    state = 1;
+		    continue;
+		}
+
+	    } 
+
+	    // In the middle of a field not started by a quote.
+	    else if ( state == 1 ) {
+
+		// End of item is marked by white space
+		if ( Character.isWhitespace(c) ){
+		    state = 3;
+		    continue;
+		    
+		} 
+
+		// End of item marked by comma without preceeding whitespace.
+		else if ( C.compareTo(comma) == 0 ){
+		    Values.add(next.toString());
+		    next = new StringBuffer();
+		    state = 0;
+		    continue;
+		    
+		} 
+
+		// Do not allow an unescaped quote in mid word ...
+		else if ( C.compareTo(quote) == 0 ){
+		    throw new ToyConfigException( "Unexpected \" character in record: " + record );
+		} 
+
+		// Next character is to be escaped.
+		else if ( C.compareTo(slash) == 0 ) {
+		    next.append(c);
+		    state = 11;
+		    continue;
+
+		} 
+
+		// Not a special character, just add it to the string.
+		else{
+		    next.append(c);
+		    continue;
+		}
+
+	    } 
+
+	    // In the middle of a field started by a quote.
+	    else if ( state == 2 ){
+
+
+		// Terminal quote marks the end of an item.
+		if ( C.compareTo(quote) == 0 ){
+		    state = 3;
+
+		} 
+
+		// Escape the next character.
+		else if ( C.compareTo(slash) == 0 ) {
+		    next.append(c);
+		    state = 12;
+		    continue;
+
+		} 
+
+		// Add character. Comma and white space are normal characters in this case.
+		else {
+		    next.append(c);
+		    continue;
+		}
+
+	    } 
+
+	    // Finished with a field but have not yet seen a comma or end of record.
+	    else if ( state == 3 ){
+
+		// Skip white space between fields.
+		if ( Character.isWhitespace(c) ) {
+		    continue;
+
+		} 
+		// Add the previous item to the output list.
+		else if ( C.compareTo(comma) == 0 ) {
+		    Values.add(next.toString());
+		    next = new StringBuffer();
+		    state = 0;
+		    continue;
+		}
+	    } 
+
+	    // On a legal record there is no way to reach this else.
+	    else{
+		throw new ToyConfigException("Confused state while parsing record: " + record );
+
+	    }// end main branch, starting with: "if ( state > 9 )"
+
+	} // end loop over characters
+
+
+	// Record ended with an unterminated quote
+	if ( state == 2 || state == 12 ){
+	    throw new ToyConfigException("Unclosed quotes in this record: " + record );
+
+	// Treat an end of record as the trailing delimiter for the last field.
+	} else if ( state == 1 || state == 3 ){
+	    Values.add(next.toString());
+
+	// Last non-blank item was a non-quoted comma.
+	// So add a blank item to the list.
+	} else if ( isList && state ==0 ){
+	    if ( Values.size() > 0 ){
+		Values.add("");
+	    }
+	} 
+
+	// On a legal record there is no way to reach this else.
+	else{
+	    throw new ToyConfigException("Confused final state while parsing: " + record );
+	}
+
+	// For a scalar record, make sure that there was exactly 1 item.
+	if ( !isList ){
+	    if( Values.size() != 1 ){
+		throw new ToyConfigException("Too many values for a scalar type  record: " + record );
+	    }
+	}
+
+    }
+ 
+
+    /**
+     * 
+     * Check that the type of the current record matches the specified type.
+     *
+     */
+    private void CheckType( String s) throws ToyConfigException{
+	if ( Type.compareTo(s) !=0 ){
+	    throw new ToyConfigException("Requested type (" + s + ") does not match record: "
+				      + record );
+	}
+    }
+
+    /**
+     * 
+     * Check that the type of the current record is one of the List types.
+     *
+     */
+    private void AnyList() throws ToyConfigException{
+	if ( Type.substring(0,5).compareTo("List<") != 0 ){
+	    throw new ToyConfigException("Requested a list for a non-list record: "
+				      + record );
+	}
+    }
+
+
+
+}
CVSspam 0.2.8