[jsap] 03/04: Imported Upstream version 2.1

Emmanuel Bourg ebourg-guest at moszumanska.debian.org
Thu Sep 3 13:07:45 UTC 2015


This is an automated email from the git hooks/post-receive script.

ebourg-guest pushed a commit to annotated tag debian/2.1-1
in repository jsap.

commit b0e2edba5508187f7fed3a5d005639d999467254
Author: Guillaume Turri <guillaume.turri at gmail.com>
Date:   Thu Sep 3 10:04:53 2015 +0200

    Imported Upstream version 2.1
---
 CHANGELOG.TXT                                      |   52 +
 LICENSE.TXT                                        |   31 +
 build.xml                                          |  320 +++++
 src/doc/CHANGELOG.TXT                              |   52 +
 src/doc/manual.css                                 |   61 +
 .../martiansoftware/jsap/CommandLineTokenizer.java |  110 ++
 .../com/martiansoftware/jsap/DefaultSource.java    |   30 +
 src/java/com/martiansoftware/jsap/Defaults.java    |  147 +++
 .../com/martiansoftware/jsap/ExceptionMap.java     |   52 +
 src/java/com/martiansoftware/jsap/Flagged.java     |   65 +
 .../com/martiansoftware/jsap/FlaggedOption.java    |  314 +++++
 src/java/com/martiansoftware/jsap/IDMap.java       |  108 ++
 .../jsap/IllegalMultipleDeclarationException.java  |   48 +
 src/java/com/martiansoftware/jsap/JSAP.java        |  862 ++++++++++++
 .../com/martiansoftware/jsap/JSAPException.java    |   55 +
 src/java/com/martiansoftware/jsap/JSAPResult.java  | 1382 ++++++++++++++++++++
 src/java/com/martiansoftware/jsap/Option.java      |  238 ++++
 src/java/com/martiansoftware/jsap/Parameter.java   |  264 ++++
 .../com/martiansoftware/jsap/ParseException.java   |   52 +
 src/java/com/martiansoftware/jsap/Parser.java      |  471 +++++++
 .../martiansoftware/jsap/PropertyStringParser.java |   87 ++
 .../com/martiansoftware/jsap/QualifiedSwitch.java  |  229 ++++
 .../jsap/RequiredParameterMissingException.java    |   42 +
 src/java/com/martiansoftware/jsap/SimpleJSAP.java  |  167 +++
 .../com/martiansoftware/jsap/StringParser.java     |   78 ++
 src/java/com/martiansoftware/jsap/Switch.java      |  204 +++
 .../com/martiansoftware/jsap/SyntaxException.java  |   25 +
 src/java/com/martiansoftware/jsap/TestAll.java     |   40 +
 .../jsap/TestCommandLineTokenizer.java             |  184 +++
 .../com/martiansoftware/jsap/TestDefaults.java     |  213 +++
 .../martiansoftware/jsap/TestFlaggedOption.java    |   54 +
 .../jsap/TestJSAPConfiguration.java                |  117 ++
 src/java/com/martiansoftware/jsap/TestOption.java  |   71 +
 src/java/com/martiansoftware/jsap/TestParser.java  |  118 ++
 src/java/com/martiansoftware/jsap/TestSwitch.java  |  100 ++
 .../com/martiansoftware/jsap/TestUsageString.java  |   77 ++
 .../com/martiansoftware/jsap/UnflaggedOption.java  |  240 ++++
 .../martiansoftware/jsap/UnknownFlagException.java |   47 +
 .../jsap/UnspecifiedParameterException.java        |   38 +
 .../com/martiansoftware/jsap/ant/DefaultValue.java |   40 +
 .../jsap/ant/FlaggedOptionConfiguration.java       |  158 +++
 .../com/martiansoftware/jsap/ant/JSAPAntTask.java  |  545 ++++++++
 .../jsap/ant/OptionConfiguration.java              |  299 +++++
 .../jsap/ant/ParameterConfiguration.java           |  149 +++
 .../martiansoftware/jsap/ant/ParserProperty.java   |   62 +
 .../jsap/ant/SwitchConfiguration.java              |  117 ++
 .../jsap/ant/UnflaggedOptionConfiguration.java     |  104 ++
 .../jsap/defaultsources/PropertyDefaultSource.java |  234 ++++
 .../jsap/defaultsources/TestAll.java               |   25 +
 .../defaultsources/TestPropertyDefaultSource.java  |   73 ++
 .../martiansoftware/jsap/examples/Example1.java    |   42 +
 .../jsap/examples/Manual_HelloWorld_1.java         |   10 +
 .../jsap/examples/Manual_HelloWorld_2.java         |   40 +
 .../jsap/examples/Manual_HelloWorld_3.java         |   48 +
 .../jsap/examples/Manual_HelloWorld_4.java         |   65 +
 .../jsap/examples/Manual_HelloWorld_5.java         |   70 +
 .../jsap/examples/Manual_HelloWorld_6.java         |   72 +
 .../jsap/examples/Manual_HelloWorld_7.java         |   83 ++
 .../jsap/examples/Manual_HelloWorld_8.java         |   91 ++
 .../jsap/examples/Manual_HelloWorld_9.java         |   75 ++
 .../jsap/examples/Manual_HelloWorld_9.jsap         |   38 +
 .../jsap/examples/Manual_HelloWorld_Simple.java    |   55 +
 .../jsap/stringparsers/BigDecimalStringParser.java |   74 ++
 .../jsap/stringparsers/BigIntegerStringParser.java |   73 ++
 .../jsap/stringparsers/BooleanStringParser.java    |  114 ++
 .../jsap/stringparsers/ByteStringParser.java       |   69 +
 .../jsap/stringparsers/CharacterStringParser.java  |   70 +
 .../jsap/stringparsers/ClassStringParser.java      |   77 ++
 .../jsap/stringparsers/ColorStringParser.java      |  272 ++++
 .../jsap/stringparsers/DateStringParser.java       |  118 ++
 .../jsap/stringparsers/DoubleStringParser.java     |   73 ++
 .../jsap/stringparsers/EnumeratedStringParser.java |  220 ++++
 .../jsap/stringparsers/FileStringParser.java       |  126 ++
 .../jsap/stringparsers/FloatStringParser.java      |   75 ++
 .../jsap/stringparsers/ForNameStringParser.java    |   62 +
 .../stringparsers/InetAddressStringParser.java     |   75 ++
 .../jsap/stringparsers/IntSizeStringParser.java    |   43 +
 .../jsap/stringparsers/IntegerStringParser.java    |   70 +
 .../jsap/stringparsers/LongSizeStringParser.java   |  120 ++
 .../jsap/stringparsers/LongStringParser.java       |   69 +
 .../jsap/stringparsers/PackageStringParser.java    |   69 +
 .../jsap/stringparsers/ShortStringParser.java      |   73 ++
 .../jsap/stringparsers/StringStringParser.java     |   56 +
 .../jsap/stringparsers/TestAll.java                |   30 +
 .../jsap/stringparsers/TestColorStringParser.java  |  123 ++
 .../jsap/stringparsers/TestLongStringParser.java   |   45 +
 .../jsap/stringparsers/URLStringParser.java        |   76 ++
 .../martiansoftware/jsap/xml/FlaggedConfig.java    |   41 +
 .../jsap/xml/FlaggedOptionConfig.java              |   91 ++
 .../com/martiansoftware/jsap/xml/JSAPConfig.java   |   78 ++
 .../com/martiansoftware/jsap/xml/JSAPXStream.java  |   30 +
 .../martiansoftware/jsap/xml/ParameterConfig.java  |   74 ++
 .../com/martiansoftware/jsap/xml/Property.java     |   35 +
 .../jsap/xml/QualifiedSwitchConfig.java            |   29 +
 .../jsap/xml/StringParserConfig.java               |   63 +
 .../com/martiansoftware/jsap/xml/SwitchConfig.java |   27 +
 .../com/martiansoftware/jsap/xml/TryDumpXML.java   |   51 +
 .../com/martiansoftware/jsap/xml/TryLoadXML.java   |   21 +
 .../jsap/xml/UnflaggedOptionConfig.java            |   95 ++
 .../com/martiansoftware/jsap/xml/silly-example.xml |   47 +
 src/java/com/martiansoftware/util/StringUtils.java |   91 ++
 101 files changed, 12385 insertions(+)

diff --git a/CHANGELOG.TXT b/CHANGELOG.TXT
new file mode 100644
index 0000000..868c695
--- /dev/null
+++ b/CHANGELOG.TXT
@@ -0,0 +1,52 @@
+v2.0a (July 23, 2005)
+
+ * Fixed an embarrassing bug that resulted in unexpected characters
+   in auto-generated help output.
+   
+ * Fixed bugs in FileStringParser that prevented enforcement of
+   mustExist(), mustBeFile(), and mustBeDirectory()
+   
+v2.0 (June 21, 2005)
+
+ * Fixed setHelp() methods to return a reference to "this", consistent
+   with other setters.
+   
+ * New layout for option help to avoid problems with excessively long
+   option descriptions
+
+ * Added constructors with built-in help text setters.
+ 
+ * Added SimpleJSAP class to speed up common configurations.
+ 
+ * Added unbreakable spaces between flags and arguments in auto-generated
+   usage text.  
+
+ * Added IntSizeStringParser and LongSizeStringParser to allow numeric
+   parameters with SI suffixes (e.g., "10K" returns an Integer or Long
+   with value 10,000.  Currently supported suffixes include K, M, G, T, 
+   P, Ki, Mi, Gi, Ti, Pi.
+   
+ * Added ForNameStringParser that takes a class name and returns an object
+   constructed via class.forName().
+   
+ * Added singleton instances of most StringParsers to avoid unnecessary
+   object creation.  Singletons are available as public static fields of
+   JSAP, e.g., JSAP.BIG_DECIMAL_PARSER.
+   
+ * Default values for parameters are now indicated in auto-generated help.
+
+ * Added no-space short options, a la GNU getopt.  "-b5" is equivalent to
+   "-b 5"
+   
+ * Several minor bugfixes and documentation corrections.
+
+ * Added JSAPResult.contains(String id) to determine if the result contains
+   any values at all (including default values) for the specified parameter.
+   
+ * Added JSAPResult.userSpecified(String id) to determine if the result
+   contains a USER-SPECIFIED value (i.e., not including default values)
+   for the specified parameter.
+   
+ * Added XML support for JSAP configuration (experimental).
+ 
+ * Brought JIRA bug tracker online at http://jira.martiansoftware.com.
\ No newline at end of file
diff --git a/LICENSE.TXT b/LICENSE.TXT
new file mode 100644
index 0000000..7992529
--- /dev/null
+++ b/LICENSE.TXT
@@ -0,0 +1,31 @@
+JSAP - Java Simple Argument Parser
+Copyright (c) 2002-2004, Martian Software, Inc.
+
+
+LICENSE
+-------  
+JSAP is licensed under the Lesser GNU Public License.
+A copy of this license is available at
+http://www.fsf.org/licenses/licenses.html#LGPL
+
+Alternate licensing terms may be obtained by contacting
+the author: http://www.martiansoftware.com/contact.html
+
+
+JAVA LGPL CLARIFICATION
+-----------------------
+JSAP is Free Software.  The LGPL license is sufficiently
+flexible to allow the use of JSAP in both open source
+and commercial projects.  Using JSAP (by importing JSAP's
+public interfaces in your Java code), and extending JSAP
+(by subclassing) are considered by the authors of JSAP
+to be dynamic linking.  Hence our interpretation of the
+LGPL is that the use of the unmodified JSAP source or
+binary, or the rebundling of unmodified JSAP classes into
+your program's .jar file, does not affect the license of 
+your application code.
+
+If you modify JSAP and redistribute your modifications,
+the LGPL applies. 
+
+(based upon similar clarification at http://www.hibernate.org/ )
diff --git a/build.xml b/build.xml
new file mode 100644
index 0000000..50dd0f7
--- /dev/null
+++ b/build.xml
@@ -0,0 +1,320 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+==========================================================================
+
+JSAP build file
+
+Author:
+ Marty Lamb
+ http://www.martiansoftware.com/contact.html
+
+Legal:
+ Copyright (c) 2002-2006, Martian Software, Inc.
+ This file is made available under the LGPL as described in the accompanying
+ LICENSE.TXT file.
+
+==============================================================================
+ 
+--> 
+<project basedir="." default="help" name="jsap">
+
+
+
+	<!-- PROPERTIES ***************************************************************************************** -->
+
+	<!-- Basics -->
+   	<property name="name" value="JSAP" />
+    <property name="version" value="2.1"/>
+    <property name="year" value="2002-2006"/>
+
+	<!-- Documentation -->
+	<property name="j2se.apiurl" value="http://java.sun.com/j2se/1.4/docs/api/"/>
+	<property name="xstream.apiurl" value="http://xstream.codehaus.org/javadoc/"/>
+  
+	<!-- Directories -->
+	<property name="buildfile" value="build.xml"/>
+    <property name="src" value="src"/>
+    <property name="javasrc" value="${src}/java"/>
+    <property name="build" value="build"/>
+    <property name="doc" value="doc"/>
+    <property name="javadoc" value="${doc}/javadoc"/>
+	<property name="javadoc.tmp.src" value="${javadoc}/tmpsrc"/>
+    <property name="dist" value="dist"/>
+    <property name="lib" value="lib"/>
+	
+	<!-- Special Tasks -->
+	<taskdef
+    	name="snip"
+     	classname="com.martiansoftware.snip.Snip" 
+     	classpath="${lib}/snip-0.11.jar"/>
+
+	<taskdef
+    	name="rundoc"
+     	classname="com.martiansoftware.rundoc.RunDoc" 
+     	classpath="${lib}/rundoc-0.11.jar"/>
+
+	<!-- Special Files -->
+    <property name="testFiles" value="**/Test*" />
+    <property name="exampleFiles" value="com/martiansoftware/jsap/examples/" />
+    <property name="manualFiles" value="manual" />
+    <property name="jsapXmlFiles" value="**/*.jsap" />
+	
+    <property name="jarname" value="${name}-${version}.jar" />
+
+	<!-- Administrivia -->
+	<property name="url" value="http://www.martiansoftware.com/jsap" />
+    <property name="copyrightowners" value="Martian Software, Inc." />
+    <property name="copyright.html" value="Copyright &copy; ${year}, ${copyrightowners}.  All Rights Reserved." />
+	<property name="updates.html" value="For the latest version and documentation, please visit <a href='${url}'>${url}</a>" />
+	<!-- END OF PROPERTIES ********************************************************************************** -->
+
+	<!-- required for build to work in eclipse -->
+	<target name="eclipse-setcompiler" if="eclipse.running">
+	    <property name="build.compiler" value="org.eclipse.jdt.core.JDTCompilerAdapter" />
+	</target>
+
+	<!-- ensure required directories exist -->
+    <target name="init" depends="eclipse-setcompiler">
+        <mkdir dir="${build}"/>
+        <mkdir dir="${javadoc}"/>
+        <mkdir dir="${dist}"/>
+        <!-- Who knows... we might need this for something. -->
+        <tstamp/>
+    </target>
+
+	<!-- jars required for build -->
+	<path id="lib.class.path">
+		<pathelement location="${lib}/ant.jar"/>
+		<pathelement location="${lib}/junit.jar"/>
+		<pathelement location="${lib}/xstream-1.1.2.jar"/>
+	</path>
+
+	<!-- print target list -->
+	<target name="help">
+		<echo>
+
+----------------------------------------------------------------------
+
+Targets:
+
+  clean         - delete all intermediate & compiled files
+  compile-api   - compiles the JSAP API
+  compile-tests - compiles the JSAP API and JUnit test cases
+  compile-all	- compiles the JSAP API, JUnit test cases, and examples
+  test          - compiles the JSAP API and tests it
+  jar           - compiles the JSAP API and builds JSAP.jar
+  javadoc       - compiles the JSAP API and generates javadoc documentation
+  dist			- compiles the JSAP API, builds the jar file, runs javadoc, and
+  				  creates source and binary distribution files.
+
+----------------------------------------------------------------------
+		</echo>
+	</target>
+
+	<!-- delete all intermediate files -->
+    <target name="clean" depends="init">
+        <delete dir="${build}" verbose="true"/>
+        <delete dir="${javadoc}" verbose="true"/>
+        <delete dir="${dist}" verbose="true"/>
+        <mkdir dir="${build}"/>
+        <mkdir dir="${javadoc}"/>
+        <mkdir dir="${dist}"/>
+    </target>
+    
+    <!-- compile the JSAP API -->
+    <target name="compile-api" depends="init" >
+        <javac target="1.4" source="1.4" destdir="${build}" srcdir="${javasrc}">
+<!--        	<exclude name="${testFiles}" />
+        	<exclude name="${exampleFiles}" /> -->
+        	<classpath refid="lib.class.path" />
+        </javac>
+        <copy todir="${build}">
+    		<fileset dir="${javasrc}">
+    			<include name="${jsapXmlFiles}" />
+			</fileset>
+  		</copy>
+    </target>
+
+	<!-- compile the JSAP API and JUnit tests -->
+    <target name="compile-tests" depends="init" >
+        <javac target="1.4" source="1.4" destdir="${build}" srcdir="${javasrc}">
+<!--        	<exclude name="${exampleFiles}" /> -->
+        	<classpath refid="lib.class.path" />
+        </javac>
+        <copy todir="${build}">
+    		<fileset dir="${javasrc}">
+    			<include name="${testConfFiles}" />
+			</fileset>
+  		</copy>
+    </target>
+
+	<!-- compile the JSAP API, JUnit tests, and examples -->
+    <target name="compile-all" depends="init" >
+        <javac target="1.4" source="1.4" destdir="${build}" srcdir="${javasrc}">
+        	<classpath refid="lib.class.path" />
+        </javac>
+    </target>
+
+	<!-- run the JUnit tests -->
+	<target name="test" depends="compile-tests" >
+        <junit printsummary="on">
+            <test name="com.martiansoftware.jsap.TestAll"/>
+            <formatter type="plain" usefile="false"/>
+            <classpath refid="lib.class.path" />
+            <classpath>
+                <pathelement location="${build}"/>
+            </classpath>
+        </junit>
+    </target>
+
+	<!-- make the jar -->
+    <target name="jar" depends="clean,test">
+        <mkdir dir="${dist}"/>
+        <jar basedir="${build}" jarfile="${dist}/${jarname}">
+        	<exclude name="${testFiles}" />
+        	<exclude name="${exampleFiles}" />
+        	<exclude name="${manualFiles}" />
+        </jar>
+    </target> 
+
+	<!-- generate javadocs -->
+    <target name="javadoc" depends="compile-api">
+    	<delete dir="${javadoc}"/>
+        <mkdir dir="${javadoc}"/>
+
+        <javadoc 
+        	public="true" 
+        	destdir="${javadoc}" 
+        	sourcepath="${javasrc}" 
+        	packagenames="com.martiansoftware.*"
+        	windowtitle="JSAP: the Java Simple Argument Parser"
+        	classpathref="lib.class.path">
+        	
+			<link href="${j2se.apiurl}"/>
+			<link href="${xstream.apiurl}"/>
+			<bottom>${copyright.html}<br>${updates.html}</bottom>
+			
+        </javadoc>
+    </target>
+
+	<!-- generate manual -->
+	<target name="manual" depends="compile-all">
+		<snip xmlsafe="true">
+     		<fileset dir="${javasrc}" includes="**/*.java" />
+     		<fileset dir="${javasrc}" includes="**/*.jsap" />
+ 		</snip>
+ 		<mkdir dir="${build}/manual"/>
+		<copy encoding="ISO-8859-1" outputencoding="ISO-8859-1" overwrite="true" verbose="true" file="${src}/doc/manual.xml" todir="${build}/manual">
+			<filterset>
+    			<filter token="Manual_HelloWorld_1" value="${snip.Manual_HelloWorld_1}"/>
+    			<filter token="Manual_HelloWorld_2" value="${snip.Manual_HelloWorld_2}"/>
+    			<filter token="Manual_HelloWorld_3" value="${snip.Manual_HelloWorld_3}"/>
+    			<filter token="Manual_HelloWorld_4" value="${snip.Manual_HelloWorld_4}"/>
+    			<filter token="Manual_HelloWorld_5" value="${snip.Manual_HelloWorld_5}"/>
+    			<filter token="Manual_HelloWorld_6" value="${snip.Manual_HelloWorld_6}"/>
+    			<filter token="Manual_HelloWorld_7" value="${snip.Manual_HelloWorld_7}"/>
+    			<filter token="Manual_HelloWorld_8" value="${snip.Manual_HelloWorld_8}"/>
+        		<filter token="Manual_HelloWorld_9" value="${snip.Manual_HelloWorld_9}"/>
+        		<filter token="Manual_HelloWorld_Simple" value="${snip.Manual_HelloWorld_Simple}"/>
+        		<filter token="Manual_HelloWorld_9_jsap" value="${snip.Manual_HelloWorld_9_jsap}"/>
+  			</filterset>
+  		</copy>
+
+ 		<rundoc prompt="[mlamb at morbo]$ " format="docbook">
+       		<fileset file="${build}/manual/manual.xml"/>
+       		<env key="CLASSPATH" value="${build}:${lib}/xstream-1.1.2.jar"/>
+ 			<env key="LANG" value="en_US.iso88591"/>
+		</rundoc>
+
+		<!-- anyone want to write a non-platform-specific task to do this? -->
+ 		<exec dir="${build}/manual" executable="xmlto">
+ 			<arg value="html"/>
+ 			<arg value="manual.xml"/>
+ 		</exec>
+		
+  		<replace encoding="ISO-8859-1" dir="${build}/manual" includes="*.html"
+			token="<head>"
+			value="<head><link rel='stylesheet' type='text/css' href='manual.css'/>" />
+			
+  		<replace encoding="ISO-8859-1" dir="${build}/manual" includes="*.html"
+			token="&#x09;"
+			value="    " />
+			
+  		<replace encoding="ISO-8859-1" dir="${build}/manual" includes="*.html"
+			token="&#xc2;"
+			value="" />
+
+  			<replace encoding="ISO-8859-1" dir="${build}/manual" includes="*.html"
+			token=" "
+			value="&nbsp;" />
+
+		<copy overwrite="true" verbose="true" todir="${doc}">
+			<fileset dir="${build}/manual" includes="*.html"/>
+		</copy>
+		<copy overwrite="true" verbose="true" file="${src}/doc/manual.css" todir="${doc}"/>
+ 		
+	</target>
+	
+	<!-- build distributable files -->
+	<target name="dist" depends="jar,javadoc,manual">
+	
+		<!-- copy files to a temp directory for packaging -->
+		<property name="distimage" value="${dist}/${name}-${version}"/>
+		<property name="distimage.short" value="${name}-${version}"/>
+		<delete dir="${distimage}" />
+		<mkdir dir="${distimage}" />
+		<copy todir="${distimage}" file="${buildfile}" />
+		<copy todir="${distimage}" file="LICENSE.TXT" />
+		<copy todir="${distimage}" file="src/doc/CHANGELOG.TXT" />
+
+		<!-- copy source files into temp directory -->
+		<mkdir dir="${distimage}/${src}"/>
+		<copy todir="${distimage}/${src}">
+			<fileset dir="${src}" />
+		</copy>
+
+		<!-- copy jar files into temp directory -->
+		<mkdir dir="${distimage}/${lib}"/>
+		<copy todir="${distimage}/${lib}" file="${dist}/${jarname}" />
+		<copy todir="${distimage}/${lib}">
+			<fileset dir="${lib}" />
+		</copy>
+		
+		<!-- copy documentation into temp directory -->
+		<mkdir dir="${distimage}/${doc}"/>
+		<copy todir="${distimage}/${doc}">
+			<fileset dir="${doc}">
+				<exclude name="**/src/**"/>
+			</fileset>
+		</copy>
+
+		<!-- make the source zip file -->
+		<zip destfile="${dist}/${name}-${version}-src.zip" basedir="${dist}" includes="${distimage.short}/**" update="false" />
+		<tar tarfile="${dist}/${name}-${version}-src.tar" basedir="${dist}" includes="${distimage.short}/**"/>
+		<gzip zipfile="${dist}/${name}-${version}-src.tar.gz" src="${dist}/${name}-${version}-src.tar"/>
+		
+		<!-- delete the buildfile, source files and development libaries from the temp directory. -->
+		<!-- the binary distribution file should just contain the jar and the documentation. -->
+		<delete file="${distimage}/${buildfile}" />
+		<delete dir="${distimage}/${src}" />
+		<delete>
+			<fileset dir="${distimage}/${lib}" excludes="${jarname}" />
+		</delete>
+
+		<!-- make the binary zip file -->
+		<zip destfile="${dist}/${name}-${version}.zip" basedir="${dist}" includes="${distimage.short}/**" update="false" />
+		<tar tarfile="${dist}/${name}-${version}.tar" basedir="${dist}" includes="${distimage.short}/**"/>
+		<gzip zipfile="${dist}/${name}-${version}.tar.gz" src="${distimage}.tar"/>
+
+		<!-- delete the tar files that are now gzipped -->
+		<delete>
+			<fileset dir="${dist}" includes="*.tar" />
+		</delete>
+<!--		<delete dir="${distimage}"/> -->
+	</target>
+
+	<!-- build distributable files from scratch -->
+	<target name="cleandist" depends="clean, dist">
+	</target>
+	
+</project>
diff --git a/src/doc/CHANGELOG.TXT b/src/doc/CHANGELOG.TXT
new file mode 100644
index 0000000..868c695
--- /dev/null
+++ b/src/doc/CHANGELOG.TXT
@@ -0,0 +1,52 @@
+v2.0a (July 23, 2005)
+
+ * Fixed an embarrassing bug that resulted in unexpected characters
+   in auto-generated help output.
+   
+ * Fixed bugs in FileStringParser that prevented enforcement of
+   mustExist(), mustBeFile(), and mustBeDirectory()
+   
+v2.0 (June 21, 2005)
+
+ * Fixed setHelp() methods to return a reference to "this", consistent
+   with other setters.
+   
+ * New layout for option help to avoid problems with excessively long
+   option descriptions
+
+ * Added constructors with built-in help text setters.
+ 
+ * Added SimpleJSAP class to speed up common configurations.
+ 
+ * Added unbreakable spaces between flags and arguments in auto-generated
+   usage text.  
+
+ * Added IntSizeStringParser and LongSizeStringParser to allow numeric
+   parameters with SI suffixes (e.g., "10K" returns an Integer or Long
+   with value 10,000.  Currently supported suffixes include K, M, G, T, 
+   P, Ki, Mi, Gi, Ti, Pi.
+   
+ * Added ForNameStringParser that takes a class name and returns an object
+   constructed via class.forName().
+   
+ * Added singleton instances of most StringParsers to avoid unnecessary
+   object creation.  Singletons are available as public static fields of
+   JSAP, e.g., JSAP.BIG_DECIMAL_PARSER.
+   
+ * Default values for parameters are now indicated in auto-generated help.
+
+ * Added no-space short options, a la GNU getopt.  "-b5" is equivalent to
+   "-b 5"
+   
+ * Several minor bugfixes and documentation corrections.
+
+ * Added JSAPResult.contains(String id) to determine if the result contains
+   any values at all (including default values) for the specified parameter.
+   
+ * Added JSAPResult.userSpecified(String id) to determine if the result
+   contains a USER-SPECIFIED value (i.e., not including default values)
+   for the specified parameter.
+   
+ * Added XML support for JSAP configuration (experimental).
+ 
+ * Brought JIRA bug tracker online at http://jira.martiansoftware.com.
\ No newline at end of file
diff --git a/src/doc/manual.css b/src/doc/manual.css
new file mode 100644
index 0000000..86fe0fa
--- /dev/null
+++ b/src/doc/manual.css
@@ -0,0 +1,61 @@
+body { background-image: url("/images/wave.png"); color: black; background: white;  }
+
+.blue { color: #33275b; background: white }
+.green { color: #80c254; background: white; }
+.green-on-blue { color: #80c254; background: #33275b; }
+
+a:link { color: #33275b; }
+a:link img, a:visited img { border-style: none }
+a:visited { color: #33275b; }
+
+pre.programlisting { 
+	font-size: 9pt; 
+	margin: 1em 1em 1em 1em; 
+	padding: 0 1em 0 1em; 
+	border: 1px solid #80c254; 
+	font-family: courier, monospace; 
+	color: #33275b; 
+	background: #ffffff;
+}
+
+pre.screen { 
+	font-size: 9pt;
+	white-space:pre; 
+	padding: 0 1em 0 1em; 
+	border: 1px solid #80c254; 
+	font-family: courier, monospace; 
+	color: #ffffff; 
+	background: #000000;
+}
+
+b.command {
+	white-space: normal;
+}
+
+div.tip, div.note, div.caution, div.warning {
+	background-repeat: no-repeat;
+	background: #e3f4b0;
+	border: 1px solid #80c254; 
+	margin-top: 0.5em;
+	margin-bottom: 0.5em;
+}
+
+div.tip h3.title, div.note h3.title, div.caution h3.title, div.warning h3.title {
+	color: #33275b;
+	font-family: helvetica, sans-serif;
+	margin-left: 1em;
+	margin-right: 1em;
+	margin-top: 0.5em;
+}
+
+div.tip p, div.note p, div.caution p, div.warning p {
+	color: #33275b;
+	margin-left: 1em;
+	margin-right: 1em;
+}
+
+p {margin: 0 0 1em; font-family: Book Antiqua, New York, New Century Schoolbook, Times New Roman, Times, serif; }
+li { font-family: Book Antiqua, New York, New Century Schoolbook, Times New Roman, Times, serif; }
+h1 { margin: 1em 0 0; font-size: 150%; font-family: helvetica, sans-serif; color: #33275b; background: white; font-weight: normal; border-bottom: 2px solid #80c254 }
+h2 { margin: 2em 0 0; font-size: 125%; font-family: helvetica, sans-serif; color: #33275b; background: white; font-weight: normal; border-bottom: 2px solid #80c254 }
+hr { color: #80c254; background: white; }
diff --git a/src/java/com/martiansoftware/jsap/CommandLineTokenizer.java b/src/java/com/martiansoftware/jsap/CommandLineTokenizer.java
new file mode 100644
index 0000000..0af56fd
--- /dev/null
+++ b/src/java/com/martiansoftware/jsap/CommandLineTokenizer.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2002-2004, Martian Software, Inc.
+ * This file is made available under the LGPL as described in the accompanying
+ * LICENSE.TXT file.
+ */
+
+package com.martiansoftware.jsap;
+
+import java.util.List;
+/**
+ * <p>A utility class to parse a command line contained in a single String into
+ * an array of argument tokens, much as the JVM (or more accurately, your
+ * operating system) does before calling your programs' <code>public static
+ * void main(String[] args)</code>
+ * methods.</p>
+ *
+ * <p>This class has been developed to parse the command line in the same way
+ * that MS Windows 2000 does.  Arguments containing spaces should be enclosed
+ * in quotes. Quotes that should be in the argument string should be escaped
+ * with a preceding backslash ('\') character.  Backslash characters that
+ * should be in the argument string should also be escaped with a preceding
+ * backslash character.</p>
+ *
+ * Whenever <code>JSAP.parse(String)</code> is called, the specified String is
+ * tokenized by this class, then forwarded to <code>JSAP.parse(String[])</code>
+ * for further processing.
+ *
+ * @author <a href="http://www.martiansoftware.com/contact.html">Marty Lamb</a>
+ * @see com.martiansoftware.jsap.JSAP#parse(String)
+ * @see com.martiansoftware.jsap.JSAP#parse(String[])
+ */
+public class CommandLineTokenizer {
+
+    /**
+     * Hide the constructor.
+     */
+    private CommandLineTokenizer() {
+    }
+
+    /**
+     * Goofy internal utility to avoid duplicated code.  If the specified
+     * StringBuffer is not empty, its contents are appended to the resulting
+     * array (temporarily stored in the specified ArrayList).  The StringBuffer
+     * is then emptied in order to begin storing the next argument.
+     * @param resultBuffer the List temporarily storing the resulting
+     * argument array.
+     * @param buf the StringBuffer storing the current argument.
+     */
+    private static void appendToBuffer(
+        List resultBuffer,
+        StringBuffer buf) {
+        if (buf.length() > 0) {
+            resultBuffer.add(buf.toString());
+            buf.setLength(0);
+        }
+    }
+
+    /**
+     * Parses the specified command line into an array of individual arguments.
+     * Arguments containing spaces should be enclosed in quotes.
+     * Quotes that should be in the argument string should be escaped with a
+     * preceding backslash ('\') character.  Backslash characters that should
+     * be in the argument string should also be escaped with a preceding
+     * backslash character.
+     * @param commandLine the command line to parse
+     * @return an argument array representing the specified command line.
+     */
+    public static String[] tokenize(String commandLine) {
+        List resultBuffer = new java.util.ArrayList();
+
+        if (commandLine != null) {
+            int z = commandLine.length();
+            boolean insideQuotes = false;
+            StringBuffer buf = new StringBuffer();
+
+            for (int i = 0; i < z; ++i) {
+                char c = commandLine.charAt(i);
+                if (c == '"') {
+                    appendToBuffer(resultBuffer, buf);
+                    insideQuotes = !insideQuotes;
+                } else if (c == '\\') {
+                    if ((z > i + 1)
+                        && ((commandLine.charAt(i + 1) == '"')
+                            || (commandLine.charAt(i + 1) == '\\'))) {
+                        buf.append(commandLine.charAt(i + 1));
+                        ++i;
+                    } else {
+                        buf.append("\\");
+                    }
+                } else {
+                    if (insideQuotes) {
+                        buf.append(c);
+                    } else {
+                        if (Character.isWhitespace(c)) {
+                            appendToBuffer(resultBuffer, buf);
+                        } else {
+                            buf.append(c);
+                        }
+                    }
+                }
+            }
+            appendToBuffer(resultBuffer, buf);
+
+        }
+
+        String[] result = new String[resultBuffer.size()];
+        return ((String[]) resultBuffer.toArray(result));
+    }
+
+}
diff --git a/src/java/com/martiansoftware/jsap/DefaultSource.java b/src/java/com/martiansoftware/jsap/DefaultSource.java
new file mode 100644
index 0000000..b17a200
--- /dev/null
+++ b/src/java/com/martiansoftware/jsap/DefaultSource.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2002-2004, Martian Software, Inc.
+ * This file is made available under the LGPL as described in the accompanying
+ * LICENSE.TXT file.
+ */
+
+package com.martiansoftware.jsap;
+
+/**
+ * An interface describing an object as being able to produce a set of default
+ * values.
+ * @author <a href="http://www.martiansoftware.com/contact.html">Marty Lamb</a>
+ * @see com.martiansoftware.jsap.Defaults
+ */
+public interface DefaultSource {
+
+    /**
+     * Returns a set of default values given the configuration described by the
+     * specified IDMap.  Any encountered exceptions are stored in the specified
+     * JSAPResult.
+     * @param idMap an IDMap containing JSAP configuration information.
+     * @param exceptionMap the ExceptionMap object within which any Exceptions
+     * will be returned.
+     * @return a set of default values given the configuration described by the
+     * specified IDMap.
+     * @see com.martiansoftware.jsap.ExceptionMap#addException(String,Exception)
+     */
+    Defaults getDefaults(IDMap idMap, ExceptionMap exceptionMap);
+
+}
diff --git a/src/java/com/martiansoftware/jsap/Defaults.java b/src/java/com/martiansoftware/jsap/Defaults.java
new file mode 100644
index 0000000..6b6a180
--- /dev/null
+++ b/src/java/com/martiansoftware/jsap/Defaults.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (c) 2002-2004, Martian Software, Inc.
+ * This file is made available under the LGPL as described in the accompanying
+ * LICENSE.TXT file.
+ */
+
+package com.martiansoftware.jsap;
+
+import java.util.HashMap;
+import java.util.Iterator;
+
+/**
+ * Stores a collection of default values, associated with their respective
+ * parameters by the parameters' unique IDs.
+ *
+ * @author <a href="http://www.martiansoftware.com/contact.html">Marty Lamb</a>
+ */
+public class Defaults {
+
+    /**
+     * Hashmap associating arrays of Strings (containing default values) with
+     * parameter IDs.
+     */
+    private HashMap defaults = null;
+
+    /**
+     *  Creates a new, empty Defaults object.
+     */
+    public Defaults() {
+        defaults = new HashMap();
+    }
+
+    /**
+     * Sets a single default value for the parameter with the specified ID.
+     * This replaces any default values currently associated with the specified
+     * parameter if any exist.
+     * @param paramID the unique ID of the parameter for which the specified
+     * value is the default.
+     * @param defaultValue the new default value for the specified parameter.
+     */
+    public void setDefault(String paramID, String defaultValue) {
+        String[] newArray = new String[1];
+        newArray[0] = defaultValue;
+        setDefault(paramID, newArray);
+    }
+
+    /**
+     * Sets an array of default values for the parameter with the specified ID.
+     * These replace any default values currently associated with the specified
+     * parameter if any exist.
+     * @param paramID the unique ID of the parameter for which the specified
+     * values are the defaults.
+     * @param defaultValue the new default values for the specified parameter.
+     */
+    public void setDefault(String paramID, String[] defaultValue) {
+        defaults.put(paramID, defaultValue);
+    }
+
+    /**
+     * Adds a single default value to any that might already be defined for the
+     * parameter with the the specified ID.
+     * @param paramID the unique ID of the parameter for which the specified
+     * value is an additional default.
+     * @param defaultValue the default value to add to the specified parameter.
+     */
+    public void addDefault(String paramID, String defaultValue) {
+        String[] newArray = new String[1];
+        newArray[0] = defaultValue;
+        addDefault(paramID, newArray);
+    }
+
+    /**
+     * Adds an array of default values to any that might already be defined for
+     * the parameter with the the specified ID.
+     * @param paramID the unique ID of the parameter for which the specified
+     * value is an additional default.
+     * @param defaultValue the default values to add to the specified parameter.
+     */
+    public void addDefault(String paramID, String[] defaultValue) {
+        String[] curDefault = getDefault(paramID);
+        if (curDefault == null) {
+            curDefault = new String[0];
+        }
+        String[] newDefault =
+            new String[curDefault.length + defaultValue.length];
+        int index = 0;
+        for (int i = 0; i < curDefault.length; ++i) {
+            newDefault[index] = curDefault[i];
+            ++index;
+        }
+        for (int i = 0; i < defaultValue.length; ++i) {
+            newDefault[index] = defaultValue[i];
+            ++index;
+        }
+        setDefault(paramID, newDefault);
+    }
+
+    /**
+     * Sets a single default value for the parameter with the specified ID if
+     * and only if the specified parameter currently has no default values.
+     * @param paramID the unique ID of the parameter for which the specified
+     * value is the default.
+     * @param defaultValue the new default value for the specified parameter.
+     */
+    protected void setDefaultIfNeeded(String paramID, String defaultValue) {
+        if (!defaults.containsKey(paramID)) {
+            setDefault(paramID, defaultValue);
+        }
+    }
+
+    /**
+     * Sets an array of default values for the parameter with the specified ID
+     * if and only if the specified parameter currently has no default values.
+     * @param paramID the unique ID of the parameter for which the specified
+     * value is the default.
+     * @param defaultValue the new default values for the specified parameter.
+     */
+    protected void setDefaultIfNeeded(String paramID, String[] defaultValue) {
+        if (!defaults.containsKey(paramID)) {
+            setDefault(paramID, defaultValue);
+        }
+    }
+
+    /**
+     * Returns an array of the default values defined for the parameter with
+     * the specified ID, or null if no default values are defined.
+     * @param paramID the unique ID of the parameter for which default values
+     * are desired.
+     * @return an array of the default values defined for the parameter with
+     * the specified ID, or null if no default values are defined.
+     */
+    public String[] getDefault(String paramID) {
+        return ((String[]) defaults.get(paramID));
+    }
+
+    /**
+     * Returns an Iterator over the unique IDs of all parameters with defaults
+     * defined in this Defaults object.
+     * @return an Iterator over the unique IDs of all parameters with defaults
+     * defined in this Defaults object.
+     * @see java.util.Iterator
+     */
+    public Iterator idIterator() {
+        return (defaults.keySet().iterator());
+    }
+
+}
diff --git a/src/java/com/martiansoftware/jsap/ExceptionMap.java b/src/java/com/martiansoftware/jsap/ExceptionMap.java
new file mode 100644
index 0000000..d04eb4f
--- /dev/null
+++ b/src/java/com/martiansoftware/jsap/ExceptionMap.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2002-2004, Martian Software, Inc.
+ * This file is made available under the LGPL as described in the accompanying
+ * LICENSE.TXT file.
+ */
+
+package com.martiansoftware.jsap;
+
+/**
+ * A class for aggregating exceptions thrown by JSAP's parsing process.  This
+ * class is necessary as it is desirable to have information regarding ALL of
+ * the reasons for a failed parse rather than just the FIRST reason.
+ * @author <a href="http://www.martiansoftware.com/contact.html">Marty Lamb</a>
+ */
+public interface ExceptionMap {
+
+    /**
+     * Adds the specified exception to the exception map.  Exceptions are
+     * keyed by the ID of the parameters with which they are associated.
+     * "General" exceptions not associated with a particular parameter have a
+     * null key.
+     * @param id the unique ID of the parameter with which the specified values
+     * are associated.
+     * @param exception the exception to associate with the specified key.
+     */
+    void addException(String id, Exception exception);
+
+    /**
+     * Returns the first exception associated with the specified parameter ID.
+     * "General" exceptions can be retrieved with a null id.  If no exceptions
+     * are associated with the specified parameter ID, null is returned.
+     * @param id the unique ID of the parameter for which the first exception
+     * is requested
+     * @return the first exception associated with the specified ID, or null if
+     * no exceptions are associated with the specified ID.
+     */
+    Exception getException(String id);
+
+    /**
+     * Returns an array of ALL exceptions associated with the specified
+     * parameter ID. If no exceptions are associated with the specified
+     * parameter ID, an empty (zero-length) array is returned.
+     * @param id the unique ID of the parameter for which the exceptions are
+     * requested.
+     * @return an array of ALL exceptions associated with the specified
+     * parameter ID,
+     * or an empty (zero-length) array if no exceptions are associated with the
+     * specified parameter ID.
+     */
+    Exception[] getExceptionArray(String id);
+
+}
diff --git a/src/java/com/martiansoftware/jsap/Flagged.java b/src/java/com/martiansoftware/jsap/Flagged.java
new file mode 100644
index 0000000..1f2503c
--- /dev/null
+++ b/src/java/com/martiansoftware/jsap/Flagged.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2002-2004, Martian Software, Inc.
+ * This file is made available under the LGPL as described in the accompanying
+ * LICENSE.TXT file.
+ */
+
+package com.martiansoftware.jsap;
+
+/**
+ * <p>Marks an argument as being "flagged" - that is, as having its value on the
+ * command line preceded by an indicator.  Flagged arguments can be preceded by
+ * a "short flag," a hyphen followed by a single character, or a "long flag,"
+ * two hyphens followed by a word.</p>
+ *
+ * <p>For example, a short flag 'i' marking an option as meaning "input file"
+ * might result in a command line that contains the substring "-i myfile.txt" or
+ * "-i=myfile.txt", whereas the same option with a long flag of "inputfile"
+ * might contain a substring such as "--inputfile myfile.txt" or
+ * "--inputfile=myfile.txt"</p>
+ *
+ * Note that the setter methods setShortFlag() and setLongFlag() are not
+ * part of this Interface.  This is because including them would prevent the
+ * setShortFlag() and setLongFlag() methods in FlaggedOption and Switch from
+ * returning references to themselves, which is inconvenient and inconsistent
+ * with the rest of the API.
+ *
+ * @author <a href="http://www.martiansoftware.com/contact.html">Marty Lamb</a>
+ * @see com.martiansoftware.jsap.Switch
+ * @see com.martiansoftware.jsap.FlaggedOption
+ */
+public interface Flagged {
+
+    //    /**
+    //     *  Sets the short flag for this object.
+    //     *  @param shortFlag the new short flag for this object.
+    //     *  @return the modified Flagged object.
+    //     */
+    //    Flagged setShortFlag(char shortFlag);
+
+    /**
+     *  Returns the short flag for this object in the form of a char.
+     *  @return the short flag for this object in the form of a char.
+     */
+    char getShortFlag();
+
+    /**
+     *  Returns the short flag for this object in the form of a Character.
+     *  @return the short flag for this object in the form of a Character.
+     */
+    Character getShortFlagCharacter();
+
+    //    /**
+    //     *  Sets the long flag for this object.
+    //     *  @param longFlag the new long flag for this object.
+    //     *  @return the modified Flagged object.
+    //     */
+    //    Flagged setLongFlag(String longFlag);
+
+    /**
+     *  Returns the long flag for this object.
+     *  @return the long flag for this object.
+     */
+    String getLongFlag();
+
+}
diff --git a/src/java/com/martiansoftware/jsap/FlaggedOption.java b/src/java/com/martiansoftware/jsap/FlaggedOption.java
new file mode 100644
index 0000000..0a220e6
--- /dev/null
+++ b/src/java/com/martiansoftware/jsap/FlaggedOption.java
@@ -0,0 +1,314 @@
+/*
+ * Copyright (c) 2002-2004, Martian Software, Inc.
+ * This file is made available under the LGPL as described in the accompanying
+ * LICENSE.TXT file.
+ */
+
+package com.martiansoftware.jsap;
+
+/**
+ * An option that implements the Flagged interface.  A flagged option is
+ * preceded by a short flag or a long flag; i.e. "-n 5" or "--number 5".
+ * FlaggedOptions also provide an additional features over unflagged
+ * options, namely the ability to be declared more than once in a command line
+ * (e.g., "-n 5 -n 10").  This is not possible with unflagged options, as they
+ * are never declared.
+ * @author <a href="http://www.martiansoftware.com/contact.html">Marty Lamb</a>
+ * @see com.martiansoftware.jsap.Flagged
+ * @see com.martiansoftware.jsap.Option
+ */
+public class FlaggedOption extends Option implements Flagged {
+
+    /**
+     * The current short flag for this FlaggedOption.  Default is
+     * JSAP.NO_SHORTFLAG.
+     */
+    private char shortFlag = JSAP.NO_SHORTFLAG;
+
+    /**
+     * The current long flag for this FlaggedOption.  Default is
+     * JSAP.NO_LONGLAG.
+     */
+    private String longFlag = JSAP.NO_LONGFLAG;
+
+    /**
+     * If true, this FlaggedOption may be declared more than once on a
+     * command line.
+     * Default is JSAP.NO_MULTIPLEDECLARATIONS.
+     */
+    private boolean allowMultipleDeclarations = JSAP.NO_MULTIPLEDECLARATIONS;
+
+    /**
+     * Creates a new FlaggedOption with the specified unique ID.
+     * @param id the unique ID for this FlaggedOption.
+     */
+    public FlaggedOption(String id) {
+        super(id);
+    }
+
+    /**
+     * A shortcut constructor that creates a new FlaggedOption and configures
+     * its most commonly used settings, including help.
+     * @param id the unique ID for this FlaggedOption.
+     * @param stringParser the StringParser this FlaggedOption should use.
+     * @param defaultValue the default value for this FlaggedOption (may be
+     * null).
+     * @param required if true, this FlaggedOption is required.
+     * @param shortFlag the short flag for this option (may be set to
+     * JSAP.NO_SHORTFLAG for none).
+     * @param longFlag the long flag for this option (may be set to
+     * JSAP.NO_LONGFLAG for none).
+     * @param help the help text for this option (may be set to {@link JSAP#NO_HELP} for none).
+     */
+    public FlaggedOption(
+        String id,
+        StringParser stringParser,
+        String defaultValue,
+        boolean required,
+        char shortFlag,
+        String longFlag,
+		String help) {
+        this(id);
+        setStringParser(stringParser);
+        setDefault(defaultValue);
+        setShortFlag(shortFlag);
+        setLongFlag(longFlag);
+        setRequired(required);
+        setHelp(help);
+    }
+
+    /**
+     * A shortcut constructor that creates a new FlaggedOption and configures
+     * its most commonly used settings.
+     * @param id the unique ID for this FlaggedOption.
+     * @param stringParser the StringParser this FlaggedOption should use.
+     * @param defaultValue the default value for this FlaggedOption (may be
+     * null).
+     * @param required if true, this FlaggedOption is required.
+     * @param shortFlag the short flag for this option (may be set to
+     * JSAP.NO_SHORTFLAG for none).
+     * @param longFlag the long flag for this option (may be set to
+     * JSAP.NO_LONGFLAG for none).
+     */
+    public FlaggedOption(
+        String id,
+        StringParser stringParser,
+        String defaultValue,
+        boolean required,
+        char shortFlag,
+        String longFlag) {
+        this(id, stringParser, defaultValue, required, shortFlag, longFlag, JSAP.NO_HELP);
+    }
+
+
+    /**
+     * Sets the short flag for this FlaggedOption.  To use no short flag at all,
+     * set the value to JSAP.NO_SHORTFLAG.
+     * @param shortFlag the short flag for this FlaggedOption.
+     * @return the modified FlaggedOption
+     */
+    public FlaggedOption setShortFlag(char shortFlag) {
+        enforceParameterLock();
+        this.shortFlag = shortFlag;
+        return (this);
+    }
+
+    /**
+     * Returns the short flag for this FlaggedOption.  If this FlaggedOption
+     * has no short flag, the
+     * return value will be equal to JSAP.NO_SHORTFLAG.
+     * @return the short flag for this FlaggedOption.  If this FlaggedOption
+     * has no short flag, the
+     * return value will be equal to JSAP.NO_SHORTFLAG.
+     */
+    public char getShortFlag() {
+        return (shortFlag);
+    }
+
+    /**
+     * Returns the short flag for this FlaggedOption.  If this FlaggedOption
+     * has no short flag, the
+     * return value will be null.
+     * @return the short flag for this FlaggedOption.  If this FlaggedOption
+     * has no short flag, the
+     * return value will be null.
+     */
+    public Character getShortFlagCharacter() {
+        return (
+            (shortFlag == JSAP.NO_SHORTFLAG) ? null : new Character(shortFlag));
+    }
+
+    /**
+     * Sets the long flag for this FlaggedOption.  To use no long flag at all,
+     * set the value to JSAP.NO_LONGFLAG.
+     * @param longFlag the long flag for this FlaggedOption.
+     * @return the modified FlaggedOption
+     */
+    public FlaggedOption setLongFlag(String longFlag) {
+        enforceParameterLock();
+        this.longFlag = longFlag;
+        return (this);
+    }
+
+    /**
+     * Sets the name that will be displayed when getSyntax() is called
+     * @param usageName the name to use, or null if the id should be used (default)
+     * @return the modified FlaggedOption
+     */
+    public FlaggedOption setUsageName(String usageName) {
+    	_setUsageName(usageName);
+    	return (this);
+    }
+    
+    /**
+     * Returns the long flag for this FlaggedOption.  If this FlaggedOption has
+     * no long flag, the return
+     * value will be equal to JSAP.NO_LONGFLAG.
+     * @return the long flag for this FlaggedOption.  If this FlaggedOption has
+     * no long flag, the return
+     * value will be equal to JSAP.NO_LONGFLAG.
+     */
+    public String getLongFlag() {
+        return (longFlag);
+    }
+
+    /**
+     * <p>Sets this FlaggedOption to allow or disallow multiple declarations.
+     * If multiple declarations are allowed,
+     * the flag may be specified multiple times on the command line (e.g.,
+     * "-n 5 -n 10").  All of the results
+     * are aggregated in the resulting JSAPResult.</p>
+     *
+     * <p>Default behavior is to disallow multiple declarations.</p>
+     * @param allowMultipleDeclarations if true, multiple declarations are
+     * allowed.
+     * @return the modified FlaggedOption
+     */
+    public FlaggedOption setAllowMultipleDeclarations(
+        boolean allowMultipleDeclarations) {
+        enforceParameterLock();
+        this.allowMultipleDeclarations = allowMultipleDeclarations;
+        return (this);
+    }
+
+    /**
+     * Returns a boolean indicating whether multiple declarations are allowed
+     * for this FlaggedOption.
+     * @return a boolean indicating whether multiple declarations are allowed
+     * for this FlaggedOption.
+     * @see #setAllowMultipleDeclarations(boolean)
+     */
+    public boolean allowMultipleDeclarations() {
+        return (allowMultipleDeclarations);
+    }
+
+    /**
+     * Returns syntax instructions for this FlaggedOption.
+     * @return syntax instructions for this FlaggedOption based upon its current
+     * configuration.
+     */
+    public String getSyntax() {
+        StringBuffer result = new StringBuffer();
+        if (!required()) {
+            result.append("[");
+        }
+
+        if ((getLongFlag() != JSAP.NO_LONGFLAG)
+            || (getShortFlag() != JSAP.NO_SHORTFLAG)) {
+            if (getLongFlag() == JSAP.NO_LONGFLAG) {
+                result.append("-" + getShortFlag() + JSAP.SYNTAX_SPACECHAR);
+            } else if (getShortFlag() == JSAP.NO_SHORTFLAG) {
+                result.append("--" + getLongFlag() + JSAP.SYNTAX_SPACECHAR);
+            } else {
+                result.append(
+                    "(-" + getShortFlag() + "|--" + getLongFlag() + ")" + JSAP.SYNTAX_SPACECHAR);
+            }
+        }
+        String un = getUsageName();
+        char sep = this.getListSeparator();
+        if (this.isList()) {
+            result.append(
+                un + "1" + sep + un + "2" + sep + "..." + sep + un + "N ");
+        } else {
+            result.append("<" + getUsageName() + ">");
+        }
+        if (!required()) {
+            result.append("]");
+        }
+        return (result.toString());
+    }
+
+    /**
+     * Sets whether this FlaggedOption is a list.  Default behavior is
+     * JSAP.NOT_LIST.
+     * @param isList if true, this Option is a list.
+     * @return the modified FlaggedOption
+     */
+    public FlaggedOption setList(boolean isList) {
+        super.internalSetList(isList);
+        return (this);
+    }
+
+    /**
+     * Sets the list separator character for this FlaggedOption.  The default
+     * list separator is JSAP.DEFAULT_LISTSEPARATOR.
+     * @param listSeparator the list separator for this Option.
+     * @return the modified FlaggedOption
+     */
+    public FlaggedOption setListSeparator(char listSeparator) {
+        super.internalSetListSeparator(listSeparator);
+        return (this);
+    }
+
+    /**
+     * Sets whether this FlaggedOption is required.  Default is
+     * JSAP.NOT_REQUIRED.
+     * @param required if true, this Option will be required.
+     * @return the modified FlaggedOption
+     */
+    public FlaggedOption setRequired(boolean required) {
+        super.internalSetRequired(required);
+        return (this);
+    }
+
+    /**
+     * Sets one or more default values for this parameter.  This method
+     * should be used whenever a parameter has more than one default
+     * value.
+     * @param defaultValues the default values for this parameter.
+     * @see #setDefault(String)
+     */
+    public FlaggedOption setDefault(String[] defaultValues) {
+    	_setDefault(defaultValues);
+    	return (this);
+    }
+    
+    /**
+     * Sets a default value for this parameter.  The default is specified
+     * as a String, and is parsed as a single value specified on the
+     * command line.  In other words, default values for "list"
+     * parameters or parameters allowing multiple declarations should be
+     * set using setDefault(String[]), as JSAP
+     * would otherwise treat the entire list of values as a single value.
+     *
+     * @param defaultValue the default value for this parameter.
+     * @see #setDefault(String)
+     */
+    public FlaggedOption setDefault(String defaultValue) {
+    	_setDefault(defaultValue);
+    	return (this);
+    }    
+    
+    /**
+     * Sets the StringParser to which this FlaggedOption's parse() method
+     * should delegate.
+     * @param stringParser the StringParser to which this Option's parse()
+     * method should delegate.
+     * @see com.martiansoftware.jsap.StringParser
+     * @return the modified FlaggedOption
+     */
+    public FlaggedOption setStringParser(StringParser stringParser) {
+        super.internalSetStringParser(stringParser);
+        return (this);
+    }
+}
diff --git a/src/java/com/martiansoftware/jsap/IDMap.java b/src/java/com/martiansoftware/jsap/IDMap.java
new file mode 100644
index 0000000..5e0326d
--- /dev/null
+++ b/src/java/com/martiansoftware/jsap/IDMap.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2002-2004, Martian Software, Inc.
+ * This file is made available under the LGPL as described in the accompanying
+ * LICENSE.TXT file.
+ */
+
+package com.martiansoftware.jsap;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A utility class to allow lookups of parameter IDs by short flag or long flag.
+ * This class is used by DefaultSource in order to populate Defaults objects.
+ *
+ * @author <a href="http://www.martiansoftware.com/contact.html">Marty Lamb</a>
+ * @see com.martiansoftware.jsap.Flagged
+ * @see com.martiansoftware.jsap.DefaultSource
+ * @see com.martiansoftware.jsap.Defaults
+ */
+public class IDMap {
+
+    /**
+     * A list of the unique IDs of all the parameters stored in this IDMap.
+     */
+    private List ids = null;
+
+    /**
+     * A Map associating short flags with parameter IDs.
+     */
+    private Map byShortFlag = null;
+
+    /**
+     * A Map associating long flags with parameter IDs.
+     */
+    private Map byLongFlag = null;
+
+    /**
+     * Creates a new IDMap.
+     * @param ids a List of the unique IDs of all the parameters to store
+     * in this IDMap.
+     * @param byShortFlag a Map with keys equal to the short flags of the
+     * parameters (as Character objects),
+     * and values equal to the unique IDs of the parameters associated with
+     * those short flags.
+     * @param byLongFlag a Map with keys equal to the long flags of the
+     * parameters (as Strings),
+     * and values equal to the unique IDs of the parameters associated with
+     * those short flags.
+     */
+    public IDMap(List ids, Map byShortFlag, Map byLongFlag) {
+        this.ids = new java.util.ArrayList(ids);
+        this.byShortFlag = new java.util.HashMap(byShortFlag);
+        this.byLongFlag = new java.util.HashMap(byLongFlag);
+    }
+
+    /**
+     * Returns an Iterator over all parameter IDs stored in this IDMap.
+     * @return an Iterator over all parameter IDs stored in this IDMap.
+     * @see java.util.Iterator
+     */
+    public Iterator idIterator() {
+        return (ids.iterator());
+    }
+
+    /**
+     * Returns true if the specified ID is stored in this IDMap, false if not.
+     * @param id the id to search for in this IDMap
+     * @return true if the specified ID is stored in this IDMap, false if not.
+     */
+    public boolean idExists(String id) {
+        return (ids.contains(id));
+    }
+
+    /**
+     * Returns the unique ID of the parameter with the specified short flag, or
+     * null if the specified short flag is not defined in this IDMap.
+     * @param c the short flag to search for in this IDMap.
+     * @return the unique ID of the parameter with the specified short flag, or
+     * null if the specified short flag is not defined in this IDMap.
+     */
+    public String getIDByShortFlag(Character c) {
+        return ((String) byShortFlag.get(c));
+    }
+
+    /**
+     * Returns the unique ID of the parameter with the specified short flag, or
+     * null if the specified short flag is not defined in this IDMap.
+     * @param c the short flag to search for in this IDMap.
+     * @return the unique ID of the parameter with the specified short flag, or
+     * null if the specified short flag is not defined in this IDMap.
+     */
+    public String getIDByShortFlag(char c) {
+        return (getIDByShortFlag(new Character(c)));
+    }
+
+    /**
+     * Returns the unique ID of the parameter with the specified long flag, or
+     * null if the specified long flag is not defined in this IDMap.
+     * @param s the long flag to search for in this IDMap.
+     * @return the unique ID of the parameter with the specified long flag, or
+     * null if the specified long flag is not defined in this IDMap.
+     */
+    public String getIDByLongFlag(String s) {
+        return ((String) byLongFlag.get(s));
+    }
+}
diff --git a/src/java/com/martiansoftware/jsap/IllegalMultipleDeclarationException.java b/src/java/com/martiansoftware/jsap/IllegalMultipleDeclarationException.java
new file mode 100644
index 0000000..d8a7247
--- /dev/null
+++ b/src/java/com/martiansoftware/jsap/IllegalMultipleDeclarationException.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2002-2004, Martian Software, Inc.
+ * This file is made available under the LGPL as described in the accompanying
+ * LICENSE.TXT file.
+ */
+
+package com.martiansoftware.jsap;
+
+/**
+ * An exception indicating that a parameter has illegally been declared multiple
+ * times.
+ *
+ * @see
+ *  com.martiansoftware.jsap.FlaggedOption#setAllowMultipleDeclarations(boolean)
+ * @author <a href="http://www.martiansoftware.com/contact.html">Marty Lamb</a>
+ * @see com.martiansoftware.jsap.Flagged
+ * @see com.martiansoftware.jsap.Option
+ */
+public class IllegalMultipleDeclarationException extends JSAPException {
+
+    /**
+     * The unique ID of the parameter that was illegally declared more than
+     * once.
+     */
+    private String id = null;
+
+    /**
+     * Creates a new IllegalMultipleDeclarationException referencing the
+     * specified parameter.
+     * @param paramID the unique ID of the parameter that was illegally declared
+     * more than once.
+     */
+    public IllegalMultipleDeclarationException(String paramID) {
+        super("Parameter '" + paramID + "' cannot be declared more than once.");
+        this.id = paramID;
+    }
+
+    /**
+     * Returns the unique ID of the parameter that was illegally declared more
+     * than once.
+     * @return the unique ID of the parameter that was illegally declared more
+     * than once.
+     */
+    public String getID() {
+        return (this.id);
+    }
+
+}
diff --git a/src/java/com/martiansoftware/jsap/JSAP.java b/src/java/com/martiansoftware/jsap/JSAP.java
new file mode 100644
index 0000000..0b6162d
--- /dev/null
+++ b/src/java/com/martiansoftware/jsap/JSAP.java
@@ -0,0 +1,862 @@
+/*
+ * Copyright (c) 2002-2004, Martian Software, Inc.
+ * This file is made available under the LGPL as described in the accompanying
+ * LICENSE.TXT file.
+ */
+
+package com.martiansoftware.jsap;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.Map;
+import java.util.List;
+import java.util.Iterator;
+
+import com.martiansoftware.jsap.stringparsers.BigDecimalStringParser;
+import com.martiansoftware.jsap.stringparsers.BigIntegerStringParser;
+import com.martiansoftware.jsap.stringparsers.BooleanStringParser;
+import com.martiansoftware.jsap.stringparsers.ByteStringParser;
+import com.martiansoftware.jsap.stringparsers.CharacterStringParser;
+import com.martiansoftware.jsap.stringparsers.ClassStringParser;
+import com.martiansoftware.jsap.stringparsers.ColorStringParser;
+import com.martiansoftware.jsap.stringparsers.DoubleStringParser;
+import com.martiansoftware.jsap.stringparsers.FloatStringParser;
+import com.martiansoftware.jsap.stringparsers.InetAddressStringParser;
+import com.martiansoftware.jsap.stringparsers.IntSizeStringParser;
+import com.martiansoftware.jsap.stringparsers.IntegerStringParser;
+import com.martiansoftware.jsap.stringparsers.LongSizeStringParser;
+import com.martiansoftware.jsap.stringparsers.LongStringParser;
+import com.martiansoftware.jsap.stringparsers.PackageStringParser;
+import com.martiansoftware.jsap.stringparsers.ShortStringParser;
+import com.martiansoftware.jsap.stringparsers.StringStringParser;
+import com.martiansoftware.jsap.stringparsers.URLStringParser;
+import com.martiansoftware.jsap.xml.JSAPConfig;
+import com.martiansoftware.util.StringUtils;
+
+/**
+ * The core class of the JSAP (Java Simple Argument Parser) API.
+ *
+ * <p>A JSAP is responsible for converting an array of Strings, typically
+ * received from a  command line in the main class' main() method, into a
+ * collection of Objects that are retrievable by a unique ID assigned by the
+ * developer.</p>
+ *
+ * <p>Before a JSAP parses a command line, it is configured with the Switches,
+ * FlaggedOptions, and UnflaggedOptions it will accept.  As a result, the
+ * developer can rest assured that if no Exceptions are thrown by the JSAP's
+ * parse() method, the entire command line was parsed successfully.</p>
+ *
+ * <p>For example, to parse a command line with the syntax "[--verbose]
+ * {-n|--number} Mynumber", the following code could be used:</p.
+ *
+ * <code><pre>
+ * JSAP myJSAP = new JSAP();
+ * myJSAP.registerParameter( new Switch( "verboseSwitch", JSAP.NO_SHORTFLAG,
+ * "verbose" ) );
+ * myJSAP.registerParameter( new FlaggedOption( "numberOption", new
+ * IntegerStringParser(), JSAP.NO_DEFAULT,
+ * JSAP.NOT_REQUIRED, 'n', "number" ) );
+ * JSAPResult result = myJSAP.parse(args);
+ * </pre></code>
+ *
+ * <p>The results of the parse could then be obtained with:</p>
+ *
+ * <code><pre>
+ * int n = result.getInt("numberOption");
+ * boolean isVerbose = result.getBoolean("verboseSwitch");
+ * </pre></code>
+ *
+ * <h3>Generating a JSAP from ANT</h3>
+ * <p>If you don't want to register all your parameters manually as shown
+ * above, the JSAP API provides a custom ANT task that will generate a
+ * custom JSAP subclass to suit your needs.  See
+ * com.martiansoftware.jsap.ant.JSAPAntTask for details.</p>
+ * <p>See the accompanying documentation for examples and further information.
+ *
+ * @author <a href="http://www.martiansoftware.com/contact.html">Marty Lamb</a>
+ * @author Klaus Berg (bug fixes in help generation)
+ * @author Wolfram Esser (contributed code for custom line separators in help)
+ * @see com.martiansoftware.jsap.ant.JSAPAntTask
+ */
+public class JSAP {
+
+    /**
+     * Map of this JSAP's AbstractParameters keyed on their unique ID.
+     */
+    private Map paramsByID = null;
+
+    /**
+     * Map of this JSAP's AbstractParameters keyed on their short flag.
+     */
+    private Map paramsByShortFlag = null;
+
+    /**
+     * Map of this JSAP's AbstractParameters keyed on their long flag.
+     */
+    private Map paramsByLongFlag = null;
+
+    /**
+     * List of this JSAP's UnflaggedOptions, in order of declaration.
+     */
+    private List unflaggedOptions = null;
+
+    /**
+     * List of all of this JSAP's AbstractParameters, in order of
+     * declaration.
+     */
+    private List paramsByDeclarationOrder = null;
+
+    /**
+     * List of all of this JSAP's DefaultSources, in order of declaration.
+     */
+    private List defaultSources = null;
+
+    /**
+     * If not null, overrides the automatic usage info.
+     */
+    private String usage = null;
+
+    /**
+     * If not null, overrides the automatic help info.
+     */
+    private String help = null;
+
+    /**
+     * Does not have a short flag.
+     *
+     * @see com.martiansoftware.jsap.FlaggedOption
+     * @see com.martiansoftware.jsap.UnflaggedOption
+     */
+    public static final char NO_SHORTFLAG = '\0';
+
+    /**
+     * Does not have a long flag.
+     *
+     * @see com.martiansoftware.jsap.FlaggedOption
+     * @see com.martiansoftware.jsap.UnflaggedOption
+     */
+    public static final String NO_LONGFLAG = null;
+
+    /**
+     * The default separator for list parameters (equivalent to
+     * java.io.File.pathSeparatorChar)
+     *
+     * @see FlaggedOption#setListSeparator(char)
+     */
+    public static final char DEFAULT_LISTSEPARATOR =
+        java.io.File.pathSeparatorChar;
+
+    /**
+     * The default separator between parameters in generated help (a newline
+     * by default)
+     */
+    public static final String DEFAULT_PARAM_HELP_SEPARATOR = "\n";
+    
+    /**
+     * The parameter is required.
+     *
+     * @see FlaggedOption#setRequired(boolean)
+     */
+    public static final boolean REQUIRED = true;
+
+    /**
+     * The parameter is not required.
+     *
+     * @see FlaggedOption#setRequired(boolean)
+     */
+    public static final boolean NOT_REQUIRED = false;
+
+    /**
+     * The parameter is a list.
+     *
+     * @see FlaggedOption#setList(boolean)
+     */
+    public static final boolean LIST = true;
+
+    /**
+     * The parameter is not a list.
+     *
+     * @see FlaggedOption#setList(boolean)
+     */
+    public static final boolean NOT_LIST = false;
+
+    /**
+     * The parameter allows multiple declarations.
+     *
+     * @see FlaggedOption#setAllowMultipleDeclarations(boolean)
+     */
+    public static final boolean MULTIPLEDECLARATIONS = true;
+
+    /**
+     * The parameter does not allow multiple declarations.
+     *
+     * @see FlaggedOption#setAllowMultipleDeclarations(boolean)
+     */
+    public static final boolean NO_MULTIPLEDECLARATIONS = false;
+
+    /**
+     * The parameter consumes the command line.
+     *
+     * @see com.martiansoftware.jsap.UnflaggedOption#setGreedy(boolean)
+     */
+    public static final boolean GREEDY = true;
+
+    /**
+     * The parameter does not consume the command line.
+     *
+     * @see com.martiansoftware.jsap.UnflaggedOption#setGreedy(boolean)
+     */
+    public static final boolean NOT_GREEDY = false;
+
+    /** The parameter has no default value.
+     */
+    public static final String NO_DEFAULT = null;
+
+    /**
+     * The parameter has no help text.
+     *
+     * @see com.martiansoftware.jsap.Parameter#setHelp(String)
+     */
+    public static final String NO_HELP = null;
+
+    /** 
+     * The only instance of a {@link com.martiansoftware.jsap.stringparsers.BigDecimalStringParser}.
+     */
+    
+    public static final BigDecimalStringParser BIGDECIMAL_PARSER = BigDecimalStringParser.getParser();
+    
+    /** 
+     * The only instance of a {@link com.martiansoftware.jsap.stringparsers.BigIntegerStringParser}.
+     */
+    
+    public static final BigIntegerStringParser BIGINTEGER_PARSER = BigIntegerStringParser.getParser();
+    
+    /** 
+     * The only instance of a {@link com.martiansoftware.jsap.stringparsers.BooleanStringParser}.
+     */
+    
+    public static final BooleanStringParser BOOLEAN_PARSER = BooleanStringParser.getParser();
+    
+    /** 
+     * The only instance of a {@link com.martiansoftware.jsap.stringparsers.ByteStringParser}.
+     */
+    
+    public static final ByteStringParser BYTE_PARSER = ByteStringParser.getParser();
+    
+    
+    /** 
+     * The only instance of a {@link com.martiansoftware.jsap.stringparsers.CharacterStringParser}.
+     */
+    
+    public static final CharacterStringParser CHARACTER_PARSER = CharacterStringParser.getParser();
+    
+    /** 
+     * The only instance of a {@link com.martiansoftware.jsap.stringparsers.ClassStringParser}.
+     */
+    
+    public static final ClassStringParser CLASS_PARSER = ClassStringParser.getParser();
+
+    
+    /** 
+     * The only instance of a {@link com.martiansoftware.jsap.stringparsers.ColorStringParser}.
+     */
+    
+    public static final ColorStringParser COLOR_PARSER = ColorStringParser.getParser();
+
+    
+    /** 
+     * The only instance of a {@link com.martiansoftware.jsap.stringparsers.DoubleStringParser}.
+     */
+    
+    public static final DoubleStringParser DOUBLE_PARSER = DoubleStringParser.getParser();
+
+    
+    /** 
+     * The only instance of a {@link com.martiansoftware.jsap.stringparsers.FloatStringParser}.
+     */
+    
+    public static final FloatStringParser FLOAT_PARSER = FloatStringParser.getParser();
+
+    /** 
+     * The only instance of a {@link com.martiansoftware.jsap.stringparsers.InetAddressStringParser}.
+     */
+    
+    public static final InetAddressStringParser INETADDRESS_PARSER = InetAddressStringParser.getParser();
+
+    /** 
+     * The only instance of a {@link com.martiansoftware.jsap.stringparsers.IntegerStringParser}.
+     */
+    
+    public static final IntegerStringParser INTEGER_PARSER = IntegerStringParser.getParser();
+
+    /** 
+     * The only instance of a {@link com.martiansoftware.jsap.stringparsers.IntSizeStringParser}.
+     */
+    
+    public static final IntSizeStringParser INTSIZE_PARSER = IntSizeStringParser.getParser();
+
+    /** 
+     * The only instance of a {@link com.martiansoftware.jsap.stringparsers.LongSizeStringParser}.
+     */
+    
+    public static final LongSizeStringParser LONGSIZE_PARSER = LongSizeStringParser.getParser();
+
+    /** 
+     * The only instance of a {@link com.martiansoftware.jsap.stringparsers.LongStringParser}.
+     */
+    
+    public static final LongStringParser LONG_PARSER = LongStringParser.getParser();
+
+    /** 
+     * The only instance of a {@link com.martiansoftware.jsap.stringparsers.PackageStringParser}.
+     */
+    
+    public static final PackageStringParser PACKAGE_PARSER = PackageStringParser.getParser();
+
+    /** 
+     * The only instance of a {@link com.martiansoftware.jsap.stringparsers.ShortStringParser}.
+     */
+    
+    public static final ShortStringParser SHORT_PARSER = ShortStringParser.getParser();
+
+    /** 
+     * The only instance of a {@link com.martiansoftware.jsap.stringparsers.StringStringParser}.
+     */
+    
+    public static final StringStringParser STRING_PARSER = StringStringParser.getParser();
+
+    /** 
+     * The only instance of a {@link com.martiansoftware.jsap.stringparsers.URLStringParser}.
+     */
+    
+    public static final URLStringParser URL_PARSER = URLStringParser.getParser();
+
+    /**
+     * The default screen width used for formatting help.
+     */
+    public static final int DEFAULT_SCREENWIDTH = 80;
+
+    /**
+     * Temporary fix for bad console encodings screwing up non-breaking spaces.
+     */
+    static char SYNTAX_SPACECHAR = ' ';
+    
+    static {
+    	if (Boolean.valueOf(System.getProperty("com.martiansoftware.jsap.usenbsp", "false")).booleanValue()) {
+    		SYNTAX_SPACECHAR = '\u00a0';
+    	}
+    }
+    
+    /**
+     * Creates a new JSAP with an empty configuration.  It must be configured
+     * with registerParameter() before its parse() methods may be called.
+     */
+    public JSAP() {
+    	init();
+    }
+    
+    /**
+     * Creates a new JSAP configured as specified in the referenced xml.
+     * @param jsapXML reference to xml representation of the JSAP configuration
+     * @throws IOException if an I/O error occurs
+     * @throws JSAPException if the configuration is not valid
+     */
+    public JSAP(URL jsapXML) throws IOException, JSAPException {
+    	init();
+    	JSAPConfig.configure(this, jsapXML);
+    }
+
+    /**
+     * Creates a new JSAP configured as specified in the referenced xml.
+     * @param resourceName name of the resource (accessible via this JSAP's classloader)
+     * containing the xml representation of the JSAP configuration
+     * @throws IOException if an I/O error occurs
+     * @throws JSAPException if the configuration is not valid
+     */
+    public JSAP(String resourceName) throws IOException, JSAPException {
+    	this(JSAP.class.getClassLoader().getResource(resourceName));
+    }
+    
+    private void init() {
+        paramsByID = new java.util.HashMap();
+        paramsByShortFlag = new java.util.HashMap();
+        paramsByLongFlag = new java.util.HashMap();
+        unflaggedOptions = new java.util.ArrayList();
+        paramsByDeclarationOrder = new java.util.ArrayList();
+        defaultSources = new java.util.ArrayList();    	
+    }
+    
+    /**
+     * Sets the usage string manually, overriding the automatically-
+     * generated String.  To remove the override, call setUsage(null).
+     * @param usage the manually-set usage string.
+     */
+    public void setUsage(String usage) {
+        this.usage = usage;
+    }
+
+    /**
+     * Sets the help string manually, overriding the automatically-
+     * generated String.  To remove the override, call setHelp(null).
+     * @param help the manualy-set help string.
+     */
+    public void setHelp(String help) {
+        this.help = help;
+    }
+
+    /**
+     * A shortcut method for calling getHelp(80, "\n").
+     * @see #getHelp(int,String)
+     * @return the same as gethelp(80, "\n")
+     */
+    public String getHelp() {
+        return (getHelp(DEFAULT_SCREENWIDTH, DEFAULT_PARAM_HELP_SEPARATOR));
+    }
+
+    /**
+     * A shortcut method for calling getHelp(screenWidth, "\n").
+     * @param screenWidth the screen width for which to format the help.
+     * @see #getHelp(int,String)
+     * @return the same as gethelp(screenWidth, "\n")
+     */
+    public String getHelp(int screenWidth) {
+    	return (getHelp(screenWidth, DEFAULT_PARAM_HELP_SEPARATOR));
+    }
+    
+    /**
+     * If the help text has been manually set, this method simply
+     * returns it, ignoring the screenWidth parameter.  Otherwise,
+     * an automatically-formatted help message is returned, tailored
+     * to the specified screen width.
+     * @param screenWidth the screen width (in characters) for which
+     * the help text will be formatted.  If zero, help will not be
+     * line-wrapped.
+     * @return complete help text for this JSAP.
+     */
+    public String getHelp(int screenWidth, String paramSeparator) {
+        String result = help;
+        if (result == null) {
+            StringBuffer buf = new StringBuffer();
+
+            // We'll wrap at screenWidth - 8
+            int wrapWidth = screenWidth - 8;
+
+            // now loop through all the params again and display their help info
+            for (Iterator i = paramsByDeclarationOrder.iterator();
+                i.hasNext();) {
+                Parameter param = (Parameter) i.next();
+                StringBuffer defaultText = new StringBuffer();
+                String[] defaultValue = param.getDefault();
+                if ( !(param instanceof Switch) && defaultValue != null ) {
+                    defaultText.append(" (default: ");
+                    for(int j = 0; j < defaultValue.length; j++ ) {
+                        if (j > 0) defaultText.append( ", " );
+                        defaultText.append(defaultValue[ j ]);
+                    }
+                    defaultText.append(")");
+                }
+                Iterator helpInfo =
+                    StringUtils
+                        .wrapToList(param.getHelp() + defaultText, wrapWidth)
+                        .iterator();
+
+                buf.append("  "); // the two leading spaces
+                buf.append(param.getSyntax());
+                buf.append("\n");
+
+                while (helpInfo.hasNext()) {
+                    buf.append("        ");
+                    buf.append( helpInfo.next() );
+                    buf.append("\n");
+                }
+                if (i.hasNext()) {
+                    buf.append(paramSeparator);
+                }
+            }
+            result = buf.toString();
+        }
+        return (result);
+    }
+
+    /**
+     * Returns an automatically generated usage description based upon this
+     * JSAP's  current configuration.
+     *
+     * @return an automatically generated usage description based upon this
+     * JSAP's current configuration.
+     */
+    public String getUsage() {
+        String result = usage;
+        if (result == null) {
+            StringBuffer buf = new StringBuffer();
+            for (Iterator i = paramsByDeclarationOrder.iterator();
+                i.hasNext();) {
+                Parameter param = (Parameter) i.next();
+                if (buf.length() > 0) {
+                    buf.append(" ");
+                }
+                buf.append(param.getSyntax());
+            }
+            result = buf.toString();
+        }
+        return (result);
+    }
+
+    /**
+     * Returns an automatically generated usage description based upon this
+     * JSAP's  current configuration.  This returns exactly the same result
+     * as getUsage().
+     *
+     * @return an automatically generated usage description based upon this
+     * JSAP's current configuration.
+     */
+    public String toString() {
+        return (getUsage());
+    }
+
+    /**
+     * Returns an IDMap associating long and short flags with their associated
+     * parameters' IDs, and allowing the listing of IDs.  This is probably only
+     * useful for developers creating their own DefaultSource classes.
+     * @return an IDMap based upon this JSAP's current configuration.
+     */
+    public IDMap getIDMap() {
+        List ids = new java.util.ArrayList(paramsByDeclarationOrder.size());
+        for (Iterator i = paramsByDeclarationOrder.iterator(); i.hasNext();) {
+            Parameter param = (Parameter) i.next();
+            ids.add(param.getID());
+        }
+
+        Map byShortFlag = new java.util.HashMap();
+        for (Iterator i = paramsByShortFlag.keySet().iterator();
+          i.hasNext();) {
+            Character c = (Character) i.next();
+            byShortFlag.put(
+                c,
+                ((Parameter) paramsByShortFlag.get(c)).getID());
+        }
+
+        Map byLongFlag = new java.util.HashMap();
+        for (Iterator i = paramsByLongFlag.keySet().iterator(); i.hasNext();) {
+            String s = (String) i.next();
+            byLongFlag.put(
+                s,
+                ((Parameter) paramsByLongFlag.get(s)).getID());
+        }
+
+        return (new IDMap(ids, byShortFlag, byLongFlag));
+    }
+
+    /**
+     * Returns the requested Switch, FlaggedOption, or UnflaggedOption with the
+     * specified ID.  Depending upon what you intend to do with the result, it
+     * may be necessary to re-cast the result as a Switch, FlaggedOption, or
+     * UnflaggedOption as appropriate.
+     *
+     * @param id the ID of the requested Switch, FlaggedOption, or
+     * UnflaggedOption.
+     * @return the requested Switch, FlaggedOption, or UnflaggedOption, or null
+     * if no Parameter with the specified ID is defined in this JSAP.
+     */
+    public Parameter getByID(String id) {
+        return ((Parameter) paramsByID.get(id));
+    }
+
+    /**
+     * Returns the requested Switch or FlaggedOption with the specified long
+     * flag. Depending upon what you intend to do with the result, it may be
+     * necessary to re-cast the result as a Switch or FlaggedOption as
+     * appropriate.
+     *
+     * @param longFlag the long flag of the requested Switch or FlaggedOption.
+     * @return the requested Switch or FlaggedOption, or null if no Flagged
+     * object with the specified long flag is defined in this JSAP.
+     */
+    public Flagged getByLongFlag(String longFlag) {
+        return ((Flagged) paramsByLongFlag.get(longFlag));
+    }
+
+    /**
+     * Returns the requested Switch or FlaggedOption with the specified short
+     * flag. Depending upon what you intend to do with the result, it may be
+     * necessary to re-cast the result as a Switch or FlaggedOption as
+     * appropriate.
+     *
+     * @param shortFlag the short flag of the requested Switch or FlaggedOption.
+     * @return the requested Switch or FlaggedOption, or null if no Flagged
+     * object with the specified short flag is defined in this JSAP.
+     */
+    public Flagged getByShortFlag(Character shortFlag) {
+        return ((Flagged) paramsByShortFlag.get(shortFlag));
+    }
+
+    /**
+     * Returns the requested Switch or FlaggedOption with the specified short
+     * flag. Depending upon what you intend to do with the result, it may be
+     * necessary to re-cast the result as a Switch or FlaggedOption as
+     * appropriate.
+     *
+     * @param shortFlag the short flag of the requested Switch or FlaggedOption.
+     * @return the requested Switch or FlaggedOption, or null if no Flagged
+     * object with the specified short flag is defined in this JSAP.
+     */
+    public Flagged getByShortFlag(char shortFlag) {
+        return (getByShortFlag(new Character(shortFlag)));
+    }
+
+    /**
+     * Returns an Iterator over all UnflaggedOptions currently registered with
+     * this JSAP.
+     *
+     * @return an Iterator over all UnflaggedOptions currently registered with
+     * this JSAP.
+     * @see java.util.Iterator
+     */
+    public Iterator getUnflaggedOptionsIterator() {
+        return (unflaggedOptions.iterator());
+    }
+
+    /**
+     * Registers a new DefaultSource with this JSAP, at the end of the current
+     * DefaultSource chain, but before the defaults defined within the
+     * AbstractParameters themselves.
+     *
+     * @param ds the DefaultSource to append to the DefaultSource chain.
+     * @see com.martiansoftware.jsap.DefaultSource
+     */
+    public void registerDefaultSource(DefaultSource ds) {
+        defaultSources.add(ds);
+    }
+
+    /**
+     * Removes the specified DefaultSource from this JSAP's DefaultSource chain.
+     * If this specified DefaultSource is not currently in this JSAP's
+     * DefaultSource chain, this method does nothing.
+     *
+     * @param ds the DefaultSource to remove from the DefaultSource chain.
+     */
+    public void unregisterDefaultSource(DefaultSource ds) {
+        defaultSources.remove(ds);
+    }
+
+    /**
+     * Returns a Defaults object representing the default values defined within
+     * this JSAP's AbstractParameters themselves.
+     *
+     * @return a Defaults object representing the default values defined within
+     * this JSAP's AbstractParameters themselves.
+     */
+    private Defaults getSystemDefaults() {
+        Defaults defaults = new Defaults();
+        for (Iterator i = paramsByDeclarationOrder.iterator(); i.hasNext();) {
+            Parameter param = (Parameter) i.next();
+            defaults.setDefault(param.getID(), param.getDefault());
+        }
+        return (defaults);
+    }
+
+    /**
+     * Merges the specified Defaults objects, only copying Default values from
+     * the source to the destination if they are NOT currently defined in the
+     * destination.
+     *
+     * @param dest the destination Defaults object into which the source should
+     * be merged.
+     * @param src the source Defaults object.
+     */
+    private void combineDefaults(Defaults dest, Defaults src) {
+        if (src != null) {
+            for (Iterator i = src.idIterator(); i.hasNext();) {
+                String paramID = (String) i.next();
+                dest.setDefaultIfNeeded(paramID, src.getDefault(paramID));
+            }
+        }
+    }
+
+    /**
+     * Returns a Defaults object representing the merged Defaults of every
+     * DefaultSource in the DefaultSource chain and the default values specified
+     * in the AbstractParameters themselves.
+     *
+     * @param exceptionMap the ExceptionMap object within which any encountered
+     * exceptions will be returned.
+     * @return a Defaults object representing the Defaults of the entire JSAP.
+     * @see com.martiansoftware.jsap.DefaultSource#getDefaults(IDMap, ExceptionMap)
+     */
+    protected Defaults getDefaults(ExceptionMap exceptionMap) {
+        Defaults defaults = new Defaults();
+        IDMap idMap = getIDMap();
+        for (Iterator dsi = defaultSources.iterator(); dsi.hasNext();) {
+            DefaultSource ds = (DefaultSource) dsi.next();
+            combineDefaults(defaults, ds.getDefaults(idMap, exceptionMap));
+        }
+        combineDefaults(defaults, getSystemDefaults());
+        return (defaults);
+    }
+
+    /**
+     * Registers the specified Parameter (i.e., Switch, FlaggedOption,
+     * or UnflaggedOption) with this JSAP.
+     *
+     * <p>Registering an Parameter <b>locks</b> the parameter.
+     * Attempting to change its properties (ID, flags, etc.) while it is locked
+     * will result in a JSAPException.  To unlock an Parameter, it must
+     * be unregistered from the JSAP.
+     *
+     * @param param the Parameter to register.
+     * @throws JSAPException if this Parameter cannot be added. Possible
+     * reasons include:
+     * <ul>
+     *     <li>Another Parameter with the same ID has already been
+     *      registered.</li>
+     *  <li>You are attempting to register a Switch or FlaggedOption with
+     *      neither a short nor long flag.</li>
+     *  <li>You are attempting to register a Switch or FlaggedOption with a long
+     *      or short flag that is already
+     *  defined in this JSAP.</li>
+     *  <li>You are attempting to register a second greedy UnflaggedOption</li>
+     * </ul>
+     */
+    public void registerParameter(Parameter param)
+        throws JSAPException {
+        String paramID = param.getID();
+
+        if (paramsByID.containsKey(paramID)) {
+            throw (
+                new JSAPException(
+                    "A parameter with ID '"
+                        + paramID
+                        + "' has already been registered."));
+        }
+
+        if (param instanceof Flagged) {
+            Flagged f = (Flagged) param;
+            if ((f.getShortFlagCharacter() == null)
+                && (f.getLongFlag() == null)) {
+                throw (
+                    new JSAPException(
+                        "FlaggedOption '"
+                            + paramID
+                            + "' has no flags defined."));
+            }
+            if (paramsByShortFlag.containsKey(f.getShortFlagCharacter())) {
+                throw (
+                    new JSAPException(
+                        "A parameter with short flag '"
+                            + f.getShortFlag()
+                            + "' has already been registered."));
+            }
+            if (paramsByLongFlag.containsKey(f.getLongFlag())) {
+                throw (
+                    new JSAPException(
+                        "A parameter with long flag '"
+                            + f.getLongFlag()
+                            + "' has already been registered."));
+            }
+        } else {
+            if ((unflaggedOptions.size() > 0)
+                && (((UnflaggedOption) unflaggedOptions
+                    .get(unflaggedOptions.size() - 1))
+                    .isGreedy())) {
+                throw (
+                    new JSAPException(
+                        "A greedy unflagged option has already been registered;"
+                            + " option '"
+                            + paramID
+                            + "' will never be reached."));
+            }
+        }
+
+        if (param instanceof Option) {
+            ((Option) param).register();
+        }
+
+        // if we got this far, it's safe to insert it.
+        param.setLocked(true);
+        paramsByID.put(paramID, param);
+        paramsByDeclarationOrder.add(param);
+        if (param instanceof Flagged) {
+            Flagged f = (Flagged) param;
+            if (f.getShortFlagCharacter() != null) {
+                paramsByShortFlag.put(f.getShortFlagCharacter(), param);
+            }
+            if (f.getLongFlag() != null) {
+                paramsByLongFlag.put(f.getLongFlag(), param);
+            }
+        } else if (param instanceof Option) {
+            unflaggedOptions.add(param);
+        }
+    }
+
+    /**
+     * Unregisters the specified Parameter (i.e., Switch, FlaggedOption,
+     * or UnflaggedOption) from this JSAP.  Unregistering an Parameter
+     * also unlocks it, allowing changes to its properties (ID, flags, etc.).
+     *
+     * @param param the Parameter to unregister from this JSAP.
+     */
+    public void unregisterParameter(Parameter param) {
+        if (paramsByID.containsKey(param.getID())) {
+
+            if (param instanceof Option) {
+                ((Option) param).unregister();
+            }
+
+            paramsByID.remove(param.getID());
+            paramsByDeclarationOrder.remove(param);
+            if (param instanceof Flagged) {
+                Flagged f = (Flagged) param;
+                paramsByShortFlag.remove(f.getShortFlagCharacter());
+                paramsByLongFlag.remove(f.getLongFlag());
+            } else if (param instanceof UnflaggedOption) {
+                unflaggedOptions.remove(param);
+            }
+            param.setLocked(false);
+        }
+    }
+
+    /**
+     * Parses the specified command line array.  If no Exception is thrown, the
+     * entire command line has been parsed successfully, and its results have
+     * been successfully instantiated.
+     *
+     * @param args An array of command line arguments to parse.  This array is
+     * typically provided in the application's main class' main() method.
+     * @return a JSAPResult containing the resulting Objects.
+     */
+    public JSAPResult parse(String[] args) {
+        Parser p = new Parser(this, args);
+        return (p.parse());
+    }
+
+    /**
+     * Parses the specified command line.  The specified command line is first
+     * parsed into an array, much like the operating system does for the JVM
+     * prior to calling your application's main class' main() method.  If no
+     * Exception is thrown, the entire command line has been parsed
+     * successfully, and its results have been successfully instantiated.
+     *
+     * @param cmdLine An array of command line arguments to parse.  This array
+     * is typically provided in the application's main class' main() method.
+     * @return a JSAPResult containing the resulting Objects.
+     */
+    public JSAPResult parse(String cmdLine) {
+        String[] args = CommandLineTokenizer.tokenize(cmdLine);
+        return (parse(args));
+    }
+
+    /**
+     * Unregisters all registered AbstractParameters, allowing them to perform
+     * their cleanup.
+     */
+    public void finalize() {
+        Parameter[] params =
+            (Parameter[]) paramsByDeclarationOrder.toArray(
+                new Parameter[0]);
+        int paramCount = params.length;
+        for (int i = 0; i < paramCount; ++i) {
+            unregisterParameter(params[i]);
+        }
+    }
+
+}
diff --git a/src/java/com/martiansoftware/jsap/JSAPException.java b/src/java/com/martiansoftware/jsap/JSAPException.java
new file mode 100644
index 0000000..d2fb0c1
--- /dev/null
+++ b/src/java/com/martiansoftware/jsap/JSAPException.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2002-2004, Martian Software, Inc.
+ * This file is made available under the LGPL as described in the accompanying
+ * LICENSE.TXT file.
+ */
+
+package com.martiansoftware.jsap;
+
+/**
+ * The base class for all of JSAP's exceptions.  A JSAPException can
+ * encapsulate another Exception, which can be obtained via the getRootCause()
+ * method.  This is useful in cases where subclasses might need to
+ * throw an exception that JSAP is not expecting, such as an IOException while
+ * loading a DefaultSource.  The subclass can in these cases throw a new
+ * JSAPException encapsulating the IOException.
+ * @author <a href="http://www.martiansoftware.com/contact.html">Marty Lamb</a>
+ * @see com.martiansoftware.jsap.Flagged
+ * @see com.martiansoftware.jsap.Option
+ */
+public class JSAPException extends Exception {
+
+    /**
+     * Creates a new JSAPException.
+     */
+    public JSAPException() {
+        super();
+    }
+
+    /**
+     * Creates a new JSAPException with the specified message.
+     * @param msg the message for this JSAPException.
+     */
+    public JSAPException(String msg) {
+        super(msg);
+    }
+
+    /**
+     * Creates a new JSAPException encapsulating the specified Throwable.
+     * @param cause the Throwable to encapsulate.
+     */
+    public JSAPException(Throwable cause) {
+        super(cause);
+    }
+
+    /**
+     * Creates a new JSAPException with the specified message encapsulating
+     * the specified Throwable.
+     * @param msg the message for this JSAPException.
+     * @param cause the Throwable to encapsulate.
+     */
+    public JSAPException(String msg, Throwable cause) {
+        super(msg, cause);
+    }
+
+}
diff --git a/src/java/com/martiansoftware/jsap/JSAPResult.java b/src/java/com/martiansoftware/jsap/JSAPResult.java
new file mode 100644
index 0000000..6123def
--- /dev/null
+++ b/src/java/com/martiansoftware/jsap/JSAPResult.java
@@ -0,0 +1,1382 @@
+ /*
+ * Copyright (c) 2002-2004, Martian Software, Inc.
+ * This file is made available under the LGPL as described in the accompanying
+ * LICENSE.TXT file.
+ */
+
+package com.martiansoftware.jsap;
+
+import java.util.Map;
+import java.io.File;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.net.InetAddress;
+import java.net.URL;
+import java.awt.Color;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+/** Encapsulates the results of JSAP's parse() methods.  The most basic means of
+ * obtaining a parse result from a JSAPResult is the {@link #getObject(String)}
+ * method, but a number of getXXX() methods are provided
+ * to make your code more readable and to avoid re-casting.
+ * @author <a href="http://www.martiansoftware.com/contact.html">Marty Lamb</a>
+ */
+public class JSAPResult implements ExceptionMap {
+
+    /**
+     * Contains all of the results, as a Map of Lists keyed by
+     * parameter ID.
+     */
+    private Map allResults = null;
+
+    /**
+     * Contains a map of the QualifiedSwitch IDs with Booleans
+     * indicating whether they were present.
+     */
+    private Map qualifiedSwitches = null;
+    
+    /**
+     * Contains all of the exceptions, as a Map of Lists, keyed by
+     * parameter ID.
+     * "General" exceptions that are not associated with a specific parameter
+     * have a null
+     * key.
+     */
+    private Map allExceptions = null;
+
+    /**
+     * A chronological listing of all of the exceptions thrown during parsing.
+     */
+    private List chronologicalErrorMessages = null;
+
+    /**
+     * A set containing the IDs of parameters supplied by the user
+     * (as opposed to default values)
+     */
+    private Set userSpecifiedIDs = null;
+    
+    /**
+     * If true, any values "add"ed to this JSAPResult came from the
+     * user, and not from any default values.
+     */
+    private boolean valuesFromUser = false;
+    
+    /**
+     *  Creates new JSAPResult
+     */
+    protected JSAPResult() {
+        allResults = new java.util.HashMap();
+        allExceptions = new java.util.HashMap();
+        chronologicalErrorMessages = new java.util.LinkedList();
+        qualifiedSwitches = new java.util.HashMap();
+        userSpecifiedIDs = new java.util.HashSet();
+    }
+
+    /**
+     * Sets internal flag indicating sources of subsequent values
+     * added to this JSAPResult
+     * @param valuesFromUser if true, values subsequently added to
+     * this JSAPResult originated from the user.
+     */
+    void setValuesFromUser(boolean valuesFromUser) {
+    	this.valuesFromUser = valuesFromUser;
+    }
+    
+    /**
+     * Returns true if this JSAPResult contains any results for
+     * the specified id.  Note that these results may be default
+     * values, and thus contains(id) might return true even when
+     * the user has not herself supplied the parameter.
+     * 
+     * <P>This is just a means to see if there are values to retrieve.
+     * 
+     * @param id the ID to check
+     * @return true if there are any values in this JSAPResult associated
+     * with the specified ID.
+     */
+    public boolean contains(String id) {
+    	return (allResults.containsKey(id));
+    }
+    
+    /**
+     * Returns true if this JSAPResult contains any <i>user-specified</i>
+     * values for the specified id.  If this JSAPResult contains default values
+     * (or no values) for the specified id, this method returns false.
+     * 
+     * @param id the ID to check
+     * @return indication of whether the user specified a value for the specified id.
+     */
+    public boolean userSpecified(String id) {
+    	return (userSpecifiedIDs.contains(id));
+    }
+    
+    /**
+     * Adds the specified values to any existing values already associated with
+     * the specified id, if any.
+     * @param id the unique ID of the parameter with which the specified values
+     * are associated.
+     * @param values a List containing the additional values to associate
+     * with the specified ID.
+     */
+    protected void add(String id, List values) {
+        List al = null;
+        if (allResults.containsKey(id)) {
+            al = (List) allResults.get(id);
+        } else {
+            al = new java.util.ArrayList();
+            allResults.put(id, al);
+        }
+        al.addAll(values);
+        if (valuesFromUser) {
+        	userSpecifiedIDs.add(id);
+        }
+    }
+
+    /**
+     * Adds the specified exception to the exception map.  Exceptions are
+     * keyed by the ID of the parameters with which they are associated.
+     * "General"
+     * exceptions not associated with a particular parameter have a null key.
+     * @param id the unique ID of the parameter with which the specified values
+     * are associated.
+     * @param exception the exception to associate with the specified key.
+     * @see com.martiansoftware.jsap.ExceptionMap#addException(String,Exception)
+     */
+    public void addException(String id, Exception exception) {
+        List el = null;
+        if (allExceptions.containsKey(id)) {
+            el = (List) allExceptions.get(id);
+        } else {
+            el = new java.util.ArrayList();
+            allExceptions.put(id, el);
+        }
+        el.add(exception);
+        chronologicalErrorMessages.add(exception.getMessage());
+    }
+
+    /**
+     * Returns the <b>first</b> object associated with the specified ID.  If
+     * more than one object is
+     * expected, call getObjectArray() instead.
+     * @param id the unique ID of the parameter for which the first value is
+     * requested
+     * @return the <b>first</b> object associated with the specified ID.  If
+     * more than one object is
+     * expected, call getObjectArray() instead.
+     */
+    public Object getObject(String id) {
+        Object result = null;
+        List al = (List) allResults.get(id);
+        if ((al != null) && (al.size() > 0)) {
+            result = al.get(0);
+        }
+        return (result);
+    }
+
+    /**
+     * Returns all values associated with the specified ID.  If no values are
+     * currently associated with
+     * the specified ID, an empty (zero-length) array is returned.
+     * @param id the unique ID of the parameter for which the values are
+     * requested
+     * @return all values associated with the specified ID.  If no values are
+     * currently associated with
+     * the specified ID, an empty (zero-length) array is returned.
+     */
+    public Object[] getObjectArray(String id) {
+        List al = (List) allResults.get(id);
+        if (al == null) {
+            al = new java.util.ArrayList(0);
+        }
+        return (al.toArray());
+    }
+
+    /**
+     * Returns an array containing all of the values associated with the
+     * specified ID. The runtime type
+     * of the returned array is that of the specified array. If the list fits
+     * in the specified array,
+     * it is returned therein. Otherwise, a new array is allocated with the
+     * runtime type of the specified
+     * array and the size of this list..
+     * @param id the unique ID of the parameter for which the values are
+     * requested
+     * @param a  the array into which the elements of the list are to be
+     * stored, if it is big enough;
+     * otherwise, a new array of the same runtime type is allocated for this
+     * purpose.
+     * @return an array containing all of the values associated with the
+     * specified ID. The runtime type
+     * of the returned array is that of the specified array. If the list fits
+     * in the specified array,
+     * it is returned therein. Otherwise, a new array is allocated with the
+     * runtime type of the specified
+     * array and the size of this list..
+     */
+    public Object[] getObjectArray(String id, Object[] a) {
+        List al = (List) allResults.get(id);
+        if (al == null) {
+            al = new java.util.ArrayList();
+        }
+        return (al.toArray(a));
+    }
+
+    /**
+     * Returns the first boolean value associated with the specified id.
+     * @param id the id of the boolean value to retrieve
+     * @return the boolean value associated with the specified id.
+     * @see #getBoolean(String, boolean)
+     * @see #getBooleanArray(String)
+     */
+    public boolean getBoolean(String id) {
+    	Boolean b = (Boolean) qualifiedSwitches.get(id);
+    	if (b == null) {
+        	if (! contains(id)) throw new UnspecifiedParameterException(id);
+    		b = (Boolean) getObject(id);
+    	}
+        return (b.booleanValue());
+    }
+
+    /**
+     * Returns the first boolean value associated with the specified id.  If
+     * the specified id does not
+     * exist within this JSAPResult, or if the object(s) associated with the
+     * specified id are not of
+     * type java.lang.Boolean, the specified default value is returned.
+     * @param id the id of the boolean value to retrieve
+     * @param defaultValue the value to return if the specified id does not
+     * exist within this
+     * JSAPResult, or if the object(s) associated with the specified id are
+     * not of type
+     * java.lang.Boolean.
+     * @return the first boolean value associated with the specified id.  If
+     * the specified id does not
+     * exist within this JSAPResult, or if the object(s) associated with the
+     * specified id are not of
+     * type java.lang.Boolean, the specified default value is returned.
+     * @see #getBoolean(String)
+     * @see #getBooleanArray(String)
+     */
+    public boolean getBoolean(String id, boolean defaultValue) {
+        Boolean b = (Boolean) qualifiedSwitches.get(id); 
+        if (b == null) {
+        	b = (Boolean) getObject(id);
+        }
+        return ((b == null) ? defaultValue : b.booleanValue());
+    }
+
+    /**
+     * Returns an array of boolean values associated with the specified id.  If
+     * the specified id does
+     * not exist within this JSAPResult, this method returns an empty array
+     * (i.e., array.length==0).
+     * @param id the id of the boolean value(s) to return.
+     * @return an array containing the boolean value(s) associated with the
+     * specified id, or an
+     * empty array if the specified id does not exist within this JSAPResult.
+     * @see #getBoolean(String)
+     * @see #getBoolean(String,boolean)
+     */
+    public boolean[] getBooleanArray(String id) {
+        Boolean[] tmp = (Boolean[]) getObjectArray(id, new Boolean[0]);
+        int tmplength = tmp.length;
+        boolean[] result = new boolean[tmplength];
+        for (int i = 0; i < tmplength; ++i) {
+            result[i] = tmp[i].booleanValue();
+        }
+        return (result);
+    }
+
+    /**
+     * Returns the first integer value associated with the specified id.
+     * @param id the id of the integer value to retrieve
+     * @return the integer value associated with the specified id.
+     * @see #getInt(String,int)
+     * @see #getIntArray(String)
+     */
+    public int getInt(String id) {
+    	if (! contains(id)) throw new UnspecifiedParameterException(id);
+        return (((Integer) getObject(id)).intValue());
+    }
+
+    /**
+     * Returns the first integer value associated with the specified id.  If
+     * the specified id does not
+     * exist within this JSAPResult, or if the object(s) associated with the
+     * specified id are not of
+     * type java.lang.Integer, the specified default value is returned.
+     * @param id the id of the boolean value to retrieve
+     * @param defaultValue the value to return if the specified id does not
+     * exist within this
+     * JSAPResult, or if the object(s) associated with the specified id are
+     * not of type
+     * java.lang.Integer.
+     * @return the first integer value associated with the specified id.  If
+     * the specified id does not
+     * exist within this JSAPResult, or if the object(s) associated with the
+     * specified id are not of
+     * type java.lang.Integer, the specified default value is returned.
+     * @see #getInt(String)
+     * @see #getIntArray(String)
+     */
+    public int getInt(String id, int defaultValue) {
+        Integer result = (Integer) getObject(id);
+        return ((result == null) ? defaultValue : result.intValue());
+    }
+    // KPB<<<<<<
+    /**
+     * Returns the string value associated with the specified ID for QualifiedSwitches.
+     * 
+     * @param id the unique ID of the parameter for which the value is requested
+     * @return the value for the QualifiedSwitch associated with the specified ID
+     *         or null is no such value is present or the ID does not belong
+     *         to a QualifiedSwitch
+     */
+    public String getQualifiedSwitchValue(String id) {
+    	Object result = null;
+    	List al = (List) allResults.get(id);
+    	if ((al != null) && (al.size() == 2)) {
+    		result = al.get(1);
+    	}
+    	return(String)result;
+    }
+    // KPB<<<<<
+    
+    /**
+     * Returns an array of integer values associated with the specified id.
+     * If the specified id does
+     * not exist within this JSAPResult, this method returns an empty array
+     * (i.e., array.length==0).
+     * @param id the id of the integer value(s) to return.
+     * @return an array containing the integer value(s) associated with the
+     * specified id, or an
+     * empty array if the specified id does not exist within this JSAPResult.
+     * @see #getInt(String)
+     * @see #getInt(String,int)
+     */
+    public int[] getIntArray(String id) {
+        Integer[] tmp = (Integer[]) getObjectArray(id, new Integer[0]);
+        int tmplength = tmp.length;
+        int[] result = new int[tmplength];
+        for (int i = 0; i < tmplength; ++i) {
+            result[i] = tmp[i].intValue();
+        }
+        return (result);
+    }
+
+    /**
+     * Returns the first long value associated with the specified id.
+     * @param id the id of the long value to retrieve
+     * @return the long value associated with the specified id.
+     * @see #getLong(String,long)
+     * @see #getLongArray(String)
+     */
+    public long getLong(String id) {
+    	if (! contains(id)) throw new UnspecifiedParameterException(id);
+        return (((Long) getObject(id)).longValue());
+    }
+
+    /**
+     * Returns the first long value associated with the specified id.  If the
+     * specified id does not
+     * exist within this JSAPResult, or if the object(s) associated with the
+     * specified id are not of
+     * type java.lang.Long, the specified default value is returned.
+     * @param id the id of the long value to retrieve
+     * @param defaultValue the value to return if the specified id does not
+     * exist within this
+     * JSAPResult, or if the object(s) associated with the specified id are
+     * not of type
+     * java.lang.Long.
+     * @return the first long value associated with the specified id.  If the
+     * specified id does not
+     * exist within this JSAPResult, or if the object(s) associated with the
+     * specified id are not of
+     * type java.lang.Long, the specified default value is returned.
+     * @see #getLong(String)
+     * @see #getLongArray(String)
+     */
+    public long getLong(String id, long defaultValue) {
+        Long result = (Long) getObject(id);
+        return ((result == null) ? defaultValue : result.longValue());
+    }
+
+    /**
+     * Returns an array of long values associated with the specified id.  If
+     * the specified id does
+     * not exist within this JSAPResult, this method returns an empty array
+     * (i.e., array.length==0).
+     * @param id the id of the long value(s) to return.
+     * @return an array containing the long value(s) associated with the
+     * specified id, or an
+     * empty array if the specified id does not exist within this JSAPResult.
+     * @see #getLong(String)
+     * @see #getLong(String,long)
+     */
+    public long[] getLongArray(String id) {
+        Long[] tmp = (Long[]) getObjectArray(id, new Long[0]);
+        int tmplength = tmp.length;
+        long[] result = new long[tmplength];
+        for (int i = 0; i < tmplength; ++i) {
+            result[i] = tmp[i].longValue();
+        }
+        return (result);
+    }
+
+    //    public Iterator iterator(String id) {
+    //        Object[] o = getObjectArray(id, new Object[0]);
+    //    }
+
+    /**
+     * Returns the first byte value associated with the specified id.
+     * @param id the id of the byte value to retrieve
+     * @return the byte value associated with the specified id.
+     * @see #getByte(String,byte)
+     * @see #getByteArray(String)
+     */
+    public byte getByte(String id) {
+    	if (! contains(id)) throw new UnspecifiedParameterException(id);
+        return (((Byte) getObject(id)).byteValue());
+    }
+
+    /**
+     * Returns the first byte value associated with the specified id.  If the
+     * specified id does not
+     * exist within this JSAPResult, or if the object(s) associated with the
+     * specified id are not of
+     * type java.lang.Byte, the specified default value is returned.
+     * @param id the id of the byte value to retrieve
+     * @param defaultValue the value to return if the specified id does not
+     * exist within this
+     * JSAPResult, or if the object(s) associated with the specified id are
+     * not of type
+     * java.lang.Byte.
+     * @return the first byte value associated with the specified id.  If the
+     * specified id does not
+     * exist within this JSAPResult, or if the object(s) associated with the
+     * specified id are not of
+     * type java.lang.Byte, the specified default value is returned.
+     * @see #getByte(String)
+     * @see #getByteArray(String)
+     */
+    public byte getByte(String id, byte defaultValue) {
+        Byte result = (Byte) getObject(id);
+        return ((result == null) ? defaultValue : result.byteValue());
+    }
+
+    /**
+     * Returns an array of byte values associated with the specified id.  If
+     * the specified id does
+     * not exist within this JSAPResult, this method returns an empty array
+     * (i.e., array.length==0).
+     * @param id the id of the byte value(s) to return.
+     * @return an array containing the byte value(s) associated with the
+     * specified id, or an
+     * empty array if the specified id does not exist within this JSAPResult.
+     * @see #getByte(String)
+     * @see #getByte(String,byte)
+     */
+    public byte[] getByteArray(String id) {
+        Byte[] tmp = (Byte[]) getObjectArray(id, new Byte[0]);
+        int tmplength = tmp.length;
+        byte[] result = new byte[tmplength];
+        for (int i = 0; i < tmplength; ++i) {
+            result[i] = tmp[i].byteValue();
+        }
+        return (result);
+    }
+
+    /**
+     * Returns the first char value associated with the specified id.
+     * @param id the id of the char value to retrieve
+     * @return the char value associated with the specified id.
+     * @see #getChar(String,char)
+     * @see #getCharArray(String)
+     */
+    public char getChar(String id) {
+    	if (! contains(id)) throw new UnspecifiedParameterException(id);
+        return (((Character) getObject(id)).charValue());
+    }
+
+    /**
+     * Returns the first char value associated with the specified id.  If the
+     * specified id does not
+     * exist within this JSAPResult, or if the object(s) associated with the
+     * specified id are not of
+     * type java.lang.Character, the specified default value is returned.
+     * @param id the id of the char value to retrieve
+     * @param defaultValue the value to return if the specified id does not
+     * exist within this
+     * JSAPResult, or if the object(s) associated with the specified id are
+     * not of type
+     * java.lang.Character.
+     * @return the first char value associated with the specified id.  If the
+     * specified id does not
+     * exist within this JSAPResult, or if the object(s) associated with the
+     * specified id are not of
+     * type java.lang.Character, the specified default value is returned.
+     * @see #getChar(String)
+     * @see #getCharArray(String)
+     */
+    public char getChar(String id, char defaultValue) {
+        Character result = (Character) getObject(id);
+        return ((result == null) ? defaultValue : result.charValue());
+    }
+
+    /**
+     * Returns an array of char values associated with the specified id.  If
+     * the specified id does
+     * not exist within this JSAPResult, this method returns an empty array
+     * (i.e., array.length==0).
+     * @param id the id of the char value(s) to return.
+     * @return an array containing the char value(s) associated with the
+     * specified id, or an
+     * empty array if the specified id does not exist within this JSAPResult.
+     * @see #getChar(String)
+     * @see #getChar(String,char)
+     */
+    public char[] getCharArray(String id) {
+        Character[] tmp = (Character[]) getObjectArray(id, new Character[0]);
+        int tmplength = tmp.length;
+        char[] result = new char[tmplength];
+        for (int i = 0; i < tmplength; ++i) {
+            result[i] = tmp[i].charValue();
+        }
+        return (result);
+    }
+
+    /**
+     * Returns the first short value associated with the specified id.
+     * @param id the id of the short value to retrieve
+     * @return the short value associated with the specified id.
+     * @see #getShort(String,short)
+     * @see #getShortArray(String)
+     */
+    public short getShort(String id) {
+    	if (! contains(id)) throw new UnspecifiedParameterException(id);
+        return (((Short) getObject(id)).shortValue());
+    }
+
+    /**
+     * Returns the first short value associated with the specified id.  If the
+     * specified id does not
+     * exist within this JSAPResult, or if the object(s) associated with the
+     * specified id are not of
+     * type java.lang.Short, the specified default value is returned.
+     * @param id the id of the short value to retrieve
+     * @param defaultValue the value to return if the specified id does not
+     * exist within this
+     * JSAPResult, or if the object(s) associated with the specified id are
+     * not of type
+     * java.lang.Short.
+     * @return the first short value associated with the specified id.  If the
+     * specified id does not
+     * exist within this JSAPResult, or if the object(s) associated with the
+     * specified id are not of
+     * type java.lang.Short, the specified default value is returned.
+     * @see #getShort(String)
+     * @see #getShortArray(String)
+     */
+    public short getShort(String id, short defaultValue) {
+        Short result = (Short) getObject(id);
+        return ((result == null) ? defaultValue : result.shortValue());
+    }
+
+    /**
+     * Returns an array of short values associated with the specified id.  If
+     * the specified id does
+     * not exist within this JSAPResult, this method returns an empty array
+     * (i.e., array.length==0).
+     * @param id the id of the short value(s) to return.
+     * @return an array containing the short value(s) associated with the
+     * specified id, or an
+     * empty array if the specified id does not exist within this JSAPResult.
+     * @see #getShort(String)
+     * @see #getShort(String,short)
+     */
+    public short[] getShortArray(String id) {
+        Short[] tmp = (Short[]) getObjectArray(id, new Short[0]);
+        int tmplength = tmp.length;
+        short[] result = new short[tmplength];
+        for (int i = 0; i < tmplength; ++i) {
+            result[i] = tmp[i].shortValue();
+        }
+        return (result);
+    }
+
+    /**
+     * Returns the first double value associated with the specified id.
+     * @param id the id of the double value to retrieve
+     * @return the double value associated with the specified id.
+     * @see #getDouble(String,double)
+     * @see #getDoubleArray(String)
+     */
+    public double getDouble(String id) {
+    	if (! contains(id)) throw new UnspecifiedParameterException(id);
+        return (((Double) getObject(id)).doubleValue());
+    }
+
+    /**
+     * Returns the first double value associated with the specified id.  If the
+     * specified id does not
+     * exist within this JSAPResult, or if the object(s) associated with the
+     * specified id are not of
+     * type java.lang.Double, the specified default value is returned.
+     * @param id the id of the double value to retrieve
+     * @param defaultValue the value to return if the specified id does not
+     * exist within this
+     * JSAPResult, or if the object(s) associated with the specified id are
+     * not of type
+     * java.lang.Double.
+     * @return the first double value associated with the specified id.  If
+     * the specified id does not
+     * exist within this JSAPResult, or if the object(s) associated with the
+     * specified id are not of
+     * type java.lang.Double, the specified default value is returned.
+     * @see #getDouble(String)
+     * @see #getDoubleArray(String)
+     */
+    public double getDouble(String id, double defaultValue) {
+        Double result = (Double) getObject(id);
+        return ((result == null) ? defaultValue : result.doubleValue());
+    }
+
+    /**
+     * Returns an array of double values associated with the specified id.  If
+     * the specified id does
+     * not exist within this JSAPResult, this method returns an empty array
+     * (i.e., array.length==0).
+     * @param id the id of the double value(s) to return.
+     * @return an array containing the double value(s) associated with the
+     * specified id, or an
+     * empty array if the specified id does not exist within this JSAPResult.
+     * @see #getDouble(String)
+     * @see #getDouble(String,double)
+     */
+    public double[] getDoubleArray(String id) {
+        Double[] tmp = (Double[]) getObjectArray(id, new Double[0]);
+        int tmplength = tmp.length;
+        double[] result = new double[tmplength];
+        for (int i = 0; i < tmplength; ++i) {
+            result[i] = tmp[i].doubleValue();
+        }
+        return (result);
+    }
+
+    /**
+     * Returns the first float value associated with the specified id.
+     * @param id the id of the float value to retrieve
+     * @return the float value associated with the specified id.
+     * @see #getFloat(String,float)
+     * @see #getFloatArray(String)
+     */
+    public float getFloat(String id) {
+    	if (! contains(id)) throw new UnspecifiedParameterException(id);
+        return (((Float) getObject(id)).floatValue());
+    }
+
+    /**
+     * Returns the first float value associated with the specified id.  If the
+     * specified id does not
+     * exist within this JSAPResult, or if the object(s) associated with the
+     * specified id are not of
+     * type java.lang.Float, the specified default value is returned.
+     * @param id the id of the float value to retrieve
+     * @param defaultValue the value to return if the specified id does not
+     * exist within this
+     * JSAPResult, or if the object(s) associated with the specified id are
+     * not of type
+     * java.lang.Float.
+     * @return the first float value associated with the specified id.  If the
+     * specified id does not
+     * exist within this JSAPResult, or if the object(s) associated with the
+     * specified id are not of
+     * type java.lang.Float, the specified default value is returned.
+     * @see #getFloat(String)
+     * @see #getFloatArray(String)
+     */
+    public float getFloat(String id, float defaultValue) {
+        Float result = (Float) getObject(id);
+        return ((result == null) ? defaultValue : result.floatValue());
+    }
+
+    /**
+     * Returns an array of float values associated with the specified id.  If
+     * the specified id does
+     * not exist within this JSAPResult, this method returns an empty array
+     * (i.e., array.length==0).
+     * @param id the id of the float value(s) to return.
+     * @return an array containing the float value(s) associated with the
+     * specified id, or an
+     * empty array if the specified id does not exist within this JSAPResult.
+     * @see #getFloat(String)
+     * @see #getFloat(String,float)
+     */
+    public float[] getFloatArray(String id) {
+        Float[] tmp = (Float[]) getObjectArray(id, new Float[0]);
+        int tmplength = tmp.length;
+        float[] result = new float[tmplength];
+        for (int i = 0; i < tmplength; ++i) {
+            result[i] = tmp[i].floatValue();
+        }
+        return (result);
+    }
+
+    /**
+     * Returns the first String value associated with the specified id.
+     * @param id the id of the String value to retrieve
+     * @return the String value associated with the specified id.
+     * @see #getString(String,String)
+     * @see #getStringArray(String)
+     */
+    public String getString(String id) {
+        return ((String) getObject(id));
+    }
+
+    /**
+     * Returns the first String value associated with the specified id.  If
+     * the specified id does not
+     * exist within this JSAPResult, or if the object(s) associated with the
+     * specified id are not of
+     * type java.lang.String, the specified default value is returned.
+     * @param id the id of the String value to retrieve
+     * @param defaultValue the value to return if the specified id does not
+     * exist within this
+     * JSAPResult, or if the object(s) associated with the specified id are
+     * not of type
+     * java.lang.String.
+     * @return the first String value associated with the specified id.  If
+     * the specified id does not
+     * exist within this JSAPResult, or if the object(s) associated with the
+     * specified id are not of
+     * type java.lang.String, the specified default value is returned.
+     * @see #getString(String)
+     * @see #getStringArray(String)
+     */
+    public String getString(String id, String defaultValue) {
+        String result = (String) getObject(id);
+        return ((result == null) ? defaultValue : result);
+    }
+
+    /**
+     * Returns an array of String values associated with the specified id.
+     * If the specified id does
+     * not exist within this JSAPResult, this method returns an empty array
+     * (i.e., array.length==0).
+     * @param id the id of the String value(s) to return.
+     * @return an array containing the String value(s) associated with the
+     * specified id, or an
+     * empty array if the specified id does not exist within this JSAPResult.
+     * @see #getString(String)
+     * @see #getString(String,String)
+     */
+    public String[] getStringArray(String id) {
+        return ((String[]) getObjectArray(id, new String[0]));
+    }
+
+    /**
+     * Returns the first BigDecimal value associated with the specified id.
+     * @param id the id of the BigDecimal value to retrieve
+     * @return the BigDecimal value associated with the specified id.
+     * @see #getBigDecimal(String, BigDecimal)
+     * @see #getBigDecimalArray(String)
+     * @see java.math.BigDecimal
+     */
+    public BigDecimal getBigDecimal(String id) {
+        return ((BigDecimal) getObject(id));
+    }
+
+    /**
+     * Returns the first BigDecimal value associated with the specified id.
+     * If the specified id does not
+     * exist within this JSAPResult, or if the object(s) associated with the
+     * specified id are not of
+     * type java.math.BigDecimal, the specified default value is returned.
+     * @param id the id of the BigDecimal value to retrieve
+     * @param defaultValue the value to return if the specified id does not
+     * exist within this
+     * JSAPResult, or if the object(s) associated with the specified id are
+     * not of type
+     * java.math.BigDecimal.
+     * @return the first BigDecimal value associated with the specified id.
+     * If the specified id does not
+     * exist within this JSAPResult, or if the object(s) associated with the
+     * specified id are not of
+     * type java.lang.BigDecimal, the specified default value is returned.
+     * @see #getBigDecimal(String)
+     * @see #getBigDecimalArray(String)
+     * @see java.math.BigDecimal
+     */
+    public BigDecimal getBigDecimal(String id, BigDecimal defaultValue) {
+        BigDecimal result = (BigDecimal) getObject(id);
+        return ((result == null) ? defaultValue : result);
+    }
+
+    /**
+     * Returns an array of BigDecimal values associated with the specified id.
+     * If the specified id does
+     * not exist within this JSAPResult, this method returns an empty array
+     * (i.e., array.length==0).
+     * @param id the id of the BigDecimal value(s) to return.
+     * @return an array containing the BigDecimal value(s) associated with the
+     * specified id, or an
+     * empty array if the specified id does not exist within this JSAPResult.
+     * @see #getBigDecimal(String)
+     * @see #getBigDecimal(String,BigDecimal)
+     * @see java.math.BigDecimal
+     */
+    public BigDecimal[] getBigDecimalArray(String id) {
+        return ((BigDecimal[]) getObjectArray(id, new BigDecimal[0]));
+    }
+
+    /**
+     * Returns the first BigInteger value associated with the specified id.
+     * @param id the id of the BigInteger value to retrieve
+     * @return the BigInteger value associated with the specified id.
+     * @see #getBigInteger(String, BigInteger)
+     * @see #getBigIntegerArray(String)
+     * @see java.math.BigInteger
+     */
+    public BigInteger getBigInteger(String id) {
+        return ((BigInteger) getObject(id));
+    }
+
+    /**
+     * Returns the first BigInteger value associated with the specified id.
+     * If the specified id does not
+     * exist within this JSAPResult, or if the object(s) associated with the
+     * specified id are not of
+     * type java.math.BigInteger, the specified default value is returned.
+     * @param id the id of the BigInteger value to retrieve
+     * @param defaultValue the value to return if the specified id does not
+     * exist within this
+     * JSAPResult, or if the object(s) associated with the specified id are not
+     * of type
+     * java.math.BigInteger.
+     * @return the first boolean value associated with the specified id.  If
+     * the specified id does not
+     * exist within this JSAPResult, or if the object(s) associated with the
+     * specified id are not of
+     * type java.lang.BigInteger, the specified default value is returned.
+     * @see #getBigInteger(String)
+     * @see #getBigIntegerArray(String)
+     * @see java.math.BigInteger
+     */
+    public BigInteger getBigInteger(String id, BigInteger defaultValue) {
+        BigInteger result = (BigInteger) getObject(id);
+        return ((result == null) ? defaultValue : result);
+    }
+
+    /**
+     * Returns an array of BigInteger values associated with the specified id.
+     * If the specified id does
+     * not exist within this JSAPResult, this method returns an empty array
+     * (i.e., array.length==0).
+     * @param id the id of the BigInteger value(s) to return.
+     * @return an array containing the BigInteger value(s) associated with the
+     * specified id, or an
+     * empty array if the specified id does not exist within this JSAPResult.
+     * @see #getBigInteger(String)
+     * @see #getBigInteger(String,BigInteger)
+     * @see java.math.BigInteger
+     */
+    public BigInteger[] getBigIntegerArray(String id) {
+        return ((BigInteger[]) getObjectArray(id, new BigInteger[0]));
+    }
+
+    /**
+     * Returns the first Class value associated with the specified id.
+     * @param id the id of the Class value to retrieve
+     * @return the Class value associated with the specified id.
+     * @see #getClass(String, Class)
+     * @see #getClassArray(String)
+     * @see java.lang.Class
+     */
+    public Class getClass(String id) {
+        return ((Class) getObject(id));
+    }
+
+    /**
+     * Returns the first Class value associated with the specified id.  If the
+     * specified id does not
+     * exist within this JSAPResult, or if the object(s) associated with the
+     * specified id are not of
+     * type java.lang.Class, the specified default value is returned.
+     * @param id the id of the Class value to retrieve
+     * @param defaultValue the value to return if the specified id does not
+     * exist within this
+     * JSAPResult, or if the object(s) associated with the specified id are
+     * not of type
+     * java.lang.Class.
+     * @return the first Class value associated with the specified id.  If
+     * the specified id does not
+     * exist within this JSAPResult, or if the object(s) associated with the
+     * specified id are not of
+     * type java.lang.Class, the specified default value is returned.
+     * @see #getClass(String)
+     * @see #getClassArray(String)
+     * @see java.lang.Class
+     */
+    public Class getClass(String id, Class defaultValue) {
+        Class result = (Class) getObject(id);
+        return ((result == null) ? defaultValue : result);
+    }
+
+    /**
+     * Returns an array of Class values associated with the specified id.
+     * If the specified id does
+     * not exist within this JSAPResult, this method returns an empty array
+     * (i.e., array.length==0).
+     * @param id the id of the Class value(s) to return.
+     * @return an array containing the Class value(s) associated with the
+     * specified id, or an
+     * empty array if the specified id does not exist within this JSAPResult.
+     * @see #getClass(String)
+     * @see #getClass(String,Class)
+     * @see java.lang.Class
+     */
+    public Class[] getClassArray(String id) {
+        return ((Class[]) getObjectArray(id, new Class[0]));
+    }
+
+    /**
+     * Returns the first InetAddress value associated with the specified id.
+     * @param id the id of the InetAddress value to retrieve
+     * @return the InetAddress value associated with the specified id.
+     * @see #getInetAddress(String, InetAddress)
+     * @see #getInetAddressArray(String)
+     * @see java.net.InetAddress
+     */
+    public InetAddress getInetAddress(String id) {
+        return ((InetAddress) getObject(id));
+    }
+
+    /**
+     * Returns the first InetAddress value associated with the specified id.
+     * If the specified id does not
+     * exist within this JSAPResult, or if the object(s) associated with the
+     * specified id are not of
+     * type java.net.InetAddress, the specified default value is returned.
+     * @param id the id of the InetAddress value to retrieve
+     * @param defaultValue the value to return if the specified id does not
+     * exist within this
+     * JSAPResult, or if the object(s) associated with the specified id are
+     * not of type java.net.InetAddress.
+     * @return the first InetAddress value associated with the specified id.
+     * If the specified id does not
+     * exist within this JSAPResult, or if the object(s) associated with the
+     * specified id are not of
+     * type java.lang.InetAddress, the specified default value is returned.
+     * @see #getInetAddress(String)
+     * @see #getInetAddressArray(String)
+     * @see java.net.InetAddress
+     */
+    public InetAddress getInetAddress(String id, InetAddress defaultValue) {
+        InetAddress result = (InetAddress) getObject(id);
+        return ((result == null) ? defaultValue : result);
+    }
+
+    /**
+     * Returns an array of InetAddress values associated with the specified id.
+     * If the specified id does
+     * not exist within this JSAPResult, this method returns an empty array
+     * (i.e., array.length==0).
+     * @param id the id of the InetAddress value(s) to return.
+     * @return an array containing the InetAddress value(s) associated with the
+     * specified id, or an
+     * empty array if the specified id does not exist within this JSAPResult.
+     * @see #getInetAddress(String)
+     * @see #getInetAddress(String,InetAddress)
+     * @see java.net.InetAddress
+     */
+    public InetAddress[] getInetAddressArray(String id) {
+        return ((InetAddress[]) getObjectArray(id, new InetAddress[0]));
+    }
+
+    /**
+     * Returns the first Package value associated with the specified id.
+     * @param id the id of the Package value to retrieve
+     * @return the Package value associated with the specified id.
+     * @see #getPackage(String, Package)
+     * @see #getPackageArray(String)
+     * @see java.lang.Package
+     */
+    public Package getPackage(String id) {
+        return ((Package) getObject(id));
+    }
+
+    /**
+     * Returns the first Package value associated with the specified id.  If
+     * the specified id does not
+     * exist within this JSAPResult, or if the object(s) associated with the
+     * specified id are not of
+     * type java.lang.Package, the specified default value is returned.
+     * @param id the id of the Package value to retrieve
+     * @param defaultValue the value to return if the specified id does not
+     * exist within this
+     * JSAPResult, or if the object(s) associated with the specified id are
+     * not of type
+     * java.lang.Package.
+     * @return the first Package value associated with the specified id.  If
+     * the specified id does not
+     * exist within this JSAPResult, or if the object(s) associated with the
+     * specified id are not of
+     * type java.lang.Package, the specified default value is returned.
+     * @see #getPackage(String)
+     * @see #getPackageArray(String)
+     * @see java.lang.Package
+     */
+    public Package getPackage(String id, Package defaultValue) {
+        Package result = (Package) getObject(id);
+        return ((result == null) ? defaultValue : result);
+    }
+
+    /**
+     * Returns an array of Package values associated with the specified id.  If
+     * the specified id does
+     * not exist within this JSAPResult, this method returns an empty array
+     * (i.e., array.length==0).
+     * @param id the id of the Package value(s) to return.
+     * @return an array containing the Package value(s) associated with the
+     * specified id, or an
+     * empty array if the specified id does not exist within this JSAPResult.
+     * @see #getPackage(String)
+     * @see #getPackage(String,Package)
+     * @see java.lang.Package
+     */
+    public Package[] getPackageArray(String id) {
+        return ((Package[]) getObjectArray(id, new Package[0]));
+    }
+
+    /**
+     * Returns the first URL value associated with the specified id.
+     * @param id the id of the URL value to retrieve
+     * @return the URL value associated with the specified id.
+     * @see #getURL(String,URL)
+     * @see #getURLArray(String)
+     * @see java.net.URL
+     */
+    public URL getURL(String id) {
+        return ((URL) getObject(id));
+    }
+
+    /**
+     * Returns the first URL value associated with the specified id.  If the
+     * specified id does not
+     * exist within this JSAPResult, or if the object(s) associated with the
+     * specified id are not of
+     * type java.net.URL, the specified default value is returned.
+     * @param id the id of the URL value to retrieve
+     * @param defaultValue the value to return if the specified id does not
+     * exist within this
+     * JSAPResult, or if the object(s) associated with the specified id are
+     * not of type
+     * java.net.URL.
+     * @return the first URL value associated with the specified id.  If the
+     * specified id does not
+     * exist within this JSAPResult, or if the object(s) associated with the
+     * specified id are not of
+     * type java.net.URL, the specified default value is returned.
+     * @see #getURL(String)
+     * @see #getURLArray(String)
+     * @see java.net.URL
+     */
+    public URL getURL(String id, URL defaultValue) {
+        URL result = (URL) getObject(id);
+        return ((result == null) ? defaultValue : result);
+    }
+
+    /**
+     * Returns an array of URL values associated with the specified id.  If the
+     * specified id does
+     * not exist within this JSAPResult, this method returns an empty array
+     * (i.e., array.length==0).
+     * @param id the id of the URL value(s) to return.
+     * @return an array containing the URL value(s) associated with the
+     * specified id, or an
+     * empty array if the specified id does not exist within this JSAPResult.
+     * @see #getURL(String)
+     * @see #getURL(String,URL)
+     * @see java.net.URL
+     */
+    public URL[] getURLArray(String id) {
+        return ((URL[]) getObjectArray(id, new URL[0]));
+    }
+
+    /**
+     * Returns the first Color value associated with the specified id.
+     * @param id the id of the Color value to retrieve
+     * @return the Color value associated with the specified id.
+     * @see #getColor(String,Color)
+     * @see #getColorArray(String)
+     * @see java.awt.Color
+     */
+    public Color getColor(String id) {
+        return ((Color) getObject(id));
+    }
+
+    /**
+     * Returns the first File value associated with the specified id.
+     * @param id the id of the File value to retrieve
+     * @return the File value associated with the specified id.
+     * @see #getFile(String,File)
+     * @see #getFileArray(String)
+     * @see java.io.File
+     */
+    public File getFile(String id) {
+    	return ((File) getObject(id));
+    }
+    
+    /**
+     * Returns the first Color value associated with the specified id.  If the
+     * specified id does not
+     * exist within this JSAPResult, or if the object(s) associated with the
+     * specified id are not of
+     * type java.awt.Color, the specified default value is returned.
+     * @param id the id of the Color value to retrieve
+     * @param defaultValue the value to return if the specified id does not
+     * exist within this
+     * JSAPResult, or if the object(s) associated with the specified id are
+     * not of type
+     * java.lang.Color.
+     * @return the first Color value associated with the specified id.  If the
+     * specified id does not
+     * exist within this JSAPResult, or if the object(s) associated with the
+     * specified id are not of
+     * type java.awt.Color, the specified default value is returned.
+     * @see #getColor(String)
+     * @see #getColorArray(String)
+     * @see java.awt.Color
+     */
+    public Color getColor(String id, Color defaultValue) {
+        Color result = (Color) getObject(id);
+        return ((result == null) ? defaultValue : result);
+    }
+
+    /**
+     * Returns the first File value associated with the specified id.  If the
+     * specified id does not
+     * exist within this JSAPResult, or if the object(s) associated with the
+     * specified id are not of
+     * type java.io.File, the specified default value is returned.
+     * @param id the id of the File value to retrieve
+     * @param defaultValue the value to return if the specified id does not
+     * exist within this
+     * JSAPResult, or if the object(s) associated with the specified id are
+     * not of type
+     * java.io.File.
+     * @return the first File value associated with the specified id.  If the
+     * specified id does not
+     * exist within this JSAPResult, or if the object(s) associated with the
+     * specified id are not of
+     * type java.io.File, the specified default value is returned.
+     * @see #getFile(String)
+     * @see #getFileArray(String)
+     * @see java.io.File
+     */
+    public File getFile(String id, File defaultValue) {
+    	File result = (File) getObject(id);
+        return ((result == null) ? defaultValue : result);
+    }
+    
+    /**
+     * Returns an array of Color values associated with the specified id.  If
+     * the specified id does
+     * not exist within this JSAPResult, this method returns an empty array
+     * (i.e., array.length==0).
+     * @param id the id of the Color value(s) to return.
+     * @return an array containing the Color value(s) associated with the
+     * specified id, or an
+     * empty array if the specified id does not exist within this JSAPResult.
+     * @see #getColor(String)
+     * @see #getColor(String,Color)
+     * @see java.awt.Color
+     */
+    public Color[] getColorArray(String id) {
+        return ((Color[]) getObjectArray(id, new Color[0]));
+    }
+
+    /**
+     * Returns an array of File values associated with the specified id.  If
+     * the specified id does
+     * not exist within this JSAPResult, this method returns an empty array
+     * (i.e., array.length==0).
+     * @param id the id of the File value(s) to return.
+     * @return an array containing the File value(s) associated with the
+     * specified id, or an
+     * empty array if the specified id does not exist within this JSAPResult.
+     * @see #getFile(String)
+     * @see #getFile(String,File)
+     * @see java.io.File
+     */
+    public File[] getFileArray(String id) {
+        return ((File[]) getObjectArray(id, new File[0]));
+    }
+    /**
+     * Returns the first Date value associated with the specified id.
+     * @param id the id of the Date value to retrieve
+     * @return the Date value associated with the specified id.
+     * @see #getDate(String,Date)
+     * @see #getDateArray(String)
+     * @see java.util.Date
+     */
+    public Date getDate(String id) {
+        return ((Date) getObject(id));
+    }
+
+    /**
+     * Returns the first Date value associated with the specified id.  If the
+     * specified id does not
+     * exist within this JSAPResult, or if the object(s) associated with the
+     * specified id are not of
+     * type java.util.Date, the specified default value is returned.
+     * @param id the id of the Date value to retrieve
+     * @param defaultValue the value to return if the specified id does not
+     * exist within this
+     * JSAPResult, or if the object(s) associated with the specified id are not
+     * of type
+     * java.util.Date.
+     * @return the first Date value associated with the specified id.  If the
+     * specified id does not
+     * exist within this JSAPResult, or if the object(s) associated with the
+     * specified id are not of
+     * type java.util.Date, the specified default value is returned.
+     * @see #getDate(String)
+     * @see #getDateArray(String)
+     * @see java.util.Date
+     */
+    public Date getDate(String id, Date defaultValue) {
+        Date result = (Date) getObject(id);
+        return ((result == null) ? defaultValue : result);
+    }
+
+    /**
+     * Returns an array of Date values associated with the specified id.  If
+     * the specified id does
+     * not exist within this JSAPResult, this method returns an empty array
+     * (i.e., array.length==0).
+     * @param id the id of the Date value(s) to return.
+     * @return an array containing the Date value(s) associated with the
+     * specified id, or an
+     * empty array if the specified id does not exist within this JSAPResult.
+     * @see #getDate(String)
+     * @see #getDate(String,Date)
+     * @see java.util.Date
+     */
+    public Date[] getDateArray(String id) {
+        return ((Date[]) getObjectArray(id, new Date[0]));
+    }
+
+    /**
+     * Returns the first exception associated with the specified parameter ID.
+     * "General"
+     * exceptions can be retrieved with a null id.  If no exceptions are
+     * associated
+     * with the specified parameter ID, null is returned.
+     * @param id the unique ID of the parameter for which the first exception
+     * is requested
+     * @return the first exception associated with the specified ID, or null
+     * if no
+     * exceptions are associated with the specified ID.
+     * @see com.martiansoftware.jsap.ExceptionMap#getException(String)
+     */
+    public Exception getException(String id) {
+        Exception result = null;
+        List el = (List) allExceptions.get(id);
+        if ((el != null) && (el.size() > 0)) {
+            result = (Exception) el.get(0);
+        }
+        return (result);
+    }
+
+    /**
+     * Returns an array of ALL exceptions associated with the specified
+     * parameter ID.
+     * If no exceptions are associated with the specified parameter ID, an empty
+     * (zero-length) array is returned.
+     * @param id the unique ID of the parameter for which the exceptions are
+     * requested.
+     * @return an array of ALL exceptions associated with the specified
+     * parameter ID,
+     * or an empty (zero-length) array if no exceptions are associated with the
+     * specified parameter ID.
+     * @see com.martiansoftware.jsap.ExceptionMap#getExceptionArray(String)
+     */
+    public Exception[] getExceptionArray(String id) {
+        Exception[] result = new Exception[0];
+        List el = (List) allExceptions.get(id);
+        if (el != null) {
+            result = (Exception[]) el.toArray(result);
+        }
+        return (result);
+    }
+
+    /**
+     * Returns an Iterator ovar ALL exceptions associated with the specified
+     * parameter ID.
+     * If no exceptions are associated with the specified parameter ID, an empty
+     * iterator (NOT null) is returned.
+     * @param id the unique ID of the parameter for which the exceptions are
+     * requested.
+     * @return an Iterator over ALL exceptions associated with the specified
+     * parameter ID
+     */
+    public Iterator getExceptionIterator(String id) {
+        List el = (List) allExceptions.get(id);
+        if (el == null) {
+            el = new java.util.ArrayList();
+        }
+        return (el.iterator());
+    }
+
+    /**
+     * Returns an iterator over all error messages generated during parsing.
+     * If no errors occured, the iterator will be empty.
+     * @return an iterator over all error messages generated during parsing.
+     */
+    public Iterator getErrorMessageIterator() {
+        return (chronologicalErrorMessages.iterator());
+    }
+
+    /**
+     * Returns an Iterator over the IDs of all parameters with associated
+     * exceptions (which can in turn be obtained via
+     * getExceptionIterator(String)).
+     * General exceptions not associated with any particular parameter are
+     * associated with the null ID, so null may be returned by this Iterator.
+     *
+     * @return an Iterator over the IDs of all parameters with associated
+     * exceptions.
+     */
+    public Iterator getBadParameterIDIterator() {
+        return (allExceptions.keySet().iterator());
+    }
+
+    /**
+     * Returns a boolean indicating whether the parse that produced this
+     * JSAPResult
+     * was successful.  If this method returns false, detailed information
+     * regarding
+     * the reasons for the failed parse can be obtained via the getException()
+     * methods.
+     * @return a boolean indicating whether the parse that produced this
+     * JSAPResult
+     * was successful.
+     */
+    public boolean success() {
+        return (allExceptions.size() == 0);
+    }
+
+    /**
+     * Informs the JSAPResult of a QualifiedSwitch (necessary
+     * for getBoolean() to work properly for QualifiedSwitches)
+     * @param qsid the ID of the QualifiedSwitch
+     * @param present boolean indicating whether the QualifiedSwitch is present
+     */
+    void registerQualifiedSwitch(String qsid, boolean present) {
+    	qualifiedSwitches.put(qsid, new Boolean(present));
+    }
+}
diff --git a/src/java/com/martiansoftware/jsap/Option.java b/src/java/com/martiansoftware/jsap/Option.java
new file mode 100644
index 0000000..e881fbc
--- /dev/null
+++ b/src/java/com/martiansoftware/jsap/Option.java
@@ -0,0 +1,238 @@
+/*
+ * Copyright (c) 2002-2004, Martian Software, Inc.
+ * This file is made available under the LGPL as described in the accompanying
+ * LICENSE.TXT file.
+ */
+
+package com.martiansoftware.jsap;
+
+import java.util.List;
+
+/**
+ * The base class from which FlaggedOption and UnflaggedOption are derived.
+ * An Option is a Parameter
+ * that requires some information (unlike a Switch whose mere presence is
+ * significant).<br>
+ * <br> 
+ * Options may be declared as lists, or multiple values separated by a
+ * delimiting character.  An example of
+ * a list option might be a classpath, which is a collection of paths separated
+ * by a ":" on *nix systems and
+ * a ";" on DOS/Windows systems.  JSAP automatically separates list options
+ * into multiple tokens before calling
+ * their StringParsers' parse() method.
+ * 
+ * <p>The default list separator is JSAP.DEFAULT_LISTSEPARATOR, which is defined
+ * as the platform's path separator
+ * character (":" on *nix and ";" on DOS/Windows as described above).
+ * @author <a href="http://www.martiansoftware.com/contact.html">Marty Lamb</a>
+ * @see com.martiansoftware.jsap.Flagged
+ * @see com.martiansoftware.jsap.Option
+ */
+public abstract class Option extends Parameter {
+
+    /**
+     * Boolean indicating whether this option is a list.  Default is
+     * JSAP.NOT_LIST.
+     */
+    private boolean isList = JSAP.NOT_LIST;
+
+    /**
+     * Boolean indicating whether this option is required.  Default is
+     * JSAP.NOT_REQUIRED.
+     */
+    private boolean required = JSAP.NOT_REQUIRED;
+
+    /**
+     * The current list separator character.  Default is
+     * JSAP.DEFAULT_LISTSEPARATOR.
+     */
+    private char listSeparator = JSAP.DEFAULT_LISTSEPARATOR;
+
+    /**
+     * The current StringParser.  Default is null, although a
+     * StringStringParser will be created if necessary
+     * when this Option's parse() method is called.
+     */
+    private StringParser stringParser = null;
+
+    /**
+     * Creates a new Option with the specified unique ID.
+     * @param id the unique ID for this Option.
+     */
+    public Option(String id) {
+        super(id);
+    }
+
+    /**
+     * Sets whether this Option is a list.  Default behavior is JSAP.NOT_LIST.
+     * @param isList if true, this Option is a list.
+     */
+    protected final void internalSetList(boolean isList) {
+        enforceParameterLock();
+        this.isList = isList;
+    }
+
+    /**
+     * Returns a boolean indicating whether this Option is a list.
+     * @return a boolean indicating whether this Option is a list.
+     */
+    public final boolean isList() {
+        return (isList);
+    }
+
+    /**
+     * Sets the list separator character for this Option.  The default list
+     * separator is JSAP.DEFAULT_LISTSEPARATOR.
+     * @param listSeparator the list separator for this Option.
+     */
+    protected final void internalSetListSeparator(char listSeparator) {
+        enforceParameterLock();
+        this.listSeparator = listSeparator;
+    }
+
+    /**
+     * Returns the current list separator character for this Option.
+     * @return the current list separator character for this Option.
+     */
+    public final char getListSeparator() {
+        return (listSeparator);
+    }
+
+    /**
+     * Sets whether this Option is required.  Default is JSAP.NOT_REQUIRED.
+     * @param required if true, this Option will be required.
+     */
+    protected final void internalSetRequired(boolean required) {
+        enforceParameterLock();
+        this.required = required;
+    }
+
+    /**
+     * Returns a boolean indicating whether this Option is required.
+     * @return a boolean indicating whether this Option is required.
+     */
+    public final boolean required() {
+        return (required);
+    }
+
+    /**
+     * Sets the StringParser to which this Option's parse() method should
+     * delegate.
+     * @param stringParser the StringParser to which this Option's parse()
+     * method should delegate.
+     * @see com.martiansoftware.jsap.StringParser
+     */
+    protected final void internalSetStringParser(StringParser stringParser) {
+        enforceParameterLock();
+        this.stringParser = stringParser;
+    }
+
+    /**
+     * Returns the StringParser to which this Option will delegate calls to its
+     * parse() method, or null if
+         * no StringParser is currently defined.
+     * @return the StringParser to which this Option will delegate calls to its
+     * parse() method, or null if
+         * no StringParser is currently defined.
+     */
+    public final StringParser getStringParser() {
+        return (stringParser);
+    }
+
+    /**
+     * Delegates the parsing of a single argument to the current StringParser,
+     * and stores the result in the
+     * specified List.  List options are broken into multiple arguments,
+     * resulting in multiple calls
+     * to this method.
+     * @param result the List to which the result should be appended.
+     * @param argToParse the argument to parse.
+     * @throws ParseException if the specified argument cannot be parsed.
+     */
+    private void storeParseResult(List result, String argToParse)
+        throws ParseException {
+    	if (argToParse == null) return;
+        Object parseResult = getStringParser().parse(argToParse);
+        if (parseResult != null) {
+            result.add(parseResult);
+        }
+    }
+
+    /**
+     * Parses the specified argument, returning the results in an ArrayList.
+     * List options are tokenized
+     * before the parse call is delegated to the StringParser.
+     * @param arg the argument to parse.
+     * @return a List of objects resulting from the parse.
+     * @throws ParseException if the specified argument (or one of its tokens,
+     * in the case of a list) cannot
+     * be parsed.
+     */
+    protected final List parse(String arg) throws ParseException {
+        List result = new java.util.ArrayList();
+        if (getStringParser() == null) {
+            boolean wasLocked = this.locked();
+            setLocked(false);
+            internalSetStringParser( JSAP.STRING_PARSER );
+            setLocked(wasLocked);
+        }
+
+        if ((arg == null)
+            || !isList()) { // handle null arguments and non-list arguments
+            storeParseResult(result, arg);
+        } else {
+            StringBuffer subarg = new StringBuffer();
+            int arglen = arg.length();
+            for (int index = 0; index < arglen; ++index) {
+                char c = arg.charAt(index);
+                if (c == getListSeparator()) {
+                    // parse the subargument stored so far...
+                    storeParseResult(result, subarg.toString());
+                    subarg.setLength(0);
+                } else {
+                    subarg.append(c);
+                    if (index == arglen - 1) {
+                        // needed to parse the last subargument
+                        storeParseResult(result, subarg.toString());
+                    }
+                }
+            }
+        }
+        return (result);
+    }
+
+    /**
+     * Informs this Option's StringParser that this Option is being registered
+     * with a JSAP.  If the StringParser requires any setup not taken care of
+     * in its constructor, it should override StringParser.setUp().
+     * @throws JSAPException if the underlying StringParser throws it.
+     * @see com.martiansoftware.jsap.StringParser#setUp()
+     */
+    protected void register() throws JSAPException {
+        StringParser stringParser = getStringParser();
+        try {
+            if (stringParser != null) {
+                stringParser.setUp();
+            }
+        } catch (Exception e) {
+            throw (new JSAPException(e.getMessage(), e));
+        }
+    }
+
+    /**
+     * Informs this Option's StringParser that this Option is being
+     * unregistered
+     * from a JSAP.  If the StringParser requires any cleanup, it should
+     * ovverride
+     * StringParser.tearDown().
+     * @see com.martiansoftware.jsap.StringParser#tearDown()
+     */
+    protected void unregister() {
+        StringParser stringParser = getStringParser();
+        if (stringParser != null) {
+            stringParser.tearDown();
+        }
+    }
+
+}
diff --git a/src/java/com/martiansoftware/jsap/Parameter.java b/src/java/com/martiansoftware/jsap/Parameter.java
new file mode 100644
index 0000000..0b0bf35
--- /dev/null
+++ b/src/java/com/martiansoftware/jsap/Parameter.java
@@ -0,0 +1,264 @@
+/*
+ * Copyright (c) 2002-2004, Martian Software, Inc.
+ * This file is made available under the LGPL as described in the accompanying
+ * LICENSE.TXT file.
+ */
+
+package com.martiansoftware.jsap;
+
+import java.util.List;
+
+
+/**
+ * <p>Top-level abstraction of a <b>parameter</b>.  A <b>parameter</b> consists
+ * of one or more <b>arguments</b> (command line tokens) that have a special
+ * meaning when taken together.
+ * For example, a command-line switch "-v" is a parameter consisting of a single
+ * argument, whereas a command-line option "--file somefile.txt" is a parameter
+ * consisting of two arguments.  Some parameters can be quite large, such as an
+ * option for a file compression utility that allows you to specify any number
+ * of files to comporess.</p>
+ * 
+ * <p>This is an abstract class.  See its subclasses {@link com.martiansoftware.jsap.Switch}, 
+ * {@link com.martiansoftware.jsap.FlaggedOption}, and {@link com.martiansoftware.jsap.UnflaggedOption}
+ * for details on the various types of parameters. Functionality common to all three
+ * types of Parameters is described below.</p>
+ * 
+ * <p>Each parameter has a unique ID assigned in its constructor.  This ID is
+ * used to retrieve values from the parser after the command line is parsed.
+ * You  can set the ID to any String value you wish, although in general you'll
+ * want them to be brief and descriptive to provide a degree of documentation.
+ * A "-h" switch, for example, might have the ID "help".
+ * The calling program can then determine if the user specified the -h switch on
+ * the command line with the simple call <code>getBoolean("help")</code> against
+ * the JSAPResult object produced by JSAP.parse().
+ *
+ * @author <a href="http://www.martiansoftware.com/contact.html">Marty Lamb</a>
+ * @see com.martiansoftware.jsap.Switch
+ * @see com.martiansoftware.jsap.FlaggedOption
+ * @see com.martiansoftware.jsap.UnflaggedOption
+ * @see com.martiansoftware.jsap.JSAPResult#getBoolean(String)
+ * @see com.martiansoftware.jsap.JSAP#parse(String[])
+ */
+
+public abstract class Parameter {
+
+    /**
+     * This parameter's unique ID.
+     */
+    private String id = null;
+
+    /**
+     * The name to use for this AbstractParameter when generating
+     * usage information.  If null, the id will be used.
+     */
+    private String usageName = null;
+    
+    /**
+     * If true, this parameter is "locked" (i.e., cannot be modified).
+     * Parameters are locked when they are stored in a JSAPConfiguration.
+     */
+    private boolean locked = false;
+
+    /**
+     * The default values for this parameter.  Multiple default values
+     * are permitted to accommodate parameters that allow lists or
+     * multiple declarations.
+     */
+    private String[] defaultValue = null;
+
+    /**
+     * This parameter's help text.
+     */
+    private String help = null;
+
+    /**
+     * Creates a new Parameter.  Subclasses should call this
+     * constructor.
+     *
+     * @param id   The ID for this argument.  All arguments MUST have
+     * a unique ID.
+     */
+    public Parameter(String id) {
+        this.id = id;
+    }
+
+    /**
+     * Returns this parameter's unique ID.
+     *
+     * @return this parameter's unique ID.
+     */
+    public String getID() {
+        return (id);
+    }
+
+    /**
+     * Locks or unlocks this parameter.  Locked parameters cannot be
+     * modified.  This is necessary because the JSAP object with
+     * which parameters are registered performs certain validation
+     * routines at the time of registration.  See
+     * JSAP.registerParameter(Parameter) for more information.
+     *
+     * @param locked if <code>TRUE</code>, locks this parameter.  if
+     * <code>FALSE</code>, unlocks it.
+     * @see
+     *    com.martiansoftware.jsap.JSAP#registerParameter(Parameter)
+     */
+    protected final void setLocked(boolean locked) {
+        this.locked = locked;
+    }
+
+    /**
+     * Returns a boolean indicating whether this parameter is locked.
+     *
+     * @return a boolean indicating whether this parameter is currently
+     * locked.
+     */
+    protected final boolean locked() {
+        return (locked);
+    }
+
+    /**
+     * Helper method that can be called by any methods that modify this
+     * parameter (or its subclasses).
+     * If the parameter is currently locked, an IllegalStateException
+     * is thrown.
+     */
+    protected final void enforceParameterLock() {
+        if (locked) {
+            throw (
+                new IllegalStateException(
+                    "Parameter '" + getID() + "' may not be changed."));
+        }
+    }
+
+    /**
+     * Sets a default value for this parameter.  The default is specified
+     * as a String, and is parsed as a single value specified on the
+     * command line.  In other words, default values for "list"
+     * parameters or parameters allowing multiple declarations should be
+     * set using setDefault(String[]), as JSAP
+     * would otherwise treat the entire list of values as a single value.
+     *
+     * @param defaultValue the default value for this parameter.
+     */
+    protected final void _setDefault(String defaultValue) {
+        if (defaultValue == JSAP.NO_DEFAULT) {
+            this.defaultValue = null;
+        } else {
+            this.defaultValue = new String[1];
+            this.defaultValue[0] = defaultValue;
+        }
+    }
+
+    /**
+     * Sets one or more default values for this parameter.  This method
+     * should be used whenever a parameter has more than one default
+     * value.
+     * @param defaultValues the default values for this parameter.
+     */
+    protected final void _setDefault(String[] defaultValues) {
+        this.defaultValue = defaultValues;
+    }
+
+    /**
+     * Adds a single default value to any currently defined for this
+     * parameter.
+     * @param defaultValue the default value to add to this parameter.
+     */
+    public final void addDefault(String defaultValue) {
+        if (defaultValue != JSAP.NO_DEFAULT) {
+            if (this.defaultValue == null) {
+                this.defaultValue = new String[0];
+            }
+            int defaultValueCount = this.defaultValue.length + 1;
+            String[] newDefaultValue = new String[defaultValueCount];
+            for (int i = 0; i < defaultValueCount - 1; ++i) {
+                newDefaultValue[i] = this.defaultValue[i];
+            }
+            newDefaultValue[defaultValueCount - 1] = defaultValue;
+            this.defaultValue = newDefaultValue;
+        }
+    }
+
+    /**
+     * Sets the name of this AbstractParameter for the purposes of
+     * usage information.  If null, the id will be used.
+     * @param usageName the usage name for this AbstractParameter
+     */
+    protected final void _setUsageName(String usageName) {
+    	this.usageName = usageName;
+    }
+    
+    /**
+     * Returns the name of this AbstractParameter for the purposes of
+     * usage information.  The returned value is by default the id of
+     * this parameter, but can be overridden via setUsageName(String).
+     * @return the name of this AbstractParameter for the purposes of
+     * usage information.
+     */
+    public final String getUsageName() {
+    	return (usageName == null ? id : usageName);
+    }
+    
+    /**
+     * Returns an array of default values for this parameter, or
+     * null if no default values have been defined.
+     * @return an array of default values for this parameter, or
+     * null if no default values have been defined.
+     */
+    public final String[] getDefault() {
+        return (this.defaultValue);
+    }
+
+    /**
+     * Returns an ArrayList of values resulting from the parsing
+     * of the specified argument. Multiple values may be returned
+     * if the arg is a list (such as a PATH or CLASSPATH variable),
+     * or if the semantics of the parameter type otherwise represent
+     * multiple values.
+     *
+     * @param   arg the argument to parse.
+     * @return  a List of values resulting from the parse.
+     * @throws  ParseException if the specified argument cannot be parsed.
+     */
+    protected abstract List parse(String arg) throws ParseException;
+
+    /**
+     * A convenience method for automatically generating syntax
+     * information based upon a JSAP configuration.  A call to
+     * JSAP.getSyntax() returns a String built by calling getSyntax() on
+     * every parameter registered to a JSAP in the order in which they
+     * were registered.  This method does not provide a help
+     * description (see getHelp()), but rather the syntax for this
+     * parameter.
+     * @return usage information for this parameter.
+     */
+    public abstract String getSyntax();
+
+	/**
+	 * Deprecated - use getSyntax()
+	 * @return deprecated - use getSyntax()
+	 * @deprecated use getSyntax() instead
+	 */
+	public final String getUsage() {
+		return (getSyntax());
+	}
+	
+    /**
+     * Returns a description of the option's usage.
+     * @return a textual description of this option's usage
+     */
+    public final String getHelp() {
+        return (help == null ? "" : help);
+    }
+
+    /**
+     * Sets the help text for this parameter.
+     * @param help the help text for this parameter.
+     */
+    public final Parameter setHelp(String help) {
+        this.help = help;
+        return this;
+    }
+}
diff --git a/src/java/com/martiansoftware/jsap/ParseException.java b/src/java/com/martiansoftware/jsap/ParseException.java
new file mode 100644
index 0000000..d83e76e
--- /dev/null
+++ b/src/java/com/martiansoftware/jsap/ParseException.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2002-2004, Martian Software, Inc.
+ * This file is made available under the LGPL as described in the accompanying
+ * LICENSE.TXT file.
+ */
+
+package com.martiansoftware.jsap;
+
+/**
+ * A JSAPException subclass notifying the application of a parse error.
+ * Additional information
+ * in the form of an exception may be encapsulated in this object, as in other
+ * JSAPExceptions.
+ * @author <a href="http://www.martiansoftware.com/contact.html">Marty Lamb</a>
+ * @see com.martiansoftware.jsap.JSAPException
+ */
+public class ParseException extends JSAPException {
+
+    /**
+     * Creates a new ParseException.
+     */
+    public ParseException() {
+        super();
+    }
+
+    /**
+     * Creates a new ParseException with the specified message.
+     * @param msg the message for this ParseException.
+     */
+    public ParseException(String msg) {
+        super(msg);
+    }
+
+    /**
+     * Creates a new ParseException encapsulating the specified Throwable.
+     * @param cause the Throwable to encapsulate.
+     */
+    public ParseException(Throwable cause) {
+        super(cause);
+    }
+
+    /**
+     * Creates a new ParseException with the specified message encapsulating the
+     * specified Throwable.
+     * @param msg the message for this ParseException.
+     * @param cause the Throwable to encapsulate.
+     */
+    public ParseException(String msg, Throwable cause) {
+        super(msg, cause);
+    }
+
+}
diff --git a/src/java/com/martiansoftware/jsap/Parser.java b/src/java/com/martiansoftware/jsap/Parser.java
new file mode 100644
index 0000000..485e8c4
--- /dev/null
+++ b/src/java/com/martiansoftware/jsap/Parser.java
@@ -0,0 +1,471 @@
+/*
+ * Copyright (c) 2002-2004, Martian Software, Inc.
+ * This file is made available under the LGPL as described in the accompanying
+ * LICENSE.TXT file.
+ */
+
+/*
+ * 09/26/2002	ML	:	Modified parseShortForm() and parseLongForm() to allow
+ *                      options that begin with a minus sign.  Previous
+ *                      behavior assumed that the minus sign indicated another
+ *                      flag and therefore no value for the current option.
+ */
+package com.martiansoftware.jsap;
+
+import java.util.Iterator;
+
+/**
+ * The class that performs the actual parsing on a set of arguments.  This is 
+ * created and managed by a JSAP;
+ * developers should never need to access this class directly.
+ * @author <a href="http://www.martiansoftware.com/contact.html">Marty Lamb</a>
+ * @see com.martiansoftware.jsap.JSAP
+ */
+class Parser {
+
+    /**
+     * A reference to the JSAP for which this Parser is working.
+     */
+    private JSAP config = null;
+
+    /**
+     * An Iterator through the declared unflagged options, in order of their 
+     * declaration.
+     */
+    private Iterator unflaggedOptions = null;
+
+    /**
+     * A reference to the currently expected UnflaggedOption.
+     */
+    private UnflaggedOption curUnflaggedOption = null;
+
+    /**
+     * The array of arguments to parse.
+     */
+    private String[] args = null;
+
+    /**
+     * Boolean indicating whether this Parser has already been run (a parser 
+     * can only run once).
+     */
+    private boolean parsed = false;
+
+    /**
+     * The result of the parse.
+     */
+    private JSAPResult result = null;
+
+    /**
+     * Creates a new Parser for the specified JSAP and argument list.
+     * @param config the JSAP for which this Parser is working.
+     * @param args the arguments to parse.
+     */
+    public Parser(JSAP config, String[] args) {
+        this.config = config;
+        this.args = args;
+        this.parsed = false;
+        this.result = new JSAPResult();
+        this.unflaggedOptions = config.getUnflaggedOptionsIterator();
+        advanceUnflaggedOption();
+        
+    }
+
+    /**
+     * Advances the current UnflaggedOption pointer to the next UnflaggedOption 
+     * (by declaration order).
+     * If there are no more expected UnflaggedOptions, the current 
+     * UnflaggedOption pointer is set to null.
+     */
+    private void advanceUnflaggedOption() {
+        if (unflaggedOptions.hasNext()) {
+            curUnflaggedOption = (UnflaggedOption) unflaggedOptions.next();
+        }
+        else {
+            curUnflaggedOption = null;
+        }
+    }
+
+    /**
+     * Instructs the specified parameter to parse the specified value, and 
+     * stores the results in the working
+     * JSAPResult.  Any ParseExceptions or IllegalMultipleDeclarationExceptions 
+     * that are encountered
+     * are associated with the specified parameter.
+     * @param param the parameter to parse the value
+     * @param valueToParse the value to parse
+     */
+    private void processParameter(Parameter param, 
+        String valueToParse) {
+
+        // if this argument has already been parsed at least once...
+        if (result.getObject(param.getID()) != null) {
+            if (((param instanceof FlaggedOption)
+                && (!((FlaggedOption) param).allowMultipleDeclarations()))
+                || (param instanceof Switch)
+				|| (param instanceof QualifiedSwitch)
+                || ((param instanceof UnflaggedOption)
+                && (!((UnflaggedOption) param).isGreedy()))) {
+                result.addException(param.getID(), 
+                    new IllegalMultipleDeclarationException(param.getID()));
+            }
+        }
+
+        if (param instanceof QualifiedSwitch) {
+        	result.registerQualifiedSwitch(param.getID(), true);
+        }
+        
+        try {
+            result.add(param.getID(), param.parse(valueToParse));
+        }
+        catch (ParseException pe) {
+            result.addException(param.getID(), pe);
+        }
+    }
+
+    /**
+     * Parses the next argument in the array.
+     *
+     * @param args the array containing the arguments
+     * @param index the index of the next argument to parse
+     * @return the index of the next argument to parse
+     */
+    private int parseArg(String[] args, int index) {
+        if (args[index].startsWith("--") && !args[index].equals("--")) {
+            return(parseLongForm(args, index));
+        }
+        else if (args[index].startsWith("-") && !args[index].equals("-") && !args[index].equals("--")) {
+            return(parseShortForm(args, index));
+        }
+        else {
+            return(parseUnflaggedOption(args, index));
+        }
+    }
+
+    /**
+     * Processes the default values for this parser's JSAP, and stores their 
+     * values in the 
+     * working JSAPResult
+     */
+    private void processDefaults() {
+        Defaults defaults = config.getDefaults(result);
+        for (Iterator i = defaults.idIterator(); i.hasNext();) {
+            String paramID = (String) i.next();
+            if (result.getObject(paramID) == null) {
+                String[] paramValues = defaults.getDefault(paramID);
+                if (paramValues != null) {
+                    int z = paramValues.length;
+                    for (int index = 0; index < z; ++index) {
+                        processParameter(
+                            config.getByID(paramID),
+                            paramValues[index]);
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Checks the current JSAPResult against the JSAP's configuration, and 
+     * stores a
+     * RequiredParameterMissingException in the JSAPResult for any missing 
+     * parameters.
+     */
+    private void enforceRequirements() {
+        IDMap idMap = config.getIDMap();
+        for (Iterator i = idMap.idIterator(); i.hasNext();) {
+            String id = (String) i.next();
+            Object o = config.getByID(id);
+            if (o instanceof Option) {
+                Option option = (Option) o;
+                if (option.required() && (result.getObject(id) == null)) {
+                	
+                	if (result.getException(id) == null) {
+	                    result.addException(option.getID(), 
+	                        new RequiredParameterMissingException(id));
+                	}
+                }
+            }
+        }
+    }
+
+    /**
+     * Runs the parser.
+     * @return a JSAPResult representing the parsed data.
+     */
+    public JSAPResult parse() {
+        if (parsed) {
+            result.addException(null, 
+                new JSAPException("This Parser has already run."));
+        }
+        else {
+        	result.setValuesFromUser(true);
+        	preregisterQualifiedSwitches();
+            int z = args.length;
+            int i = 0;
+            while (i < z) {
+                i = parseArg(args, i);
+            }
+            result.setValuesFromUser(false);
+            processDefaults();
+            enforceRequirements();
+            this.parsed = true;
+        }
+        return(result);
+    }
+
+    /**
+     * Loops through all parameters, informing the JSAPResult
+     * of any QualifiedSwitches so it can assume they're not present.
+     */
+    private void preregisterQualifiedSwitches() {
+    	for (Iterator iter = config.getIDMap().idIterator(); iter.hasNext();) {
+    		String thisID = (String) iter.next();
+    		Parameter param = config.getByID(thisID);
+    		if (param instanceof QualifiedSwitch) {
+    			result.registerQualifiedSwitch(thisID, false);
+    		}
+    	}
+    		
+    }
+    /**
+     * Parses the long form of an Option or Switch (i.e., preceded by a double 
+     * hyphen).
+     *
+     * @param  args    the argument array being parsed
+     * @param  index   the index of the current argument to parse
+     * @return the index of the next argument to parse
+     */
+    private int parseLongForm(String[] args, int index) {
+        int equalsIndex = args[index].indexOf('=');
+        int colonIndex = args[index].indexOf(':'); // KPB
+        String paramFlag = null;
+        String paramEquals = null;
+        // KPB<<<<<<
+        String paramColon = null; 
+        // if no equals sign and no colon for QualifiedSwitches, this whole argument is the long flag
+        if (equalsIndex == -1 && colonIndex == -1) {
+            paramFlag = args[index].substring(2);
+        }
+        if (equalsIndex != -1) {
+            // there's a value assignment in this argument, too; possible colons are taken as part of the value
+            paramFlag = args[index].substring(2, equalsIndex);
+            paramEquals = args[index].substring(equalsIndex + 1);
+        }
+        if (equalsIndex == -1 && colonIndex != -1) {
+            // we have a QualifiedSwitch
+            paramFlag = args[index].substring(2, colonIndex);
+            paramColon = args[index].substring(colonIndex); // parameter starts with ':'
+        }
+        // <<<<<<KPB
+        ++index;
+
+        // we'll need to reference the appropriate parameter both as a Flagged 
+        // and as 
+        // an Parameter down below.
+        Flagged option = config.getByLongFlag(paramFlag);
+        Parameter param = (Parameter) option;
+
+        // unknown long flag
+        if (option == null) {
+            result.addException(null, new UnknownFlagException(paramFlag));
+        }
+        else {
+            // if it's a switch, first check to make sure no value is specified.
+            if (option instanceof Switch) {
+                if (equalsIndex != -1) {
+                    result.addException(param.getID(), 
+                        new SyntaxException("Switch \"" 
+                        + paramFlag 
+                        + "\" does not take any parameters."));         
+                }
+                else {
+                    processParameter(param, null);
+                }
+            } else if (param instanceof QualifiedSwitch) {
+            	String paramValue = null;
+            	if (colonIndex == -1) {
+            		processParameter(param, null);
+            	} else {
+            		processParameter(param, args[index-1].substring(colonIndex + 1));
+            	}
+            	
+            } else {
+            	// it's an option
+            	if (equalsIndex == -1) {
+                    // no "=" supplied on command line, so next item must 
+                    // contain the value
+
+//					ML: Changed this and the corresponding line in
+//					parseShortForm.  Longer version (commented below) would
+//                  not allow values that begin with a minus sign.
+//					if ((index >= args.length) || args[index].startsWith("-")) {
+                    if (index >= args.length) {
+                        result.addException(param.getID(), 
+                            new SyntaxException("No value specified for option \"" 
+                            + paramFlag + "\""));
+                    }
+                    else {
+                        paramEquals = args[index];
+                        ++index;
+                        processParameter(param, paramEquals);
+                    }
+                }
+                else {
+                    processParameter(param, paramEquals);
+                }
+            }
+        }
+        return(index);
+    }
+
+    /**
+     * Parses the short form of an Option or Switch (i.e., preceded by a single 
+     * hyphen).
+     *
+     * @param  args    the argument array being parsed
+     * @param  index   the index of the current argument to parse
+     * @return the index of the next argument to parse
+     */
+    private int parseShortForm(String[] args, int index) {
+        Character paramFlag = null;
+        int equalsIndex = args[index].indexOf('=');
+        int parseTo = args[index].length();
+        if (equalsIndex != -1) {
+            parseTo = equalsIndex;
+        }
+
+        // loop through all the single characters up to the equals sign (if
+        // there is one) or the end
+        // of the argument
+        for (int charPos = 1; charPos < parseTo; ++charPos) {
+
+            // paramFlag is the single short character used as a flag.  
+            // multiple flags might exist here,
+            // which is why we're looping through them.
+            paramFlag = new Character(args[index].charAt(charPos));
+
+            // we'll need to reference the appropriate parameter both as a 
+            // Flagged and as 
+            // an Parameter down below.
+            Flagged option = config.getByShortFlag(paramFlag);
+            Parameter param = (Parameter) option;
+
+            // unknown flag
+            if (option == null) {
+                result.addException(null, new UnknownFlagException(paramFlag));
+            }
+            else {
+                // flag is a switch.  if it is, first check to make sure a value
+                // isn't specified,
+                // then process it.
+                if (option instanceof Switch) {
+                    if (charPos == (equalsIndex - 1)) {
+                        result.addException(param.getID(), 
+                            new SyntaxException("Switch \"" 
+                            + paramFlag 
+                            + "\" does not take any parameters."));
+                    } else {
+//                        // KPB-start
+//                        // Qualified switches have a format like "-o:value"
+//                        if (parseTo >= "-o:".length() && args[index].charAt(charPos+1) == ':') {
+//                            // we have a QualifiedSwitch
+//                            processParameter(param, args[index].substring(charPos+1)); 
+//                            break; // args[index] completely parsed, so get out of loop
+//                        }
+//                        else {
+                            processParameter(param, null);
+//                        }
+//                        //>>>>>KPB
+                    }
+                    // flag is not a switch, so we first check to make sure there IS
+                    // a value specified.
+                } else if(option instanceof QualifiedSwitch) {
+                	// flag is a QualifiedSwitch.
+                	if ((args[index].length() > (charPos + 1))
+							&& args[index].charAt(charPos+1) == ':') {
+                		// the QualifiedSwitch is, in fact, qualified
+                		processParameter(param, args[index].substring(charPos+2));// ':' must be part of the argument
+                		break; // args[index] completely parsed, so get out of loop
+                	}
+                	else {
+                		processParameter(param, null);
+                	}
+                	
+                } else {
+                	// it's an option
+                    String paramEquals = null;
+
+                    /* We now allow stuff like -b12.
+                     * 
+                     * if (charPos != (parseTo - 1)) {
+                        result.addException(param.getID(), 
+                            new SyntaxException("No value specified for option \"" 
+                            + paramFlag + "\"."));
+                    }
+                    else {*/
+                    
+                    // if there's an equals sign, the value is whatever is 
+                    // on the right
+                    if (equalsIndex != -1) {
+                    	paramEquals = 
+                    		args[index].substring(equalsIndex + 1);
+                    	processParameter(param, paramEquals);
+                    }
+                    else {
+                    	if (charPos < parseTo - 1) {
+                    		// the value is just after the short option (no space)
+                    		paramEquals = args[index].substring(charPos + 1, parseTo);
+                    		processParameter(param, paramEquals);
+                    		break;
+                    	}
+                    	else {
+                    		// otherwise, it's the next argument.
+                    		++index;
+                    		
+                    		//					ML: Changed this and the corresponding line in
+                    		//					parseLongForm.  Longer version (commented below) would
+                    		//                  not allow values that begin with a minus sign.
+                    		//							if ((index >= args.length) 
+                    		//								|| args[index].startsWith("-")) {
+                    		if (index >= args.length) {
+                    			result.addException(param.getID(), 
+                    					new SyntaxException(
+                    							"No value specified for option \"" 
+                    							+ paramFlag + "\"."));
+                    		}
+                    		else {
+                    			paramEquals = args[index];
+                    			processParameter(param, paramEquals);
+                    		}
+                    	}
+                    }
+                }
+            }
+        }
+
+        ++index;
+        return(index);
+    }
+
+    /**
+     * Parses the unflagged form of an Option (i.e., preceded by a no hyphens).
+     *
+     * @param  args    the argument array being parsed
+     * @param  index   the index of the current argument to parse
+     * @return the index of the next argument to parse
+     */
+    private int parseUnflaggedOption(String[] args, int index) {
+        if (curUnflaggedOption != null) {
+            processParameter(curUnflaggedOption, args[index]);
+            if (!curUnflaggedOption.isGreedy()) {
+                advanceUnflaggedOption();
+            }
+        }
+        else {
+            result.addException(null, new JSAPException("Unexpected argument: " 
+                + args[index]));
+        }
+        return(index + 1);
+    }
+
+}
diff --git a/src/java/com/martiansoftware/jsap/PropertyStringParser.java b/src/java/com/martiansoftware/jsap/PropertyStringParser.java
new file mode 100644
index 0000000..33747f4
--- /dev/null
+++ b/src/java/com/martiansoftware/jsap/PropertyStringParser.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2002-2004, Martian Software, Inc.
+ * This file is made available under the LGPL as described in the accompanying
+ * LICENSE.TXT file.
+ */
+
+package com.martiansoftware.jsap;
+
+import java.util.Properties;
+
+/**
+ * A {@link com.martiansoftware.jsap.StringParser} subclass that provides a means for setting/getting properties.
+ * This
+ * is intended to support StringParsers that might requires some configuration,
+ * such
+ * as DateStringParser (which needs a format String).
+ *
+ * @author <a href="http://www.martiansoftware.com/contact.html">Marty Lamb</a>
+ * @see com.martiansoftware.jsap.StringParser
+ * @see com.martiansoftware.jsap.stringparsers.DateStringParser
+ */
+public abstract class PropertyStringParser extends StringParser {
+
+    /**
+     * This PropertyStringParser's properties.
+     */
+    private Properties properties = null;
+
+    /**
+     * Replaces all properties in this PropertyStringParser with the
+     * specified Properties.
+     * @param properties the new properties for this PropertyStringParser.
+     */
+    private void setProperties(Properties properties) {
+        this.properties = properties;
+    }
+
+    /**
+     * Returns the internal Properties object.  If none is currently defined, a
+     * new Properties object is created.
+     * @return the internal Properties object.  If none is currently defined, a
+     * new Properties object is created.
+     */
+    private Properties getProperties() {
+        if (properties == null) {
+            setProperties(new Properties());
+        }
+        return (properties);
+    }
+
+    /**
+     * Sets the property with the specified key to the specified value.
+     * @param key the key of the property to set.
+     * @param value the value to associated with the specified key.
+     * @see java.util.Properties#setProperty(String,String)
+     */
+    public void setProperty(String key, String value) {
+        Properties properties = getProperties();
+        properties.setProperty(key, value);
+    }
+
+    /**
+     * Returns the property associated with the specified key, or null if no
+     * such
+     * property exists.
+     * @param key the key of the desired property
+     * @return the property associated with the specified key, or null if no
+     * such
+     * property exists.
+     */
+    public String getProperty(String key) {
+        return (getProperties().getProperty(key));
+    }
+    
+    /**
+     * Returns the property associated with the specified key, or the specified
+     * default value if no such property exists.
+     * @param key the key of the desired property
+     * @param defaultValue the default value to return if no such property exists
+     * @return the requested property, or the specified default value if no such property exists.
+     */
+    public String getProperty(String key, String defaultValue) {
+    	String result = getProperty(key);
+    	return (result == null ? defaultValue : result);
+    }
+
+}
diff --git a/src/java/com/martiansoftware/jsap/QualifiedSwitch.java b/src/java/com/martiansoftware/jsap/QualifiedSwitch.java
new file mode 100644
index 0000000..207c0a3
--- /dev/null
+++ b/src/java/com/martiansoftware/jsap/QualifiedSwitch.java
@@ -0,0 +1,229 @@
+/*
+ * Copyright (c) 2002-2004, Martian Software, Inc.
+ * This file is made available under the LGPL as described in the accompanying
+ * LICENSE.TXT file.
+ */
+
+package com.martiansoftware.jsap;
+
+import com.martiansoftware.jsap.FlaggedOption;
+
+/**
+ * A QualifiedSwitch is a parameter that has something in common with a Switch,
+ * i.e., its presence or absence is significant, but different from a "pure"
+ * Switch it can have an additional value (or values) prefixed by a ':' sign 
+ * that qualifies the Switch - making it behave like a FlaggedOption if a value 
+ * is specified.
+ * <p>
+ * QualifiedSwitch in fact extends FlaggedOption, providing all of its 
+ * functionality including the ability to use any StringParser to parse its 
+ * optional value(s).
+ * Values are retrieved from the JSAPResult in the same manner they would
+ * be retrieved for an equivalent FlaggedOption.
+ * <p>
+ * Additionally, the QualifiedSwitch's presence on the command line can be
+ * determined via JSAPResult.getBoolean(id).  This presents a small challenge
+ * in the unlikely event that you're also using a BooleanStringParser with
+ * the QualifiedSwitch; if you are, JSAPResult.getBoolean(id) will <b>not</b>
+ * return the optional values, but will still signify the QualifiedSwitch's
+ * presence.  Boolean optional values can be retrieved via 
+ * JSAPResult.getBooleanArray(id).  The first boolean in the array will be the
+ * first optionally specified boolean value qualifying the switch.
+ * <p>
+ * The following are some examples of a QualifiedSwitch's use:
+ * <ul>
+ * <li><b>-s:value</b> is a QualifiedSwitch with only one qualifying value.</li>
+ * <li><b>-s</b> has no qualifying value.</li>
+ * <li><b>--qswitch:a,b,c</b> (configured with <code>setList(true)</code>
+ * and <code>setListSeparator(',')</code>) has three qualifying values.</li>
+ * </ul> 
+ * <p>
+ * Please note that QualifiedSwitch is currently <b>experimental</b>, although
+ * it has no known problems.
+ * <p>
+ * QualifiedSwitch and its supporting code in other JSAP classes was generously 
+ * contributed to JSAP by Klaus P. Berg of Siemens AG, Munich, Germany.
+ * @since 1.03
+ * @author  Klaus P. Berg, Siemens AG, Munich, Germany
+ * @author <a href="http://www.martiansoftware.com/contact.html">Marty Lamb</a>
+ */
+ /* ML - switched superclass from Switch to FlaggedOption */
+public final class QualifiedSwitch extends FlaggedOption {
+
+    /**
+     * The current short flag for this UnflaggedOption.  
+         * Default is JSAP.NO_SHORTFLAG.
+     */
+//    private char shortFlag = JSAP.NO_SHORTFLAG;
+
+    /**
+     * The current long flag for this UnflaggedOption.  
+         *Default is JSAP.NO_LONGFLAG.
+     */
+//    private String longFlag = JSAP.NO_LONGFLAG;
+
+//    private String qualifyingValues; // the qualifying values that are allowed for the switch
+//    boolean checkEnumeratedValues = false; // tells the parser if the qualifyingValues are enumerated values
+
+	
+	/**
+	 * A shortcut constructor that creates a new QualifiedSwitch and configures
+	 * its most commonly used settings, including help.
+	 * @param id the unique ID for this FlaggedOption.
+	 * @param stringParser the StringParser this FlaggedOption should use.
+	 * @param defaultValue the default value for this FlaggedOption (may be
+	 * null).
+	 * @param required if true, this FlaggedOption is required.
+	 * @param shortFlag the short flag for this option (may be set to
+	 * JSAP.NO_SHORTFLAG for none).
+	 * @param longFlag the long flag for this option (may be set to
+	 * JSAP.NO_LONGFLAG for none).
+	 * @param help the help text for this option (may be set to {@link JSAP#NO_HELP} for none).
+	 */
+	public QualifiedSwitch(
+			String id,
+			StringParser stringParser,
+			String defaultValue,
+			boolean required,
+			char shortFlag,
+			String longFlag,
+			String help) {
+	
+			super(id, stringParser, defaultValue, required, shortFlag, longFlag, help);
+	}
+
+	/**
+	 * A shortcut constructor that creates a new QualifiedSwitch and configures
+	 * its most commonly used settings.
+	 * @param id the unique ID for this FlaggedOption.
+	 * @param stringParser the StringParser this FlaggedOption should use.
+	 * @param defaultValue the default value for this FlaggedOption (may be
+	 * null).
+	 * @param required if true, this FlaggedOption is required.
+	 * @param shortFlag the short flag for this option (may be set to
+	 * JSAP.NO_SHORTFLAG for none).
+	 * @param longFlag the long flag for this option (may be set to
+	 * JSAP.NO_LONGFLAG for none).
+	 */
+	public QualifiedSwitch(
+			String id,
+			StringParser stringParser,
+			String defaultValue,
+			boolean required,
+			char shortFlag,
+			String longFlag) {
+	
+			super(id, stringParser, defaultValue, required, shortFlag, longFlag);
+	}
+
+
+    /**
+     * A shortcut constructor that creates a new QualifiedSwitch
+     * 
+     * @param id the unique ID for this QualifiedSwitch.         
+     */
+    public QualifiedSwitch(String id) {
+    	super(id);
+    }   
+
+    /**
+     * Returns syntax instructions for this QualifiedSwitch.
+     * @return syntax instructions for this QualifiedSwitch based upon its current
+     * configuration.
+     */
+    public String getSyntax() {
+    	StringBuffer result = new StringBuffer();
+    	if (!required()) {
+    		result.append("[");
+    	}
+
+    	if ((getLongFlag() != JSAP.NO_LONGFLAG)
+		|| (getShortFlag() != JSAP.NO_SHORTFLAG)) {
+    		if (getLongFlag() == JSAP.NO_LONGFLAG) {
+    			result.append("-" + getShortFlag());
+    		} else if (getShortFlag() == JSAP.NO_SHORTFLAG) {
+    			result.append("--" + getLongFlag());
+    		} else {
+    			result.append(
+    					"(-" + getShortFlag() + "|--" + getLongFlag() + ")");
+    		}
+    	}
+    	result.append("[:");
+    	String un = getUsageName();
+    	char sep = this.getListSeparator();
+    	if (this.isList()) {
+    		result.append(
+    				un + "1" + sep + un + "2" + sep + "..." + sep + un + "N ");
+    	} else {
+    		result.append("<" + un + ">");
+    	}
+    	if (!required()) {
+    		result.append("]");
+    	}
+    	result.append("]");
+    	return (result.toString());
+    } 
+    
+    
+    
+    /**
+     * Creates a new QualifiedSwitchValuesParser to which it delegates the parsing of 
+     * the specified argument.
+     * The result is as follows:
+     * <ul>
+     * <li>ArrayList[0] contains a single Boolean that tells the
+     *  user whether this QualifiedSwitch is present or not.
+     * <li>ArrayList[1] is a string that contains the qualifying value.
+     * </ul>
+     * 
+     * @param arg the argument to parse.
+     * @return an ArrayList containing the parse results.
+     * @throws ParseException if the specified parameter cannot be parsed.
+     */
+//    protected final List parse(String arg) throws ParseException {
+//    	List result = new java.util.ArrayList();
+//        if (arg != null && arg.startsWith(":")) {
+//            result.add(Boolean.TRUE); // flag is present
+//            if (checkEnumeratedValues) {
+//                result.add((new EnumeratedStringParser(qualifyingValues)).parse(arg.substring(1))); // ad the value allowed
+//            }
+//            else {
+//                result.add((new StringStringParser()).parse(arg.substring(1))); // add arbitrary string value
+//            }
+//        }
+//        else {
+//            result.add((new BooleanStringParser()).parse(arg));
+//        }
+//        return result;
+//    }
+
+//    /**
+//     * Returns usage instructions for this Switch.
+//     * @return usage instructions for this Switch based upon its current 
+//     * configuration.
+//     */
+//    public String getSyntax() {
+//        StringBuffer buf = new StringBuffer();
+//        boolean shortFlag = false;
+//        buf.append("[");
+//        if (getShortFlag() != JSAP.NO_SHORTFLAG) {
+//            buf.append("-" + getShortFlag());
+//            shortFlag = true;
+//        }
+//        if (getLongFlag() != JSAP.NO_LONGFLAG) {
+//            if (shortFlag) {
+//                buf.append("|");
+//            }
+//            buf.append("--" + getLongFlag());
+//        }
+//        buf.append("[:");
+//        if (checkEnumeratedValues) {
+//            buf.append(qualifyingValues.replace(';', '|'));
+//        }
+//        else {
+//            buf.append(qualifyingValues);
+//        }
+//        buf.append("]]");
+//        return(buf.toString());
+//    }
+}
diff --git a/src/java/com/martiansoftware/jsap/RequiredParameterMissingException.java b/src/java/com/martiansoftware/jsap/RequiredParameterMissingException.java
new file mode 100644
index 0000000..5d934a3
--- /dev/null
+++ b/src/java/com/martiansoftware/jsap/RequiredParameterMissingException.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2002-2004, Martian Software, Inc.
+ * This file is made available under the LGPL as described in the accompanying
+ * LICENSE.TXT file.
+ */
+
+package com.martiansoftware.jsap;
+
+/**
+ * An exception indicating that a required parameter was missing from the
+ * supplied arguments and defaults.
+ *
+ * @see FlaggedOption#setAllowMultipleDeclarations(boolean)
+ * @author <a href="http://www.martiansoftware.com/contact.html">Marty Lamb</a>
+ * @see com.martiansoftware.jsap.Option#required()
+ */
+public class RequiredParameterMissingException extends JSAPException {
+
+    /**
+     * The unique ID of the parameter that was missing.
+     */
+    private String id = null;
+
+    /**
+     * Creates a new RequiredParameterMissingException referencing the
+     * specified parameter.
+     * @param paramID the unique ID of the parameter that was missing.
+     */
+    public RequiredParameterMissingException(String paramID) {
+        super("Parameter '" + paramID + "' is required.");
+        this.id = paramID;
+    }
+
+    /**
+     * Returns the unique ID of the parameter that was missing.
+     * @return the unique ID of the parameter that was missing.
+     */
+    public String getID() {
+        return (this.id);
+    }
+
+}
diff --git a/src/java/com/martiansoftware/jsap/SimpleJSAP.java b/src/java/com/martiansoftware/jsap/SimpleJSAP.java
new file mode 100644
index 0000000..7f25182
--- /dev/null
+++ b/src/java/com/martiansoftware/jsap/SimpleJSAP.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (c) 2002-2004, Martian Software, Inc.
+ * This file is made available under the LGPL as described in the accompanying
+ * LICENSE.TXT file.
+ */
+
+package com.martiansoftware.jsap;
+
+import java.util.Iterator;
+import java.util.List;
+
+import com.martiansoftware.util.StringUtils;
+
+/** A simple interface to {@link com.martiansoftware.jsap.JSAP} that handles directly help,
+ * explanation and an array of parameters.
+ * 
+ * <P>More precisely, instances of this class behave exactly like those of
+ * {@link com.martiansoftware.jsap.JSAP}, but additionally require a command name, an explanation
+ * (a wordy description) and an array of parameters (which will be registered automatically). 
+ * A switch activated by <samp>--help</samp> is always
+ * registered under the ID <samp>help</samp>. 
+ * 
+ * <p>A message will be automatically printed upon invocation
+ * of the <code>parse()</code> methods if an error occurs, or if the help switch is detected. In this
+ * case, {@link #messagePrinted()} will return true, and the caller may check this condition
+ * to stop its actions.
+ * 
+ * <P>The screen width used to format the help text may be set using {@link #setScreenWidth(int)}.
+ * The formatter will preserve newlines.
+ * <i>Note: as of 2.0a non-breaking spaces are temporarily disabled until some console encoding
+ * issues have been worked out. - ML</i>
+ * 
+ * @author Sebastiano Vigna
+ */
+public class SimpleJSAP extends JSAP {
+
+	/** The screen witdh used for formatting. */
+	private int screenWidth = DEFAULT_SCREENWIDTH;
+	
+	/** A wordy explanation. */
+	private String explanation;
+
+	/** True if the last parsing caused the help text to be printed. */
+	private boolean messagePrinted;
+	
+	/** The name of the command that will appear in the help message. */
+	final private String name;
+	
+	/** Creates a new simple JSAP with default screen width. 
+	 * 
+	 * @param name the name of the command for which help will be printed.
+	 * @param explanation a wordy explanation of the command, or <code>null</code> for no explanation.
+	 * @param parameter an array of parameters, which will be registered for you, or <code>null</code>.
+	 */
+
+	public SimpleJSAP( final String name, final String explanation, final Parameter[] parameter ) throws JSAPException {
+		super();
+
+		this.name = name;
+		this.explanation = explanation;
+		
+		final Switch help = new Switch( "help", JSAP.NO_SHORTFLAG, "help" );
+		help.setHelp( "Prints this help message." );
+		this.registerParameter( help );
+
+		if ( parameter != null ) 
+			for( int i = 0; i < parameter.length; i++ ) this.registerParameter( parameter[ i ] );
+	}
+
+	/** Creates a new simple JSAP with default screen width. 
+	 * 
+	 * @param name the name of the command for which help will be printed.
+	 * @param explanation a wordy explanation of the command, or <code>null</code> for no explanation.
+	 */
+
+	public SimpleJSAP( final String name, final String explanation ) throws JSAPException {
+		this( name, explanation, null );
+	}
+		
+	/** Creates a new simple JSAP with a help switch, no explanation and default screen width. 
+	 * 
+	 * @param name the name of the command for which help will be printed.
+	 */
+
+	public SimpleJSAP( final String name ) throws JSAPException {
+		this( name, null );
+	}
+	
+	public JSAPResult parse( String arg ) {
+		JSAPResult jsapResult = super.parse( arg );
+		messagePrinted = printMessageIfUnsuccessfulOrHelpRequired( jsapResult );
+		return jsapResult;
+	}
+
+	public JSAPResult parse( String[] arg ) {
+		JSAPResult jsapResult = super.parse( arg );
+		messagePrinted = printMessageIfUnsuccessfulOrHelpRequired( jsapResult );
+		return jsapResult;
+	}
+
+	/** Checks the given JSAP result for errors or help requests and acts accordingly.
+	 * 
+	 * @param jsapResult the result of a JSAP parsing.
+	 * @return true if some message has been printed (i.e., if there was an error or a help request).
+	 */
+	
+	private boolean printMessageIfUnsuccessfulOrHelpRequired( final JSAPResult jsapResult ) {
+		if ( ! ( jsapResult.success() ) || jsapResult.getBoolean( "help" ) ) {
+
+			//  To avoid spurious missing argument errors we never print errors if help is required.
+			if ( ! jsapResult.getBoolean( "help" ) ) {
+				for ( Iterator err = jsapResult.getErrorMessageIterator(); err.hasNext(); )
+					System.err.println( "Error: " + err.next() );
+				
+				return true;
+			}
+			System.err.println();
+			System.err.println( "Usage:" );
+
+			List l = StringUtils.wrapToList( name + " " + getUsage(), screenWidth );
+			for( Iterator i = l.iterator(); i.hasNext(); ) System.err.println( "  " + i.next().toString() );
+
+			if ( explanation != null ) {
+				System.err.println();
+				l = StringUtils.wrapToList( explanation, screenWidth );
+				for( Iterator i = l.iterator(); i.hasNext(); ) System.err.println( i.next() );
+			}
+			
+			System.err.println();
+			System.err.println();
+			System.err.println( getHelp( screenWidth ) );
+			return true;
+		}
+		
+		return false;
+	}
+
+	/** Returns the current screen width.
+	 * 
+	 * <P>This value will be passed to {@link com.martiansoftware.jsap.JSAP#getHelp(int)}, and used
+	 * to format the explanation.
+	 * 
+	 * @return the current screen width.
+	 */
+	public int getScreenWidth() {
+		return screenWidth;
+	}
+	
+	/** Sets the screen width.
+	 * @param screenWidth the new screen width.
+	 * @return this simple JSAP.
+	 */
+	
+	public SimpleJSAP setScreenWidth( final int screenWidth ) {
+		this.screenWidth = screenWidth;
+		return this;
+	}
+	
+	/** Returns true if the last parsing caused the a message to be printed.
+	 * 
+	 * @return true if the last parsing caused a message to be printed.
+	 */
+	
+	public boolean messagePrinted() {
+		return messagePrinted;
+	}
+}
\ No newline at end of file
diff --git a/src/java/com/martiansoftware/jsap/StringParser.java b/src/java/com/martiansoftware/jsap/StringParser.java
new file mode 100644
index 0000000..2d998d7
--- /dev/null
+++ b/src/java/com/martiansoftware/jsap/StringParser.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2002-2004, Martian Software, Inc.
+ * This file is made available under the LGPL as described in the accompanying
+ * LICENSE.TXT file.
+ */
+
+package com.martiansoftware.jsap;
+
+/**
+ * Class responsible for converting Strings into Objects.  Each subclass of
+ * StringParser
+ * is capable of parsing a String into a different class of object.  <b>To
+ * extend JSAP to
+ * recognize new data types, a new StringParser must be created for the new
+ * type.</b><br>
+ * <br>
+ * "List" options (such as your environment's PATH and CLASSPATH variables)
+ * that contain
+ * multiple values are split into individual value tokens prior to the calling
+ * of the
+ * StringParser's parse() method.  For example, if you had a StringStringParser
+ * parsing
+ * your PATH environment variable, that StringStringParser's parse() method
+ * would be
+ * called once for each item in the list.  As a result, each StringParser only
+ * needs
+ * to know how to create an object based upon a single, simple token.
+ * @author <a href="http://www.martiansoftware.com/contact.html">Marty Lamb</a>
+ */
+public abstract class StringParser {
+
+    /**
+     * Performs any initialization not handled by this StringParser's
+     * constructor.
+     * The contract for this method is that it will be called AT LEAST once
+     * before
+     * this object's parse() method is called.<br><br>
+     * In the JSAP API, this method is called every time an Option containing
+     * this StringParser is
+     * registered with a JSAP.  If there is an initialization error, this method
+     * should throw a JSAPException to prevent the Option from being registered.
+     * @throws Exception if an initialization error occurs that should prevent
+     * the Option containing this StringParser from being registered.
+     */
+    public void setUp() throws Exception {
+    }
+
+    /**
+     * Performs any cleanup necessary for this StringParser.  The contract for
+     * this method is that it MAY be called at any time after the setUp method
+     * has been called.  It may be called more than once.<br>
+     * <br>
+     * In the JSAP API, this method is called every
+     * time an Option containing this StringParser is unregistered from a JSAP.
+     * During
+     * finalization, any registered Options are unregistered from a JSAP.
+     */
+    public void tearDown() {
+    }
+
+    /**
+     * Parses the specified argument into an Object of the appropriate type.
+     * If
+     * the specified argument cannot be converted into the desired Object, a
+     * ParseException
+     * should be thrown.<br>
+     * <br>
+     * <b>Note:</b> this method MAY BE CALLED with a <b>null</b> argument.
+     * Take this
+     * into consideration when subclassing!
+     * @param arg the argument to convert to an Object of class appropriate to
+     * the StringParser subclass.
+     * @return the Object resulting from the parsed argument.
+     * @throws ParseException if the specified argument cannot be parsed.
+     */
+    public abstract Object parse(String arg) throws ParseException;
+
+}
diff --git a/src/java/com/martiansoftware/jsap/Switch.java b/src/java/com/martiansoftware/jsap/Switch.java
new file mode 100644
index 0000000..9300b43
--- /dev/null
+++ b/src/java/com/martiansoftware/jsap/Switch.java
@@ -0,0 +1,204 @@
+/*
+ * Copyright (c) 2002-2004, Martian Software, Inc.
+ * This file is made available under the LGPL as described in the accompanying
+ * LICENSE.TXT file.
+ */
+ 
+package com.martiansoftware.jsap;
+
+import java.util.List;
+
+import com.martiansoftware.jsap.stringparsers.BooleanStringParser;
+
+/**
+ * A Switch is a parameter whose presence alone is significant; another 
+ * commonly used term for a Switch is "Flag".
+ * Switches use a {@link com.martiansoftware.jsap.stringparsers.BooleanStringParser}
+ * internally, so their results can be 
+ * obtained from a JSAPResult using
+ * the getBoolean() methods.
+ * 
+ * <p>An example of a command line using a Switch is "dosomething -v", where 
+ * "-v" might mean "verbose."  
+ * @author <a href="http://www.martiansoftware.com/contact.html">Marty Lamb</a>
+ * @see com.martiansoftware.jsap.Flagged
+ * @see com.martiansoftware.jsap.stringparsers.BooleanStringParser
+ */
+public class Switch extends Parameter implements Flagged {
+
+	/**
+	 * The current short flag for this UnflaggedOption.  Default is 
+	 * JSAP.NO_SHORTFLAG.
+	 */
+	private char shortFlag = JSAP.NO_SHORTFLAG;
+                          
+	/**
+	 * The current long flag for this UnflaggedOption.  Default is 
+	 * JSAP.NO_LONGFLAG.
+	 */
+	private String longFlag = JSAP.NO_LONGFLAG;
+
+	/**
+	 * Creates a new Switch with the specified unique ID.
+	 * @param id the unique ID for this Switch.
+	 */
+	public Switch(String id) {
+		super(id);
+		setDefault("FALSE");
+	}
+
+	/**
+	 * A shortcut constructor that creates a new Switch and configures all of 
+	 * its settings, including help.
+	 * @param id the unique ID for this Switch.
+	 * @param shortFlag the short flag for this Switch (may be set to 
+	 * JSAP.NO_SHORTFLAG for none).
+	 * @param longFlag the long flag for this Switch (may be set to 
+	 * JSAP.NO_LONGFLAG for none).
+	 * @param help the help text for this Switch (may be set to {@link JSAP#NO_HELP}for none).
+	 * */
+	public Switch(String id, char shortFlag, String longFlag, String help) {
+		this(id);
+		setShortFlag(shortFlag);
+		setLongFlag(longFlag);
+		setHelp(help);
+	}
+
+	/**
+	 * A shortcut constructor that creates a new Switch and configures all of 
+	 * its settings.
+	 * @param id the unique ID for this Switch.
+	 * @param shortFlag the short flag for this Switch (may be set to 
+	 * JSAP.NO_SHORTFLAG for none).
+	 * @param longFlag the long flag for this Switch (may be set to 
+	 * JSAP.NO_LONGFLAG for none).
+	 * */
+	public Switch(String id, char shortFlag, String longFlag) {
+		this(id, shortFlag, longFlag, JSAP.NO_HELP);
+	}
+
+		/**
+	 * Sets the short flag for this Switch.  To use no short flag at all, set 
+	 * the value to JSAP.NO_SHORTFLAG.
+	 * @param shortFlag the short flag for this Switch.
+	 * @return the modified Switch
+	 */
+	public Switch setShortFlag(char shortFlag) {
+		enforceParameterLock();
+		this.shortFlag = shortFlag;
+		return (this);
+	}
+
+	/**
+	 * Returns the short flag for this Switch.  If this Switch has no short 
+	 * flag, the
+	 * return value will be equal to JSAP.NO_SHORTFLAG.
+	 * @return the short flag for this Switch.  If this Switch has no short 
+	 * flag, the
+	 * return value will be equal to JSAP.NO_SHORTFLAG.
+	 */
+	public char getShortFlag() {
+		return (shortFlag);
+	}
+
+	/**
+	 * Returns the short flag for this Switch.  If this Switch has no short 
+	 * flag, the
+	 * return value will be null.
+	 * @return the short flag for this Switch.  If this Switch has no short 
+	 * flag, the
+	 * return value will be null.
+	 */
+	public Character getShortFlagCharacter() {
+		return ((shortFlag == '\0') ? null : new Character(shortFlag));
+	}
+
+	/**
+	 * Sets the long flag for this Switch.  To use no long flag at all, set 
+	 * the value to JSAP.NO_LONGFLAG.
+	 * @param longFlag the long flag for this Switch.
+	 * @return the modified Switch
+	 */
+	public Switch setLongFlag(String longFlag) {
+		enforceParameterLock();
+		this.longFlag = longFlag;
+		return (this);
+	}
+
+	/**
+	 * Returns the long flag for this Switch.  If this Switch has no long flag, 
+	 * the return
+	 * value will be equal to JSAP.NO_LONGFLAG.
+	 * @return the long flag for this FlaggedOption.  If this FlaggedOption has 
+	 * no long flag, the return
+	 * value will be equal to JSAP.NO_LONGFLAG.
+	 */
+	public String getLongFlag() {
+		return (longFlag);
+	}
+
+	/**
+	 * Creates a new BooleanStringParser to which it delegates the parsing of 
+	 * the specified argument.
+	 * The result is always a single Boolean.
+	 * @param arg the argument to parse.
+	 * @return an ArrayList containing a single Boolean.
+	 * @throws ParseException if the specified parameter cannot be parsed.
+	 */
+	protected List parse(String arg) throws ParseException {
+		List result = new java.util.ArrayList(1);
+		result.add((BooleanStringParser.getParser()).parse(arg));
+		return (result);
+	}
+
+	/**
+	 * Returns usage instructions for this Switch.
+	 * @return usage instructions for this Switch based upon its current 
+	 * configuration.
+	 */
+	public String getSyntax() {
+		StringBuffer buf = new StringBuffer();
+		boolean shortFlag = false;
+		buf.append("[");
+		if (getShortFlag() != JSAP.NO_SHORTFLAG) {
+			buf.append("-" + getShortFlag());
+			shortFlag = true;
+		}
+		if (getLongFlag() != JSAP.NO_LONGFLAG) {
+			if (shortFlag) {
+				buf.append("|");
+			}
+			buf.append("--" + getLongFlag());
+		}
+		buf.append("]");
+		return (buf.toString());
+	}
+	
+	/**
+	 * Sets a default value for this parameter.  The default is specified
+	 * as a String, and is parsed as a single value specified on the
+	 * command line.  In other words, default values for "list"
+	 * parameters or parameters allowing multiple declarations should be
+	 * set using setDefault(String[]), as JSAP
+	 * would otherwise treat the entire list of values as a single value.
+	 *
+	 * @param defaultValue the default value for this parameter.
+	 * @see #setDefault(String)
+	 */
+	public Switch setDefault(String defaultValue) {
+		_setDefault(defaultValue);
+		return (this);
+	}    
+
+	/**
+	 * Sets one or more default values for this parameter.  This method
+	 * should be used whenever a parameter has more than one default
+	 * value.
+	 * @param defaultValues the default values for this parameter.
+	 * @see #setDefault(String)
+	 */
+	public Switch setDefault(String[] defaultValues) {
+		_setDefault(defaultValues);
+		return (this);
+	}
+}
diff --git a/src/java/com/martiansoftware/jsap/SyntaxException.java b/src/java/com/martiansoftware/jsap/SyntaxException.java
new file mode 100644
index 0000000..966ccb9
--- /dev/null
+++ b/src/java/com/martiansoftware/jsap/SyntaxException.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2002-2004, Martian Software, Inc.
+ * This file is made available under the LGPL as described in the accompanying
+ * LICENSE.TXT file.
+ */
+
+package com.martiansoftware.jsap;
+
+/**
+ * An exception indicating that a syntax error was encountered in the argument
+ * list.
+ *
+ * @author <a href="http://www.martiansoftware.com/contact.html">Marty Lamb</a>
+ */
+public class SyntaxException extends JSAPException {
+
+    /**
+     * Creates a new SyntaxException with the specified message.
+     * @param msg a description of the syntax problem.
+     */
+    public SyntaxException(String msg) {
+        super(msg);
+    }
+
+}
diff --git a/src/java/com/martiansoftware/jsap/TestAll.java b/src/java/com/martiansoftware/jsap/TestAll.java
new file mode 100644
index 0000000..41108d9
--- /dev/null
+++ b/src/java/com/martiansoftware/jsap/TestAll.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2002-2004, Martian Software, Inc.
+ * This file is made available under the LGPL as described in the accompanying
+ * LICENSE.TXT file.
+ */
+
+package com.martiansoftware.jsap;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+/**
+ * Runs all of the JSAP tests, including those in sub-packages.
+ * @author <a href="http://www.martiansoftware.com/contact.html">Marty Lamb</a>
+ */
+public class TestAll extends TestCase {
+
+    /**
+     * Returns a collection of all the JSAP tests, including those in
+     * subpackages.
+     * @return a collection of all the JSAP tests, including those in
+     * subpackages.
+     */
+    public static Test suite() {
+        TestSuite suite = new TestSuite("All JUnit Tests");
+        suite.addTest(TestSwitch.suite());
+        suite.addTest(TestOption.suite());
+        suite.addTest(TestFlaggedOption.suite());
+        suite.addTest(TestJSAPConfiguration.suite());
+        suite.addTest(TestParser.suite());
+        suite.addTest(TestDefaults.suite());
+        suite.addTest(TestCommandLineTokenizer.suite());
+        suite.addTest(TestUsageString.suite());
+        suite.addTest(com.martiansoftware.jsap.stringparsers.TestAll.suite());
+        suite.addTest(com.martiansoftware.jsap.defaultsources.TestAll.suite());
+        return (suite);
+    }
+
+}
diff --git a/src/java/com/martiansoftware/jsap/TestCommandLineTokenizer.java b/src/java/com/martiansoftware/jsap/TestCommandLineTokenizer.java
new file mode 100644
index 0000000..a7414a6
--- /dev/null
+++ b/src/java/com/martiansoftware/jsap/TestCommandLineTokenizer.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright (c) 2002-2004, Martian Software, Inc.
+ * This file is made available under the LGPL as described in the accompanying
+ * LICENSE.TXT file.
+ */
+
+package com.martiansoftware.jsap;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+/**
+ * This junit TestCase runs a barrage of tests against the CommandLineTokenizer.
+ * This TestCase is unusual for this project in that it contains a main()
+ * method;
+ * this method is used to generate test methods to paste into this class.
+ * @see CommandLineTokenizer
+ * @author <a href="http://www.martiansoftware.com/contact.html">Marty Lamb</a>
+ */
+public class TestCommandLineTokenizer extends TestCase {
+
+    /**
+     * Creates and returns a new Test based upon this class.
+     * @return a new Test based upon this class.
+     */
+    public static Test suite() {
+        return (new TestSuite(TestCommandLineTokenizer.class));
+    }
+
+    /**
+     * Tests for correct parsing of [this is a test].
+     */
+    public void test1() {
+        String cmdLine = "this is a test";
+        String[] tokens = CommandLineTokenizer.tokenize(cmdLine);
+
+        assertEquals(4, tokens.length);
+
+        assertEquals("this", tokens[0]);
+        assertEquals("is", tokens[1]);
+        assertEquals("a", tokens[2]);
+        assertEquals("test", tokens[3]);
+    }
+
+    /**
+     * Tests for corect parsing of [this is a "test"]
+     */
+    public void test2() {
+        String cmdLine = "";
+        String[] tokens = CommandLineTokenizer.tokenize("this is a \"test\"");
+
+        assertEquals(4, tokens.length);
+
+        assertEquals("this", tokens[0]);
+        assertEquals("is", tokens[1]);
+        assertEquals("a", tokens[2]);
+        assertEquals("test", tokens[3]);
+    }
+
+    /**
+     * Tests for correct parsing of ["this is a test"]
+     */
+    public void test3() {
+        String cmdLine = "\"this is a test\"";
+        String[] tokens = CommandLineTokenizer.tokenize(cmdLine);
+
+        assertEquals(1, tokens.length);
+
+        assertEquals("this is a test", tokens[0]);
+    }
+
+    /**
+     * Tests for correct parsing of [this is a "test]
+     */
+    public void test4() {
+        String cmdLine = "this is a \"test";
+        String[] tokens = CommandLineTokenizer.tokenize(cmdLine);
+
+        assertEquals(4, tokens.length);
+
+        assertEquals("this", tokens[0]);
+        assertEquals("is", tokens[1]);
+        assertEquals("a", tokens[2]);
+        assertEquals("test", tokens[3]);
+    }
+
+    /**
+     * Tests for correct parsing of [thi\s is a \"test]
+     */
+    public void test5() {
+        String cmdLine = "thi\\s is a \\\"test";
+        String[] tokens = CommandLineTokenizer.tokenize(cmdLine);
+
+        assertEquals(4, tokens.length);
+
+        assertEquals("thi\\s", tokens[0]);
+        assertEquals("is", tokens[1]);
+        assertEquals("a", tokens[2]);
+        assertEquals("\"test", tokens[3]);
+    }
+
+    /**
+     * Tests for correct parsing of [thi\s is a \"test\\]
+     */
+    public void test6() {
+        String cmdLine = "thi\\s is a \\\"test\\";
+        String[] tokens = CommandLineTokenizer.tokenize(cmdLine);
+
+        assertEquals(4, tokens.length);
+
+        assertEquals("thi\\s", tokens[0]);
+        assertEquals("is", tokens[1]);
+        assertEquals("a", tokens[2]);
+        assertEquals("\"test\\", tokens[3]);
+    }
+
+    /**
+     * Tests for correct parsing of [thi\s is a \"test\\\"]
+     */
+    public void test7() {
+        String cmdLine = "thi\\s is a \\\"test\\\\\"";
+        String[] tokens = CommandLineTokenizer.tokenize(cmdLine);
+
+        assertEquals(4, tokens.length);
+
+        assertEquals("thi\\s", tokens[0]);
+        assertEquals("is", tokens[1]);
+        assertEquals("a", tokens[2]);
+        assertEquals("\"test\\", tokens[3]);
+    }
+
+    /**
+     * Tests for correct parsing of a null command line.
+     */
+    public void test8() {
+        String[] tokens = CommandLineTokenizer.tokenize(null);
+        assertEquals(0, tokens.length);
+    }
+
+    /**
+     * Tests for correct parsing of a whitespace-only command line.
+     */
+    public void test9() {
+        String cmdLine = "   \t\t  \t ";
+        String[] tokens = CommandLineTokenizer.tokenize(cmdLine);
+        assertEquals(0, tokens.length);
+    }
+
+    /**
+     * Tests for correct parsing of ["this is a test]
+     */
+    public void test10() {
+        String cmdLine = "\"this is a test";
+        String[] tokens = CommandLineTokenizer.tokenize(cmdLine);
+
+        assertEquals(1, tokens.length);
+
+        assertEquals("this is a test", tokens[0]);
+    }
+
+    /**
+     * A helper method to write additional test cases based upon the specified
+     * argument array.
+     * @param args the argument array that CommandLineTokenizer.tokenize()
+     * should produce.
+     */
+    public static void main(String[] args) {
+
+        System.out.println("public void testXXX(){");
+        System.out.println("\tString cmdLine = \"\";");
+        System.out.println(
+            "\tString[] tokens = CommandLineTokenizer.tokenize(cmdLine);");
+        System.out.println("\n");
+        System.out.println(
+            "\tassertEquals(" + args.length + ", tokens.length);\n");
+        for (int i = 0; i < args.length; ++i) {
+            System.out.println(
+                "\tassertEquals(\"" + args[i] + "\", tokens[" + i + "]);");
+        }
+        System.out.println("}");
+    }
+
+}
diff --git a/src/java/com/martiansoftware/jsap/TestDefaults.java b/src/java/com/martiansoftware/jsap/TestDefaults.java
new file mode 100644
index 0000000..7b22c5a
--- /dev/null
+++ b/src/java/com/martiansoftware/jsap/TestDefaults.java
@@ -0,0 +1,213 @@
+/*
+ * Copyright (c) 2002-2004, Martian Software, Inc.
+ * This file is made available under the LGPL as described in the accompanying
+ * LICENSE.TXT file.
+ */
+
+package com.martiansoftware.jsap;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Properties;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import com.martiansoftware.jsap.defaultsources.PropertyDefaultSource;
+
+/**
+ * Tests JSAP's handling of default values.
+ * @author <a href="http://www.martiansoftware.com/contact.html">Marty Lamb</a>
+ */
+public class TestDefaults extends TestCase {
+
+    /**
+     * JSAP object created in setUp()
+     */
+    private JSAP jsap = null;
+
+    /**
+     * Returns the tests defined in this class.
+     * @return the tests defined in this class.
+     */
+    public static Test suite() {
+        return (new TestSuite(TestDefaults.class));
+    }
+
+    /**
+     * Ensures that the setUp() method was successful.
+     */
+    public void testConfig() {
+        Switch b = (Switch) jsap.getByID("b");
+        assertNotNull(b);
+        Switch b2 = (Switch) jsap.getByShortFlag('b');
+        assertNotNull(b2);
+        assertEquals(b, b2);
+    }
+
+    /**
+     * Tests the parameter-level defaults.
+     */
+    public void testSimpleDefault() {
+        assertNotNull(jsap);
+
+        String[] args = { "-a", "2a 2b 2c" };
+        JSAPResult result = null;
+
+        result = jsap.parse(args);
+        assertEquals(true, result.success());
+
+        assertEquals(true, result.getBoolean("a"));
+        assertEquals("field1-default", result.getString("field1"));
+        assertEquals(false, result.getBoolean("b"));
+
+        // attempt to throw a ClassCastException
+        try {
+            String s = result.getString("a");
+            fail("Switch returned a String [" + s + "]");
+        } catch (Exception e) {
+            // this is normal
+        }
+    }
+
+    /**
+     * Another test of parameter-level defaults.
+     */
+    public void testDefaults2() {
+        assertNotNull(jsap);
+        String[] args = { "-b", "--field1", "HELLO" };
+        JSAPResult result = null;
+        result = jsap.parse(args);
+        assertEquals(true, result.success());
+        assertEquals(false, result.getBoolean("a"));
+        assertEquals(true, result.getBoolean("b"));
+        assertEquals("HELLO", result.getString("field1"));
+        assertEquals(0, result.getObjectArray("field2").length);
+    }
+
+    /**
+     * Tests JSAP's enforcing of required parameters.
+     */
+    public void testRequired() {
+        FlaggedOption requiredOption = new FlaggedOption("required");
+        requiredOption.setRequired(true);
+        requiredOption.setLongFlag("required");
+        try {
+            jsap.registerParameter(requiredOption);
+        } catch (Exception e) {
+            fail(e.getMessage());
+        }
+        String[] args = { "-b", "--field1", "HELLO" };
+
+        JSAPResult result = null;
+
+        result = jsap.parse(args);
+        assertEquals(
+            "Required field not provided, but no exception thrown.",
+            false,
+            result.success());
+
+        String[] args2 =
+            { "-b", "--field1", "HELLO", "--required", "requiredinfo" };
+
+        result = jsap.parse(args2);
+        assertEquals(true, result.success());
+        assertNotNull(result);
+        assertEquals(false, result.getBoolean("a"));
+        assertEquals(true, result.getBoolean("b"));
+        assertEquals("HELLO", result.getString("field1"));
+        assertEquals(0, result.getObjectArray("field2").length);
+        assertEquals("requiredinfo", result.getString("required"));
+
+        jsap.unregisterParameter(requiredOption);
+        requiredOption.setDefault("required_default");
+        try {
+            jsap.registerParameter(requiredOption);
+        } catch (JSAPException e) {
+            fail(e.getMessage());
+        }
+        result = null;
+        result = jsap.parse(args);
+        assertEquals(true, result.success());
+        assertEquals("required_default", result.getString("required"));
+
+    }
+
+    /**
+     * Tests a single level of property defaults.
+     */
+    public void testOneLevelDefaults() {
+        try {
+            File propertyTest = File.createTempFile("jsap-", ".properties");
+            OutputStream out =
+                new BufferedOutputStream(new FileOutputStream(propertyTest));
+            Properties properties = new Properties();
+            properties.setProperty("field1", "FromPropertyFile");
+            properties.setProperty("a", "true");
+            properties.store(
+                out,
+                "JUnit test for " + this.getClass().getName());
+            out.close();
+            PropertyDefaultSource pds =
+                new PropertyDefaultSource(propertyTest.getAbsolutePath(), true);
+
+            jsap.registerDefaultSource(pds);
+
+            JSAPResult result = null;
+            String[] args = { "-b", "--field1", "HELLO" };
+            result = jsap.parse(args);
+            assertEquals(true, result.success());
+            assertNotNull(result);
+            assertEquals("HELLO", result.getString("field1"));
+
+            String[] args2 = { "-b" };
+            result = jsap.parse(args2);
+            assertEquals(true, result.success());
+
+            assertNotNull(result);
+            assertEquals("FromPropertyFile", result.getString("field1"));
+            assertEquals(true, result.getBoolean("a"));
+        } catch (IOException e) {
+            fail(e.getMessage());
+        }
+    }
+
+    /**
+     * Configures the JSAP object for the tests.
+     * @throws JSAPException if the JSAP object cannot be instantiated.
+     */
+    public void setUp() throws JSAPException {
+        // set up a command line parser for the syntax
+        // [-a] [-b] [--field1 field1] [field2 field3 field4]
+        jsap = new JSAP();
+
+        Switch a = new Switch("a");
+        a.setShortFlag('a');
+        jsap.registerParameter(a);
+
+        Switch b = new Switch("b");
+        b.setShortFlag('b');
+        jsap.registerParameter(b);
+
+        FlaggedOption field1 = new FlaggedOption("field1");
+        field1.setLongFlag("field1");
+        field1.setDefault("field1-default");
+        jsap.registerParameter(field1);
+
+        UnflaggedOption field2 = new UnflaggedOption("field2");
+        field2.setGreedy(true);
+        jsap.registerParameter(field2);
+    }
+
+    /**
+     * Cleans up the JSAP object.
+     */
+    public void tearDown() {
+        jsap = null;
+    }
+
+}
diff --git a/src/java/com/martiansoftware/jsap/TestFlaggedOption.java b/src/java/com/martiansoftware/jsap/TestFlaggedOption.java
new file mode 100644
index 0000000..f34d5b5
--- /dev/null
+++ b/src/java/com/martiansoftware/jsap/TestFlaggedOption.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2002-2004, Martian Software, Inc.
+ * This file is made available under the LGPL as described in the accompanying
+ * LICENSE.TXT file.
+ */
+
+package com.martiansoftware.jsap;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+/**
+ *
+ * @author <a href="http://www.martiansoftware.com/contact.html">Marty Lamb</a>
+ */
+public class TestFlaggedOption extends TestCase {
+
+    /**
+     * Creates a TestCase with the specified name.
+     * @param name the name for this TestCase.
+     */
+    public TestFlaggedOption(String name) {
+        super(name);
+    }
+
+    /**
+     * Returns a suite of all tests defined in this class.
+     * @return a suite of all tests defined in this class.
+     */
+    public static Test suite() {
+        return (new TestSuite(TestFlaggedOption.class));
+    }
+
+    /**
+     * Tests the ability to set/get short flags.
+     */
+    public void testShortFlag() {
+        FlaggedOption option = new FlaggedOption("testOption");
+        option.setShortFlag('x');
+        assertEquals('x', option.getShortFlag());
+        assertEquals(new Character('x'), option.getShortFlagCharacter());
+    }
+
+    /**
+     * Tests the ability to set/get long flags.
+     */
+    public void testLongFlag() {
+        FlaggedOption option = new FlaggedOption("testOption");
+        option.setLongFlag("test");
+        assertEquals("test", option.getLongFlag());
+    }
+
+}
diff --git a/src/java/com/martiansoftware/jsap/TestJSAPConfiguration.java b/src/java/com/martiansoftware/jsap/TestJSAPConfiguration.java
new file mode 100644
index 0000000..c17e799
--- /dev/null
+++ b/src/java/com/martiansoftware/jsap/TestJSAPConfiguration.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2002-2004, Martian Software, Inc.
+ * This file is made available under the LGPL as described in the accompanying
+ * LICENSE.TXT file.
+ */
+
+package com.martiansoftware.jsap;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+/**
+ * Tests the JSAPConfiguration class
+ * @author <a href="http://www.martiansoftware.com/contact.html">Marty Lamb</a>
+ */
+public class TestJSAPConfiguration extends TestCase {
+
+    /**
+     * Returns a suite of tests defined by this class.
+     * @return a suite of tests defined by this class.
+     */
+    public static Test suite() {
+        return (new TestSuite(TestJSAPConfiguration.class));
+    }
+
+    /**
+     * Tests various configuration situations, such as changing properties of
+     * registered
+     * parameters, retrieving parameters, etc.
+     */
+    public void testConfigurationGymnastics() {
+        JSAP config = new JSAP();
+        FlaggedOption option = new FlaggedOption("testOption");
+
+        // this registration should fail - it's a flagged option with no flags
+        try {
+            config.registerParameter(option);
+            fail("Successfully registered a FlaggedOption with no flags!");
+        } catch (JSAPException e) {
+            // this is normal
+        }
+
+        option.setShortFlag('t');
+        option.setLongFlag("test");
+
+        // this registration should go fine
+        try {
+            config.registerParameter(option);
+        } catch (JSAPException e) {
+            fail(e.getMessage());
+        }
+
+        // the option is now locked, so it should be immutable
+        try {
+            option.setRequired(true);
+            fail("locked option was changed!");
+        } catch (Exception e) {
+            // this is normal
+        }
+
+        // duplicate registration should fail
+        try {
+            config.registerParameter(option);
+            fail("Registered the same option ID twice.");
+        } catch (JSAPException e) {
+            // this is normal
+        }
+
+        // this registration should work
+        FlaggedOption option2 = new FlaggedOption("test2");
+        option2.setShortFlag('2');
+        try {
+            config.registerParameter(option2);
+        } catch (JSAPException e) {
+            fail(e.getMessage());
+        }
+
+        // registering a duplicate shortflag should fail
+        FlaggedOption option3 = new FlaggedOption("test3");
+        option3.setShortFlag('2');
+        try {
+            config.registerParameter(option3);
+            fail("Registered the same short flag twice.");
+        } catch (JSAPException e) {
+            // this is normal
+        }
+
+        FlaggedOption option2b = (FlaggedOption) config.getByShortFlag('2');
+        assertEquals(option2, option2b);
+
+        FlaggedOption optionb = (FlaggedOption) config.getByLongFlag("test");
+        assertEquals(option, optionb);
+
+        FlaggedOption nulloption =
+            (FlaggedOption) config.getByLongFlag("nosuchflag");
+        assertNull(nulloption);
+
+        config.unregisterParameter(option);
+        try {
+            option.setRequired(true);
+        } catch (Exception e) {
+            fail("Unable to modify unlocked option.");
+        }
+
+        optionb = (FlaggedOption) config.getByLongFlag("test");
+        assertNull(optionb);
+
+        try {
+            config.registerParameter(option);
+        } catch (Exception e) {
+            fail("Unable to register arg that was previously unregistered.");
+        }
+
+    }
+
+}
diff --git a/src/java/com/martiansoftware/jsap/TestOption.java b/src/java/com/martiansoftware/jsap/TestOption.java
new file mode 100644
index 0000000..dca483e
--- /dev/null
+++ b/src/java/com/martiansoftware/jsap/TestOption.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2002-2004, Martian Software, Inc.
+ * This file is made available under the LGPL as described in the accompanying
+ * LICENSE.TXT file.
+ */
+
+package com.martiansoftware.jsap;
+
+import java.util.List;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import com.martiansoftware.jsap.stringparsers.StringStringParser;
+
+/**
+ * Tests the Option class
+ * @author <a href="http://www.martiansoftware.com/contact.html">Marty Lamb</a>
+ */
+public class TestOption extends TestCase {
+
+    /**
+     * Returns a suite of tests defined by this class.
+     * @return a suite of tests defined by this class.
+     */
+    public static Test suite() {
+        return (new TestSuite(TestOption.class));
+    }
+
+    /**
+     * Tests the various setters and getters on an option.
+     */
+    public void testSettersAndGetters() {
+        FlaggedOption option = new FlaggedOption("testOption");
+
+        assertEquals("testOption", option.getID());
+        option.setList(true);
+        assertEquals(true, option.isList());
+        option.setListSeparator('W');
+        assertEquals('W', option.getListSeparator());
+        option.setAllowMultipleDeclarations(true);
+        assertEquals(true, option.allowMultipleDeclarations());
+        option.setRequired(true);
+        assertEquals(true, option.required());
+        assertNull(option.getStringParser());
+        StringStringParser sop = StringStringParser.getParser();
+        option.setStringParser(sop);
+        assertEquals(sop, option.getStringParser());
+    }
+
+    /**
+     * Tests the ability to parse via the option's parse() method.
+     */
+    public void testParsing() {
+        FlaggedOption option = new FlaggedOption("testOption");
+
+        option.setListSeparator(' ');
+        option.setList(true);
+        List parseResult = null;
+
+        try {
+            parseResult = option.parse("this is a test");
+        } catch (JSAPException e) {
+            fail(e.getMessage());
+        }
+        assertEquals(4, parseResult.size());
+        assertEquals("this", parseResult.get(0));
+        assertEquals("test", parseResult.get(3));
+    }
+}
diff --git a/src/java/com/martiansoftware/jsap/TestParser.java b/src/java/com/martiansoftware/jsap/TestParser.java
new file mode 100644
index 0000000..b4abc75
--- /dev/null
+++ b/src/java/com/martiansoftware/jsap/TestParser.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2002-2004, Martian Software, Inc.
+ * This file is made available under the LGPL as described in the accompanying
+ * LICENSE.TXT file.
+ */
+
+package com.martiansoftware.jsap;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import com.martiansoftware.jsap.stringparsers.IntegerStringParser;
+import com.martiansoftware.jsap.stringparsers.StringStringParser;
+
+/**
+ * Tests the Parser class.
+ * @author <a href="http://www.martiansoftware.com/contact.html">Marty Lamb</a>
+ */
+public class TestParser extends TestCase {
+
+    /**
+     * Returns a suite of tests defined by this class.
+     * @return a suite of tests defined by this class.
+     */
+    public static Test suite() {
+        return (new TestSuite(TestParser.class));
+    }
+
+    /**
+     * Tests the ability to parse when allowMultipleDeclarations is set to true.
+     */
+    public void testMultipleDeclarations() {
+        FlaggedOption opt = new FlaggedOption("flagged");
+        opt.setShortFlag('f');
+        opt.setLongFlag("longflag");
+        JSAP jsap = new JSAP();
+
+        try {
+            jsap.registerParameter(opt);
+        } catch (Exception e) {
+            fail(e.getMessage());
+        }
+
+        String[] args = { "-f", "testone" };
+        JSAPResult result = null;
+        try {
+            result = jsap.parse(args);
+        } catch (Exception e) {
+            fail(e.getMessage());
+        }
+
+        assertEquals("testone", (String) result.getObject("flagged"));
+
+        String[] args2 = { "-f", "testone", "-f", "testtwo" };
+        result = jsap.parse(args2);
+        assertEquals(false, result.success());
+
+        jsap.unregisterParameter(opt);
+        opt.setAllowMultipleDeclarations(true);
+
+        try {
+            jsap.registerParameter(opt);
+        } catch (Exception e) {
+            fail(e.getMessage());
+        }
+
+        try {
+            result = jsap.parse(args2);
+        } catch (Exception e) {
+            fail(e.getMessage());
+        }
+
+        String[] testresult =
+            (String[]) result.getObjectArray("flagged", new String[0]);
+        assertEquals(2, testresult.length);
+        assertEquals("testone", testresult[0]);
+        assertEquals("testtwo", testresult[1]);
+    }
+
+    /**
+     * Tests the parser with both a String and an Integer option.
+     */
+    public void testParser() {
+        FlaggedOption opt = new FlaggedOption("flagged");
+        opt.setShortFlag('f');
+        opt.setLongFlag("longflag");
+        opt.setStringParser(StringStringParser.getParser());
+        JSAP jsap = new JSAP();
+
+        FlaggedOption opt2 = new FlaggedOption("flaggedInteger");
+        opt2.setLongFlag("integer");
+        opt2.setStringParser(IntegerStringParser.getParser());
+
+        try {
+            jsap.registerParameter(opt);
+            jsap.registerParameter(opt2);
+        } catch (JSAPException je) {
+            fail(je.getMessage());
+        }
+
+        String[] args = { "-f", "myflagthing", "--integer", "42" };
+
+        JSAPResult result = null;
+        try {
+            result = jsap.parse(args);
+        } catch (Exception je2) {
+            je2.printStackTrace();
+            fail(je2.getMessage());
+        }
+
+        assertEquals("myflagthing", (String) result.getObject("flagged"));
+        assertEquals(
+            42,
+            ((Integer) result.getObject("flaggedInteger")).intValue());
+
+    }
+}
diff --git a/src/java/com/martiansoftware/jsap/TestSwitch.java b/src/java/com/martiansoftware/jsap/TestSwitch.java
new file mode 100644
index 0000000..af383b1
--- /dev/null
+++ b/src/java/com/martiansoftware/jsap/TestSwitch.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2002-2004, Martian Software, Inc.
+ * This file is made available under the LGPL as described in the accompanying
+ * LICENSE.TXT file.
+ */
+
+package com.martiansoftware.jsap;
+
+import java.util.List;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+/**
+ * Tests the Switch class.
+ * @author <a href="http://www.martiansoftware.com/contact.html">Marty Lamb</a>
+ */
+public class TestSwitch extends TestCase {
+
+    /**
+     * Creates a new test case with the specified name.
+     * @param name the name for this test case.
+     */
+    public TestSwitch(String name) {
+        super(name);
+    }
+
+    /**
+     * Returns a suite of tests defined by this class
+     * @return a suite of tests defined by this class
+     */
+    public static Test suite() {
+        return (new TestSuite(TestSwitch.class));
+    }
+
+    /**
+     * Tests the ability to retrieve the Switch's ID.
+     */
+    public void testID() {
+        Switch mySwitch = new Switch("mySwitch");
+        assertEquals("mySwitch", mySwitch.getID());
+    }
+
+    /**
+     * Tests the ability to set/get the Switch's short flag.
+     */
+    public void testShortFlag() {
+        Switch mySwitch = new Switch("mySwitch");
+        mySwitch.setShortFlag('c');
+        assertEquals('c', mySwitch.getShortFlag());
+        assertEquals(new Character('c'), mySwitch.getShortFlagCharacter());
+    }
+
+    /**
+     * Tests the ability to set/get the Switch's long flag.
+     */
+    public void testLongFlag() {
+        Switch mySwitch = new Switch("mySwitch");
+        mySwitch.setLongFlag("thisIsMyLongFlag");
+        assertEquals("thisIsMyLongFlag", mySwitch.getLongFlag());
+    }
+
+    /**
+     * Utility method to ensure that the specified List contains a single,
+     * true boolean.
+     * @param a the List to test.
+     */
+    private void ensureTrueList(List a) {
+        assertNotNull(a);
+        assertEquals(1, a.size());
+        assertEquals(a.get(0), new Boolean(true));
+    }
+
+    /**
+     * Utility method to ensure that the specified List contains a single,
+     * false boolean.
+     * @param a the List to test.
+     */
+    private void ensureFalseList(List a) {
+        assertNotNull(a);
+        assertEquals(1, a.size());
+        assertEquals(a.get(0), new Boolean(false));
+    }
+
+    /**
+     * Tests the parsing ability of the Switch.
+     */
+    public void testSwitchParse() {
+        try {
+            Switch mySwitch = new Switch("mySwitch");
+            ensureTrueList(mySwitch.parse(null));
+            ensureTrueList(mySwitch.parse("true"));
+            ensureFalseList(mySwitch.parse("false"));
+        } catch (JSAPException e) {
+            fail(e.getMessage());
+        }
+
+    }
+}
diff --git a/src/java/com/martiansoftware/jsap/TestUsageString.java b/src/java/com/martiansoftware/jsap/TestUsageString.java
new file mode 100644
index 0000000..659a627
--- /dev/null
+++ b/src/java/com/martiansoftware/jsap/TestUsageString.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2002-2004, Martian Software, Inc.
+ * This file is made available under the LGPL as described in the accompanying
+ * LICENSE.TXT file.
+ */
+
+package com.martiansoftware.jsap;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+/**
+ * Tests the ability to automatically create usage information.
+ * @author <a href="http://www.martiansoftware.com/contact.html">Marty Lamb</a>
+ */
+public class TestUsageString extends TestCase {
+
+    /**
+     * Creates a new TestCase with the specified name.
+     * @param arg0 the name for this TestCase.
+     */
+    public TestUsageString(String arg0) {
+        super(arg0);
+    }
+
+    /**
+     * Returns a suite of tests defined by this class.
+     * @return a suite of tests defined by this class.
+     */
+    public static Test suite() {
+        return (new TestSuite(TestUsageString.class));
+    }
+
+    /**
+     * Tests usage info for a single option, both required and not required.
+     */
+    public void testUsage1() {
+        JSAP config = new JSAP();
+
+        FlaggedOption opt1 = new FlaggedOption("flaggedOption");
+        opt1.setShortFlag('f');
+        opt1.setLongFlag("flagged");
+        opt1.setRequired(JSAP.REQUIRED);
+        try {
+            config.registerParameter(opt1);
+        } catch (JSAPException e) {
+            fail("Unable to register opt1");
+        }
+        assertEquals("(-f|--flagged)" + JSAP.SYNTAX_SPACECHAR + "<flaggedOption>", config.getUsage());
+
+        config.unregisterParameter(opt1);
+        opt1.setRequired(JSAP.NOT_REQUIRED);
+        try {
+            config.registerParameter(opt1);
+        } catch (JSAPException e) {
+            fail("Unable to register opt1");
+        }
+        assertEquals("[(-f|--flagged)" + JSAP.SYNTAX_SPACECHAR + "<flaggedOption>]", config.getUsage());
+    }
+
+    /**
+     * Tests usage info for a Switch.
+     */
+    public void testUsage2() {
+        JSAP config = new JSAP();
+        Switch sw = new Switch("testSwitch");
+        sw.setShortFlag('s');
+        sw.setLongFlag("switch");
+        try {
+            config.registerParameter(sw);
+        } catch (JSAPException e) {
+            fail("Unable to register sw");
+        }
+        assertEquals("[-s|--switch]", config.getUsage());
+    }
+}
diff --git a/src/java/com/martiansoftware/jsap/UnflaggedOption.java b/src/java/com/martiansoftware/jsap/UnflaggedOption.java
new file mode 100644
index 0000000..ade8876
--- /dev/null
+++ b/src/java/com/martiansoftware/jsap/UnflaggedOption.java
@@ -0,0 +1,240 @@
+/*
+ * Copyright (c) 2002-2004, Martian Software, Inc.
+ * This file is made available under the LGPL as described in the accompanying
+ * LICENSE.TXT file.
+ */
+
+package com.martiansoftware.jsap;
+
+/**
+ * An option whose meaning is derived from its <i>position in the argument
+ * list</i> rather than a flag
+ * that precedes it.  UnflaggedOptions allow the parsing of command lines
+ * without flags, such as
+ * "compressfiles destinationFile file1 file2 file3 file4 ...", where
+ * "destinationFile" is the name of the
+ * file to create, and file1 through file 4 (and beyond) are the names of the
+ * files to compress.  The JSAP
+ * that supports this command line has only two options defined: the first
+ * accepts a single destination file,
+ * and the second is "greedy," consuming the remaining unflagged options.
+ * @author <a href="http://www.martiansoftware.com/contact.html">Marty Lamb</a>
+ * @see com.martiansoftware.jsap.FlaggedOption
+ * @see com.martiansoftware.jsap.Option
+ */
+public final class UnflaggedOption extends Option {
+
+    /**
+     * Boolean indicating whether or not this UnflaggedOption is greedy.
+     * Default is JSAP.NOT_GREEDY.
+     */
+    private boolean greedy = JSAP.NOT_GREEDY;
+
+    /**
+     * Creates a new UnflaggedOption with the specified unique ID.
+     * @param id the unique ID for this UnflaggedOption.
+     */
+    public UnflaggedOption(String id) {
+        super(id);
+    }
+
+    /**
+     * A shortcut constructor that creates a new UnflaggedOption and configures
+     * its most commonly used settings.
+     * @param id the unique ID for this UnflaggedOption
+     * @param stringParser the StringParser this UnflaggedOption should use.
+     * @param defaultValue the default value for this UnflaggedOption (may be
+     * null).
+     * @param required if true, this UnflaggedOption is required.
+     * @param greedy if true, this UnflaggedOption is greedy.
+     * @param help the help text for this option (may be set to {@link JSAP#NO_HELP} for none).
+     * */
+    public UnflaggedOption(
+        String id,
+        StringParser stringParser,
+        String defaultValue,
+        boolean required,
+        boolean greedy,
+		String help) {
+        this(id);
+        setStringParser(stringParser);
+        setDefault(defaultValue);
+        setRequired(required);
+        setGreedy(greedy);
+        setHelp(help);
+    }
+
+    /**
+     * A shortcut constructor that creates a new UnflaggedOption and configures
+     * its most commonly used settings.
+     * @param id the unique ID for this UnflaggedOption
+     * @param stringParser the StringParser this UnflaggedOption should use.
+     * @param defaultValue the default value for this UnflaggedOption (may be
+     * null).
+     * @param required if true, this UnflaggedOption is required.
+     * @param greedy if true, this UnflaggedOption is greedy.
+     * */
+    public UnflaggedOption(
+        String id,
+        StringParser stringParser,
+        String defaultValue,
+        boolean required,
+        boolean greedy) {
+        this(id);
+        setStringParser(stringParser);
+        setDefault(defaultValue);
+        setRequired(required);
+        setGreedy(greedy);
+    }
+
+    /**
+     * A shortcut constructor that creates a new non-greedy UnflaggedOption with no default value 
+     * and configures its most commonly used settings.
+     * 
+     * @param id the unique ID for this UnflaggedOption
+     * @param stringParser the StringParser this UnflaggedOption should use.
+     * @param required if true, this UnflaggedOption is required.
+     * @param help the help text for this option (may be set to {@link JSAP#NO_HELP} for none).
+     */
+    public UnflaggedOption(
+        String id,
+        StringParser stringParser,
+        boolean required,
+		String help) {
+        this(id);
+        setStringParser(stringParser);
+        setRequired(required);
+        setGreedy(JSAP.NOT_GREEDY);
+        setHelp(help);
+    }
+
+    /**
+     * Sets whether this UnflaggedOption is greedy.  A greedy UnflaggedOption
+     * consumes all the remaining
+     * UnflaggedOptions from the argument list.  As a result, only one greedy
+     * UnflaggedOption may be registered with
+     * a JSAP, and it must be the last UnflaggedOption registered.
+     * @param greedy if true, this UnflaggedOption will be greedy.
+     * @return the modified UnflaggedOption
+     */
+    public UnflaggedOption setGreedy(boolean greedy) {
+        enforceParameterLock();
+        this.greedy = greedy;
+        return (this);
+    }
+
+    /**
+     * Sets the name that will be displayed when getUsage() is called
+     * @param usageName the name to use, or null if the id should be used (default)
+     * @return the modified UnflaggedOption
+     */
+    public UnflaggedOption setUsageName(String usageName) {
+    	_setUsageName(usageName);
+    	return (this);
+    }
+
+    /**
+     * Returns a boolean indicating whether this UnflaggedOption is greedy.
+     * @return a boolean indicating whether this UnflaggedOption is greedy.
+     */
+    public boolean isGreedy() {
+        return (greedy);
+    }
+
+    /**
+     * Returns syntax instructions for this FlaggedOption.
+     * @return syntax instructions for this FlaggedOption based upon its current
+     * configuration.
+     */
+    public String getSyntax() {
+        StringBuffer result = new StringBuffer();
+        if (!required()) {
+            result.append("[");
+        }
+
+        String un = getUsageName();
+        if (this.isGreedy()) {
+            result.append(un + "1" + JSAP.SYNTAX_SPACECHAR + un + "2" + JSAP.SYNTAX_SPACECHAR + "..." + JSAP.SYNTAX_SPACECHAR + un + "N");
+        } else {
+            result.append("<" + un + ">");
+        }
+        if (!required()) {
+            result.append("]");
+        }
+        return (result.toString());
+    }
+
+    /**
+     * Sets whether this UnflaggedOption is a list.  Default behavior is
+     * JSAP.NOT_LIST.
+     * @param isList if true, this Option is a list.
+     * @return the modified UnflaggedOption
+     */
+    public UnflaggedOption setList(boolean isList) {
+        super.internalSetList(isList);
+        return (this);
+    }
+
+    /**
+     * Sets the list separator character for this UnflaggedOption.  The default
+     * list separator is JSAP.DEFAULT_LISTSEPARATOR.
+     * @param listSeparator the list separator for this Option.
+     * @return the modified UnflaggedOption
+     */
+    public UnflaggedOption setListSeparator(char listSeparator) {
+        super.internalSetListSeparator(listSeparator);
+        return (this);
+    }
+
+    /**
+     * Sets whether this UnflaggedOption is required.  Default is
+     * JSAP.NOT_REQUIRED.
+     * @param required if true, this Option will be required.
+     * @return the modified UnflaggedOption
+     */
+    public UnflaggedOption setRequired(boolean required) {
+        super.internalSetRequired(required);
+        return (this);
+    }
+
+    /**
+     * Sets the StringParser to which this UnflaggedOption's parse() method
+     * should delegate.
+     * @param stringParser the StringParser to which this Option's parse()
+     * method should delegate.
+     * @see com.martiansoftware.jsap.StringParser
+     * @return the modified UnflaggedOption
+     */
+    public UnflaggedOption setStringParser(StringParser stringParser) {
+        super.internalSetStringParser(stringParser);
+        return (this);
+    }
+    
+    /**
+     * Sets a default value for this parameter.  The default is specified
+     * as a String, and is parsed as a single value specified on the
+     * command line.  In other words, default values for "list"
+     * parameters or parameters allowing multiple declarations should be
+     * set using setDefault(String[]), as JSAP
+     * would otherwise treat the entire list of values as a single value.
+     *
+     * @param defaultValue the default value for this parameter.
+     * @see #setDefault(String)
+     */
+    public UnflaggedOption setDefault(String defaultValue) {
+    	_setDefault(defaultValue);
+    	return (this);
+    }        
+    
+    /**
+     * Sets one or more default values for this parameter.  This method
+     * should be used whenever a parameter has more than one default
+     * value.
+     * @param defaultValues the default values for this parameter.
+     * @see #setDefault(String)
+     */
+    public UnflaggedOption setDefault(String[] defaultValues) {
+    	_setDefault(defaultValues);
+    	return (this);
+    }    
+}
diff --git a/src/java/com/martiansoftware/jsap/UnknownFlagException.java b/src/java/com/martiansoftware/jsap/UnknownFlagException.java
new file mode 100644
index 0000000..2308487
--- /dev/null
+++ b/src/java/com/martiansoftware/jsap/UnknownFlagException.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2002-2004, Martian Software, Inc.
+ * This file is made available under the LGPL as described in the accompanying
+ * LICENSE.TXT file.
+ */
+package com.martiansoftware.jsap;
+
+/**
+ * An exception indicating that a unknown flag has been specified.
+ *
+ * @author <a href="http://www.martiansoftware.com/contact.html">Marty Lamb</a>
+ * @see com.martiansoftware.jsap.Flagged
+ */
+public class UnknownFlagException extends JSAPException {
+
+    /**
+     * The unknown flag that was encountered.
+     */
+    private String flag = null;
+
+    /**
+     * Creates a new UnknownFlagException referencing the specified parameter.
+     * @param flag the unknown flag that was encountered.
+     */
+    public UnknownFlagException(String flag) {
+        super("Unknown flag '" + flag + "'.");
+        this.flag = flag;
+    }
+
+    /**
+     * Creates a new UnknownFlagException referencing the specified parameter.
+     * @param flag the unknown flag that was encountered.
+     */
+    public UnknownFlagException(Character flag) {
+        super("Unknown flag '" + flag + "'.");
+        this.flag = flag.toString();
+    }
+
+    /**
+     * Returns the unknown flag that was encountered.
+     * @return the unknown flag that was encountered.
+     */
+    public String getFlag() {
+        return (this.flag);
+    }
+
+}
diff --git a/src/java/com/martiansoftware/jsap/UnspecifiedParameterException.java b/src/java/com/martiansoftware/jsap/UnspecifiedParameterException.java
new file mode 100644
index 0000000..23ab8c7
--- /dev/null
+++ b/src/java/com/martiansoftware/jsap/UnspecifiedParameterException.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2002-2004, Martian Software, Inc.
+ * This file is made available under the LGPL as described in the accompanying
+ * LICENSE.TXT file.
+ */
+
+package com.martiansoftware.jsap;
+
+/** An exception thrown when an argument that requires a conversion
+ * (e.g., an integer) has no associated value, but it is retrieved
+ * by means of a type-specified method (e.g., {@link com.martiansoftware.jsap.JSAPResult#getInt(String)}).
+ * 
+ * @author Sebastiano Vigna
+ */
+public class UnspecifiedParameterException extends RuntimeException {
+
+    /**
+     * The unique ID of the parameter whose retrieval was attempted.
+     */
+    private String id = null;
+
+    /** Creates a new {@link UnspecifiedParameterException} referencing the
+     * specified parameter.
+     * @param paramID the unique ID of the parameter whose retrieval was attempted.
+     */
+    public UnspecifiedParameterException(String paramID) {
+        super("Parameter '" + paramID + "' has no associated value.");
+        this.id = paramID;
+    }
+
+    /**
+     * Returns the unique ID of the parameter whose retrieval was attempted.
+     * @return the unique ID of the parameter whose retrieval was attempted.
+     */
+    public String getID() {
+        return (this.id);
+    }
+}
diff --git a/src/java/com/martiansoftware/jsap/ant/DefaultValue.java b/src/java/com/martiansoftware/jsap/ant/DefaultValue.java
new file mode 100644
index 0000000..e453ffd
--- /dev/null
+++ b/src/java/com/martiansoftware/jsap/ant/DefaultValue.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2002-2004, Martian Software, Inc.
+ * This file is made available under the LGPL as described in the accompanying
+ * LICENSE.TXT file.
+ */
+
+package com.martiansoftware.jsap.ant;
+
+/**
+ * Provides a means of specifying default values for the jsap task containing
+ * <default> elements.
+ * For detailed information on using the jsap task, see the documentation for
+ * JSAPAntTask.
+ *
+ * @author <a href="http://www.martiansoftware.com/contact.html">Marty Lamb</a>
+ * @see JSAPAntTask
+ */
+public class DefaultValue {
+
+    /**
+     * The default value.
+     */
+    private String value = null;
+
+    /**
+     * Sets the default value.
+     * @param text the default value.
+     */
+    public void addText(String text) {
+        this.value = text;
+    }
+
+    /**
+     * Returns the default value.
+     * @return the default value.
+     */
+    public String getValue() {
+        return (value);
+    }
+}
diff --git a/src/java/com/martiansoftware/jsap/ant/FlaggedOptionConfiguration.java b/src/java/com/martiansoftware/jsap/ant/FlaggedOptionConfiguration.java
new file mode 100644
index 0000000..a65b324
--- /dev/null
+++ b/src/java/com/martiansoftware/jsap/ant/FlaggedOptionConfiguration.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright (c) 2002-2004, Martian Software, Inc.
+ * This file is made available under the LGPL as described in the accompanying
+ * LICENSE.TXT file.
+ */
+
+package com.martiansoftware.jsap.ant;
+
+import java.io.PrintStream;
+
+import com.martiansoftware.jsap.Parameter;
+import com.martiansoftware.jsap.FlaggedOption;
+import com.martiansoftware.jsap.JSAP;
+
+/**
+ * Stores/provides configuration data for flaggedoptions nested inside a jsap
+ * ant task.
+ * For detailed information on using the jsap task, see the documentation for
+ * JSAPAntTask.
+ *
+ * @author <a href="http://www.martiansoftware.com/contact.html">Marty Lamb</a>
+ * @see JSAPAntTask
+ * @see com.martiansoftware.jsap.FlaggedOption
+ */
+
+public class FlaggedOptionConfiguration extends OptionConfiguration {
+
+    /**
+     * The short flag for this option.
+     */
+    private char shortFlag = JSAP.NO_SHORTFLAG;
+
+    /**
+     * The long flag for this option.
+     */
+    private String longFlag = JSAP.NO_LONGFLAG;
+
+    /**
+     * If true, this option may be declared multiple times on a single command
+     * line.
+     */
+    private boolean allowMultipleDeclarations = JSAP.NO_MULTIPLEDECLARATIONS;
+
+    /**
+     * If true, the allowMultipleDeclarations field has been set by the user.
+     */
+    private boolean declaredAllowMultipleDeclarations = false;
+
+    /**
+     * Creates a new FlaggedOptionConfiguration.
+     */
+    public FlaggedOptionConfiguration() {
+        super();
+    }
+
+    /**
+     * Sets the short flag for this option.
+     * @param shortFlag the short flag for this option.
+     */
+    public void setShortflag(char shortFlag) {
+        this.shortFlag = shortFlag;
+    }
+
+    /**
+     * Returns the short flag for this option.
+     * @return the short flag for this option.
+     */
+    public char getShortflag() {
+        return (shortFlag);
+    }
+
+    /**
+     * Sets the long flag for this option.
+     * @param longFlag the long flag for this option.
+     */
+    public void setLongflag(String longFlag) {
+        this.longFlag = longFlag;
+    }
+
+    /**
+     * Returns the long flag for this option.
+     * @return the long flag for this option.
+     */
+    public String getLongflag() {
+        return (longFlag);
+    }
+
+    /**
+     * Specifies whether this option can be declared multiple times on the same
+     * command line.
+     * @param allowMultipleDeclarations if true, this option can be declared
+     * multiple times on the same
+     * command line.
+     */
+    public void setAllowmultipledeclarations(
+    	boolean allowMultipleDeclarations) {
+
+        this.allowMultipleDeclarations = allowMultipleDeclarations;
+        declaredAllowMultipleDeclarations = true;
+    }
+
+    /**
+     * Returns a FlaggedOption preconfigured according to this configuration.
+     * @return a FlaggedOption preconfigured according to this configuration.
+     */
+    public Parameter getParameter() {
+        FlaggedOption result = new FlaggedOption(this.getId());
+
+        result.setShortFlag(this.getShortflag());
+        result.setLongFlag(this.getLongflag());
+        result.setRequired(this.getRequired());
+        result.setList(this.getIslist());
+        if (this.declaredListSeparator()) {
+            result.setListSeparator(this.getListseparator());
+        }
+        if (this.declaredAllowMultipleDeclarations) {
+            result.setAllowMultipleDeclarations(this.allowMultipleDeclarations);
+        }
+        setupStringParser(result);
+        result.setDefault(this.getDefaults());
+        return (result);
+    }
+
+    /**
+     * Creates source code for a java method that will instantiate a
+     * FlaggedOption matching this
+     * configuration.
+     * @param methodName the name of the method to create
+     * @param out the PrintStream to which the java source for the method
+     * should be written.
+     */
+    public void createMethod(String methodName, PrintStream out) {
+
+        out.println("    private FlaggedOption " + methodName + "() {");
+        out.println(
+            "        FlaggedOption result = new FlaggedOption(\""
+                + this.getId()
+                + "\");");
+        if (getShortflag() != JSAP.NO_SHORTFLAG) {
+            out.println(
+                "        result.setShortFlag('" + getShortflag() + "');");
+        }
+        if (getLongflag() != JSAP.NO_LONGFLAG) {
+            out.println(
+                "        result.setLongFlag(\"" + getLongflag() + "\");");
+        }
+        if (this.declaredAllowMultipleDeclarations) {
+            out.println(
+                "        result.setAllowMultipleDeclarations("
+                    + (allowMultipleDeclarations ? "true" : "false")
+                    + ");");
+        }
+        super.createParentStatements("result", out);
+        out.println("        return (result);");
+        out.println("    }");
+    }
+
+}
diff --git a/src/java/com/martiansoftware/jsap/ant/JSAPAntTask.java b/src/java/com/martiansoftware/jsap/ant/JSAPAntTask.java
new file mode 100644
index 0000000..3632ed4
--- /dev/null
+++ b/src/java/com/martiansoftware/jsap/ant/JSAPAntTask.java
@@ -0,0 +1,545 @@
+/*
+ * Copyright (c) 2002-2004, Martian Software, Inc.
+ * This file is made available under the LGPL as described in the accompanying
+ * LICENSE.TXT file.
+ */
+
+package com.martiansoftware.jsap.ant;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.util.Enumeration;
+import java.util.Vector;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Task;
+
+import com.martiansoftware.jsap.JSAP;
+import com.martiansoftware.jsap.JSAPException;
+
+/**
+ * <p>An ANT task that generates a custom subclass of JSAP to simplify its use
+ * in
+ * a program.  Rather than create all of the Switches, FlaggedOptions, and
+ * UnflaggedOptions and registering each with the JSAP, the developer does not
+ * need to do anything but instantiate the custom JSAP produced by this task.
+ * </p>
+ *
+ * <p>To use this task, you must first declare it in your ANT build file with a
+ * <taskdef> tag, as follows:</p>
+ *
+ * <p><code>
+ * <taskdef name="jsap" classname="com.martiansoftware.jsap.ant.JSAPAntTask"
+ * classpath="${lib}/[jsap jarfile]"/>
+ * </code></p>
+ *
+ * <p>Note that this <code>taskdef</code> must be placed in your build file
+ * BEFORE your jsap task.  The
+ * <code>classpath</code> attribute in the above example assumes that
+ * [jsap jarfile] is the name of the JSAP jarfile you're using, and
+ * is in the directory referenced by the ANT "lib" property.</p>
+ *
+ * <p>Once declared, the jsap task can be used as many times as you wish.  The
+ * jsap
+ * task supports the following attributes:</p>
+ * <ul>
+ * <li><b>srcdir</b> (required): the source directory below which the generated
+ * JSAP source code should be placed.</li>
+ * <li><b>classname</b> (required): the fully-qualified classname of the JSAP to
+ * generate.</li>
+ * <li><b>public</b> (optional): "true" or "false" (default false).  If true,
+ * the
+ * generated class will be declared public.</li>
+ * </ul>
+ *
+ * <p>The jsap task supports the following nested elements:</p>
+ *
+ * <ul>
+ * <li><b>switch</b> - declares a Switch (com.martiansoftware.jsap.Switch).</li>
+ * <li><b>flaggedoption</b> - declares a FlaggedOption
+ * (com.martiansoftware.jsap.FlaggedOption).</li>
+ * <li><b>unflaggedoption</b> - declares an UnflaggedOption
+ * (com.martiansoftware.jsap.UnflaggedOption).</li>
+ * </ul>
+ *
+ * <p>These nested elements support the following attributes:</p>
+ *
+ * <center>
+ * <table width="95%" border="1" cellpadding="0" cellspacing="0">
+ * <tr>
+ *    <td align="center"><b>Attribute</b></td>
+ *    <td align="center"><b>Description</b></td>
+ *    <td align="center"><b>switch</b></td>
+ *    <td align="center"><b>flaggedoption</b></td>
+ *    <td align="center"><b>unflaggedoption</b></td>
+ *    <td align="center"><b>qualifiedswitch</b></td>
+ * </tr>
+ * <tr>
+ *    <td align="center"><b>id</b></td>
+ *    <td align="left">Unique id for this parameter.  This must be unique among
+ * all parameters defined in this ANT task.</td>
+ *    <td align="center">Required</td>
+ *    <td align="center">Required</td>
+ *    <td align="center">Required</td>
+ *    <td align="center">Required</td>
+ * </tr>
+ * <tr>
+ *    <td align="center"><b>shortflag</b></td>
+ *    <td align="left">Short flag for this parameter.  Only the first character
+ * of this attribute is read by the jsap task.  This must be unique among all
+ * short
+ * flags defined in this ANT task.</td>
+ *    <td align="left">Either <b>shortflag</b> or <b>longflag</b> is required.
+ * Both may be specified.</td>
+ *    <td align="left">Either <b>shortflag</b> or <b>longflag</b> is required.
+ * Both may be specified.</td>
+ *    <td align="center">N/A</td>
+ *    <td align="left">Either <b>shortflag</b> or <b>longflag</b> is required.
+ * Both may be specified.</td>
+ * </tr>
+ * <tr>
+ *    <td align="center"><b>longflag</b></td>
+ *    <td align="left">Long flag for this parameter.  This must be unique among
+ * all long flags defined in this ANT task.</td>
+ *    <td align="left">Either <b>shortflag</b> or <b>longflag</b> is required.
+ * Both may be specified.</td>
+ *    <td align="left">Either <b>shortflag</b> or <b>longflag</b> is required.
+ * Both may be specified.</td>
+ *    <td align="center">N/A</td>
+ *    <td align="left">Either <b>shortflag</b> or <b>longflag</b> is required.
+ * Both may be specified.</td>
+ * </tr>
+ * <tr>
+ *    <td align="center"><b>required</b></td>
+ *    <td align="left">"true" or "false" (default false).  Indicates whether the
+ * specified parameter is required.</td>
+ *    <td align="center">N/A</td>
+ *    <td align="center">Optional</td>
+ *    <td align="center">Optional</td>
+ *    <td align="center">Optional</td>
+ * </tr>
+ * <tr>
+ *    <td align="center"><b>islist</b></td>
+ *    <td align="left">"true" or "false" (default false).  Indicates whether the
+ * specified parameter can be supplied as a list of values separated by a
+ * delimiter
+ * character.</td>
+ *    <td align="center">N/A</td>
+ *    <td align="center">Optional</td>
+ *    <td align="center">Optional</td>
+ *    <td align="center">Optional</td>
+ * </tr>
+ * <tr>
+ *    <td align="center"><b>listseparator</b></td>
+ *    <td align="left">Specifies the delimiter character to use for list
+ * parameters.
+ * Default is JSAP.DEFAULT_LISTSEPARATOR</td>
+ *    <td align="center">N/A</td>
+ *    <td align="center">Optional</td>
+ *    <td align="center">Optional</td>
+ *    <td align="center">Optional</td>
+ * </tr>
+ * <tr>
+ *    <td align="center"><b>stringparser</b></td>
+ *    <td align="left">Specifies the subclass of
+ * com.martiansoftware.jsap.StringParser
+ * to be used in parsing this parameter's values.  If the specified class name
+ * contains
+ * no "dot" characters, it is assumed to be in the package
+ * com.martiansoftware.jsap.stringparsers.
+ * Default value is
+ * com.martiansoftware.jsap.stringparsers.StringStringParser.</td>
+ *    <td align="center">N/A</td>
+ *    <td align="center">Optional</td>
+ *    <td align="center">Optional</td>
+ *    <td align="center">Optional</td>
+ * </tr>
+ * <tr>
+ *    <td align="center"><b>greedy</b></td>
+ *    <td align="left">"true" or "false" (default false).  Specifies whether the
+ * unflaggedoption should be "greedy"; that is, should consume all remaining
+ * unflagged arguments from the command line.</td>
+ *    <td align="center">N/A</td>
+ *    <td align="center">N/A</td>
+ *    <td align="center">Optional</td>
+ *    <td align="center">N/A</td>
+ * </tr>
+ * </table>
+ * </center>
+ *
+ * <p>All of these nested elements support multiple nested
+ * <code><default></code>
+ * elements.  The text content of these tags is used as a default value for the
+ * parameter containing the tag.<p>
+ *
+ * <p>Finally, the <flaggedoption> and <unflaggedoption> support
+ * multiple
+ * nested <property> elements, with similar syntax to ANT's
+ * <property>
+ * elements.  These properties are set within the parameter's StringParser,
+ * assuming
+ * it is a subclass of com.martiansoftware.jsap.PropertyStringParser.</p>
+ *
+ * <h2>Example</h2>
+ *
+ * <pre>
+* & lt;taskdef name = "jsap" classname = "com.martiansoftware.jsap.ant.JSAPAntTask"
+ *      classpath="${build}"/>
+ *
+ *     <target name="createExampleAntJSAP">
+ *
+ *         <jsap srcdir="${src}"
+ *          classname="com.martiansoftware.jsap.examples.ExampleAntJSAP">
+ *
+ *             <!-- create a switch flagged by "v" or "verbose" -->
+ *             <switch id="verbose" shortflag="v" longflag="verbose"/>
+ *
+ *             <!-- create a required flaggedoption looking for an integer,
+ *              flagged by "n" (e.g. "-n 5") -->
+ *             <flaggedoption id="num" required="true" shortflag="n"
+ *              stringparser="IntegerStringParser">
+ *                 <default>5</default>
+ *             </flaggedoption>
+ *
+ *             <!-- create an unflaggedoption that reads all of the unflagged
+ *              arguments from the
+ *              command line -->
+ *             <unflaggedoption id="files" greedy="true" />
+ *
+ *             <!-- create a flaggedoption looking for a Date in "MM/DD/YY"
+ *              format, flagged by "d"
+ *                  or "date", defaulting to Christmas, 2002. -->
+ *             <flaggedoption id="date" shortflag="d" longflag="date"
+ *              stringparser="DateStringParser">
+ *                 <property name="format" value="MM/DD/YYYY"/>
+ *                 <default>12/25/2002</default>
+ *             </flaggedoption>
+ *                 
+ *         </jsap>
+ *     
+ *     </target>
+ *</pre>
+ *
+ * @author <a href="http://www.martiansoftware.com/contact.html">Marty Lamb</a>
+ * @see com.martiansoftware.jsap.JSAP
+ * @see com.martiansoftware.jsap.Parameter
+ * @see com.martiansoftware.jsap.Switch
+ * @see com.martiansoftware.jsap.FlaggedOption
+ * @see com.martiansoftware.jsap.UnflaggedOption
+ * @see com.martiansoftware.jsap.StringParser
+ * @see com.martiansoftware.jsap.PropertyStringParser
+ */
+public class JSAPAntTask extends Task {
+
+    /**
+     * if true, the generated JSAP class should be declared public.
+     */
+    private boolean isPublic = false;
+
+    /**
+     * the top-level directory holding the source code (probably has a "com"
+     * subdirectory, etc.)
+     */
+    private File srcDir = null;
+
+    /**
+     * the FULL class name of the JSAP subclass to create (i.e.,
+     * "fullpackage.Class").
+     */
+    private String className = null;
+
+    /**
+     * A Vector containing all of the nested parameter configurations, in
+     * declaration order.
+     */
+    private Vector parameterConfigs = null;
+
+    /**
+     * A "Test JSAP" that is instantiated to validate the configuration before
+     * the class file is
+     * generated.
+     */
+    private JSAP jsap = null;
+
+    /**
+     * If true, this JSAP uses FlaggedOptions (used for import statements).
+     */
+    private boolean containsFlaggedOptions = false;
+
+    /**
+     * If true, this JSAP uses UnflaggedOptions (used for import statements).
+     */
+    private boolean containsUnflaggedOptions = false;
+
+    /**
+     * If true, this JSAP uses Switches (used for import statements).
+     */
+    private boolean containsSwitches = false;
+
+    /**
+     * If true, this JSAP's StringParser has properties.
+     */
+    private boolean hasProperties = false;
+
+    /**
+     * Creates a new JSAPAntTask.  One JSAPAntTask is created for each
+     * <jsap> section in an
+     * ant built file.
+     */
+    public JSAPAntTask() {
+        parameterConfigs = new Vector();
+    }
+
+    /**
+     * Sets whether the generated JSAP should be declared public.  Default is
+     * false.
+     * @param isPublic if true, the generated JSAP will be declared public.
+     */
+    public void setPublic(boolean isPublic) {
+        this.isPublic = isPublic;
+    }
+
+    /**
+     * Sets the top-level source directory under which the generated JSAP class
+     * file should be written.
+     * @param srcDir the top-level source directory under which the generated
+     * JSAP class file should be written.
+     */
+    public void setSrcdir(File srcDir) {
+        this.srcDir = srcDir;
+    }
+
+    /**
+     * Sets the full classname for the generated JSAP.
+     * @param className the full classname for the generated JSAP.
+     */
+    public void setClassname(String className) {
+        this.className = className.trim();
+    }
+
+    /**
+     * Adds a nested FlaggedOptionConfiguration to the generated JSAP.
+     * @param flaggedOptionConfig the nested FlaggedOptionConfiguration to add
+     * to the generated JSAP.
+     */
+    public void addConfiguredFlaggedoption(FlaggedOptionConfiguration flaggedOptionConfig) {
+        containsFlaggedOptions = true;
+        parameterConfigs.add(flaggedOptionConfig);
+    }
+
+    /**
+     * Adds a nested UnflaggedOptionConfiguration to the generated JSAP.
+     * @param unflaggedOptionConfig the nested UnflaggedOptionConfiguration to
+     * add to the generated JSAP.
+     */
+    public void addConfiguredUnflaggedoption(UnflaggedOptionConfiguration unflaggedOptionConfig) {
+        containsUnflaggedOptions = true;
+        parameterConfigs.add(unflaggedOptionConfig);
+    }
+
+    /**
+     * Adds a nested SwitchConfiguration to the generated JSAP.
+     * @param switchConfig the nested SwitchConfiguration to add to the
+     * generated JSAP.
+     */
+    public void addConfiguredSwitch(SwitchConfiguration switchConfig) {
+        containsSwitches = true;
+        parameterConfigs.add(switchConfig);
+    }
+
+    /**
+     * Builds a JSAP that conforms to the configuration in the build file.  If
+     * all of the specified
+     * parameters can successfully be registered with the JSAP, the java file
+     * will be written.
+     * @throws JSAPException if there are any problems with the specified
+     * configuration.
+     */
+    private void buildJSAP() throws JSAPException {
+        JSAP jsap = new JSAP();
+        for (Enumeration e = parameterConfigs.elements();
+            e.hasMoreElements();
+            ) {
+
+            ParameterConfiguration pc =
+                (ParameterConfiguration) e.nextElement();
+
+            if (pc.hasProperties()) {
+                hasProperties = true;
+            }
+            jsap.registerParameter(pc.getParameter());
+        }
+    }
+
+    /**
+     * Generates a JSAP java file that implements the specified configuration.
+     * @throws IOException if an I/O error occurs.
+     */
+    private void writeJSAP() throws IOException {
+        int lastDotPos = className.lastIndexOf(".");
+        String packageName = "";
+        String shortClassName = className;
+        if (lastDotPos > -1) {
+            packageName = className.substring(0, lastDotPos);
+            shortClassName = className.substring(lastDotPos + 1);
+        }
+
+        System.out.println("package name: [" + packageName + "]");
+        System.out.println("shortClassName: [" + shortClassName + "]");
+
+        File classFileDir =
+            new File(
+                srcDir.getCanonicalPath()
+                    + File.separatorChar
+                    + packageName.replace('.', File.separatorChar));
+
+        File classFile = new File(classFileDir, shortClassName + ".java");
+
+        System.out.println(
+            "Creating directory \"" + classFileDir.toString() + "\"");
+        classFileDir.mkdirs();
+        System.out.println("Creating JSAP class file \"" + classFile + "\"");
+        classFile.createNewFile();
+        System.out.println("Created");
+        PrintStream out =
+            new PrintStream(
+                new BufferedOutputStream(new FileOutputStream(classFile)));
+
+        createJavaFile(shortClassName, packageName, out);
+        out.close();
+    }
+
+    /**
+     * Simple utility to capitalize the first letter of the specified String.
+     * @param s the String to represent in proper case.
+     * @return the specified String with the first letter capitalized.
+     */
+    private String properCase(String s) {
+        String result = null;
+        if (s != null) {
+            if (s.length() < 2) {
+                result = s.toUpperCase();
+            } else {
+                result = s.toUpperCase().charAt(0) + s.substring(1);
+            }
+        }
+        return (result);
+    }
+
+    /**
+     * Writes java source code for a JSAP subclass that implements the
+     * configuration specified in the
+     * ant build file.
+     * @param shortClassName the name of the JSAP subclass, sans package name.
+     * @param packageName the name of the package to contain the generated JSAP
+     * subclass.
+     * @param out the PrintStream to which the java source should be written.
+     * @throws IOException if an I/O error occurs.
+     */
+    private void createJavaFile(
+        String shortClassName,
+        String packageName,
+        PrintStream out)
+        throws IOException {
+        if (packageName.length() > 0) {
+            out.println("package " + packageName + ";");
+            out.println();
+        }
+        out.println(" /*");
+        out.println("  * THIS FILE IS AUTOMATICALLY GENERATED - DO NOT EDIT");
+        out.println("  */");
+        out.println();
+        out.println("import com.martiansoftware.jsap.JSAP;");
+        out.println("import com.martiansoftware.jsap.JSAPException;");
+        if (containsSwitches) {
+            out.println("import com.martiansoftware.jsap.Switch;");
+        }
+        if (containsFlaggedOptions) {
+            out.println("import com.martiansoftware.jsap.FlaggedOption;");
+        }
+        if (containsUnflaggedOptions) {
+            out.println("import com.martiansoftware.jsap.UnflaggedOption;");
+        }
+        if (hasProperties) {
+            out.println(
+                "import com.martiansoftware.jsap.PropertyStringParser;");
+        }
+        out.println();
+
+        if (isPublic) {
+            out.print("public ");
+        }
+        out.println("class " + shortClassName + " extends JSAP {");
+        out.println();
+
+        out.println("    public " + shortClassName + "() {");
+        out.println("        super();");
+        out.println("        try {");
+        out.println("            init();");
+        out.println("        } catch (JSAPException e) {");
+        out.println(
+            "            throw(new IllegalStateException(e.getMessage()));");
+        out.println("        }");
+        out.println("    }");
+        out.println();
+        out.println("    private void init() throws JSAPException {");
+        for (Enumeration e1 = parameterConfigs.elements();
+            e1.hasMoreElements();
+            ) {
+
+            ParameterConfiguration pc =
+                (ParameterConfiguration) e1.nextElement();
+
+            out.println(
+                "        this.registerParameter( create"
+                    + properCase(pc.getId())
+                    + "() );");
+        }
+        out.println("    }");
+        out.println();
+        for (Enumeration e1 = parameterConfigs.elements();
+            e1.hasMoreElements();
+            ) {
+
+            ParameterConfiguration pc =
+                (ParameterConfiguration) e1.nextElement();
+
+            pc.createMethod("create" + properCase(pc.getId()), out);
+            out.println();
+        }
+        out.println("}");
+    }
+
+    /**
+     * Validates the JSAP configuration and, if successful, writes the java
+     * source code for a JSAP subclass
+     * that implements the configuration specified in the ant build file.
+     * @throws BuildException if unsuccessful for any reason.
+     */
+    public void execute() throws BuildException {
+        if (srcDir == null) {
+            throw (new BuildException("srcdir is required."));
+        }
+        if ((className == null) || (className.length() == 0)) {
+            throw (new BuildException("classname is required."));
+        }
+        if (!srcDir.isDirectory()) {
+            throw (new BuildException("srcdir must be a directory."));
+        }
+        System.out.println("srcDir=[" + srcDir + "]");
+        System.out.println("className=[" + className + "]");
+        System.out.println("public=" + isPublic);
+
+        try {
+            buildJSAP();
+            writeJSAP();
+        } catch (Exception e) {
+            throw (new BuildException(e.getMessage()));
+        }
+
+    }
+
+}
diff --git a/src/java/com/martiansoftware/jsap/ant/OptionConfiguration.java b/src/java/com/martiansoftware/jsap/ant/OptionConfiguration.java
new file mode 100644
index 0000000..81b8663
--- /dev/null
+++ b/src/java/com/martiansoftware/jsap/ant/OptionConfiguration.java
@@ -0,0 +1,299 @@
+/*
+ * Copyright (c) 2002-2004, Martian Software, Inc.
+ * This file is made available under the LGPL as described in the accompanying
+ * LICENSE.TXT file.
+ */
+
+package com.martiansoftware.jsap.ant;
+
+import java.io.PrintStream;
+import java.util.Vector;
+
+import com.martiansoftware.jsap.FlaggedOption;
+import com.martiansoftware.jsap.JSAP;
+import com.martiansoftware.jsap.Option;
+import com.martiansoftware.jsap.PropertyStringParser;
+import com.martiansoftware.jsap.StringParser;
+import com.martiansoftware.jsap.UnflaggedOption;
+
+/**
+ * Stores/provides configuration data common to both flaggedoptions and
+ * unflaggedoptions nested inside a jsap ant task.
+ * For detailed information on using the jsap task, see the documentation for
+ * JSAPAntTask.
+ *
+ * @author <a href="http://www.martiansoftware.com/contact.html">Marty Lamb</a>
+ * @see JSAPAntTask
+ */
+
+public abstract class OptionConfiguration extends ParameterConfiguration {
+
+    /**
+     * If true, this option is required.
+     */
+    private boolean required = JSAP.NOT_REQUIRED;
+
+    /**
+     * Stores any properties for this option's StringParser (if it's a
+     * PropertyStringParser)
+     */
+    private Vector parserProperties = null;
+
+    /**
+     * If true, this option stores a list of values.
+     */
+    private boolean isList = JSAP.NOT_LIST;
+
+    /**
+     * If this option is a list, this is the list separator token.
+     */
+    private char listSeparator = JSAP.DEFAULT_LISTSEPARATOR;
+
+    /**
+     * If this is true, the user has specified a list separator token.
+     */
+    private boolean declaredListSeparator = false;
+
+    /**
+     * The name of the StringParser for this option.
+     */
+    private String stringParser = null;
+
+    /**
+     * Creates a new OptionConfiguration.
+     */
+    public OptionConfiguration() {
+        super();
+        parserProperties = new Vector();
+    }
+
+    /**
+     * Sets the StringParser for this option.
+     * @param stringParser the StringParser for this option.
+     */
+    public void setStringparser(String stringParser) {
+        this.stringParser = stringParser;
+    }
+
+    /**
+     * Returns the classname of the StringParser for this option.  If the
+     * current StringParser name
+     * does not contains any "dot"
+     * characters, it is prefixed with "com.martiansoftware.jsap.stringparsers."
+     * prior to returning.
+     * @return the classname of the StringParser for this option.
+     */
+    public String getStringparser() {
+        String result = null;
+        if (stringParser != null) {
+            if (stringParser.indexOf(".") == -1) {
+                result =
+                    "com.martiansoftware.jsap.stringparsers." + stringParser;
+            } else {
+                result = stringParser;
+            }
+        }
+        return (result);
+    }
+
+    /**
+     * Sets whether this option is required.
+     * @param required if true, this option is required.
+     */
+    public void setRequired(boolean required) {
+        this.required = required;
+    }
+
+    /**
+     * Returns a boolean indicating whether this option is required.
+     * @return a boolean indicating whether this option is required.
+     */
+    public boolean getRequired() {
+        return (required);
+    }
+
+    /**
+     * Sets whether this option is a list.
+     * @param isList if true, this option is a list.
+     */
+    public void setIslist(boolean isList) {
+        this.isList = isList;
+    }
+
+    /**
+     * Returns a boolean indicating whether this option is a list.
+     * @return a boolean indicating whether this option is a list.
+     */
+    public boolean getIslist() {
+        return (isList);
+    }
+
+    /**
+     * Sets the list separator for this option, if it is a list.
+     * @param listSeparator the list separator character for this option, if it
+     * is a list.
+     */
+    public void setListseparator(char listSeparator) {
+        declaredListSeparator = true;
+        this.listSeparator = listSeparator;
+    }
+
+    /**
+     * Returns the list separator character for this option.
+     * @return the list separator character for this option.
+     */
+    public char getListseparator() {
+        return (listSeparator);
+    }
+
+    /**
+     * Returns a boolean indicating whether the list separator character has
+     * been specified by the user.
+     * @return a boolean indicating whether the list separator character has
+     * been specified by the user.
+     */
+    public boolean declaredListSeparator() {
+        return (declaredListSeparator);
+    }
+
+    /**
+     * Adds a property to the current list of properties for this option's
+     * PropertyStringParser.
+     * @param p the property to add.
+     */
+    public void addConfiguredProperty(ParserProperty p) {
+        parserProperties.add(p);
+    }
+
+    /**
+     * Returns an array of ParserProperties for this option's
+     * PropertyStringParser, or null if no ParserProperties
+     * are defined.
+     * @return an array of ParserProperties for this option's
+     * PropertyStringParser, or null if no ParserProperties
+     * are defined.
+     */
+    public ParserProperty[] getParserProperties() {
+        ParserProperty[] result = null;
+        if (parserProperties.size() > 0) {
+            result =
+                (ParserProperty[]) parserProperties.toArray(
+                    new ParserProperty[0]);
+        }
+        return (result);
+    }
+
+    /**
+     * Returns a boolean indicating whether any ParserProperties are defined.
+     * @return a boolean indicating whether any ParserProperties are defined.
+     */
+    public boolean hasProperties() {
+        ParserProperty[] props = getParserProperties();
+        return ((props != null) && (props.length > 0));
+    }
+
+    /**
+     * Instantiates and configures A {@link com.martiansoftware.jsap.StringParser} according to this
+     * configuration, and provides it to the
+     * specified Option.
+     * @param option the Option that should use the configured StringParser.
+     */
+    protected void setupStringParser(Option option) {
+        if (this.getStringparser() != null) {
+            try {
+                StringParser sp =
+                    (StringParser) Class
+                        .forName(this.getStringparser())
+                        .newInstance();
+                if (option instanceof FlaggedOption) {
+                    ((FlaggedOption) option).setStringParser(sp);
+                } else {
+                    ((UnflaggedOption) option).setStringParser(sp);
+                }
+            } catch (Exception e) {
+                throw (
+                    new IllegalArgumentException(
+                        "Unable to instantiate \""
+                            + this.getStringparser()
+                            + "\""));
+            }
+        }
+
+        if (this.hasProperties()) {
+            try {
+                PropertyStringParser psp =
+                    (PropertyStringParser) option.getStringParser();
+                ParserProperty[] props = this.getParserProperties();
+                for (int i = 0; i < props.length; ++i) {
+                    psp.setProperty(props[i].getName(), props[i].getValue());
+                }
+            } catch (Exception e) {
+                throw (
+                    new IllegalArgumentException(
+                        "Option \""
+                            + option.getID()
+                            + "\": "
+                            + option.getStringParser().getClass().getName()
+                            + " is not an instance of "
+                            + "com.martiansoftware.jsap.PropertyParser."));
+            }
+        }
+    }
+
+    /**
+     * Creates java source code to configure an option as specified in this
+     * object.  These methods are called
+     * by both UnflaggedOptionConfiguration and FlaggedOptionConfiguration.
+     * @param objName the name of the object in the generated source code
+     * @param out a PrintStream to which the source code should be written
+     */
+    protected void createParentStatements(String objName, PrintStream out) {
+        super.createParentStatements(objName, out);
+        out.println(
+            "        "
+                + objName
+                + ".setList("
+                + (getIslist() ? "true" : "false")
+                + ");");
+        out.println(
+            "        "
+                + objName
+                + ".setRequired("
+                + (getRequired() ? "true" : "false")
+                + ");");
+        if (getStringparser() != null) {
+            out.println(
+                "        "
+                    + objName
+                    + ".setStringParser( new "
+                    + getStringparser()
+                    + "() );");
+        }
+        if (hasProperties()) {
+            ParserProperty[] props = getParserProperties();
+            out.println();
+            out.println(
+                "        PropertyStringParser psp = (PropertyStringParser) "
+                    + objName
+                    + ".getStringParser();");
+            for (int i = 0; i < props.length; ++i) {
+                out.println(
+                    "        psp.setProperty(\""
+                        + props[i].getName()
+                        + "\", \""
+                        + props[i].getValue()
+                        + "\");");
+            }
+            out.println();
+        }
+        if (declaredListSeparator()) {
+            out.println(
+                "        "
+                    + objName
+                    + ".setListSeparator('"
+                    + getListseparator()
+                    + "');");
+        }
+    }
+
+}
diff --git a/src/java/com/martiansoftware/jsap/ant/ParameterConfiguration.java b/src/java/com/martiansoftware/jsap/ant/ParameterConfiguration.java
new file mode 100644
index 0000000..6e81344
--- /dev/null
+++ b/src/java/com/martiansoftware/jsap/ant/ParameterConfiguration.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (c) 2002-2004, Martian Software, Inc.
+ * This file is made available under the LGPL as described in the accompanying
+ * LICENSE.TXT file.
+ */
+
+package com.martiansoftware.jsap.ant;
+
+import java.io.IOException;
+import java.io.PrintStream;
+import java.util.Vector;
+
+import com.martiansoftware.jsap.Parameter;
+
+/**
+ * Stores/provides configuration data common to switches, flaggedoptions, and
+ * unflaggedoptions
+ * nested inside a jsap ant task.
+ * For detailed information on using the jsap task, see the documentation for
+ * JSAPAntTask.
+ *
+ * @author <a href="http://www.martiansoftware.com/contact.html">Marty Lamb</a>
+ * @see JSAPAntTask
+ */
+public abstract class ParameterConfiguration {
+
+    /**
+     * The unique ID for this parameter.
+     */
+    private String id = null;
+
+    /**
+     * The set of default values for this parameter.
+     */
+    private Vector defaults = null;
+
+    /**
+     * Creates a new ParameterConfiguration.
+     */
+    public ParameterConfiguration() {
+        defaults = new Vector();
+    }
+
+    /**
+     * Returns an Parameter (String, FlaggedOption, or UnflaggedOption)
+     * configured according
+     * to the settings contained within this object.
+     * @return an Parameter (String, FlaggedOption, or UnflaggedOption)
+     * configured according
+     * to the settings contained within this object.
+     */
+    public abstract Parameter getParameter();
+
+    /**
+     * Sets the unique ID for this parameter.
+     * @param id ths unique ID for this parameter.
+     */
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    /**
+     * Returns the unique ID for this parameter.
+     * @return the unique ID for this parameter.
+     */
+    public String getId() {
+        return (id);
+    }
+
+    /**
+     * Adds a default value to the current list of default values for this
+     * parameter.
+     * @param defaultValue the default value to add to the current list of
+     * default values for this parameter.
+     */
+    public void setDefault(String defaultValue) {
+        defaults.add(defaultValue);
+    }
+
+    /**
+     * Adds a configured default value to the current list of default values
+     * for this parameter.
+     * @param defaultValue the configured default value to add to the current
+     * list of default
+     * values for this parameter.
+     */
+    public void addConfiguredDefault(DefaultValue defaultValue) {
+        defaults.add(defaultValue.getValue());
+    }
+
+    /**
+     * Returns an array of this parameter's default values, or a zero-length
+     * array if none exist.
+     * @return an array of this parameter's default values, or a zero-length
+     * array if none exist.
+     */
+    public String[] getDefaults() {
+        return (
+            (defaults.size() == 0)
+                ? null
+                : ((String[]) defaults.toArray(new String[0])));
+    }
+
+    /**
+     * Creates java source code statements to configure an Parameter as
+     * specified in this object.
+     * @param objName the name of the object in the java source code
+     * @param out the PrintStream to which the source code should be written
+     */
+    protected void createParentStatements(String objName, PrintStream out) {
+
+        String[] defaults = getDefaults();
+        if (defaults != null) {
+            for (int i = 0; i < defaults.length; ++i) {
+                out.println(
+                    "        "
+                        + objName
+                        + ".addDefault(\""
+                        + defaults[i]
+                        + "\");");
+            }
+        }
+    }
+
+    /**
+     * Returns a boolean indicating whether this parameter has any properties
+     * associated with its
+     * StringParser.  This method always returns false; it may be overridden by
+     * subclasses.
+     * @return a boolean indicating whether this parameter has any properties
+     * associated with its
+     * StringParser.
+     */
+    public boolean hasProperties() {
+        return (false);
+    }
+
+    /**
+     * Creates source code for a java method that creates a parameter matching
+     * this object's configuration.
+     * @param methodName the name of the java method to generate.
+     * @param out the PrintStream to which the generated source code should be
+     * written.
+     * @throws IOException if any are thrown by PrintStream.
+     */
+    public abstract void createMethod(String methodName, PrintStream out)
+        throws IOException;
+
+}
diff --git a/src/java/com/martiansoftware/jsap/ant/ParserProperty.java b/src/java/com/martiansoftware/jsap/ant/ParserProperty.java
new file mode 100644
index 0000000..85016a5
--- /dev/null
+++ b/src/java/com/martiansoftware/jsap/ant/ParserProperty.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2002-2004, Martian Software, Inc.
+ * This file is made available under the LGPL as described in the accompanying
+ * LICENSE.TXT file.
+ */
+
+package com.martiansoftware.jsap.ant;
+
+/**
+ * Stores/provides a single name/value pair for a PropertyStringParser
+ * For detailed information on using the jsap task, see the documentation for
+ * JSAPAntTask.
+ *
+ * @author <a href="http://www.martiansoftware.com/contact.html">Marty Lamb</a>
+ * @see JSAPAntTask
+ * @see com.martiansoftware.jsap.PropertyStringParser
+ */
+public class ParserProperty {
+
+    /**
+     * The name of this property.
+     */
+    private String name = null;
+
+    /**
+     * The value of this property.
+     */
+    private String value = null;
+
+    /**
+     * Sets the name of this property.
+     * @param name the name of this property.
+     */
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    /**
+     * Returns the name of this property.
+     * @return the name of this property.
+     */
+    public String getName() {
+        return (name);
+    }
+
+    /**
+     * Sets the value of this property.
+     * @param value the value of this property.
+     */
+    public void setValue(String value) {
+        this.value = value;
+    }
+
+    /**
+     * Returns the value of this property.
+     * @return the value of this property.
+     */
+    public String getValue() {
+        return (value);
+    }
+
+}
diff --git a/src/java/com/martiansoftware/jsap/ant/SwitchConfiguration.java b/src/java/com/martiansoftware/jsap/ant/SwitchConfiguration.java
new file mode 100644
index 0000000..654d4dc
--- /dev/null
+++ b/src/java/com/martiansoftware/jsap/ant/SwitchConfiguration.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2002-2004, Martian Software, Inc.
+ * This file is made available under the LGPL as described in the accompanying
+ * LICENSE.TXT file.
+ */
+
+package com.martiansoftware.jsap.ant;
+
+import java.io.PrintStream;
+
+import com.martiansoftware.jsap.Parameter;
+import com.martiansoftware.jsap.JSAP;
+import com.martiansoftware.jsap.Switch;
+
+/**
+ * Stores/provides configuration data for switches nested inside a jsap ant
+ * task.
+ * For detailed information on using the jsap task, see the documentation for
+ * JSAPAntTask.
+ *
+ * @author <a href="http://www.martiansoftware.com/contact.html">Marty Lamb</a>
+ * @see JSAPAntTask
+ * @see com.martiansoftware.jsap.Switch
+ */
+
+public class SwitchConfiguration extends ParameterConfiguration {
+
+    /**
+     * The short flag for this switch.
+     */
+    private char shortFlag = JSAP.NO_SHORTFLAG;
+
+    /**
+     * The long flag for this switch.
+     */
+    private String longFlag = JSAP.NO_LONGFLAG;
+
+    /**
+     * Creates a new SwitchConfiguration.
+     */
+    public SwitchConfiguration() {
+        super();
+    }
+
+    /**
+     * Sets the short flag for this switch.
+     * @param shortFlag the short flag for this switch.
+     */
+    public void setShortflag(char shortFlag) {
+        this.shortFlag = shortFlag;
+    }
+
+    /**
+     * Returns the short flag for this switch.
+     * @return the short flag for this switch.
+     */
+    public char getShortflag() {
+        return (shortFlag);
+    }
+
+    /**
+     * Sets the long flag for this switch.
+     * @param longFlag the long flag for this switch.
+     */
+    public void setLongflag(String longFlag) {
+        this.longFlag = longFlag;
+    }
+
+    /**
+     * Returns the long flag for this switch.
+     * @return the long flag for this switch.
+     */
+    public String getLongflag() {
+        return (longFlag);
+    }
+
+    /**
+     * Returns a Switch configured according to this configuration.
+     * @return a Switch configured according to this configuration.
+     */
+    public Parameter getParameter() {
+        Switch result = new Switch(this.getId());
+        result.setShortFlag(this.getShortflag());
+        result.setLongFlag(this.getLongflag());
+        result.setDefault(this.getDefaults());
+        return (result);
+    }
+
+    /**
+     * Creates java source code for a method that will instantiate and configure
+     * a Switch
+     * according to this configuration.
+     * @param methodName the name of the method to generate
+     * @param out the PrintStream to which the generated java will be written
+     */
+    public void createMethod(String methodName, PrintStream out) {
+
+        out.println("    private Switch " + methodName + "() {");
+        out.println(
+            "        Switch result = new Switch(\"" + this.getId() + "\");");
+
+        if (getShortflag() != JSAP.NO_SHORTFLAG) {
+            out.println(
+                "        result.setShortFlag('" + getShortflag() + "');");
+        }
+
+        if (getLongflag() != JSAP.NO_LONGFLAG) {
+            out.println(
+                "        result.setLongFlag(\"" + getLongflag() + "\");");
+        }
+
+        super.createParentStatements("result", out);
+        out.println("        return (result);");
+        out.println("    }");
+    }
+
+}
diff --git a/src/java/com/martiansoftware/jsap/ant/UnflaggedOptionConfiguration.java b/src/java/com/martiansoftware/jsap/ant/UnflaggedOptionConfiguration.java
new file mode 100644
index 0000000..86c9ac6
--- /dev/null
+++ b/src/java/com/martiansoftware/jsap/ant/UnflaggedOptionConfiguration.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2002-2004, Martian Software, Inc.
+ * This file is made available under the LGPL as described in the accompanying
+ * LICENSE.TXT file.
+ */
+
+package com.martiansoftware.jsap.ant;
+
+import java.io.PrintStream;
+
+import com.martiansoftware.jsap.Parameter;
+import com.martiansoftware.jsap.JSAP;
+import com.martiansoftware.jsap.UnflaggedOption;
+
+/**
+ * Stores/provides configuration data for unflaggedoptions nested inside a jsap
+ * ant task.
+ * For detailed information on using the jsap task, see the documentation for
+ * JSAPAntTask.
+ *
+ * @author <a href="http://www.martiansoftware.com/contact.html">Marty Lamb</a>
+ * @see JSAPAntTask
+ * @see com.martiansoftware.jsap.UnflaggedOption
+ */
+
+public class UnflaggedOptionConfiguration extends OptionConfiguration {
+
+    /**
+     * If true, this unflaggedoption should be greedy.
+     * (i.e., should consume the remaining unflaggedoptions from the command
+     * line.
+     */
+    private boolean greedy = JSAP.NOT_GREEDY;
+
+    /**
+     * Creates a new UnflaggedOptionConfiguration.
+     */
+    public UnflaggedOptionConfiguration() {
+        super();
+    }
+
+    /**
+     * Sets whether this UnflaggedOption should be greedy.
+     * (i.e., should consume the remaining unflaggedoptions from the command
+     * line.
+     * @param greedy if true, this UnflaggedOption should be greedy.
+     */
+    public void setGreedy(boolean greedy) {
+        this.greedy = greedy;
+    }
+
+    /**
+     * Returns a boolean indicating whether this UnflaggedOption is greedy.
+     * @return a boolean indicating whether this UnflaggedOption is greedy.
+     */
+    public boolean getGreedy() {
+        return (greedy);
+    }
+
+    /**
+     * Returns an UnflaggedOption configured according to this configuration.
+     * @return an UnflaggedOption configured according to this configuration.
+     */
+    public Parameter getParameter() {
+        UnflaggedOption result = new UnflaggedOption(this.getId());
+
+        result.setRequired(this.getRequired());
+        result.setList(this.getIslist());
+        if (this.declaredListSeparator()) {
+            result.setListSeparator(this.getListseparator());
+        }
+        result.setGreedy(this.getGreedy());
+        result.setDefault(this.getDefaults());
+        setupStringParser(result);
+        return (result);
+    }
+
+    /**
+     * Creates java source code for a method that instantiates an
+     * UnflaggedOption and
+     * configures it according to this configuration.
+     * @param methodName the name of the method to generate
+     * @param out the PrintStream to which the java source code will be written.
+     */
+    public void createMethod(String methodName, PrintStream out)
+        {
+
+        out.println("    private UnflaggedOption " + methodName + "() {");
+        out.println(
+            "        UnflaggedOption result = new UnflaggedOption(\""
+                + this.getId()
+                + "\");");
+
+        out.println(
+            "        result.setGreedy("
+                + (getGreedy() ? "true" : "false")
+                + ");");
+
+        super.createParentStatements("result", out);
+        out.println("        return (result);");
+        out.println("    }");
+    }
+
+}
diff --git a/src/java/com/martiansoftware/jsap/defaultsources/PropertyDefaultSource.java b/src/java/com/martiansoftware/jsap/defaultsources/PropertyDefaultSource.java
new file mode 100644
index 0000000..25688c8
--- /dev/null
+++ b/src/java/com/martiansoftware/jsap/defaultsources/PropertyDefaultSource.java
@@ -0,0 +1,234 @@
+/*
+ * Copyright (c) 2002-2004, Martian Software, Inc.
+ * This file is made available under the LGPL as described in the accompanying
+ * LICENSE.TXT file.
+ */
+
+package com.martiansoftware.jsap.defaultsources;
+
+import com.martiansoftware.jsap.DefaultSource;
+import com.martiansoftware.jsap.Defaults;
+import com.martiansoftware.jsap.IDMap;
+import com.martiansoftware.jsap.JSAPException;
+import com.martiansoftware.jsap.ExceptionMap;
+
+import java.io.InputStream;
+import java.io.BufferedInputStream;
+import java.io.FileInputStream;
+import java.util.Properties;
+import java.util.Enumeration;
+import java.io.IOException;
+
+/**
+ *
+ * <p>A DefaultSource with values defined in a java.util.Properties object.
+ * In order to determine which parameter
+ * a value is associated with, each property key is first compared to each
+ * parameter's unique ID.  Failing a
+ * match, each parameter's long flag is checked, and finally the short flags
+ * are checked.  A PropertyDefaultSource
+ * may contain a mix of IDs, long flags, and short flags.</p>
+ *
+ * <p>A PropertyDefaultSource is also incredibly useful as a configuration file
+ * loader.  Multiple PropertyDefaultSources
+ * can be chained together in a JSAP in order to prioritize their entries
+ * (e.g., load "~/myproject.conf" first, then
+ * "/etc/myproject.conf").</p>
+ * @author <a href="http://www.martiansoftware.com/contact.html">Marty Lamb</a>
+ * @see com.martiansoftware.jsap.DefaultSource
+ * @see java.util.Properties
+ */
+public class PropertyDefaultSource implements DefaultSource {
+
+    /**
+     * If true, this PropertyDefaultSource is being loaded from a file.
+     * @see PropertyDefaultSource#PropertyDefaultSource(String,boolean)
+     */
+    private boolean loadFromFile = false;
+
+    /**
+     * If true, this PropertyDefaultSource is being loaded from a Properties
+     * object.
+     * @see PropertyDefaultSource#PropertyDefaultSource(Properties)
+     * @see java.util.Properties
+     */
+    private boolean loadFromProperties = false;
+
+    /**
+     * If true, this PropertyDefaultSource is being loaded from an InputStream.
+     * @see PropertyDefaultSource#PropertyDefaultSource(InputStream,boolean)
+     * @see java.io.InputStream
+     */
+    private boolean loadFromInputStream = false;
+
+    /**
+     * The InputStream from which the PropertyDefaultSource will be loaded, if
+     * any.
+     */
+    private InputStream in = null;
+
+    /**
+     * The name of the file from which the PropertyDefaultSource will be
+     * loaded, if any.
+     */
+    private String propertyFileName = null;
+
+    /**
+     * If true, any encountered IOExceptions will be thrown.  Default is true.
+     */
+    private boolean throwIOExceptions = true;
+
+    /**
+     * The Properties object containing the default values.
+     */
+    private Properties properties = null;
+
+    /**
+     * Indicates whether we have already loaded the Properties.
+     */
+    private boolean loaded = false;
+
+    /**
+     * Creates a new PropertyDefaultSource by loading the specified file.  The
+     * file is loaded when the JSAP
+     * requests the defaults from this object.
+     * @param propertyFileName the name of the properties file containing the
+     * default values.
+     * @param throwIOExceptions if true, any encountered IOExceptions will be
+     * re-thrown.  Set this to false
+     * if you want to ignore any exceptions (e.g., specified file does not
+     * exist).
+     */
+    public PropertyDefaultSource(
+        String propertyFileName,
+        boolean throwIOExceptions) {
+        this.loadFromFile = true;
+        this.propertyFileName = propertyFileName;
+        this.throwIOExceptions = throwIOExceptions;
+    }
+
+    /**
+     * Creates a new PropertyDefaultSource based upon the specified Properties
+     * object.
+     * @param properties the Properties object containing the default values.
+     */
+    public PropertyDefaultSource(Properties properties) {
+        this.loadFromProperties = true;
+        this.properties = properties;
+        loaded = true;
+    }
+
+    /**
+     * Creates a new PropertyDefaultSource based upon the specified InputStream.
+     * @param in the InputStream containing the Properties.
+     * @param throwIOExceptions if true, any encountered IOExceptions will be
+     * re-thrown.
+     */
+    public PropertyDefaultSource(InputStream in, boolean throwIOExceptions) {
+        this.loadFromInputStream = true;
+        this.in = in;
+        this.throwIOExceptions = throwIOExceptions;
+    }
+
+    /**
+     * Returns the properties for this PropertyDefaultSource.  If necessary,
+     * the properties are loaded first
+     * from a file or InputStream as specified by the constructor.
+     * @return the properties for this PropertyDefaultSource.
+     * @throws IOException if an I/O exception occurs, AND this
+     * PropertyDefaultSource is configured to throw
+     * IOExceptions.
+     */
+    private Properties getProperties() throws IOException {
+        if (!loaded) {
+            try {
+                if (loadFromFile) {
+                    in =
+                        new BufferedInputStream(
+                            new FileInputStream(propertyFileName));
+                }
+                Properties properties = new Properties();
+                properties.load(in);
+                in.close();
+                in = null;
+                this.properties = properties;
+                loaded = true;
+            } catch (IOException e) {
+                if (throwIOExceptions) {
+                    throw (e);
+                }
+            }
+        }
+        Properties result = this.properties;
+        if (result == null) {
+            this.properties = new Properties();
+            result = this.properties;
+            loaded = true;
+        }
+        return (result);
+    }
+
+    /**
+     * Returns a Defaults object based upon this PropertyDefaultSource's
+     * properties and the specified IDMap.
+     * In order to determine which parameter
+     * a value is associated with, each property key is first compared to each
+     * parameter's unique ID.  Failing a
+     * match, each parameter's long flag is checked, and finally the short
+     * flags are checked.  A
+     * PropertyDefaultSource may contain a mix of IDs, long flags, and short
+     * flags.
+     * @param idMap the IDMap containing the current JSAP configuration.
+     * @param exceptionMap the ExceptionMap object within which any encountered
+     * exceptions will be thrown.
+     * @return a Defaults object based upon this PropertyDefaultSource's
+     * properties and the specified IDMap.
+     */
+    public Defaults getDefaults(IDMap idMap, ExceptionMap exceptionMap) {
+        Defaults defaults = new Defaults();
+        try {
+            Properties properties = getProperties();
+            for (Enumeration enumeration = properties.propertyNames();
+                enumeration.hasMoreElements();
+                ) {
+
+                String thisName = (String) enumeration.nextElement();
+                if (idMap.idExists(thisName)) {
+                    defaults.addDefault(
+                        thisName,
+                        properties.getProperty(thisName));
+                } else {
+                    String paramID = idMap.getIDByLongFlag(thisName);
+                    if (paramID != null) {
+                        defaults.addDefault(
+                            paramID,
+                            properties.getProperty(thisName));
+                    } else if (thisName.length() == 1) {
+                        paramID = idMap.getIDByShortFlag(thisName.charAt(0));
+                        if (paramID != null) {
+                            defaults.addDefault(
+                                paramID,
+                                properties.getProperty(thisName));
+                        } else {
+                            exceptionMap.addException(
+                                null,
+                                new JSAPException(
+                                    "Unknown parameter: " + thisName));
+                        }
+                    } else {
+                        exceptionMap.addException(
+                            null,
+                            new JSAPException(
+                                "Unknown parameter: " + thisName));
+                    }
+                }
+            }
+        } catch (IOException ioe) {
+            exceptionMap.addException(
+                null,
+                new JSAPException("Unable to load properties.", ioe));
+        }
+        return (defaults);
+    }
+
+}
diff --git a/src/java/com/martiansoftware/jsap/defaultsources/TestAll.java b/src/java/com/martiansoftware/jsap/defaultsources/TestAll.java
new file mode 100644
index 0000000..5c7ad4d
--- /dev/null
+++ b/src/java/com/martiansoftware/jsap/defaultsources/TestAll.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2002-2004, Martian Software, Inc.
+ * This file is made available under the LGPL as described in the accompanying
+ * LICENSE.TXT file.
+ */
+
+package com.martiansoftware.jsap.defaultsources;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+/**
+ * @author <a href="http://www.martiansoftware.com/contact.html">Marty Lamb</a>
+ */
+public class TestAll {
+
+    public static Test suite() {
+        TestSuite suite =
+            new TestSuite("Test for com.martiansoftware.jsap.defaultsources");
+        //$JUnit-BEGIN$
+        suite.addTest(new TestSuite(TestPropertyDefaultSource.class));
+        //$JUnit-END$
+        return suite;
+    }
+}
diff --git a/src/java/com/martiansoftware/jsap/defaultsources/TestPropertyDefaultSource.java b/src/java/com/martiansoftware/jsap/defaultsources/TestPropertyDefaultSource.java
new file mode 100644
index 0000000..fbb5aa2
--- /dev/null
+++ b/src/java/com/martiansoftware/jsap/defaultsources/TestPropertyDefaultSource.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2002-2004, Martian Software, Inc.
+ * This file is made available under the LGPL as described in the accompanying
+ * LICENSE.TXT file.
+ */
+
+package com.martiansoftware.jsap.defaultsources;
+
+import junit.framework.TestCase;
+import com.martiansoftware.jsap.JSAP;
+import com.martiansoftware.jsap.JSAPResult;
+import com.martiansoftware.jsap.FlaggedOption;
+import com.martiansoftware.jsap.UnflaggedOption;
+import com.martiansoftware.jsap.Switch;
+import com.martiansoftware.jsap.stringparsers.StringStringParser;
+import java.util.Properties;
+
+/**
+ * @author <a href="http://www.martiansoftware.com/contact.html">Marty Lamb</a>
+ */
+public class TestPropertyDefaultSource extends TestCase {
+
+    public void testUnexpectedProperties() {
+        JSAP jsap = new JSAP();
+        try {
+            jsap.registerParameter(
+                new FlaggedOption(
+                    "testflagged",
+                    StringStringParser.getParser(),
+                    JSAP.NO_DEFAULT,
+                    JSAP.NOT_REQUIRED,
+                    'f',
+                    "flagged"));
+            jsap.registerParameter(
+                new UnflaggedOption(
+                    "testunflagged",
+                    StringStringParser.getParser(),
+                    JSAP.NO_DEFAULT,
+                    JSAP.NOT_REQUIRED,
+                    JSAP.NOT_GREEDY));
+            jsap.registerParameter(new Switch("testswitch", 's', "switch"));
+        } catch (Throwable t) {
+            fail(t.getMessage());
+        }
+
+        Properties p = new Properties();
+        p.setProperty("s", "true");
+        p.setProperty("flagged", "My Flagged Value");
+        PropertyDefaultSource pds = new PropertyDefaultSource(p);
+        jsap.registerDefaultSource(pds);
+
+        JSAPResult result = jsap.parse("");
+        assertTrue(result.success());
+
+        assertEquals(result.getBoolean("testswitch"), true);
+        assertEquals(result.getString("testflagged"), "My Flagged Value");
+
+        p.setProperty("unexpected", "jsap won't know what to do with this");
+        result = jsap.parse("");
+        assertFalse(result.success());
+
+        /*
+        for (java.util.Iterator i1 = result.getBadParameterIDIterator(); i1.hasNext(); ) {
+            String badID = (String) i1.next();
+            for (java.util.Iterator i2 = result.getExceptionIterator(badID); i2.hasNext(); ) {
+                Exception e = (Exception) i2.next();
+                System.out.println(e.getMessage());
+            }
+        }*/
+
+    }
+
+}
diff --git a/src/java/com/martiansoftware/jsap/examples/Example1.java b/src/java/com/martiansoftware/jsap/examples/Example1.java
new file mode 100644
index 0000000..bc498df
--- /dev/null
+++ b/src/java/com/martiansoftware/jsap/examples/Example1.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2002-2004, Martian Software, Inc.
+ * This file is made available under the LGPL as described in the accompanying
+ * LICENSE.TXT file.
+ */
+
+package com.martiansoftware.jsap.examples;
+
+import com.martiansoftware.jsap.FlaggedOption;
+import com.martiansoftware.jsap.JSAP;
+import com.martiansoftware.jsap.Switch;
+import com.martiansoftware.jsap.UnflaggedOption;
+
+/**
+ * @author mlamb
+ *
+ * To change the template for this generated type comment go to
+ * Window>Preferences>Java>Code Generation>Code and Comments
+ */
+public class Example1 {
+
+    public static void main(String[] args) throws Exception {
+        JSAP jsap = new JSAP();
+
+        FlaggedOption f1 = new FlaggedOption("myflagged");
+        f1.setShortFlag('f').setLongFlag("flagged").setRequired(true).setHelp(
+            "do flagged stuff");
+        jsap.registerParameter(f1);
+
+        UnflaggedOption f2 = new UnflaggedOption("myunflagged");
+        f2.setGreedy(JSAP.GREEDY).setHelp("input files");
+        jsap.registerParameter(f2);
+
+        Switch sw1 = new Switch("myswitch");
+        sw1.setLongFlag("verbose").setHelp(
+            "display extra logging information.");
+        jsap.registerParameter(sw1);
+
+        System.out.println("Usage: Example1 " + jsap.getUsage());
+        System.out.println(jsap.getHelp());
+    }
+}
diff --git a/src/java/com/martiansoftware/jsap/examples/Manual_HelloWorld_1.java b/src/java/com/martiansoftware/jsap/examples/Manual_HelloWorld_1.java
new file mode 100644
index 0000000..0ca7f94
--- /dev/null
+++ b/src/java/com/martiansoftware/jsap/examples/Manual_HelloWorld_1.java
@@ -0,0 +1,10 @@
+package com.martiansoftware.jsap.examples;
+
+// @@snip:Manual_HelloWorld_1@@
+public class Manual_HelloWorld_1 {
+
+	public static void main(String[] args){
+		System.out.println("Hello, World!");
+	}
+}
+// @@endSnip@@
\ No newline at end of file
diff --git a/src/java/com/martiansoftware/jsap/examples/Manual_HelloWorld_2.java b/src/java/com/martiansoftware/jsap/examples/Manual_HelloWorld_2.java
new file mode 100644
index 0000000..1704cb7
--- /dev/null
+++ b/src/java/com/martiansoftware/jsap/examples/Manual_HelloWorld_2.java
@@ -0,0 +1,40 @@
+package com.martiansoftware.jsap.examples;
+
+import com.martiansoftware.jsap.FlaggedOption;
+import com.martiansoftware.jsap.JSAP;
+import com.martiansoftware.jsap.JSAPResult;
+
+public class Manual_HelloWorld_2 {
+
+	/** 
+	 * Repeats the "Hello World" text multiple times.
+	 * @param args the command line.
+	 * @throws Exception for reasons made clear later in the manual.
+	 */
+	// @@snip:Manual_HelloWorld_2@@
+	public static void main(String[] args) throws Exception {
+		JSAP jsap = new JSAP();
+		
+		// create a flagged option we'll access using the id "count".
+		// it's going to be an integer, with a default value of 1.
+		// it's required (which has no effect since there's a default value)
+		// its short flag is "n", so a command line containing "-n 5"
+		//    will print our message five times.
+		// it has no long flag.
+		FlaggedOption opt1 = new FlaggedOption("count")
+								.setStringParser(JSAP.INTEGER_PARSER)
+								.setDefault("1") 
+								.setRequired(true) 
+								.setShortFlag('n') 
+								.setLongFlag(JSAP.NO_LONGFLAG);
+		
+		jsap.registerParameter(opt1);
+
+		JSAPResult config = jsap.parse(args);	
+
+		for (int i = 0; i < config.getInt("count"); ++i) {
+			System.out.println("Hello, World!");
+		}
+	}
+	// @@endSnip@@
+}
diff --git a/src/java/com/martiansoftware/jsap/examples/Manual_HelloWorld_3.java b/src/java/com/martiansoftware/jsap/examples/Manual_HelloWorld_3.java
new file mode 100644
index 0000000..46974ed
--- /dev/null
+++ b/src/java/com/martiansoftware/jsap/examples/Manual_HelloWorld_3.java
@@ -0,0 +1,48 @@
+package com.martiansoftware.jsap.examples;
+
+import com.martiansoftware.jsap.FlaggedOption;
+import com.martiansoftware.jsap.JSAP;
+import com.martiansoftware.jsap.JSAPResult;
+import com.martiansoftware.jsap.Switch;
+
+
+public class Manual_HelloWorld_3 {
+
+	/** 
+	 * Repeats the "Hello World" text multiple times.
+	 * Decides whether to say "Hi" or "Hello" depending upon verbose switch.
+	 * @param args the command line.
+	 * @throws Exception for reasons made clear later in the manual.
+	 */
+	// @@snip:Manual_HelloWorld_3@@
+	public static void main(String[] args) throws Exception {
+		JSAP jsap = new JSAP();
+		
+		FlaggedOption opt1 = new FlaggedOption("count")
+								.setStringParser(JSAP.INTEGER_PARSER)
+								.setDefault("1") 
+								.setRequired(true) 
+								.setShortFlag('n') 
+								.setLongFlag(JSAP.NO_LONGFLAG);
+		
+		jsap.registerParameter(opt1);
+		
+		// create a switch we'll access using the id "verbose".
+		// it has the short flag "-v" and the long flag "--verbose"
+		// this will govern whether we say "Hi" or "Hello".
+		Switch sw1 = new Switch("verbose")
+						.setShortFlag('v')
+						.setLongFlag("verbose");
+		
+		jsap.registerParameter(sw1);
+		
+		JSAPResult config = jsap.parse(args);	
+
+		for (int i = 0; i < config.getInt("count"); ++i) {
+			System.out.println((config.getBoolean("verbose") ? "Hello" : "Hi")
+								+ ", World!");
+		}
+		
+	}
+	// @@endSnip@@
+}
\ No newline at end of file
diff --git a/src/java/com/martiansoftware/jsap/examples/Manual_HelloWorld_4.java b/src/java/com/martiansoftware/jsap/examples/Manual_HelloWorld_4.java
new file mode 100644
index 0000000..e0b4b23
--- /dev/null
+++ b/src/java/com/martiansoftware/jsap/examples/Manual_HelloWorld_4.java
@@ -0,0 +1,65 @@
+package com.martiansoftware.jsap.examples;
+
+import com.martiansoftware.jsap.FlaggedOption;
+import com.martiansoftware.jsap.JSAP;
+import com.martiansoftware.jsap.JSAPResult;
+import com.martiansoftware.jsap.Switch;
+import com.martiansoftware.jsap.UnflaggedOption;
+
+public class Manual_HelloWorld_4 {
+
+	/**
+	 * Repeats the "Hello World" text multiple times. Decides whether to say
+	 * "Hi" or "Hello" depending upon verbose switch.
+	 * 
+	 * @param args the command line.
+	 * @throws Exception for reasons made clear later in the manual.
+	 */
+	// @@snip:Manual_HelloWorld_4@@
+	public static void main(String[] args) throws Exception {
+		JSAP jsap = new JSAP();
+
+		FlaggedOption opt1 = new FlaggedOption("count")
+								.setStringParser(JSAP.INTEGER_PARSER)
+								.setDefault("1")
+								.setRequired(true)
+								.setShortFlag('n')
+								.setLongFlag(JSAP.NO_LONGFLAG);
+
+		jsap.registerParameter(opt1);
+
+		Switch sw1 = new Switch("verbose")
+						.setShortFlag('v')
+						.setLongFlag("verbose");
+
+		jsap.registerParameter(sw1);
+
+		// Create an unflagged option called "names" that we'll use to
+		// say hello to particular people.
+		// To make it more interesting, we'll make it "greedy", so
+		// it consumes all remaining unflagged tokens on the command line
+		// as multiple values
+		UnflaggedOption opt2 = new UnflaggedOption("name")
+								.setStringParser(JSAP.STRING_PARSER)
+								.setDefault("World")
+								.setRequired(false)
+								.setGreedy(true);
+
+		jsap.registerParameter(opt2);
+
+		JSAPResult config = jsap.parse(args);
+
+		String[] names = config.getStringArray("name");
+		for (int i = 0; i < config.getInt("count"); ++i) {
+			for (int j = 0; j < names.length; ++j) {
+				System.out.println(
+					(config.getBoolean("verbose") ? "Hello" : "Hi")
+						+ ", "
+						+ names[j]
+						+ "!");
+			}
+		}
+	}
+	// @@endSnip@@
+
+}
\ No newline at end of file
diff --git a/src/java/com/martiansoftware/jsap/examples/Manual_HelloWorld_5.java b/src/java/com/martiansoftware/jsap/examples/Manual_HelloWorld_5.java
new file mode 100644
index 0000000..9f64996
--- /dev/null
+++ b/src/java/com/martiansoftware/jsap/examples/Manual_HelloWorld_5.java
@@ -0,0 +1,70 @@
+package com.martiansoftware.jsap.examples;
+
+import com.martiansoftware.jsap.FlaggedOption;
+import com.martiansoftware.jsap.JSAP;
+import com.martiansoftware.jsap.JSAPResult;
+import com.martiansoftware.jsap.Switch;
+import com.martiansoftware.jsap.UnflaggedOption;
+
+public class Manual_HelloWorld_5 {
+
+	/** 
+	 * Repeats the "Hello World" text multiple times.
+	 * Decides whether to say "Hi" or "Hello" depending upon verbose switch.
+	 * @param args the command line.
+	 * @throws Exception for reasons made clear later in the manual.
+	 */
+	// @@snip:Manual_HelloWorld_5@@
+	public static void main(String[] args) throws Exception {
+		JSAP jsap = new JSAP();
+		
+		FlaggedOption opt1 = new FlaggedOption("count")
+								.setStringParser(JSAP.INTEGER_PARSER)
+								.setDefault("1") 
+								.setRequired(true) 
+								.setShortFlag('n') 
+								.setLongFlag(JSAP.NO_LONGFLAG);
+		
+		jsap.registerParameter(opt1);
+		
+		Switch sw1 = new Switch("verbose")
+						.setShortFlag('v')
+						.setLongFlag("verbose");
+		
+		jsap.registerParameter(sw1);
+		
+		UnflaggedOption opt2 = new UnflaggedOption("name")
+								.setStringParser(JSAP.STRING_PARSER)
+								.setDefault("World")
+								.setRequired(true)
+								.setGreedy(true);
+								
+		jsap.registerParameter(opt2);
+		
+		JSAPResult config = jsap.parse(args);	
+
+		// check whether the command line was valid, and if it wasn't,
+		// display usage information and exit.
+		if (!config.success()) {
+			System.err.println();
+			System.err.println("Usage: java "
+								+ Manual_HelloWorld_5.class.getName());
+			System.err.println("                "
+								+ jsap.getUsage());
+			System.err.println();
+			System.exit(1);
+		}
+		
+		String[] names = config.getStringArray("name");
+		for (int i = 0; i < config.getInt("count"); ++i) {
+			for (int j = 0; j < names.length; ++j) {
+				System.out.println((config.getBoolean("verbose") ? "Hello" : "Hi")
+								+ ", "
+								+ names[j]
+								+ "!");
+			}
+		}
+	}
+	// @@endSnip@@
+	
+}
\ No newline at end of file
diff --git a/src/java/com/martiansoftware/jsap/examples/Manual_HelloWorld_6.java b/src/java/com/martiansoftware/jsap/examples/Manual_HelloWorld_6.java
new file mode 100644
index 0000000..c9931a4
--- /dev/null
+++ b/src/java/com/martiansoftware/jsap/examples/Manual_HelloWorld_6.java
@@ -0,0 +1,72 @@
+package com.martiansoftware.jsap.examples;
+
+import com.martiansoftware.jsap.FlaggedOption;
+import com.martiansoftware.jsap.JSAP;
+import com.martiansoftware.jsap.JSAPResult;
+import com.martiansoftware.jsap.Switch;
+import com.martiansoftware.jsap.UnflaggedOption;
+
+public class Manual_HelloWorld_6 {
+
+	/** 
+	 * Repeats the "Hello World" text multiple times.
+	 * Decides whether to say "Hi" or "Hello" depending upon verbose switch.
+	 * @param args the command line.
+	 * @throws Exception for reasons made clear later in the manual.
+	 */
+	// @@snip:Manual_HelloWorld_6@@
+	public static void main(String[] args) throws Exception {
+		JSAP jsap = new JSAP();
+		
+		FlaggedOption opt1 = new FlaggedOption("count")
+								.setStringParser(JSAP.INTEGER_PARSER)
+								.setDefault("1") 
+								.setRequired(true) 
+								.setShortFlag('n') 
+								.setLongFlag(JSAP.NO_LONGFLAG);
+								
+		opt1.setHelp("The number of times to say hello.");
+		jsap.registerParameter(opt1);
+		
+		Switch sw1 = new Switch("verbose")
+						.setShortFlag('v')
+						.setLongFlag("verbose");
+		sw1.setHelp("Requests verbose output.");
+		jsap.registerParameter(sw1);
+		
+		UnflaggedOption opt2 = new UnflaggedOption("name")
+								.setStringParser(JSAP.STRING_PARSER)
+								.setDefault("World")
+								.setRequired(true)
+								.setGreedy(true);
+		
+		opt2.setHelp("One or more names of people you would like to greet.");
+		jsap.registerParameter(opt2);
+		
+		JSAPResult config = jsap.parse(args);	
+
+		if (!config.success()) {
+			System.err.println();
+			System.err.println("Usage: java "
+								+ Manual_HelloWorld_6.class.getName());
+			System.err.println("                "
+								+ jsap.getUsage());
+			System.err.println();
+			// show full help as well
+			System.err.println(jsap.getHelp());
+			System.exit(1);
+		}
+		
+		String[] names = config.getStringArray("name");
+		for (int i = 0; i < config.getInt("count"); ++i) {
+			for (int j = 0; j < names.length; ++j) {
+				System.out.println((config.getBoolean("verbose") ? "Hello" : "Hi")
+								+ ", "
+								+ names[j]
+								+ "!");
+			}
+		}
+	}
+	// @@endSnip@@
+	
+}
\ No newline at end of file
diff --git a/src/java/com/martiansoftware/jsap/examples/Manual_HelloWorld_7.java b/src/java/com/martiansoftware/jsap/examples/Manual_HelloWorld_7.java
new file mode 100644
index 0000000..314ffd5
--- /dev/null
+++ b/src/java/com/martiansoftware/jsap/examples/Manual_HelloWorld_7.java
@@ -0,0 +1,83 @@
+package com.martiansoftware.jsap.examples;
+
+import com.martiansoftware.jsap.FlaggedOption;
+import com.martiansoftware.jsap.JSAP;
+import com.martiansoftware.jsap.JSAPResult;
+import com.martiansoftware.jsap.Switch;
+import com.martiansoftware.jsap.UnflaggedOption;
+
+public class Manual_HelloWorld_7 {
+
+	/** 
+	 * Repeats the "Hello World" text multiple times.
+	 * Decides whether to say "Hi" or "Hello" depending upon verbose switch.
+	 * @param args the command line.
+	 * @throws Exception for reasons made clear later in the manual.
+	 */
+	// @@snip:Manual_HelloWorld_7@@
+	public static void main(String[] args) throws Exception {
+		JSAP jsap = new JSAP();
+		
+		FlaggedOption opt1 = new FlaggedOption("count")
+								.setStringParser(JSAP.INTEGER_PARSER)
+								.setDefault("1") 
+								.setRequired(true) 
+								.setShortFlag('n') 
+								.setLongFlag(JSAP.NO_LONGFLAG);
+
+		opt1.setHelp("The number of times to say hello.");
+		jsap.registerParameter(opt1);
+		
+		Switch sw1 = new Switch("verbose")
+						.setShortFlag('v')
+						.setLongFlag("verbose");
+		
+		sw1.setHelp("Requests verbose output.");
+		jsap.registerParameter(sw1);
+		
+		UnflaggedOption opt2 = new UnflaggedOption("name")
+								.setStringParser(JSAP.STRING_PARSER)
+								.setDefault("World")
+								.setRequired(true)
+								.setGreedy(true);
+		
+		opt2.setHelp("One or more names of people you would like to greet.");
+		jsap.registerParameter(opt2);
+		
+		JSAPResult config = jsap.parse(args);	
+
+		if (!config.success()) {
+			
+			System.err.println();
+
+			// print out specific error messages describing the problems
+			// with the command line, THEN print usage, THEN print full
+			// help.  This is called "beating the user with a clue stick."
+			for (java.util.Iterator errs = config.getErrorMessageIterator();
+					errs.hasNext();) {
+				System.err.println("Error: " + errs.next());
+			}
+			
+			System.err.println();
+			System.err.println("Usage: java "
+								+ Manual_HelloWorld_7.class.getName());
+			System.err.println("                "
+								+ jsap.getUsage());
+			System.err.println();
+			System.err.println(jsap.getHelp());
+			System.exit(1);
+		}
+		
+		String[] names = config.getStringArray("name");
+		for (int i = 0; i < config.getInt("count"); ++i) {
+			for (int j = 0; j < names.length; ++j) {
+				System.out.println((config.getBoolean("verbose") ? "Hello" : "Hi")
+								+ ", "
+								+ names[j]
+								+ "!");
+			}
+		}
+	}
+	// @@endSnip@@
+	
+}
\ No newline at end of file
diff --git a/src/java/com/martiansoftware/jsap/examples/Manual_HelloWorld_8.java b/src/java/com/martiansoftware/jsap/examples/Manual_HelloWorld_8.java
new file mode 100644
index 0000000..6b6ab7f
--- /dev/null
+++ b/src/java/com/martiansoftware/jsap/examples/Manual_HelloWorld_8.java
@@ -0,0 +1,91 @@
+package com.martiansoftware.jsap.examples;
+
+import com.martiansoftware.jsap.FlaggedOption;
+import com.martiansoftware.jsap.JSAP;
+import com.martiansoftware.jsap.JSAPResult;
+import com.martiansoftware.jsap.QualifiedSwitch;
+import com.martiansoftware.jsap.UnflaggedOption;
+
+public class Manual_HelloWorld_8 {
+
+	/** 
+	 * Repeats the "Hello World" text multiple times.
+	 * Decides whether to say "Hi" or "Hello" depending upon verbose switch.
+	 * Uses the new QualifiedSwitch to set the language of verbosity
+	 * @param args the command line.
+	 * @throws Exception for reasons made clear later in the manual.
+	 */
+	// @@snip:Manual_HelloWorld_8@@
+	public static void main(String[] args) throws Exception {
+		JSAP jsap = new JSAP();
+		
+		FlaggedOption opt1 = new FlaggedOption("count")
+								.setStringParser(JSAP.INTEGER_PARSER)
+								.setDefault("1") 
+								.setRequired(true) 
+								.setShortFlag('n') 
+								.setLongFlag(JSAP.NO_LONGFLAG);
+
+		opt1.setHelp("The number of times to say hello.");
+		jsap.registerParameter(opt1);
+		
+		QualifiedSwitch sw1 = (QualifiedSwitch)
+								(new QualifiedSwitch("verbose")
+								.setShortFlag('v')
+								.setLongFlag("verbose")
+								.setList(true)
+								.setListSeparator(','));
+		
+		sw1.setHelp("Requests verbose output.");
+		jsap.registerParameter(sw1);
+		
+		UnflaggedOption opt2 = new UnflaggedOption("name")
+								.setStringParser(JSAP.STRING_PARSER)
+								.setDefault("World")
+								.setRequired(true)
+								.setGreedy(true);
+		
+		opt2.setHelp("One or more names of people you would like to greet.");
+		jsap.registerParameter(opt2);
+		
+		JSAPResult config = jsap.parse(args);	
+
+		if (!config.success()) {
+			
+			System.err.println();
+
+			// print out specific error messages describing the problems
+			// with the command line, THEN print usage, THEN print full
+			// help.  This is called "beating the user with a clue stick."
+			for (java.util.Iterator errs = config.getErrorMessageIterator();
+					errs.hasNext();) {
+				System.err.println("Error: " + errs.next());
+			}
+			
+			System.err.println();
+			System.err.println("Usage: java "
+								+ Manual_HelloWorld_8.class.getName());
+			System.err.println("                "
+								+ jsap.getUsage());
+			System.err.println();
+			System.err.println(jsap.getHelp());
+			System.exit(1);
+		}
+		
+		String[] names = config.getStringArray("name");
+		String[] languages = config.getStringArray("verbose");
+		for (int i = 0; i < languages.length; ++i) {
+			System.out.println("language=" + languages[i]);
+		}
+		for (int i = 0; i < config.getInt("count"); ++i) {
+			for (int j = 0; j < names.length; ++j) {
+				System.out.println((config.getBoolean("verbose") ? "Hello" : "Hi")
+								+ ", "
+								+ names[j]
+								+ "!");
+			}
+		}
+	}
+	// @@endSnip@@
+	
+}
\ No newline at end of file
diff --git a/src/java/com/martiansoftware/jsap/examples/Manual_HelloWorld_9.java b/src/java/com/martiansoftware/jsap/examples/Manual_HelloWorld_9.java
new file mode 100644
index 0000000..6f58b9f
--- /dev/null
+++ b/src/java/com/martiansoftware/jsap/examples/Manual_HelloWorld_9.java
@@ -0,0 +1,75 @@
+/*
+ * Created on Sep 4, 2004
+ *
+ * To change the template for this generated file go to
+ * Window - Preferences - Java - Code Generation - Code and Comments
+ */
+package com.martiansoftware.jsap.examples;
+
+import com.martiansoftware.jsap.JSAP;
+import com.martiansoftware.jsap.JSAPResult;
+
+/**
+ * A reimplementation of Manual_HelloWorld_8 that loads the JSAP
+ * configuration from an XML file.
+ * 
+ * @author <a href="http://www.martiansoftware.com/contact.html">Marty Lamb</a>
+ */
+public class Manual_HelloWorld_9 {
+
+	// @@snip:Manual_HelloWorld_9@@
+	public static void main(String[] args) throws Exception {
+		JSAP jsap = new JSAP(Manual_HelloWorld_9.class.getResource("Manual_HelloWorld_9.jsap"));
+		
+		JSAPResult config = jsap.parse(args);	
+
+		if (!config.success()) {
+			
+			System.err.println();
+
+			// print out specific error messages describing the problems
+			// with the command line, THEN print usage, THEN print full
+			// help.  This is called "beating the user with a clue stick."
+			for (java.util.Iterator errs = config.getErrorMessageIterator();
+					errs.hasNext();) {
+				System.err.println("Error: " + errs.next());
+			}
+			
+			System.err.println();
+			System.err.println("Usage: java "
+								+ Manual_HelloWorld_9.class.getName());
+			System.err.println("                "
+								+ jsap.getUsage());
+			System.err.println();
+			System.err.println(jsap.getHelp());
+			System.exit(1);
+		}
+		
+		String[] names = config.getStringArray("name");
+		String[] languages = config.getStringArray("verbose");
+		if (languages.length == 0) languages = new String[] {"en"};
+		
+		for (int lang = 0; lang < languages.length; ++lang) {
+			for (int i = 0; i < config.getInt("count"); ++i) {
+				for (int j = 0; j < names.length; ++j) {
+					System.out.println((config.getBoolean("verbose") ? getVerboseHello(languages[lang]) : "Hi")
+									+ ", "
+									+ names[j]
+									+ "!");
+				}
+			}
+		}
+	}
+	
+	private static String getVerboseHello(String language) {
+		if ((language == null) || "en".equalsIgnoreCase(language)) {
+			return("Hello");
+		} else if ("de".equalsIgnoreCase(language)) {
+			return("Guten Tag");
+		} else {
+			return("(Barely audible grunt)");
+		}
+	}	
+	// @@endSnip@@
+	
+}
diff --git a/src/java/com/martiansoftware/jsap/examples/Manual_HelloWorld_9.jsap b/src/java/com/martiansoftware/jsap/examples/Manual_HelloWorld_9.jsap
new file mode 100644
index 0000000..3b514c6
--- /dev/null
+++ b/src/java/com/martiansoftware/jsap/examples/Manual_HelloWorld_9.jsap
@@ -0,0 +1,38 @@
+<!-- @@snip:Manual_HelloWorld_9_jsap@@ -->
+<jsap>
+	<parameters>
+		<flaggedOption>
+			<id>count</id>
+			<stringParser>
+				<classname>IntegerStringParser</classname>
+			</stringParser> 
+			<required>true</required>
+			<shortFlag>n</shortFlag>
+			<defaults>
+				<string>1</string>
+			</defaults>
+			<help>The number of times to say hello (default=1).</help>
+		</flaggedOption>
+
+		<qualifiedSwitch>
+			<id>verbose</id>
+			<shortFlag>v</shortFlag>
+			<longFlag>verbose</longFlag>
+			<list>true</list>
+			<listSeparator>,</listSeparator>
+			<help>Requests verbose output.</help>
+		</qualifiedSwitch>
+		
+		<unflaggedOption>
+			<id>name</id>
+			<defaults>
+				<string>World</string>
+			</defaults>
+			<required>true</required>
+			<greedy>true</greedy>
+			<help>One or more names of people you would like to greet.</help>
+		</unflaggedOption>
+
+	</parameters>
+</jsap>
+<!-- @@endSnip@@ -->
diff --git a/src/java/com/martiansoftware/jsap/examples/Manual_HelloWorld_Simple.java b/src/java/com/martiansoftware/jsap/examples/Manual_HelloWorld_Simple.java
new file mode 100644
index 0000000..d215437
--- /dev/null
+++ b/src/java/com/martiansoftware/jsap/examples/Manual_HelloWorld_Simple.java
@@ -0,0 +1,55 @@
+package com.martiansoftware.jsap.examples;
+
+import com.martiansoftware.jsap.FlaggedOption;
+import com.martiansoftware.jsap.JSAP;
+import com.martiansoftware.jsap.JSAPResult;
+import com.martiansoftware.jsap.Parameter;
+import com.martiansoftware.jsap.QualifiedSwitch;
+import com.martiansoftware.jsap.SimpleJSAP;
+import com.martiansoftware.jsap.UnflaggedOption;
+
+public class Manual_HelloWorld_Simple {
+
+	/** 
+	 * Repeats the "Hello World" text multiple times.
+	 * Decides whether to say "Hi" or "Hello" depending upon verbose switch.
+	 * Uses the new QualifiedSwitch to set the language of verbosity
+	 * Uses SimpleJSAP
+	 * @param args the command line.
+	 * @throws Exception for reasons made clear later in the manual.
+	 */
+	// @@snip:Manual_HelloWorld_Simple@@
+	public static void main(String[] args) throws Exception {
+		SimpleJSAP jsap = new SimpleJSAP( 
+			"MyProgram", 
+			"Repeats \"Hello, world!\" multiple times",
+			new Parameter[] {
+				new FlaggedOption( "count", JSAP.INTEGER_PARSER, "1", JSAP.REQUIRED, 'n', JSAP.NO_LONGFLAG, 
+					"The number of times to say hello." ),
+				new QualifiedSwitch( "verbose", JSAP.STRING_PARSER, JSAP.NO_DEFAULT, JSAP.NOT_REQUIRED, 'v', "verbose", 
+					"Requests verbose output." ).setList( true ).setListSeparator( ',' ),
+				new UnflaggedOption( "name", JSAP.STRING_PARSER, "World", JSAP.REQUIRED, JSAP.GREEDY, 
+					"One or more names of people you would like to greet." )
+			}
+		);
+		
+		JSAPResult config = jsap.parse(args);	
+		if ( jsap.messagePrinted() ) System.exit( 1 );
+				
+		String[] names = config.getStringArray("name");
+		String[] languages = config.getStringArray("verbose");
+		for (int i = 0; i < languages.length; ++i) {
+			System.out.println("language=" + languages[i]);
+		}
+		for (int i = 0; i < config.getInt("count"); ++i) {
+			for (int j = 0; j < names.length; ++j) {
+				System.out.println((config.getBoolean("verbose") ? "Hello" : "Hi")
+								+ ", "
+								+ names[j]
+								+ "!");
+			}
+		}
+	}
+	// @@endSnip@@
+	
+}
\ No newline at end of file
diff --git a/src/java/com/martiansoftware/jsap/stringparsers/BigDecimalStringParser.java b/src/java/com/martiansoftware/jsap/stringparsers/BigDecimalStringParser.java
new file mode 100644
index 0000000..cbfdc2c
--- /dev/null
+++ b/src/java/com/martiansoftware/jsap/stringparsers/BigDecimalStringParser.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2002-2004, Martian Software, Inc.
+ * This file is made available under the LGPL as described in the accompanying
+ * LICENSE.TXT file.
+ */
+
+package com.martiansoftware.jsap.stringparsers;
+
+import com.martiansoftware.jsap.StringParser;
+import com.martiansoftware.jsap.ParseException;
+import java.math.BigDecimal;
+
+/**
+ * A {@link com.martiansoftware.jsap.StringParser} for parsing BigDecimals.  The parse() method delegates the
+ * actual
+ * parsing to BigDecimal's constructor.
+ * @author <a href="http://www.martiansoftware.com/contact.html">Marty Lamb</a>
+ * @see com.martiansoftware.jsap.StringParser
+ * @see java.math.BigDecimal
+ */
+public class BigDecimalStringParser extends StringParser {
+
+	private static final BigDecimalStringParser INSTANCE = new BigDecimalStringParser();
+
+	/** Returns a {@link BigDecimalStringParser}.
+	 *
+	 * <p>Convenient access to the only instance returned by
+	 * this method is available through
+	 * {@link com.martiansoftware.jsap.JSAP#BIGDECIMAL_PARSER}.
+	 *  
+	 * @return a {@link BigDecimalStringParser}.
+	 */
+	
+    public static BigDecimalStringParser getParser() {
+		return INSTANCE; 
+	}
+
+	/**
+     * Creates a new BigDecimalStringParser.
+     * @deprecated Use {@link #getParser()} or, even better, {@link com.martiansoftware.jsap.JSAP#BIGDECIMAL_PARSER}.
+     */
+    public BigDecimalStringParser() {
+        super();
+    }
+
+    /**
+     * Parses the specified argument into a BigDecimal.  This method simply
+     * delegates
+     * the parsing to <code>new BigDecimal(String)</code>.  If BigDecimal
+     * throws a
+     * NumberFormatException, it is encapsulated into a ParseException and
+     * re-thrown.
+     *
+     * @param arg the argument to parse
+     * @return a BigDecimal object with the value contained in the specified
+     * argument.
+     * @throws ParseException if <code>new BigDecimal(arg)</code> throws a
+     * NumberFormatException.
+     * @see BigDecimal
+     * @see com.martiansoftware.jsap.StringParser#parse(String)
+     */
+    public Object parse(String arg) throws ParseException {
+        BigDecimal result = null;
+        try {
+            result = new BigDecimal(arg);
+        } catch (NumberFormatException e) {
+            throw (
+                new ParseException(
+                    "Unable to convert '" + arg + "' to a BigDecimal.",
+                    e));
+        }
+        return (result);
+    }
+}
diff --git a/src/java/com/martiansoftware/jsap/stringparsers/BigIntegerStringParser.java b/src/java/com/martiansoftware/jsap/stringparsers/BigIntegerStringParser.java
new file mode 100644
index 0000000..870e796
--- /dev/null
+++ b/src/java/com/martiansoftware/jsap/stringparsers/BigIntegerStringParser.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2002-2004, Martian Software, Inc.
+ * This file is made available under the LGPL as described in the accompanying
+ * LICENSE.TXT file.
+ */
+
+package com.martiansoftware.jsap.stringparsers;
+
+import com.martiansoftware.jsap.StringParser;
+import com.martiansoftware.jsap.ParseException;
+import java.math.BigInteger;
+
+/**
+ * A {@link com.martiansoftware.jsap.StringParser} for parsing BigIntegers.  The parse() method delegates the
+ * actual
+ * parsing to BigInteger's constructor.
+ * @author <a href="http://www.martiansoftware.com/contact.html">Marty Lamb</a>
+ * @see com.martiansoftware.jsap.StringParser
+ * @see java.math.BigInteger
+ */
+public class BigIntegerStringParser extends StringParser {
+
+	private static final BigIntegerStringParser INSTANCE = new BigIntegerStringParser();	
+
+	/** Returns a {@link BigIntegerStringParser}.
+	 * 
+	 * <p>Convenient access to the only instance returned by
+	 * this method is available through
+	 * {@link com.martiansoftware.jsap.JSAP#BIGINTEGER_PARSER}.
+	 *  
+	 * @return a {@link BigIntegerStringParser}.
+	 */
+    public static BigIntegerStringParser getParser() {
+		return INSTANCE;
+	}
+
+	/**
+     * Creates a new BigIntegerStringParser.
+     * @deprecated Use {@link #getParser()} or, even better, {@link com.martiansoftware.jsap.JSAP#BIGINTEGER_PARSER}.
+     */
+    public BigIntegerStringParser() {
+        super();
+    }
+
+    /**
+    * Parses the specified argument into a BigInteger.  This method simply
+    * delegates
+    * the parsing to <code>new BigInteger(String)</code>.  If BigInteger
+    * throws a
+    * NumberFormatException, it is encapsulated into a ParseException and
+    * re-thrown.
+    *
+    * @param arg the argument to parse
+    * @return a BigInteger object with the value contained in the specified
+    * argument.
+    * @throws ParseException if <code>new BigInteger(arg)</code> throws a
+    * NumberFormatException.
+    * @see BigInteger
+    * @see com.martiansoftware.jsap.StringParser#parse(String)
+    */
+    public Object parse(String arg) throws ParseException {
+        BigInteger result = null;
+        try {
+            result = new BigInteger(arg);
+        } catch (NumberFormatException e) {
+            throw (
+                new ParseException(
+                    "Unable to convert '" + arg + "' to a BigInteger.",
+                    e));
+        }
+        return (result);
+    }
+}
diff --git a/src/java/com/martiansoftware/jsap/stringparsers/BooleanStringParser.java b/src/java/com/martiansoftware/jsap/stringparsers/BooleanStringParser.java
new file mode 100644
index 0000000..1265667
--- /dev/null
+++ b/src/java/com/martiansoftware/jsap/stringparsers/BooleanStringParser.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (c) 2002-2004, Martian Software, Inc.
+ * This file is made available under the LGPL as described in the accompanying
+ * LICENSE.TXT file.
+ */
+
+package com.martiansoftware.jsap.stringparsers;
+
+import com.martiansoftware.jsap.StringParser;
+import com.martiansoftware.jsap.ParseException;
+
+/**
+ * <p>A {@link com.martiansoftware.jsap.StringParser} for parsing Booleans.  This StringParser is also used
+ * internally
+ * by the Switch class.</p>
+ *
+ * <p>When parsing, the following arguments are interpreted as TRUE:
+ * <ul>
+ *         <li>null</i>
+ *         <li>"t" (case-insensitive)</li>
+ *         <li>"true" (case-insensitive)</li>
+ *         <li>"y" (case-insensitive)</li>
+ *         <li>"yes" (case-insensitive)</li>
+ *         <li>"1"</li>
+ * </ul>
+ * <p>The following arguments are interpreted as FALSE:
+ * <ul>
+ *         <li>"f" (case-insensitive)</li>
+ *         <li>"false" (case-insensitive)</li>
+ *         <li>"n" (case-insensitive)</li>
+ *         <li>"no" (case-insensitive)</li>
+ *         <li>"0"</li>
+ * </ul>
+ * 
+ * <p>All other input throws a ParseException.
+ * @author <a href="http://www.martiansoftware.com/contact.html">Marty Lamb</a>
+ * @see com.martiansoftware.jsap.StringParser
+ * @see java.lang.Boolean
+ */
+public class BooleanStringParser extends StringParser {
+
+	private static final BooleanStringParser INSTANCE = new BooleanStringParser();	
+
+	/** Returns a {@link BooleanStringParser}.
+	 * 
+	 * <p>Convenient access to the only instance returned by
+	 * this method is available through
+	 * {@link com.martiansoftware.jsap.JSAP#BOOLEAN_PARSER}.
+	 *  
+	 * @return a {@link BooleanStringParser}.
+	 */
+
+    public static BooleanStringParser getParser() {
+		return INSTANCE;
+	}
+
+	/**
+     *  Creates a new BooleanStringParser
+     * @deprecated Use {@link #getParser()} or, even better, {@link com.martiansoftware.jsap.JSAP#BOOLEAN_PARSER}.
+     */
+    public BooleanStringParser() {
+        super();
+    }
+
+    /**
+     * Converts the specified argument into a Boolean.
+     *
+     * <p>When parsing, the following arguments are interpreted as TRUE:
+     * <ul>
+     *         <li>null</i>
+     *         <li>"t" (case-insensitive)</li>
+     *         <li>"true" (case-insensitive)</li>
+     *         <li>"y" (case-insensitive)</li>
+     *         <li>"yes" (case-insensitive)</li>
+     *         <li>"1"</li>
+     * <ul>
+     * <p>The following arguments are interpreted as FALSE:
+     * <ul>
+     *         <li>"f" (case-insensitive)</li>
+     *         <li>"false" (case-insensitive)</li>
+     *         <li>"n" (case-insensitive)</li>
+     *         <li>"no" (case-insensitive)</li>
+     *         <li>"0"</li>
+     * </ul>
+     * 
+     * <p>All other input throws a ParseException.
+     * @param arg the argument to convert to a Boolean.
+     * @return a Boolean as described above.
+     * @throws ParseException if none of the above cases are matched.
+     */
+    public Object parse(String arg) throws ParseException {
+        boolean result = false;
+        if ((arg == null)
+            || arg.equalsIgnoreCase("t")
+            || arg.equalsIgnoreCase("true")
+            || arg.equalsIgnoreCase("y")
+            || arg.equalsIgnoreCase("yes")
+            || arg.equals("1")) {
+            result = true;
+        } else if (
+            arg.equalsIgnoreCase("f")
+                || arg.equalsIgnoreCase("false")
+                || arg.equalsIgnoreCase("n")
+                || arg.equalsIgnoreCase("no")
+                || arg.equals("0")) {
+            result = false;
+        } else {
+            throw (
+                new ParseException(
+                    "Unable to convert '" + arg + "' to a boolean value."));
+        }
+        return (new Boolean(result));
+    }
+}
diff --git a/src/java/com/martiansoftware/jsap/stringparsers/ByteStringParser.java b/src/java/com/martiansoftware/jsap/stringparsers/ByteStringParser.java
new file mode 100644
index 0000000..9541f5d
--- /dev/null
+++ b/src/java/com/martiansoftware/jsap/stringparsers/ByteStringParser.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2002-2004, Martian Software, Inc.
+ * This file is made available under the LGPL as described in the accompanying
+ * LICENSE.TXT file.
+ */
+
+package com.martiansoftware.jsap.stringparsers;
+
+import com.martiansoftware.jsap.StringParser;
+import com.martiansoftware.jsap.ParseException;
+
+/**
+ * A {@link com.martiansoftware.jsap.StringParser} for parsing Bytes.  The parse() method delegates the actual
+ * parsing to Byte.decode(String).
+ * @author <a href="http://www.martiansoftware.com/contact.html">Marty Lamb</a>
+ * @see com.martiansoftware.jsap.StringParser
+ * @see java.lang.Byte
+ */
+public class ByteStringParser extends StringParser {
+
+	private static final ByteStringParser INSTANCE = new ByteStringParser();	
+
+	/** Returns a {@link ByteStringParser}.
+	 * 
+	 * <p>Convenient access to the only instance returned by
+	 * this method is available through
+	 * {@link com.martiansoftware.jsap.JSAP#BYTE_PARSER}.
+	 *  
+	 * @return a {@link ByteStringParser}.
+	 */
+
+    public static ByteStringParser getParser() {
+		return INSTANCE;
+	}
+
+	/**
+     * Creates a new ByteStringParser.
+     * @deprecated Use {@link #getParser()} or, even better, {@link com.martiansoftware.jsap.JSAP#BYTE_PARSER}.
+     */
+    public ByteStringParser() {
+        super();
+    }
+
+    /**
+     * Parses the specified argument into a Byte.  This method simply delegates
+     * the parsing to <code>Byte.decode(String)</code>.  If Byte throws a
+     * NumberFormatException, it is encapsulated into a ParseException and
+     * re-thrown.
+     *
+     * @param arg the argument to parse
+     * @return a Byte object with the value contained in the specified argument.
+     * @throws ParseException if <code>Byte.decode(arg)</code> throws a
+     * NumberFormatException.
+     * @see java.lang.Byte
+     * @see com.martiansoftware.jsap.StringParser#parse(String)
+     */
+    public Object parse(String arg) throws ParseException {
+        Byte result = null;
+        try {
+            result = Byte.decode(arg);
+        } catch (NumberFormatException e) {
+            throw (
+                new ParseException(
+                    "Unable to convert '" + arg + "' to a Byte.",
+                    e));
+        }
+        return (result);
+    }
+}
diff --git a/src/java/com/martiansoftware/jsap/stringparsers/CharacterStringParser.java b/src/java/com/martiansoftware/jsap/stringparsers/CharacterStringParser.java
new file mode 100644
index 0000000..06ac73f
--- /dev/null
+++ b/src/java/com/martiansoftware/jsap/stringparsers/CharacterStringParser.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2002-2004, Martian Software, Inc.
+ * This file is made available under the LGPL as described in the accompanying
+ * LICENSE.TXT file.
+ */
+
+package com.martiansoftware.jsap.stringparsers;
+
+import com.martiansoftware.jsap.StringParser;
+import com.martiansoftware.jsap.ParseException;
+
+/**
+ * A {@link com.martiansoftware.jsap.StringParser} for parsing Characters.  The parse() method requires an
+ * argument of length exactly
+ * equal to 1 in order to perform the conversion; otherwise, a ParseException
+ * is thrown.
+ * @author <a href="http://www.martiansoftware.com/contact.html">Marty Lamb</a>
+ * @see com.martiansoftware.jsap.StringParser
+ * @see java.lang.Character
+ */
+public class CharacterStringParser extends StringParser {
+
+	private static final CharacterStringParser INSTANCE = new CharacterStringParser();	
+
+	/** Returns a {@link CharacterStringParser}.
+	 * 
+	 * <p>Convenient access to the only instance returned by
+	 * this method is available through
+	 * {@link com.martiansoftware.jsap.JSAP#CHARACTER_PARSER}.
+	 *  
+	 * @return a {@link CharacterStringParser}.
+	 */
+
+    public static CharacterStringParser getParser() {
+		return INSTANCE;
+	}
+
+	/**
+     * Creates a new CharacterStringParser.
+     * @deprecated Use {@link #getParser()} or, even better, {@link com.martiansoftware.jsap.JSAP#CHARACTER_PARSER}.
+     */
+    public CharacterStringParser() {
+        super();
+    }
+
+    /**
+     * Parses the specified argument into a Character.  The conversion is
+     * performed by
+     * checking that the specified argument is exactly 1 character long, then
+     * encapsulating
+     * that char in a Character object.  If the specified argument is not
+     * exactly 1 character long,
+     * a ParseException is thrown.
+     *
+     * @param arg the argument to parse
+     * @return a Character object with the value contained in the specified
+     * argument.
+     * @throws ParseException if ( (arg==null) || (arg.length()!=1) )
+     * @see java.lang.Character
+     * @see com.martiansoftware.jsap.StringParser#parse(String)
+     */
+    public Object parse(String arg) throws ParseException {
+        if ((arg == null) || (arg.length() != 1)) {
+            throw (
+                new ParseException(
+                    "Unable to convert '" + arg + "' to a Character."));
+        }
+        return (new Character(arg.charAt(0)));
+    }
+}
diff --git a/src/java/com/martiansoftware/jsap/stringparsers/ClassStringParser.java b/src/java/com/martiansoftware/jsap/stringparsers/ClassStringParser.java
new file mode 100644
index 0000000..8a85585
--- /dev/null
+++ b/src/java/com/martiansoftware/jsap/stringparsers/ClassStringParser.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2002-2004, Martian Software, Inc.
+ * This file is made available under the LGPL as described in the accompanying
+ * LICENSE.TXT file.
+ */
+
+package com.martiansoftware.jsap.stringparsers;
+
+import com.martiansoftware.jsap.StringParser;
+import com.martiansoftware.jsap.ParseException;
+
+/**
+ * A {@link com.martiansoftware.jsap.StringParser} for parsing Class objects.  The parse(arg) method calls
+ * Class.forName(arg) and returns
+ * the result.  If any exceptions are thrown by Class.forName(), they are
+ * encapsulated in a ParseException
+ * and re-thrown.
+ * 
+ * <p><b>Note:</b> The Class.forName() call attempts to load the class from the
+ * same ClassLoader that loaded
+ * this StringParser.
+ * @author <a href="http://www.martiansoftware.com/contact.html">Marty Lamb</a>
+ * @see com.martiansoftware.jsap.StringParser
+ * @see java.lang.Class
+ */
+public class ClassStringParser extends StringParser {
+
+	private static final ClassStringParser INSTANCE = new ClassStringParser();	
+
+	/** Returns a {@link ClassStringParser}.
+	 * 
+	 * <p>Convenient access to the only instance returned by
+	 * this method is available through
+	 * {@link com.martiansoftware.jsap.JSAP#CLASS_PARSER}.
+	 *  
+	 * @return a {@link ClassStringParser}.
+	 */
+
+    public static ClassStringParser getParser() {
+		return INSTANCE;
+	}
+
+	/**
+     * Creates a new ClassStringParser.
+     * @deprecated Use {@link #getParser()} or, even better, {@link com.martiansoftware.jsap.JSAP#CLASS_PARSER}.
+     */
+    public ClassStringParser() {
+        super();
+    }
+
+    /**
+     * Parses the specified argument into a Class object.  This method calls
+     * Class.forName(), passing
+     * the specified argument as the name of the class to load, and returns
+     * the resulting Class object.
+     * If an exception is thrown by Class.forName(), it is encapsulated in a
+     * ParseException and re-thrown.
+     *
+     * @param arg the argument to parse
+     * @return a Class object representing the class named by the specified
+     * argument.
+     * @throws ParseException if <code>Class.forName(arg)</code> throws an
+     * exception.
+     * @see java.lang.Class
+     * @see com.martiansoftware.jsap.StringParser#parse(String)
+     */
+    public Object parse(String arg) throws ParseException {
+        Class result = null;
+        try {
+            result = Class.forName(arg);
+        } catch (Exception e) {
+            throw (
+                new ParseException("Unable to locate class '" + arg + "'.", e));
+        }
+        return (result);
+    }
+}
diff --git a/src/java/com/martiansoftware/jsap/stringparsers/ColorStringParser.java b/src/java/com/martiansoftware/jsap/stringparsers/ColorStringParser.java
new file mode 100644
index 0000000..cd10409
--- /dev/null
+++ b/src/java/com/martiansoftware/jsap/stringparsers/ColorStringParser.java
@@ -0,0 +1,272 @@
+/*
+ * Copyright (c) 2002-2004, Martian Software, Inc.
+ * This file is made available under the LGPL as described in the accompanying
+ * LICENSE.TXT file.
+ */
+
+package com.martiansoftware.jsap.stringparsers;
+
+import com.martiansoftware.jsap.ParseException;
+import com.martiansoftware.jsap.StringParser;
+
+import java.awt.Color;
+import java.util.StringTokenizer;
+
+/**
+ * A {@link com.martiansoftware.jsap.StringParser} for parsing java.awt.Color objects.  Color information can be
+ * specified in a variety
+ * of formats:
+ * 
+ * <ul>
+ * <li>RGB, as integers in the range 0-255, separated by commas
+ * (e.g., "123,45,6")</li>
+ * <li>RGB, as floats in the range 0.0-1.0, separated by commas
+ * (e.g., "0.123,0.45,0.6")</li>
+ * <li>RGB, as hexadecimal strings following the '#' character
+ * (e.g., "#1234ef")</li>
+ * <li>By name, as matching the names of the color fields of java.awt.Color
+ * (case-insensitive).  (e.g., "black")</li>
+ * <li>RGBAlpha, as integers in the range 0-255, separated by commas
+ * (e.g., "123,45,6,128")</li>
+ * <li>RGBAlpha, as floats in the range 0.0-1.0, separated by commas
+ * (e.g., "0.123,0.45,0.6,.5")</li>
+ * <li>RGBAlpha, as hexadecimal strings following the '#' character
+ * (e.g., "#1234efab")</li>
+ * </ul>
+ *
+ * If the specified argument does not match any of these formats, a
+ * ParseException is thrown.
+ * @author <a href="http://www.martiansoftware.com/contact.html">Marty Lamb</a>
+ * @see com.martiansoftware.jsap.StringParser
+ * @see java.awt.Color
+ */
+public class ColorStringParser extends StringParser {
+
+	private static final ColorStringParser INSTANCE = new ColorStringParser();	
+
+	/** Returns a {@link ColorStringParser}.
+	 * 
+	 * <p>Convenient access to the only instance returned by
+	 * this method is available through
+	 * {@link com.martiansoftware.jsap.JSAP#COLOR_PARSER}.
+	 *  
+	 * @return a {@link ColorStringParser}.
+	 */
+
+	public static ColorStringParser getParser() {
+		return INSTANCE;
+	}
+
+	/**
+     * Creates a new ColorStringParser.
+     * @deprecated Use {@link #getParser()} or, even better, {@link com.martiansoftware.jsap.JSAP#COLOR_PARSER}.
+     */
+    public ColorStringParser() {
+        super();
+    }
+
+    /**
+     * Simple utility for creating ParseExceptions, as there are so many
+     * opportunities to throw them.
+     * @param s the ParseException's message.
+     * @return a new ParseException with the specified message.
+     */
+    private ParseException colorException(String s) {
+        return (
+            new ParseException("Unable to convert '" + s + "' to a Color."));
+    }
+
+    /**
+     * Parses a hexadecimal String into a Color.
+     * @param hexString the hexadecimal String to parse.
+     * @return a Color object based upon the specified color string.
+     * @throws ParseException if the specified String cannot be interpreted as
+     * a Color.
+     */
+    private Color parseHexColor(String hexString) throws ParseException {
+        Color result = null;
+        int hexLength = hexString.length();
+        if ((hexLength != 7) && (hexLength != 9)) {
+            throw (colorException(hexString));
+        }
+        try {
+            int red = Integer.parseInt(hexString.substring(1, 3), 16);
+            int green = Integer.parseInt(hexString.substring(3, 5), 16);
+            int blue = Integer.parseInt(hexString.substring(5, 7), 16);
+            if (hexLength == 7) {
+                result = new Color(red, green, blue);
+            } else {
+                int alpha = Integer.parseInt(hexString.substring(7, 9), 16);
+                result = new Color(red, green, blue, alpha);
+            }
+        } catch (NumberFormatException e) {
+            throw (colorException(hexString));
+        }
+        return (result);
+    }
+
+    /**
+     * Parses a 3- or 4-tuple of comma-separated floats into a Color.
+     * @param floatString the String to parse.
+     * @return a Color object based upon the specified String.
+     * @throws ParseException if the specified String cannot be interpreted as
+     * a Color.
+     */
+    private Color parseFloatTuple(String floatString) throws ParseException {
+        String[] tuple = tupleToArray(floatString);
+        Color result = null;
+        try {
+            float red = Float.parseFloat(tuple[0]);
+            float green = Float.parseFloat(tuple[1]);
+            float blue = Float.parseFloat(tuple[2]);
+            if (tuple.length == 3) {
+                result = new Color(red, green, blue);
+            } else {
+                float alpha = Float.parseFloat(tuple[3]);
+                result = new Color(red, green, blue, alpha);
+            }
+        } catch (NumberFormatException e) {
+            throw (colorException(floatString));
+        }
+        return (result);
+    }
+
+    /**
+     * Parses a 3- or 4-tuple of comma-separated integers into a Color.
+     * @param intString the String to parse.
+     * @return a Color object based upon the specified color String.
+     * @throws ParseException if the specified String cannot be interpreted as
+     * a Color.
+     */
+    private Color parseIntTuple(String intString) throws ParseException {
+        String[] tuple = tupleToArray(intString);
+        Color result = null;
+        try {
+            int red = Integer.parseInt(tuple[0]);
+            int green = Integer.parseInt(tuple[1]);
+            int blue = Integer.parseInt(tuple[2]);
+            if (tuple.length == 3) {
+                result = new Color(red, green, blue);
+            } else {
+                int alpha = Integer.parseInt(tuple[3]);
+                result = new Color(red, green, blue, alpha);
+            }
+        } catch (NumberFormatException e) {
+            throw (colorException(intString));
+        }
+        return (result);
+    }
+
+    /**
+     * Parses a String into a Color by name (names lifted from java.awt.Color).
+     * @param colorName the name of the desired Color.
+     * @return a Color object based upon the specified color name.
+     * @throws ParseException if the specified String is not a valid color name.
+     */
+    private Color parseColorName(String colorName) throws ParseException {
+        Color result = null;
+        if (colorName.equalsIgnoreCase("black")) {
+            result = Color.black;
+        } else if (colorName.equalsIgnoreCase("blue")) {
+            result = Color.blue;
+        } else if (colorName.equalsIgnoreCase("cyan")) {
+            result = Color.cyan;
+        } else if (colorName.equalsIgnoreCase("gray")) {
+            result = Color.gray;
+        } else if (colorName.equalsIgnoreCase("green")) {
+            result = Color.green;
+        } else if (colorName.equalsIgnoreCase("lightGray")) {
+            result = Color.lightGray;
+        } else if (colorName.equalsIgnoreCase("magenta")) {
+            result = Color.magenta;
+        } else if (colorName.equalsIgnoreCase("orange")) {
+            result = Color.orange;
+        } else if (colorName.equalsIgnoreCase("pink")) {
+            result = Color.pink;
+        } else if (colorName.equalsIgnoreCase("red")) {
+            result = Color.red;
+        } else if (colorName.equalsIgnoreCase("white")) {
+            result = Color.white;
+        } else if (colorName.equalsIgnoreCase("yellow")) {
+            result = Color.yellow;
+        } else {
+            throw (colorException(colorName));
+        }
+        return (result);
+    }
+
+    /**
+     * Parses a 3- or 4-tuple, comma separated, to an array.
+     * @param s the String to be parsed to an array.
+     * @return the parsed String as a 3- or 4-element array of Strings.
+     * @throws ParseException if the specified String contains fewer than 3 or
+     * more than 4 elements.
+     */
+    private String[] tupleToArray(String s) throws ParseException {
+        String[] result = null;
+        StringTokenizer tokenizer = new StringTokenizer(s, ",", true);
+        int tokenCount = tokenizer.countTokens();
+        if (tokenCount == 5) {
+            result = new String[3];
+        } else if (tokenCount == 7) {
+            result = new String[4];
+        } else {
+            throw (colorException(s));
+        }
+        result[0] = tokenizer.nextToken();
+        tokenizer.nextToken();
+        result[1] = tokenizer.nextToken();
+        tokenizer.nextToken();
+        result[2] = tokenizer.nextToken();
+        if (tokenCount == 7) {
+            tokenizer.nextToken();
+            result[3] = tokenizer.nextToken();
+        }
+        return (result);
+    }
+
+    /**
+     * Parses java.awt.Color objects from Strings.  Color information can be
+     * specified in a variety
+     * of formats:
+     * 
+     * <ul>
+     * <li>RGB, as integers in the range 0-255, separated by commas
+     * (e.g., "123,45,6")</li>
+     * <li>RGB, as floats in the range 0.0-1.0, separated by commas
+     * (e.g., "0.123,0.45,0.6")</li>
+     * <li>RGB, as hexadecimal strings following the '#' character
+     * (e.g., "#1234ef")</li>
+     * <li>By name, as matching the names of the color fields of java.awt.Color
+     * (case-insensitive).
+     * (e.g., "black")</li>
+     * <li>RGBAlpha, as integers in the range 0-255, separated by commas
+     * (e.g., "123,45,6,128")</li>
+     * <li>RGBAlpha, as floats in the range 0.0-1.0, separated by commas
+     * (e.g., "0.123,0.45,0.6,.5")</li>
+     * <li>RGBAlpha, as hexadecimal strings following the '#' character
+     * (e.g., "#1234efab")</li>
+     * </ul>
+     *
+     * If the specified argument does not match any of these formats, a
+     * ParseException is thrown.
+     * @param arg the String to convert to a Color object.
+     * @return the Color specified by arg.
+     * @throws ParseException if arg cannot be interpreted as a Color as
+     * described above.
+     */
+    public Object parse(String arg) throws ParseException {
+        Color result = null;
+        if (arg.charAt(0) == '#') {
+            result = parseHexColor(arg);
+        } else if (arg.indexOf(".") >= 0) {
+            result = parseFloatTuple(arg);
+        } else if (arg.indexOf(",") >= 0) {
+            result = parseIntTuple(arg);
+        } else {
+            result = parseColorName(arg);
+        }
+        return (result);
+    }
+
+}
diff --git a/src/java/com/martiansoftware/jsap/stringparsers/DateStringParser.java b/src/java/com/martiansoftware/jsap/stringparsers/DateStringParser.java
new file mode 100644
index 0000000..b8cfbdc
--- /dev/null
+++ b/src/java/com/martiansoftware/jsap/stringparsers/DateStringParser.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2002-2004, Martian Software, Inc.
+ * This file is made available under the LGPL as described in the accompanying
+ * LICENSE.TXT file.
+ */
+
+package com.martiansoftware.jsap.stringparsers;
+
+import com.martiansoftware.jsap.ParseException;
+import com.martiansoftware.jsap.PropertyStringParser;
+
+import java.util.Date;
+import java.text.SimpleDateFormat;
+
+/**
+ * A {@link com.martiansoftware.jsap.StringParser} for parsing java.util.Date objects.  By default, arguments
+ * are parsed using the
+ * java.text.SimpleDateFormat for the default locale.  The format can be
+ * overridden using this StringParser's
+ * setProperties() method, supplying a java.util.Properties object with a
+ * property key named "format".
+ * The value associated with the "format" property is used to create a new
+ * java.text.SimpleDateFormat
+ * to parse the argument.
+ * 
+ * <p>A ParseException is thrown if a SimpleDateFormat cannot be constructed with
+ * the specified format, or if the SimpleDateFormat throws a
+ * java.text.ParseException during parsing.
+ * 
+ * <p>The SimpleDateFormat object is instantiated when an option referencing this
+ * DateStringParser is
+ * registered with a JSAP object.
+ *
+ * @author <a href="http://www.martiansoftware.com/contact.html">Marty Lamb</a>
+ * @see com.martiansoftware.jsap.StringParser
+ * @see java.util.Date
+ * @see java.text.SimpleDateFormat
+ */
+public class DateStringParser extends PropertyStringParser {
+
+    /**
+     * The SimpleDateFormat used to do the parsing.
+     */
+    private SimpleDateFormat format = null;
+
+    /** Returns a {@link DateStringParser}.
+	 * 
+	 * @return a {@link DateStringParser}.
+	 */
+    
+	public static DateStringParser getParser() {
+		return new DateStringParser();
+	}
+
+    /**
+     * Creates a new DateStringParser.
+     * @deprecated Use {@link #getParser()}.
+     */
+    public DateStringParser() {
+        super();
+    }
+
+    /**
+     * Instantiates the SimpleDateFormat to use for parsing.
+     * @throws ParseException if a SimpleDateFormat cannot be instantiated with
+     * the contents of the
+     * "format" property.
+     */
+    public void setUp() throws ParseException {
+        String formatString = this.getProperty("format");
+        if (formatString == null) {
+            format = new SimpleDateFormat();
+        } else {
+        	try {
+        		format = new SimpleDateFormat(formatString);
+        	}
+        	catch( RuntimeException e ) {
+        		throw new ParseException( e );
+        	}
+        }
+    }
+
+    /**
+     * Destroys the SimpleDateFormat used for parsing.
+     */
+    public void tearDown() {
+        format = null;
+    }
+
+    /**
+     * Parses the specified argument using either the java.text.SimpleDateFormat
+     * for the current locale
+     * (by default) or a java.text.SimpleDateFormat as defined by this
+     * PropertyStringParser's "format"
+     * property.
+     *
+     * If the specified argument cannot be parsed by the current format, a
+     * ParseException is thrown.
+     *
+     * @param arg the argument to convert to a Date.
+     * @return a Date as described above.
+     * @throws ParseException if the specified argument cannot be parsed by the
+     * current format..
+     */
+    public Object parse(String arg) throws ParseException {
+        Date result = null;
+        try {
+            result = format.parse(arg);
+        } catch (java.text.ParseException e) {
+            throw (
+                new ParseException(
+                    "Unable to convert '" + arg + "' to a Date.",
+                    e));
+        }
+        return (result);
+    }
+
+}
diff --git a/src/java/com/martiansoftware/jsap/stringparsers/DoubleStringParser.java b/src/java/com/martiansoftware/jsap/stringparsers/DoubleStringParser.java
new file mode 100644
index 0000000..81f88b8
--- /dev/null
+++ b/src/java/com/martiansoftware/jsap/stringparsers/DoubleStringParser.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2002-2004, Martian Software, Inc.
+ * This file is made available under the LGPL as described in the accompanying
+ * LICENSE.TXT file.
+ */
+
+package com.martiansoftware.jsap.stringparsers;
+
+import com.martiansoftware.jsap.StringParser;
+import com.martiansoftware.jsap.ParseException;
+
+/**
+ * A {@link com.martiansoftware.jsap.StringParser} for parsing Doubles.  The parse() method delegates the actual
+ * parsing to new Double(String).  If a NumberFormatException is thrown by new
+ * Double(String), it
+ * is encapsulated in a ParseException and re-thrown.
+ * @author <a href="http://www.martiansoftware.com/contact.html">Marty Lamb</a>
+ * @see com.martiansoftware.jsap.StringParser
+ * @see java.lang.Double
+ */
+public class DoubleStringParser extends StringParser {
+
+	private static final DoubleStringParser INSTANCE = new DoubleStringParser();	
+
+	/** Returns a {@link DoubleStringParser}.
+	 * 
+	 * <p>Convenient access to the only instance returned by
+	 * this method is available through
+	 * {@link com.martiansoftware.jsap.JSAP#DOUBLE_PARSER}.
+	 *  
+	 * @return a {@link DoubleStringParser}.
+	 */
+
+	public static DoubleStringParser getParser() {
+		return INSTANCE;
+	}
+
+	/**
+     * Creates a new DoubleStringParser.
+     * @deprecated Use {@link #getParser()} or, even better, {@link com.martiansoftware.jsap.JSAP#DOUBLE_PARSER}.
+     */
+    public DoubleStringParser() {
+        super();
+    }
+
+    /**
+     * Parses the specified argument into a Double.  This method simply
+     * delegates the actual
+     * parsing to new Double(String).  If a NumberFormatException is thrown by
+     * new Double(String), it
+     * is encapsulated in a ParseException and re-thrown.
+     *
+     * @param arg the argument to parse
+     * @return a Double object with the value contained in the specified
+     * argument.
+     * @throws ParseException if <code>new Double(arg)</code> throws a
+     * NumberFormatException.
+     * @see java.lang.Double
+     * @see com.martiansoftware.jsap.StringParser#parse(String)
+     */
+    public Object parse(String arg) throws ParseException {
+        Double result = null;
+        try {
+            result = new Double(arg);
+        } catch (NumberFormatException e) {
+            throw (
+                new ParseException(
+                    "Unable to convert '" + arg + "' to a Double.",
+                    e));
+        }
+        return (result);
+    }
+}
diff --git a/src/java/com/martiansoftware/jsap/stringparsers/EnumeratedStringParser.java b/src/java/com/martiansoftware/jsap/stringparsers/EnumeratedStringParser.java
new file mode 100644
index 0000000..6e17328
--- /dev/null
+++ b/src/java/com/martiansoftware/jsap/stringparsers/EnumeratedStringParser.java
@@ -0,0 +1,220 @@
+/*
+ * Copyright (c) 2002-2004, Martian Software, Inc.
+ * This file is made available under the LGPL as described in the accompanying
+ * LICENSE.TXT file.
+ */
+
+package com.martiansoftware.jsap.stringparsers;
+
+import java.util.Arrays;
+import java.util.StringTokenizer;
+import com.martiansoftware.jsap.StringParser;
+import com.martiansoftware.jsap.ParseException;
+
+/**
+ * A {@link com.martiansoftware.jsap.StringParser} that enforces a limited set of String options for its
+ * values.
+ * These values are provided in the constructor together with one or two parameters
+ * that control the processing of these values.
+ * 
+ * <p>EnumeratedStringParser was generously 
+ * contributed to JSAP by Klaus-Peter Berg of Siemens AG, Munich, Germany.
+ * @since 1.03
+ * @author  Klaus-Peter Berg, Siemens AG, Munich, Germany
+ * @version 2.0
+ */
+public class EnumeratedStringParser extends StringParser {
+
+	/**
+	 * char used to separate enumerated values when they are supplied 
+	 * to the constructor
+	 */
+	public static final char CONSTRUCTOR_VALUE_SEPARATOR = ';';
+	
+
+	private String[] validOptionValuesArray = null;
+	private boolean isCaseSensitive;
+	private boolean checkOptionChars;
+
+	/**
+	 * Returns an EnumeratedParameterParser.
+	 * 
+	 * @param validOptionValues a string that contains valid values for an option 
+	 *        in the format "value_1;value_2;..;value_n"; spaces between values are allowed 
+	 *        to make things more readable, e.g., "value_1; value_2";
+	 *        option values have to be constructed using Java identifier characters
+	 *        if the checkOptionChars parameter tells the parser to do this.
+	 * @param caseSensitive tells the parser whether the option value is case sensitive
+	 * @param checkOptionChars tells the parser whether to check for Java identifier conformant characters.
+	 * @throws IllegalArgumentException if the option value string has wrong format
+	 *         or is empty
+	 */
+	public static EnumeratedStringParser getParser( String validOptionValues, boolean caseSensitive, boolean checkOptionChars ) throws IllegalArgumentException {
+		return new EnumeratedStringParser( validOptionValues, caseSensitive, checkOptionChars );
+	}
+
+	/**
+	 * Constructs a new instance of EnumeratedParameterParser.
+	 * 
+	 * @param validOptionValues a string that contains valid values for an option 
+	 *        in the format "value_1;value_2;..;value_n"; spaces between values are allowed 
+	 *        to make things more readable, e.g., "value_1; value_2";
+	 *        option values have to be constructed using Java identifier characters
+	 *        if the checkOptionChars parameter tells the parser to do this.
+	 * @param caseSensitive tells the parser whether the option value is case sensitive
+	 * @param checkOptionChars tells the parser whether to check for Java identifier conformant characters.
+	 * @throws IllegalArgumentException if the option value string has wrong format
+	 *         or is empty
+	 * @deprecated use {@link #getParser(String, boolean, boolean)}.
+	 */
+	public EnumeratedStringParser(String validOptionValues, boolean caseSensitive, boolean checkOptionChars) throws IllegalArgumentException {
+		if (validOptionValues == null) {
+			throw new IllegalArgumentException("EnumeratedStringParser validOptions parameter is null");
+		}
+		if (validOptionValues.length() == 0) {
+			throw new IllegalArgumentException("EnumeratedStringParser validOptions parameter is empty");
+		}
+
+		this.isCaseSensitive = caseSensitive;
+		this.checkOptionChars = checkOptionChars;
+		if (validOptionValues.indexOf(CONSTRUCTOR_VALUE_SEPARATOR) == -1) {
+			validOptionValuesArray = new String[1];	// we assume to have only one valid option value
+			if (isValidOptionName(validOptionValues)) {
+				validOptionValuesArray[0] = validOptionValues;
+			}
+			else {
+				throw new IllegalArgumentException("Wrong character in EnumeratedStringParser option value: "+validOptionValues
+					+ "\nsee EnumeratedStringParser javadoc for more information");
+			}
+		}
+		else {
+			StringTokenizer stok = new StringTokenizer(validOptionValues, ";");
+			validOptionValuesArray = new String[stok.countTokens()];
+			int i = 0;
+			while (stok.hasMoreTokens()) {
+				String value = stok.nextToken().trim();
+				if (!isCaseSensitive) {
+					value = value.toLowerCase();
+				}
+				if (isValidOptionName(value)) {
+					validOptionValuesArray[i++] = value;
+				}
+				else {
+					throw new IllegalArgumentException("Wrong character in EnumeratedStringParser option value: "+value
+						+ "\nsee EnumeratedStringParser javadoc for more information");
+				}               
+			}
+		}
+
+	}
+
+	/**
+	 * Returns an EnumeratedParameterParser with parameter "checkOptionChars" set to true.
+	 * 
+	 * @param validOptionValues a string that contains valid values for an option 
+	 *        in the format "value_1;value_2;..;value_n"; spaces between values are allowed 
+	 *        to make things more readable, e.g., "value_1; value_2";
+	 *        option values have to be constructed using Java identifier characters.
+	 * @param caseSensitive tells the parser wether the option value is case sensitive
+	 * @throws IllegalArgumentException if the option value string has wrong format
+	 *         or is empty
+	 */
+	public static EnumeratedStringParser getParser(String validOptionValues, boolean caseSensitive) throws IllegalArgumentException {
+		return new EnumeratedStringParser( validOptionValues, caseSensitive, true );
+	}
+
+	/**
+	 * Constructs a new instance of EnumeratedStringParser.
+	 * @deprecated use {@link #getParser(String, boolean)}.
+	 * 
+	 */
+	public EnumeratedStringParser(String validOptionValues, boolean caseSensitive) throws IllegalArgumentException {
+		this(validOptionValues, caseSensitive, true);
+	}
+
+
+	/**
+	 * Returns an EnumeratedParameterParser with parameter
+	 * "caseSensitive" set to false and "checkOptionChars" set to true.
+	 * All command line arguments for this parser and the values provided
+	 * by the user in the returned parser are converted to lower case.
+	 * 
+	 * @param validOptionValues a string that contains valid values for an option 
+	 *        in the format "value_1;value_2;..;value_n"; spaces between values are allowed 
+	 *        to make things more readable, e.g., "value_1; value_2";
+	 *        option values have to be constructed using Java identifier characters.
+	 * @throws IllegalArgumentException if the option value string has wrong format
+	 *         or is empty
+	 */
+	public static EnumeratedStringParser getParser(String validOptionValues) throws IllegalArgumentException {
+		return new EnumeratedStringParser( validOptionValues, false, true );
+	}
+
+
+	/**
+	 * Constructs a new instance of EnumeratedStringParser.
+	 * @deprecated use {@link #getParser(String)}.
+	 */
+	public EnumeratedStringParser(String validOptionValues) throws IllegalArgumentException {
+		this(validOptionValues, false, true);
+	}
+
+	/**
+	 * Parses the specified argument, making sure it matches one of the valid
+	 * options supplied to its constructor.  
+	 * If the specified argument is not a valid option, 
+	 * a ParseException is thrown.
+	 * 
+	 * @param arg the argument to parse
+	 * @return the String resulting from the parsed argument.
+	 * @throws ParseException if the specified argument cannot be parsed.
+	 */
+	public Object parse(String arg) throws ParseException {
+		if (arg == null) {
+			return null;
+		}
+		if (!isCaseSensitive) {
+			arg = arg.toLowerCase();
+		}
+		if (!isValidOptionName(arg)) {
+			throw new ParseException("Wrong character in command line option value for enumerated option: '" + arg + "'"
+				+"\nallowed are alphanumeric characters + '$' and '_' sign only",
+				new IllegalArgumentException());
+		}
+		// we cannot use Arrays.binarySearch() because strings cannot be 
+		// sorted according to the required natural order!
+		if (Arrays.asList(validOptionValuesArray).contains(arg)) {
+			return arg;
+		}
+		else {
+			throw new ParseException("Option has wrong value '" + arg + "'"
+				+ "; valid values are: "+Arrays.asList(validOptionValuesArray), new IllegalArgumentException());
+		}
+	}
+
+	/**
+	 * Check for valid enumerated option values ("names").
+	 * Allowed are Java identifier chars, i.e., alphanumeric chars + '$' + _' signs.
+	 * If you need a different validation scheme you can override this method
+	 * when subclassig EnumeratedStringParser.
+	 * 
+	 * @param name   the option value to check
+	 * 
+	 * @return true, if the value contains only valid chars, false otherwise
+	 */
+	protected boolean isValidOptionName(String name) {
+		if (!checkOptionChars) {
+			return true;
+		}
+		for (int i=0; i<name.length(); i++) {
+			char c = name.charAt(i);
+			if (Character.isJavaIdentifierPart(c)) {
+				continue;
+			}
+			else {
+				return false;
+			}
+		}
+		return true;
+	}
+}
diff --git a/src/java/com/martiansoftware/jsap/stringparsers/FileStringParser.java b/src/java/com/martiansoftware/jsap/stringparsers/FileStringParser.java
new file mode 100644
index 0000000..bb2b515
--- /dev/null
+++ b/src/java/com/martiansoftware/jsap/stringparsers/FileStringParser.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 2002-2004, Martian Software, Inc.
+ * This file is made available under the LGPL as described in the accompanying
+ * LICENSE.TXT file.
+ */
+
+package com.martiansoftware.jsap.stringparsers;
+
+import com.martiansoftware.jsap.JSAP;
+import com.martiansoftware.jsap.PropertyStringParser;
+import com.martiansoftware.jsap.ParseException;
+import java.io.File;
+
+/**
+ * A StringParser for parsing {@link File} objects.  The parse() method
+ * delegates the actual
+ * parsing to <code>new File(String)</code>.  If <code>new File(String)</code>
+ * throws a NullPointerException, it is encapsulated in a ParseException and
+ * re-thrown.
+ *
+ * @author <a href="http://www.martiansoftware.com/contact.html">Marty Lamb</a>
+ * @author  Edward Glen (edward at glencomm.com) (modified URLStringParser)
+ * @author  Eric Sword (made setters return "this", fixed bug triggered when
+ *          file does not exist)
+ * @since 1.4
+ * @see com.martiansoftware.jsap.StringParser
+ * @see java.net.URL
+ */
+public class FileStringParser extends PropertyStringParser {
+
+	public static final String MUSTBEFILE = "mustBeFile";
+	public static final String MUSTBEDIRECTORY = "mustBeDirectory";
+	public static final String MUSTEXIST = "mustExist";
+
+	private boolean mustExist = false;
+	private boolean mustBeDirectory = false;
+	private boolean mustBeFile = false;
+
+	/** Creates a new FileStringParser.
+	 * @deprecated use {@link #getParser()}.
+	 */
+	public FileStringParser() {
+		super();
+	}
+
+	/** Returns a new {@link FileStringParser}.
+	 * @return a new {@link FileStringParser}.
+	 */
+	public static FileStringParser getParser() {
+		return new FileStringParser();
+	}
+
+	public void setUp() throws ParseException {
+		BooleanStringParser bool = JSAP.BOOLEAN_PARSER;
+		setMustExist(((Boolean) bool.parse(getProperty(MUSTEXIST, (new Boolean(mustExist)).toString()))).booleanValue());
+		setMustBeDirectory(((Boolean) bool.parse(getProperty(MUSTBEDIRECTORY, (new Boolean(mustBeDirectory)).toString()))).booleanValue());
+		setMustBeFile(((Boolean) bool.parse(getProperty(MUSTBEFILE, (new Boolean(mustBeFile)).toString()))).booleanValue());
+	}
+
+	public FileStringParser setMustBeDirectory(boolean mustBeDirectory) {
+		this.mustBeDirectory = mustBeDirectory;
+        return this;
+	}
+
+	public FileStringParser setMustBeFile(boolean mustBeFile) {
+		this.mustBeFile = mustBeFile;
+        return this;
+	}
+
+	public FileStringParser setMustExist(boolean mustExist) {
+		this.mustExist = mustExist;
+        return this;
+	}
+
+	public boolean mustBeDirectory() {
+		return (mustBeDirectory);
+	}
+
+	public boolean mustBeFile() {
+		return (mustBeFile);
+	}
+
+	public boolean mustExist() {
+		return (mustExist);
+	}
+
+	public void tearDown() {
+	}
+
+	/**
+	 * Parses the specified argument into a File.  This method delegates the
+	 * actual
+	 * parsing to <code>new File(arg)</code>.  If <code>new File(arg)</code>
+	 * throws a NullPointerException, it is encapsulated in a ParseException
+	 * and re-thrown.
+	 *
+	 * @param arg the argument to parse
+	 * @return a File as specified by arg.
+	 * @throws ParseException if <code>new File(arg)</code> throws a
+	 * NullPointerException.
+	 * @see java.io File
+	 * @see com.martiansoftware.jsap.StringParser#parse(String)
+	 */
+	public Object parse(String arg) throws ParseException {
+		File result = null;
+		try {
+			result = new File(arg);
+
+            if (mustExist() && !result.exists()) {
+                throw (new ParseException(result + " does not exist."));
+            }
+			if (mustBeDirectory() && result.exists() && !result.isDirectory()) {
+				throw (new ParseException(result + " is not a directory."));
+			}
+			if (mustBeFile() && result.exists() && result.isDirectory()) {
+				throw (new ParseException(result + " is not a file."));
+			}
+		} catch (NullPointerException e) {
+			throw (
+				new ParseException(
+					"No File given to parse",
+					e));
+		}
+		return (result);
+	}
+}
diff --git a/src/java/com/martiansoftware/jsap/stringparsers/FloatStringParser.java b/src/java/com/martiansoftware/jsap/stringparsers/FloatStringParser.java
new file mode 100644
index 0000000..d338247
--- /dev/null
+++ b/src/java/com/martiansoftware/jsap/stringparsers/FloatStringParser.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2002-2004, Martian Software, Inc.
+ * This file is made available under the LGPL as described in the accompanying
+ * LICENSE.TXT file.
+ */
+
+package com.martiansoftware.jsap.stringparsers;
+
+import com.martiansoftware.jsap.StringParser;
+import com.martiansoftware.jsap.ParseException;
+
+/**
+ * A {@link com.martiansoftware.jsap.StringParser} for parsing Floats.  The <code>parse()</code> method delegates
+ * the actual
+ * parsing to <code>new Float(String)</code>.  If <code>new Float(String)</code>
+ * throws a NumberFormatException, it
+ * is encapsulated in a ParseException and re-thrown.
+ *
+ * @author <a href="http://www.martiansoftware.com/contact.html">Marty Lamb</a>
+ * @see com.martiansoftware.jsap.StringParser
+ * @see java.lang.Float
+ */
+public class FloatStringParser extends StringParser {
+
+	private static final FloatStringParser INSTANCE = new FloatStringParser();	
+
+	/** Returns a {@link FloatStringParser}.
+	 * 
+	 * <p>Convenient access to the only instance returned by
+	 * this method is available through
+	 * {@link com.martiansoftware.jsap.JSAP#FLOAT_PARSER}.
+	 *  
+	 * @return a {@link FloatStringParser}.
+	 */
+
+    public static FloatStringParser getParser() {
+		return INSTANCE;
+	}
+
+	/**
+     * Creates a new FloatStringParser.
+     * @deprecated Use {@link #getParser()} or, even better, {@link com.martiansoftware.jsap.JSAP#FLOAT_PARSER}.
+     */
+    public FloatStringParser() {
+        super();
+    }
+
+    /**
+     * Parses the specified argument into a Float.  This method delegates the
+     * actual
+     * parsing to new Float(String).  If new Float(String) throws a
+     * NumberFormatException, it
+     * is encapsulated in a ParseException and re-thrown.
+     *
+     * @param arg the argument to parse
+     * @return a Float object with the value contained in the specified
+     * argument.
+     * @throws ParseException if <code>new Float(arg)</code> throws a
+     * NumberFormatException.
+     * @see java.lang.Float
+     * @see com.martiansoftware.jsap.StringParser#parse(String)
+     */
+    public Object parse(String arg) throws ParseException {
+        Float result = null;
+        try {
+            result = new Float(arg);
+        } catch (NumberFormatException e) {
+            throw (
+                new ParseException(
+                    "Unable to convert '" + arg + "' to a Float.",
+                    e));
+        }
+        return (result);
+    }
+}
diff --git a/src/java/com/martiansoftware/jsap/stringparsers/ForNameStringParser.java b/src/java/com/martiansoftware/jsap/stringparsers/ForNameStringParser.java
new file mode 100644
index 0000000..300e7d7
--- /dev/null
+++ b/src/java/com/martiansoftware/jsap/stringparsers/ForNameStringParser.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2002-2004, Martian Software, Inc.
+ * This file is made available under the LGPL as described in the accompanying
+ * LICENSE.TXT file.
+ */
+
+package com.martiansoftware.jsap.stringparsers;
+
+import java.lang.reflect.Method;
+
+import com.martiansoftware.jsap.ParseException;
+import com.martiansoftware.jsap.StringParser;
+
+
+/** A {@link com.martiansoftware.jsap.StringParser} that passes the
+ * argument to a static method of signature <code>forName(String)</code> of a specified class.
+ * 
+ * <P>Note that, for instance, this parser can be used with {@link java.lang.Class} (resulting in a 
+ * string parser identical to {@link com.martiansoftware.jsap.stringparsers.ClassStringParser}),
+ * but also {@link java.nio.charset.Charset}, and more generally, any class using the <code>forName(String)</code>
+ * convention.
+ *
+ * @author Sebastiano Vigna
+ */
+
+public class ForNameStringParser extends StringParser {
+
+	/** The class array describing the parameters (a string) of <code>forName</code>. */
+	private final static Class[] PARAMETERS = new Class[] { String.class };
+	
+	/** The class given to the constructor. */
+	private final Class klass;
+	/** The <code>forName(String)</code> static method of {@link #klass}. */
+	private final Method forName;
+
+	private ForNameStringParser( final Class klass ) throws SecurityException, NoSuchMethodException {
+		this.klass = klass;
+		forName = klass.getMethod( "forName", PARAMETERS );
+	}
+	
+	/** Returns a class <code>forName()</code> string parser.
+	 *
+	 * <p>When required to parse an argument, the returned string parser will return the
+	 * object obtain by means of a call to a static method of <code>klass</code> of signature
+	 * <code>forName(String)</code>.
+	 *  
+	 * @param klass a class with a static method of signature <code>forName(String)</code>.
+	 */
+	
+	public static ForNameStringParser getParser( final Class klass ) throws SecurityException, NoSuchMethodException {
+		return new ForNameStringParser( klass );
+	}
+	
+	public Object parse( String arg ) throws ParseException {
+		try {
+			return forName.invoke( klass, new Object[] { arg } );
+		}
+		catch ( Exception e ) {
+			throw new ParseException ( e );
+		}
+	}
+}
diff --git a/src/java/com/martiansoftware/jsap/stringparsers/InetAddressStringParser.java b/src/java/com/martiansoftware/jsap/stringparsers/InetAddressStringParser.java
new file mode 100644
index 0000000..9340845
--- /dev/null
+++ b/src/java/com/martiansoftware/jsap/stringparsers/InetAddressStringParser.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2002-2004, Martian Software, Inc.
+ * This file is made available under the LGPL as described in the accompanying
+ * LICENSE.TXT file.
+ */
+
+package com.martiansoftware.jsap.stringparsers;
+
+import com.martiansoftware.jsap.StringParser;
+import com.martiansoftware.jsap.ParseException;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+/**
+ * A {@link com.martiansoftware.jsap.StringParser} for parsing java.net.InetAddress objects.  The parse() method
+ * delegates the actual
+ * parsing to <code>InetAddress.getByName(String)</code>.  If
+ * <code>InetAddress.getByName()</code>
+ * throws an UnknownHostException, it is encapsulated in a ParseException and
+ * re-thrown.
+ *
+ * @author <a href="http://www.martiansoftware.com/contact.html">Marty Lamb</a>
+ * @see com.martiansoftware.jsap.StringParser
+ * @see java.net.InetAddress
+ */
+public class InetAddressStringParser extends StringParser {
+
+	private static final InetAddressStringParser INSTANCE = new InetAddressStringParser();	
+
+	/** Returns a {@link InetAddressStringParser}.
+	 * 
+	 * <p>Convenient access to the only instance returned by
+	 * this method is available through
+	 * {@link com.martiansoftware.jsap.JSAP#INETADDRESS_PARSER}.
+	 *  
+	 * @return a {@link InetAddressStringParser}.
+	 */
+
+    public static InetAddressStringParser getParser() {
+		return INSTANCE;
+	}
+
+	/**
+     * Creates a new InetAddressStringParser.
+     * @deprecated Use {@link #getParser()} or, even better, {@link com.martiansoftware.jsap.JSAP#INETADDRESS_PARSER}.
+     */
+    public InetAddressStringParser() {
+        super();
+    }
+
+    /**
+     * Parses the specified argument into an InetAddress.  This method
+     * delegates the actual
+     * parsing to <code>InetAddress.getByName(arg)</code>.  If
+     * <code>InetAddress.getByName(arg)</code>
+     * throws an UnknownHostException, it is encapsulated in a ParseException
+     * and re-thrown.
+     *
+     * @param arg the argument to parse
+     * @return an InetAddress object representing the specified address.
+     * @throws ParseException if <code>InetAddress.getByName(arg)</code> throws
+     * an UnknownHostException.
+     * @see java.net InetAddress
+     * @see com.martiansoftware.jsap.StringParser#parse(String)
+     */
+    public Object parse(String arg) throws ParseException {
+        InetAddress result = null;
+        try {
+            result = InetAddress.getByName(arg);
+        } catch (UnknownHostException e) {
+            throw (new ParseException("Unknown host: " + arg, e));
+        }
+        return (result);
+    }
+}
diff --git a/src/java/com/martiansoftware/jsap/stringparsers/IntSizeStringParser.java b/src/java/com/martiansoftware/jsap/stringparsers/IntSizeStringParser.java
new file mode 100644
index 0000000..6305a9e
--- /dev/null
+++ b/src/java/com/martiansoftware/jsap/stringparsers/IntSizeStringParser.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2002-2004, Martian Software, Inc.
+ * This file is made available under the LGPL as described in the accompanying
+ * LICENSE.TXT file.
+ */
+
+package com.martiansoftware.jsap.stringparsers;
+
+import com.martiansoftware.jsap.ParseException;
+import com.martiansoftware.jsap.StringParser;
+
+/** A {@link com.martiansoftware.jsap.StringParser} that works like {@link LongSizeStringParser}, but
+ * additionally checks that the result is not larger than {@link Integer#MAX_VALUE}.
+ * 
+ * @author Sebastiano Vigna
+ */
+
+public class IntSizeStringParser extends StringParser {
+
+	/** The only instance of this parser. Aliased to <code>JSAP.INT_SIZE_PARSER</code>. */
+	final static IntSizeStringParser INSTANCE = new IntSizeStringParser();
+	
+	private IntSizeStringParser() {}
+	
+	
+	/** Returns the only instance of an {@link IntSizeStringParser}.
+	 * 
+	 * <p>Convenient access to the only instance returned by
+	 * this method is available through
+	 * {@link com.martiansoftware.jsap.JSAP#INTSIZE_PARSER}.
+	 *  
+	 * @return the only instance of an {@link IntSizeStringParser}.
+	 */
+	public static IntSizeStringParser getParser() {
+		return INSTANCE;
+	}
+
+	public Object parse( String arg ) throws ParseException {
+		final long size = LongSizeStringParser.parseSize( arg );
+		if ( size > Integer.MAX_VALUE ) throw new ParseException( "Integer size '" + arg + "' is too big." );
+		return new Integer( (int)size );
+	}
+}
diff --git a/src/java/com/martiansoftware/jsap/stringparsers/IntegerStringParser.java b/src/java/com/martiansoftware/jsap/stringparsers/IntegerStringParser.java
new file mode 100644
index 0000000..ee815f8
--- /dev/null
+++ b/src/java/com/martiansoftware/jsap/stringparsers/IntegerStringParser.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2002-2004, Martian Software, Inc.
+ * This file is made available under the LGPL as described in the accompanying
+ * LICENSE.TXT file.
+ */
+
+package com.martiansoftware.jsap.stringparsers;
+
+import com.martiansoftware.jsap.StringParser;
+import com.martiansoftware.jsap.ParseException;
+
+/**
+ * A {@link com.martiansoftware.jsap.StringParser} for parsing Integers.  The parse() method delegates the actual
+ * parsing to Integer.decode(String).
+ * @author <a href="http://www.martiansoftware.com/contact.html">Marty Lamb</a>
+ * @see com.martiansoftware.jsap.StringParser
+ * @see java.lang.Integer
+ */
+public class IntegerStringParser extends StringParser {
+	
+	private static final IntegerStringParser INSTANCE = new IntegerStringParser();	
+
+	/** Returns a {@link IntegerStringParser}.
+	 * 
+	 * <p>Convenient access to the only instance returned by
+	 * this method is available through
+	 * {@link com.martiansoftware.jsap.JSAP#INTEGER_PARSER}.
+	 *  
+	 * @return a {@link IntegerStringParser}.
+	 */
+    public static IntegerStringParser getParser() {
+		return new IntegerStringParser();
+	}
+
+	/**
+     * Creates a new IntegerStringParser.
+     * @deprecated Use {@link #getParser()} or, even better, {@link com.martiansoftware.jsap.JSAP#INTEGER_PARSER}.
+     */
+    public IntegerStringParser() {
+        super();
+    }
+
+    /**
+     * Parses the specified argument into an Integer.  This method delegates
+     * the parsing to <code>Integer.decode(arg)</code>.  If
+     * <code>Integer.decode()</code> throws a
+     * NumberFormatException, it is encapsulated into a ParseException and
+     * re-thrown.
+     *
+     * @param arg the argument to parse
+     * @return an Integer object with the value contained in the specified
+     * argument.
+     * @throws ParseException if <code>Integer.decode(arg)</code> throws a
+     * NumberFormatException.
+     * @see java.lang.Integer
+     * @see com.martiansoftware.jsap.StringParser#parse(String)
+     */
+    public Object parse(String arg) throws ParseException {
+        Integer result = null;
+        try {
+            result = Integer.decode(arg);
+        } catch (NumberFormatException nfe) {
+            throw (
+                new ParseException(
+                    "Unable to convert '" + arg + "' to an Integer.",
+                    nfe));
+        }
+        return (result);
+    }
+}
diff --git a/src/java/com/martiansoftware/jsap/stringparsers/LongSizeStringParser.java b/src/java/com/martiansoftware/jsap/stringparsers/LongSizeStringParser.java
new file mode 100644
index 0000000..ea7c8df
--- /dev/null
+++ b/src/java/com/martiansoftware/jsap/stringparsers/LongSizeStringParser.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 2002-2004, Martian Software, Inc.
+ * This file is made available under the LGPL as described in the accompanying
+ * LICENSE.TXT file.
+ */
+
+package com.martiansoftware.jsap.stringparsers;
+
+import java.math.BigInteger;
+import java.util.HashMap;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import com.martiansoftware.jsap.ParseException;
+import com.martiansoftware.jsap.StringParser;
+
+/** A {@link com.martiansoftware.jsap.StringParser} that lets the user specify sizes with an optional unit.
+ * 
+ * <P>This parser will parse its argument using {@link #parseSize(CharSequence)}.
+ * 
+ * @author Sebastiano Vigna
+ * @see com.martiansoftware.jsap.stringparsers.IntSizeStringParser
+ */
+
+public class LongSizeStringParser extends StringParser {
+
+	/** The regular expression used to parse the size specification. */
+	private static final Pattern PARSE_SIZE_REGEX = Pattern.compile( "((#|0x|0X)?[0-9A-Fa-f]+)([KMGTP]i?)?" );
+
+	/** The big integer used to check that the result is smaller than {@link Long#MAX_VALUE}. */
+	private static final BigInteger LONG_MAX_VALUE = new BigInteger( Long.toString( Long.MAX_VALUE ) );
+	
+	/** A map from units to their size. */
+	private static final HashMap UNIT2SIZE = new HashMap();
+
+	/** The only instance of this parser. Aliased to <code>JSAP.LONG_SIZE_PARSER</code>. */
+	final static LongSizeStringParser INSTANCE = new LongSizeStringParser();
+	
+	private LongSizeStringParser() {}
+	
+	/** Returns the only instance of a {@link LongSizeStringParser}.
+	 * 
+	 * <p>Convenient access to the only instance returned by
+	 * this method is available through
+	 * {@link com.martiansoftware.jsap.JSAP#LONGSIZE_PARSER}.
+	 *  
+	 * @return the only instance of a {@link LongSizeStringParser}.
+	 */
+	public static LongSizeStringParser getParser() {
+		return INSTANCE;
+	}
+	
+	
+	public Object parse( String arg ) throws ParseException {
+		return new Long( parseSize( arg ) );
+	}
+	
+	static {
+		UNIT2SIZE.put( "K", new Long( (long)1E3 ) );
+		UNIT2SIZE.put( "M", new Long( (long)1E6 ) );
+		UNIT2SIZE.put( "G", new Long( (long)1E9 ) );
+		UNIT2SIZE.put( "T", new Long( (long)1E12 ) );
+		UNIT2SIZE.put( "P", new Long( (long)1E15 ) );
+		UNIT2SIZE.put( "Ki", new Long( 1L << 10 ) );
+		UNIT2SIZE.put( "Mi", new Long( 1L << 20 ) );
+		UNIT2SIZE.put( "Gi", new Long( 1L << 30 ) );
+		UNIT2SIZE.put( "Ti", new Long( 1L << 40 ) );
+		UNIT2SIZE.put( "Pi", new Long( 1L << 50 ) );
+	}
+
+	/** Parses a size specified using units (e.g., K, Ki, M, Mi,…).
+	 * 
+	 * <P>The argument must be in the form <var>number</var> [<var>unit</var>] (with
+	 * no space inbetween). <var>number</var> is anything accepted by {@link Long#decode(java.lang.String)},
+	 * which allows, besides decimal numbers, hexadecimal numbers prefixed by <code>0x</code>, <code>0X</code> or <code>#</code>,
+	 * and octal numbers prefixed by <code>0</code>.
+	 * <var>unit</var> may be one of K (10<sup>3</sup>), Ki (2<sup>10</sup>), M (10<sup>6</sup>), Mi (2<sup>20</sup>), 
+	 * G (10<sup>9</sup>), Gi (2<sup>30</sup>), T (10<sup>12</sup>), Ti (2<sup>40</sup>), 
+	 * P (10<sup>15</sup>), Pi (2<sup>50</sup>). Thus, for instance, <samp>1Ki</samp> is
+	 * 1024, whereas <samp>9M</samp> is nine millions and <code>0x10Pi</code> is 18014398509481984. 
+	 * Note that in the number part case does not matter, but in the unit part it does.
+	 * 
+	 * @param s a size specified as above.
+	 * @return the corresponding unitless size.
+	 * @throws ParseException if <code>s</code> is malformed, <var>number</var> is negative or
+	 * the resulting size is larger than {@link Long#MAX_VALUE}.
+	 */
+		
+	public static long parseSize( final CharSequence s ) throws ParseException {
+		final Matcher m = PARSE_SIZE_REGEX.matcher( s );
+		if ( ! m.matches() ) throw new ParseException( "Invalid size specification '" + s + "'." );
+		
+		final String unit = m.group( 3 );
+
+		BigInteger unitSize = BigInteger.ONE;
+
+		if ( unit != null ) {
+			Long unitSizeObj = (Long)UNIT2SIZE.get( unit );
+			if ( unitSizeObj == null ) throw new ParseException( "Invalid unit specification '" + unit + "'." );
+			unitSize = new BigInteger( unitSizeObj.toString() );
+		}
+
+		final String number = m.group( 1 );
+		final Long size;
+
+		try {
+			size = Long.decode( number );
+			if ( size.longValue() < 0 ) throw new ParseException( "Sizes cannot be negative." );
+		}
+		catch( NumberFormatException e ) {
+			throw new ParseException( "Invalid number '" + number + "'." );
+		}
+		
+		BigInteger result = new BigInteger( size.toString() ).multiply( unitSize );
+		
+		if ( result.compareTo( LONG_MAX_VALUE ) > 0 ) throw new ParseException( "Size '" + s + "' is too big." );
+		
+		return Long.parseLong( result.toString() );
+	}
+}
diff --git a/src/java/com/martiansoftware/jsap/stringparsers/LongStringParser.java b/src/java/com/martiansoftware/jsap/stringparsers/LongStringParser.java
new file mode 100644
index 0000000..d2b9b25
--- /dev/null
+++ b/src/java/com/martiansoftware/jsap/stringparsers/LongStringParser.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2002-2004, Martian Software, Inc.
+ * This file is made available under the LGPL as described in the accompanying
+ * LICENSE.TXT file.
+ */
+
+package com.martiansoftware.jsap.stringparsers;
+
+import com.martiansoftware.jsap.StringParser;
+import com.martiansoftware.jsap.ParseException;
+
+/**
+ * A {@link com.martiansoftware.jsap.StringParser} for parsing Longs.  The parse() method delegates the actual
+ * parsing to <code>Long.decode(String)</code>.
+ * @author <a href="http://www.martiansoftware.com/contact.html">Marty Lamb</a>
+ * @see com.martiansoftware.jsap.StringParser
+ * @see java.lang.Long
+ */
+public class LongStringParser extends StringParser {
+	
+	private static final LongStringParser INSTANCE = new LongStringParser();	
+
+	/** Returns a {@link LongStringParser}.
+	 * 
+	 * <p>Convenient access to the only instance returned by
+	 * this method is available through
+	 * {@link com.martiansoftware.jsap.JSAP#LONG_PARSER}.
+	 *  
+	 * @return a {@link LongStringParser}.
+	 */
+    public static LongStringParser getParser() {
+		return INSTANCE;
+	}
+
+	/**
+     * Creates a new LongStringParser.
+     * @deprecated Use {@link #getParser()} or, even better, {@link com.martiansoftware.jsap.JSAP#LONG_PARSER}.
+     */
+    public LongStringParser() {
+        super();
+    }
+
+    /**
+     * Parses the specified argument into a Long.  This method delegates
+     * the parsing to <code>Long.decode(arg)</code>.  If
+     * <code>Long.decode()</code> throws a
+     * NumberFormatException, it is encapsulated into a ParseException and
+     * re-thrown.
+     *
+     * @param arg the argument to parse
+     * @return a Long object with the value contained in the specified argument.
+     * @throws ParseException if <code>Long.decode(arg)</code> throws a
+     * NumberFormatException.
+     * @see java.lang.Long
+     * @see com.martiansoftware.jsap.StringParser#parse(String)
+     */
+    public Object parse(String arg) throws ParseException {
+        Long result = null;
+        try {
+            result = Long.decode(arg);
+        } catch (NumberFormatException e) {
+            throw (
+                new ParseException(
+                    "Unable to convert '" + arg + "' to a Long.",
+                    e));
+        }
+        return (result);
+    }
+}
diff --git a/src/java/com/martiansoftware/jsap/stringparsers/PackageStringParser.java b/src/java/com/martiansoftware/jsap/stringparsers/PackageStringParser.java
new file mode 100644
index 0000000..233b3c0
--- /dev/null
+++ b/src/java/com/martiansoftware/jsap/stringparsers/PackageStringParser.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2002-2004, Martian Software, Inc.
+ * This file is made available under the LGPL as described in the accompanying
+ * LICENSE.TXT file.
+ */
+
+package com.martiansoftware.jsap.stringparsers;
+
+import com.martiansoftware.jsap.StringParser;
+import com.martiansoftware.jsap.ParseException;
+
+/**
+ * A {@link com.martiansoftware.jsap.StringParser} for parsing Packages.  The parse() method delegates the actual
+ * parsing to <code>Package.getPackage(String)</code>, and returns the resulting
+ * Package object.
+ * If <code>Package.getPackage()</code> returns null, a ParseException is
+ * thrown.
+ * @author <a href="http://www.martiansoftware.com/contact.html">Marty Lamb</a>
+ * @see com.martiansoftware.jsap.StringParser
+ * @see java.lang.Package
+ */
+public class PackageStringParser extends StringParser {
+	
+	private static final PackageStringParser INSTANCE = new PackageStringParser();	
+
+	/** Returns a {@link PackageStringParser}.
+	 * 
+	 * <p>Convenient access to the only instance returned by
+	 * this method is available through
+	 * {@link com.martiansoftware.jsap.JSAP#PACKAGE_PARSER}.
+	 *  
+	 * @return a {@link PackageStringParser}.
+	 */
+    public static PackageStringParser getParser() {
+		return INSTANCE;
+	}
+
+	/**
+     * Creates a new PackageStringParser
+     * @deprecated Use {@link #getParser()} or, even better, {@link com.martiansoftware.jsap.JSAP#PACKAGE_PARSER}.
+     */
+    public PackageStringParser() {
+        super();
+    }
+
+    /**
+     * Parses the specified argument into a Package object.  This method
+     * delegates the
+     * parsing to <code>Package.getPackage(String)</code>, and returns the
+     * resulting Package object.
+     * If <code>Package.getPackage()</code> returns null, a ParseException is
+     * thrown.
+     *
+     * @param arg the argument to parse
+     * @return a Package object representing the specified package.
+     * @throws ParseException if <code>Package.getPackage(arg)</code> returns
+     * null.
+     * @see java.lang.Package
+     * @see com.martiansoftware.jsap.StringParser#parse(String)
+     */
+    public Object parse(String arg) throws ParseException {
+        Package result = Package.getPackage(arg);
+        if (result == null) {
+            throw (
+                new ParseException("Unable to locate Package '" + arg + "'."));
+        }
+        return (result);
+    }
+}
diff --git a/src/java/com/martiansoftware/jsap/stringparsers/ShortStringParser.java b/src/java/com/martiansoftware/jsap/stringparsers/ShortStringParser.java
new file mode 100644
index 0000000..400fbda
--- /dev/null
+++ b/src/java/com/martiansoftware/jsap/stringparsers/ShortStringParser.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2002-2004, Martian Software, Inc.
+ * This file is made available under the LGPL as described in the accompanying
+ * LICENSE.TXT file.
+ */
+
+package com.martiansoftware.jsap.stringparsers;
+
+import com.martiansoftware.jsap.StringParser;
+import com.martiansoftware.jsap.ParseException;
+
+/**
+ * A {@link com.martiansoftware.jsap.StringParser} for parsing Shorts.  The parse() method delegates the actual
+ * parsing to <code>Short.decode(String)</code>.  If <code>Short.decode()</code>
+ * throws a
+ * NumberFormatException, it is encapsulated in a ParseException and re-thrown.
+ * @author <a href="http://www.martiansoftware.com/contact.html">Marty Lamb</a>
+ * @see com.martiansoftware.jsap.StringParser
+ * @see java.math.BigDecimal
+ */
+public class ShortStringParser extends StringParser {
+
+	private static final ShortStringParser INSTANCE = new ShortStringParser();	
+
+	/** Returns a {@link ShortStringParser}.
+	 * 
+	 * <p>Convenient access to the only instance returned by
+	 * this method is available through
+	 * {@link com.martiansoftware.jsap.JSAP#SHORT_PARSER}.
+	 *  
+	 * @return a {@link ShortStringParser}.
+	 */
+
+    public static ShortStringParser getParser() {
+		return INSTANCE;
+	}
+
+	/**
+     * Creates a new ShortStringParser.
+     * @deprecated Use {@link #getParser()} or, even better, {@link com.martiansoftware.jsap.JSAP#SHORT_PARSER}.
+     */
+    public ShortStringParser() {
+        super();
+    }
+
+    /**
+     * Parses the specified argument into a Short.  This method delegates the
+     * parsing to <code>Short.decode(String)</code>.  If
+     * <code>Short.decode()</code> throws a
+     * NumberFormatException, it is encapsulated in a ParseException and
+     * re-thrown.
+     *
+     * @param arg the argument to parse
+     * @return a Short object with the value contained in the specified
+     * argument.
+     * @throws ParseException if <code>Short.decode(arg)</code> throws a
+     * NumberFormatException.
+     * @see java.lang.Short
+     * @see com.martiansoftware.jsap.StringParser#parse(String)
+     */
+    public Object parse(String arg) throws ParseException {
+        Short result = null;
+        try {
+            result = Short.decode(arg);
+        } catch (NumberFormatException e) {
+            throw (
+                new ParseException(
+                    "Unable to convert '" + arg + "' to a Short.",
+                    e));
+        }
+        return (result);
+    }
+}
diff --git a/src/java/com/martiansoftware/jsap/stringparsers/StringStringParser.java b/src/java/com/martiansoftware/jsap/stringparsers/StringStringParser.java
new file mode 100644
index 0000000..b9970b2
--- /dev/null
+++ b/src/java/com/martiansoftware/jsap/stringparsers/StringStringParser.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2002-2004, Martian Software, Inc.
+ * This file is made available under the LGPL as described in the accompanying
+ * LICENSE.TXT file.
+ */
+
+package com.martiansoftware.jsap.stringparsers;
+
+import com.martiansoftware.jsap.StringParser;
+
+/**
+ * A {@link com.martiansoftware.jsap.StringParser} for parsing Strings.  This is the simplest possible
+ * StringParser, simply returning
+ * the specified argument in all cases.  This class never throws a
+ * ParseException under any circumstances.
+ * @author <a href="http://www.martiansoftware.com/contact.html">Marty Lamb</a>
+ * @see com.martiansoftware.jsap.StringParser
+ * @see java.lang.String
+ */
+public class StringStringParser extends StringParser {
+
+	private static final StringStringParser INSTANCE = new StringStringParser();	
+
+	/** Returns a {@link StringStringParser}.
+	 * 
+	 * <p>Convenient access to the only instance returned by
+	 * this method is available through
+	 * {@link com.martiansoftware.jsap.JSAP#STRING_PARSER}.
+	 *  
+	 * @return a {@link StringStringParser}.
+	 */
+
+    public static StringStringParser getParser() {
+		return INSTANCE;
+	}
+
+	/**
+     * Creates a new StringStringParser.
+     * @deprecated Use {@link #getParser()} or, even better, {@link com.martiansoftware.jsap.JSAP#STRING_PARSER}.
+     */
+    public StringStringParser() {
+        super();
+    }
+
+    /**
+     * Returns the specified argument as a String.
+     *
+     * @param arg the argument to parse
+     * @return the specified argument as a String.
+     * @see java.lang.String
+     * @see com.martiansoftware.jsap.StringParser#parse(String)
+     */
+    public Object parse(String arg) {
+        return (arg);
+    }
+}
diff --git a/src/java/com/martiansoftware/jsap/stringparsers/TestAll.java b/src/java/com/martiansoftware/jsap/stringparsers/TestAll.java
new file mode 100644
index 0000000..2ff506c
--- /dev/null
+++ b/src/java/com/martiansoftware/jsap/stringparsers/TestAll.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2002-2004, Martian Software, Inc.
+ * This file is made available under the LGPL as described in the accompanying
+ * LICENSE.TXT file.
+ */
+
+package com.martiansoftware.jsap.stringparsers;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+/**
+ * Contains all of the tests in the stringparsers package.
+ *
+ * @author <a href="http://www.martiansoftware.com/contact.html">Marty Lamb</a>
+ */
+public class TestAll {
+
+    /**
+     * Returns a suite of all tests in the stringparsers package.
+     * @return a suite of all tests in the stringparsers package.
+     */
+    public static Test suite() {
+        TestSuite suite =
+            new TestSuite("Test for com.martiansoftware.jsap.stringparsers");
+        suite.addTest(new TestSuite(TestColorStringParser.class));
+        suite.addTest(new TestSuite(TestLongStringParser.class));
+        return suite;
+    }
+}
diff --git a/src/java/com/martiansoftware/jsap/stringparsers/TestColorStringParser.java b/src/java/com/martiansoftware/jsap/stringparsers/TestColorStringParser.java
new file mode 100644
index 0000000..b0c625a
--- /dev/null
+++ b/src/java/com/martiansoftware/jsap/stringparsers/TestColorStringParser.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2002-2004, Martian Software, Inc.
+ * This file is made available under the LGPL as described in the accompanying
+ * LICENSE.TXT file.
+ */
+
+package com.martiansoftware.jsap.stringparsers;
+
+import junit.framework.TestCase;
+import java.awt.Color;
+import com.martiansoftware.jsap.JSAPException;
+
+/**
+ * A series of tests for the ColorStringParser
+ * @author <a href="http://www.martiansoftware.com/contact.html">Marty Lamb</a>
+ * @see com.martiansoftware.jsap.stringparsers.ColorStringParser
+ */
+public class TestColorStringParser extends TestCase {
+
+    /**
+     * Constructor for TestColorStringParser.
+     * @param arg0 name of this test
+     */
+    public TestColorStringParser(String arg0) {
+        super(arg0);
+    }
+
+    /**
+     * Tests the ability to parse tuples of integers representing RGB values.
+     */
+    public void testIntegerRGB() {
+
+        Color c = new Color(12, 34, 56);
+        ColorStringParser parser = ColorStringParser.getParser();
+
+        Color result = null;
+        try {
+            result = (Color) parser.parse("12,34,56");
+        } catch (JSAPException e) {
+            fail("12,34,56");
+        }
+        assertEquals(c, result);
+
+        try {
+            result = (Color) parser.parse("12,34,56,");
+            fail("12,34,56,");
+        } catch (JSAPException e) {
+            // this is normal behavior
+        }
+
+        try {
+            result = (Color) parser.parse("12,3b,56");
+            fail("12,3b,56");
+        } catch (JSAPException e) {
+            // this is normal behavior
+        }
+
+    }
+
+    /**
+     * Tests the ability to parse tuples of floats representing RGB values.
+     */
+    public void testFloatRGB() {
+        Color c = new Color(0.12f, 0.34f, 0.56f);
+        ColorStringParser parser = ColorStringParser.getParser();
+
+        Color result = null;
+        try {
+            result = (Color) parser.parse("0.12,.34,00.56");
+        } catch (JSAPException e) {
+            fail("0.12,.34,00.56");
+        }
+        assertEquals(c, result);
+
+        try {
+            result = (Color) parser.parse("0.12,.34");
+            fail("0.12,.34");
+        } catch (JSAPException e) {
+            // this is normal behavior
+        }
+    }
+
+    /**
+     * Tests the ability to parse hexadecimal strings as RGB values.
+     */
+    public void testHexRGB() {
+        Color c = new Color(255, 255, 255);
+        ColorStringParser parser = ColorStringParser.getParser();
+
+        Color result = null;
+        try {
+            result = (Color) parser.parse("#fFFfFF");
+        } catch (JSAPException e) {
+            fail("#fFFfFF");
+        }
+        assertEquals(c, result);
+    }
+
+    /**
+     * Tests the ability to parse color names as in java.awt.Color fields.
+     */
+    public void testByName() {
+        Color c = new Color(255, 255, 255);
+        ColorStringParser parser = ColorStringParser.getParser();
+
+        Color result = null;
+        try {
+            result = (Color) parser.parse("white");
+        } catch (JSAPException e) {
+            fail("white");
+        }
+        assertEquals(c, result);
+
+        try {
+            result = (Color) parser.parse("offwhite");
+            fail("offwhite");
+        } catch (JSAPException e) {
+            // this is normal behavior
+        }
+
+    }
+
+}
diff --git a/src/java/com/martiansoftware/jsap/stringparsers/TestLongStringParser.java b/src/java/com/martiansoftware/jsap/stringparsers/TestLongStringParser.java
new file mode 100644
index 0000000..e33d1df
--- /dev/null
+++ b/src/java/com/martiansoftware/jsap/stringparsers/TestLongStringParser.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2002-2004, Martian Software, Inc.
+ * This file is made available under the LGPL as described in the accompanying
+ * LICENSE.TXT file.
+ */
+
+package com.martiansoftware.jsap.stringparsers;
+
+import com.martiansoftware.jsap.JSAPException;
+
+import junit.framework.TestCase;
+/**
+ * A series of tests for the LongStringParser
+ * @author <a href="http://www.martiansoftware.com/contact.html">Marty Lamb</a>
+ * @see com.martiansoftware.jsap.stringparsers.ColorStringParser
+ */
+public class TestLongStringParser extends TestCase {
+
+    /**
+     * Constructor for TestLongStringParser.
+     * @param arg0 name of this test
+     */
+    public TestLongStringParser(String arg0) {
+        super(arg0);
+    }
+
+    /**
+     * Tests the ability to parse tuples of integers representing RGB values.
+     */
+    public void testBasicParse() {
+
+        LongStringParser lsp = LongStringParser.getParser();
+
+        assertEquals(456, Long.decode("456").longValue());
+        try {
+            Long result = (Long) lsp.parse("123");
+            assertEquals(123, result.longValue());
+        } catch (JSAPException e) {
+            e.printStackTrace();
+            fail(e.getMessage());
+        }
+
+    }
+
+}
diff --git a/src/java/com/martiansoftware/jsap/stringparsers/URLStringParser.java b/src/java/com/martiansoftware/jsap/stringparsers/URLStringParser.java
new file mode 100644
index 0000000..28f3ce4
--- /dev/null
+++ b/src/java/com/martiansoftware/jsap/stringparsers/URLStringParser.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2002-2004, Martian Software, Inc.
+ * This file is made available under the LGPL as described in the accompanying
+ * LICENSE.TXT file.
+ */
+
+package com.martiansoftware.jsap.stringparsers;
+
+import com.martiansoftware.jsap.StringParser;
+import com.martiansoftware.jsap.ParseException;
+import java.net.URL;
+import java.net.MalformedURLException;
+
+/**
+ * A {@link com.martiansoftware.jsap.StringParser} for parsing java.net.URL objects.  The parse() method
+ * delegates the actual
+ * parsing to <code>new URL(String)</code>.  If <code>new URL()</code>
+ * throws a MalformedURLException, it is encapsulated in a ParseException and
+ * re-thrown.
+ *
+ * @author <a href="http://www.martiansoftware.com/contact.html">Marty Lamb</a>
+ * @see com.martiansoftware.jsap.StringParser
+ * @see java.net.URL
+ */
+public class URLStringParser extends StringParser {
+
+	private static final URLStringParser INSTANCE = new URLStringParser();	
+
+	/** Returns a {@link URLStringParser}.
+	 * 
+	 * <p>Convenient access to the only instance returned by
+	 * this method is available through
+	 * {@link com.martiansoftware.jsap.JSAP#URL_PARSER}.
+	 *  
+	 * @return a {@link URLStringParser}.
+	 */
+
+    public static URLStringParser getParser() {
+		return INSTANCE;
+	}
+
+	/**
+     * Creates a new URLStringParser.
+     * @deprecated Use {@link #getParser()} or, even better, {@link com.martiansoftware.jsap.JSAP#URL_PARSER}.
+     */
+    public URLStringParser() {
+        super();
+    }
+
+    /**
+     * Parses the specified argument into a URL.  This method delegates the
+     * actual
+     * parsing to <code>new URL(arg)</code>.  If <code>new URL(arg)</code>
+     * throws a MalformedURLException, it is encapsulated in a ParseException
+     * and re-thrown.
+     *
+     * @param arg the argument to parse
+     * @return a URL as specified by arg.
+     * @throws ParseException if <code>new URL(arg)</code> throws a
+     * MalformedURLException.
+     * @see java.net URL
+     * @see com.martiansoftware.jsap.StringParser#parse(String)
+     */
+    public Object parse(String arg) throws ParseException {
+        URL result = null;
+        try {
+            result = new URL(arg);
+        } catch (MalformedURLException e) {
+            throw (
+                new ParseException(
+                    "Unable to convert '" + arg + "' to a URL.",
+                    e));
+        }
+        return (result);
+    }
+}
diff --git a/src/java/com/martiansoftware/jsap/xml/FlaggedConfig.java b/src/java/com/martiansoftware/jsap/xml/FlaggedConfig.java
new file mode 100644
index 0000000..4cccf2b
--- /dev/null
+++ b/src/java/com/martiansoftware/jsap/xml/FlaggedConfig.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2002-2004, Martian Software, Inc.
+ * This file is made available under the LGPL as described in the accompanying
+ * LICENSE.TXT file.
+ */
+package com.martiansoftware.jsap.xml;
+
+import com.martiansoftware.jsap.JSAP;
+
+/**
+ * Provides support for loading JSAP configurations at runtime
+ * via an xml file.  You don't need to access this class directly;
+ * instead, use JSAP's constructors that support xml.
+ * 
+ * @author <a href="http://www.martiansoftware.com/contact.html">Marty Lamb</a>
+ */
+abstract class FlaggedConfig extends ParameterConfig {
+	
+	private char shortFlag = JSAP.NO_SHORTFLAG;
+	private String longFlag = JSAP.NO_LONGFLAG;
+	
+	public String getLongFlag() {
+		return longFlag;
+	}
+	
+	public void setLongFlag(String longFlag) {
+		this.longFlag = longFlag;
+	}
+
+	public char getShortFlag() {
+		return shortFlag;
+	}
+
+	public void setShortFlag(char shortFlag) {
+		this.shortFlag = shortFlag;
+	}
+
+	protected void configure(com.martiansoftware.jsap.FlaggedOption option) {
+		super.configure(option);
+	}
+}
diff --git a/src/java/com/martiansoftware/jsap/xml/FlaggedOptionConfig.java b/src/java/com/martiansoftware/jsap/xml/FlaggedOptionConfig.java
new file mode 100644
index 0000000..a75f2bd
--- /dev/null
+++ b/src/java/com/martiansoftware/jsap/xml/FlaggedOptionConfig.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2002-2004, Martian Software, Inc.
+ * This file is made available under the LGPL as described in the accompanying
+ * LICENSE.TXT file.
+ */
+package com.martiansoftware.jsap.xml;
+
+import com.martiansoftware.jsap.FlaggedOption;
+import com.martiansoftware.jsap.JSAP;
+import com.martiansoftware.jsap.Parameter;
+
+/**
+ * Provides support for loading JSAP configurations at runtime
+ * via an xml file.  You don't need to access this class directly;
+ * instead, use JSAP's constructors that support xml.
+ * 
+ * @author <a href="http://www.martiansoftware.com/contact.html">Marty Lamb</a>
+ */
+class FlaggedOptionConfig extends FlaggedConfig {
+
+	private boolean allowMultipleDeclarations = JSAP.NO_MULTIPLEDECLARATIONS;
+	private boolean list = JSAP.NOT_LIST;
+	private char listSeparator = JSAP.DEFAULT_LISTSEPARATOR;
+	private StringParserConfig stringParser = null;
+	private boolean required = JSAP.NOT_REQUIRED;
+	public FlaggedOptionConfig() {
+		super();
+	}
+
+	public boolean allowMultipleDeclarations() {
+		return allowMultipleDeclarations;
+	}
+
+	public void setAllowMultipleDeclarations(boolean allowMultipleDeclarations) {
+		this.allowMultipleDeclarations = allowMultipleDeclarations;
+	}
+
+	public boolean isList() {
+		return list;
+	}
+
+	public void setList(boolean list) {
+		this.list = list;
+	}
+
+	public char getListSeparator() {
+		return listSeparator;
+	}
+
+	public void setListSeparator(char listSeparator) {
+		this.listSeparator = listSeparator;
+	}
+
+	public boolean isRequired() {
+		return required;
+	}
+
+	public void setRequired(boolean required) {
+		this.required = required;
+	}
+
+	public StringParserConfig getStringParser() {
+		return stringParser;
+	}
+
+	public void setStringParser(StringParserConfig stringParser) {
+		this.stringParser = stringParser;
+	}
+	
+	protected void configure(FlaggedOption option) {
+		super.configure(option);
+		option.setUsageName(getUsageName());
+
+		option.setShortFlag(getShortFlag());
+		option.setLongFlag(getLongFlag());
+		option.setAllowMultipleDeclarations(allowMultipleDeclarations());
+		option.setListSeparator(getListSeparator());
+		option.setList(isList());
+		option.setRequired(isRequired());
+
+		if (stringParser != null) {
+			option.setStringParser(stringParser.getConfiguredStringParser());
+		}
+	}
+	
+	public Parameter getConfiguredParameter() {
+		FlaggedOption result = new FlaggedOption(getId());
+		configure(result);
+		return (result);
+	}
+}
diff --git a/src/java/com/martiansoftware/jsap/xml/JSAPConfig.java b/src/java/com/martiansoftware/jsap/xml/JSAPConfig.java
new file mode 100644
index 0000000..7222936
--- /dev/null
+++ b/src/java/com/martiansoftware/jsap/xml/JSAPConfig.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2002-2004, Martian Software, Inc.
+ * This file is made available under the LGPL as described in the accompanying
+ * LICENSE.TXT file.
+ */
+package com.martiansoftware.jsap.xml;
+
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.net.URL;
+import java.util.Iterator;
+
+import com.martiansoftware.jsap.JSAP;
+import com.martiansoftware.jsap.JSAPException;
+
+/**
+ * Provides support for loading JSAP configurations at runtime
+ * via an xml file.  You don't need to access this class directly;
+ * instead, use JSAP's constructors that support xml.
+ * 
+ * @see com.martiansoftware.jsap.JSAP#JSAP(URL)
+ * @author <a href="http://www.martiansoftware.com/contact.html">Marty Lamb</a>
+ */
+public class JSAPConfig {
+
+	private java.util.List parameters = new java.util.ArrayList();
+	private String help = null;
+	private String usage = null;
+	
+	/**
+	 * Loads a JSAP configuration from the xml at the specified URL, and configures
+	 * the specified JSAP object accordingly
+	 * @param jsapToConfigure the JSAP to configure
+	 * @param jsapXML the configuration
+	 * @throws IOException if an I/O error occurs
+	 * @throws JSAPException if the configuration is not valid
+	 */
+	public static void configure(JSAP jsapToConfigure, URL jsapXML) throws IOException, JSAPException {
+		JSAPXStream jsx = new JSAPXStream();
+		InputStreamReader in = new InputStreamReader(jsapXML.openStream());
+
+		JSAPConfig config = (JSAPConfig) jsx.fromXML(in);
+		in.close();
+		
+		for (Iterator i = config.parameters(); i.hasNext();) {
+			ParameterConfig cfg = (ParameterConfig) i.next();
+			jsapToConfigure.registerParameter(cfg.getConfiguredParameter());
+		}
+		jsapToConfigure.setHelp(config.getHelp());
+		jsapToConfigure.setUsage(config.getUsage());
+	}
+	
+	public String getHelp() {
+		return help;
+	}
+
+	public void setHelp(String help) {
+		this.help = help;
+	}
+
+	public String getUsage() {
+		return usage;
+	}
+
+	public void setUsage(String usage) {
+		this.usage = usage;
+	}
+	
+	public void addParameter(ParameterConfig config) {
+		parameters.add(config);
+	}
+	
+	public Iterator parameters() {
+		return (parameters.iterator());
+	}
+	
+
+}
diff --git a/src/java/com/martiansoftware/jsap/xml/JSAPXStream.java b/src/java/com/martiansoftware/jsap/xml/JSAPXStream.java
new file mode 100644
index 0000000..5f19a37
--- /dev/null
+++ b/src/java/com/martiansoftware/jsap/xml/JSAPXStream.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2002-2004, Martian Software, Inc.
+ * This file is made available under the LGPL as described in the accompanying
+ * LICENSE.TXT file.
+ */
+package com.martiansoftware.jsap.xml;
+
+import com.thoughtworks.xstream.XStream;
+import com.thoughtworks.xstream.io.xml.DomDriver;
+
+/**
+ * Provides support for loading JSAP configurations at runtime
+ * via an xml file.  You don't need to access this class directly;
+ * instead, use JSAP's constructors that support xml.
+ * 
+ * @author <a href="http://www.martiansoftware.com/contact.html">Marty Lamb</a>
+ */
+class JSAPXStream extends XStream {
+
+	public JSAPXStream() {
+		super(new DomDriver());
+		alias("jsap", JSAPConfig.class);
+		alias("flaggedOption", FlaggedOptionConfig.class);
+		alias("unflaggedOption", UnflaggedOptionConfig.class);
+		alias("property", Property.class);
+		alias("qualifiedSwitch", QualifiedSwitchConfig.class);
+		alias("switch", SwitchConfig.class);
+	}
+
+}
diff --git a/src/java/com/martiansoftware/jsap/xml/ParameterConfig.java b/src/java/com/martiansoftware/jsap/xml/ParameterConfig.java
new file mode 100644
index 0000000..4fdfa97
--- /dev/null
+++ b/src/java/com/martiansoftware/jsap/xml/ParameterConfig.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2002-2004, Martian Software, Inc.
+ * This file is made available under the LGPL as described in the accompanying
+ * LICENSE.TXT file.
+ */
+package com.martiansoftware.jsap.xml;
+
+import com.martiansoftware.jsap.Parameter;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * Provides support for loading JSAP configurations at runtime
+ * via an xml file.  You don't need to access this class directly;
+ * instead, use JSAP's constructors that support xml.
+ * 
+ * @author <a href="http://www.martiansoftware.com/contact.html">Marty Lamb</a>
+ */
+abstract class ParameterConfig {
+
+	private String id = null;
+	private String help = null;
+	private String usageName = null;
+	private java.util.List defaults = null;
+	
+	public ParameterConfig() {
+		defaults = new ArrayList();
+	}
+	
+	public void addDefault(String defaultValue) {
+		getDefaults().add(defaultValue);
+	}
+	
+	public List getDefaults() {
+		if (defaults == null) {
+			defaults = new ArrayList();
+		}
+		return (defaults);
+	}
+	
+	public String getHelp() {
+		return help;
+	}
+
+	public void setHelp(String help) {
+		this.help = help;
+	}
+
+	public String getId() {
+		return id;
+	}
+
+	public void setId(String id) {
+		this.id = id;
+	}
+	
+	public String getUsageName() {
+		return usageName;
+	}
+
+	public void setUsageName(String usageName) {
+		this.usageName = usageName;
+	}
+	
+	protected void configure(Parameter param) {
+		param.setHelp(getHelp());
+		for (Iterator i = getDefaults().iterator(); i.hasNext();) {
+			String def = (String) i.next();
+			param.addDefault(def);
+		}
+	}
+	public abstract Parameter getConfiguredParameter();
+}
diff --git a/src/java/com/martiansoftware/jsap/xml/Property.java b/src/java/com/martiansoftware/jsap/xml/Property.java
new file mode 100644
index 0000000..8708b83
--- /dev/null
+++ b/src/java/com/martiansoftware/jsap/xml/Property.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2002-2004, Martian Software, Inc.
+ * This file is made available under the LGPL as described in the accompanying
+ * LICENSE.TXT file.
+ */
+package com.martiansoftware.jsap.xml;
+
+/**
+ * Provides support for loading JSAP configurations at runtime
+ * via an xml file.  You don't need to access this class directly;
+ * instead, use JSAP's constructors that support xml.
+ * 
+ * @author <a href="http://www.martiansoftware.com/contact.html">Marty Lamb</a>
+ */
+class Property {
+
+	private String name = null;
+	private String value = null;
+
+	public String getName() {
+		return name;
+	}
+
+	public void setName(String name) {
+		this.name = name;
+	}
+
+	public String getValue() {
+		return value;
+	}
+
+	public void setValue(String value) {
+		this.value = value;
+	}
+}
diff --git a/src/java/com/martiansoftware/jsap/xml/QualifiedSwitchConfig.java b/src/java/com/martiansoftware/jsap/xml/QualifiedSwitchConfig.java
new file mode 100644
index 0000000..d03daaa
--- /dev/null
+++ b/src/java/com/martiansoftware/jsap/xml/QualifiedSwitchConfig.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2002-2004, Martian Software, Inc.
+ * This file is made available under the LGPL as described in the accompanying
+ * LICENSE.TXT file.
+ */
+package com.martiansoftware.jsap.xml;
+
+import com.martiansoftware.jsap.Parameter;
+import com.martiansoftware.jsap.QualifiedSwitch;
+
+/**
+ * Provides support for loading JSAP configurations at runtime
+ * via an xml file.  You don't need to access this class directly;
+ * instead, use JSAP's constructors that support xml.
+ * 
+ * @author <a href="http://www.martiansoftware.com/contact.html">Marty Lamb</a>
+ */
+class QualifiedSwitchConfig extends FlaggedOptionConfig {
+
+	protected void configure(QualifiedSwitch qs) {
+		super.configure(qs);
+	}
+
+	public Parameter getConfiguredParameter() {
+		QualifiedSwitch result = new QualifiedSwitch(getId());
+		configure(result);
+		return (result);
+	}
+}
diff --git a/src/java/com/martiansoftware/jsap/xml/StringParserConfig.java b/src/java/com/martiansoftware/jsap/xml/StringParserConfig.java
new file mode 100644
index 0000000..502af0b
--- /dev/null
+++ b/src/java/com/martiansoftware/jsap/xml/StringParserConfig.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2002-2004, Martian Software, Inc.
+ * This file is made available under the LGPL as described in the accompanying
+ * LICENSE.TXT file.
+ */
+package com.martiansoftware.jsap.xml;
+
+import java.util.Iterator;
+import java.util.List;
+
+import com.martiansoftware.jsap.PropertyStringParser;
+import com.martiansoftware.jsap.StringParser;
+
+/**
+ * Provides support for loading JSAP configurations at runtime
+ * via an xml file.  You don't need to access this class directly;
+ * instead, use JSAP's constructors that support xml.
+ * 
+ * @author <a href="http://www.martiansoftware.com/contact.html">Marty Lamb</a>
+ */
+class StringParserConfig {
+	
+	String classname = null;
+	List properties = null;
+
+	public String getClassname() {
+		return classname;
+	}
+
+	public void setClassname(String classname) {
+		this.classname = classname;
+	}
+
+	public List getProperties() {
+		return properties;
+	}
+
+	public void setProperties(List properties) {
+		this.properties = properties;
+	}
+
+	public StringParser getConfiguredStringParser() {
+		try {
+			StringParser result = null;
+			if (classname.indexOf('.') >= 0) {
+				result = (StringParser) Class.forName(classname).newInstance();
+			} else {
+				result = (StringParser) Class.forName("com.martiansoftware.jsap.stringparsers." + classname).newInstance();
+			}
+			if ((properties != null) && (properties.size() > 0)) {
+				PropertyStringParser p = (PropertyStringParser) result;
+				for (Iterator i = properties.iterator(); i.hasNext(); ) {
+					Property property = (Property) i.next();
+					p.setProperty(property.getName(), property.getValue());
+				}
+			}
+			return (result);
+		} catch (Throwable t) {
+			throw (new RuntimeException("Unable to create StringParser " + classname + ": " + t.getMessage(), t));
+		}
+	}
+
+}
diff --git a/src/java/com/martiansoftware/jsap/xml/SwitchConfig.java b/src/java/com/martiansoftware/jsap/xml/SwitchConfig.java
new file mode 100644
index 0000000..6def319
--- /dev/null
+++ b/src/java/com/martiansoftware/jsap/xml/SwitchConfig.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2002-2004, Martian Software, Inc.
+ * This file is made available under the LGPL as described in the accompanying
+ * LICENSE.TXT file.
+ */
+package com.martiansoftware.jsap.xml;
+
+import com.martiansoftware.jsap.Parameter;
+import com.martiansoftware.jsap.Switch;
+
+/**
+ * Provides support for loading JSAP configurations at runtime
+ * via an xml file.  You don't need to access this class directly;
+ * instead, use JSAP's constructors that support xml.
+ * 
+ * @author <a href="http://www.martiansoftware.com/contact.html">Marty Lamb</a>
+ */
+class SwitchConfig extends FlaggedConfig {
+
+	public Parameter getConfiguredParameter() {
+		Switch result = new Switch(getId());
+		super.configure(result);
+		result.setShortFlag(getShortFlag());
+		result.setLongFlag(getLongFlag());
+		return (result);
+	}
+}
diff --git a/src/java/com/martiansoftware/jsap/xml/TryDumpXML.java b/src/java/com/martiansoftware/jsap/xml/TryDumpXML.java
new file mode 100644
index 0000000..7bdd9c9
--- /dev/null
+++ b/src/java/com/martiansoftware/jsap/xml/TryDumpXML.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2002-2004, Martian Software, Inc.
+ * This file is made available under the LGPL as described in the accompanying
+ * LICENSE.TXT file.
+ */
+package com.martiansoftware.jsap.xml;
+
+import java.util.ArrayList;
+
+/**
+ * Sends a JSAPConfig to System.out as xml (for test purposes)
+ * 
+ * @author <a href="http://www.martiansoftware.com/contact.html">Marty Lamb</a>
+ */
+public class TryDumpXML {
+
+	public static void main(String[] args) throws Exception {
+		JSAPXStream jsx = new JSAPXStream();
+		JSAPConfig jc = new JSAPConfig();
+		
+		FlaggedOptionConfig foc = new FlaggedOptionConfig();
+		foc.setId("flagged");
+		foc.setShortFlag('f');
+		foc.setHelp("This flag does something, but I'm not sure what.");
+		jc.addParameter(foc);
+		
+		UnflaggedOptionConfig uoc = new UnflaggedOptionConfig();
+		uoc.setId("unflagged");
+		uoc.setGreedy(true);
+		StringParserConfig spc = new StringParserConfig();
+		spc.setClassname("DateStringParser");
+		ArrayList props = new java.util.ArrayList();
+		Property p = new Property();
+		p.setName("DateFormat");
+		p.setValue("MM/dd/yyyy");
+		props.add(p);
+		p = new Property();
+		p.setName("Another Property");
+		p.setValue("123");
+		props.add(p);
+		spc.setProperties(props);
+		uoc.setStringParser(spc);
+		
+		jc.addParameter(uoc);
+		System.out.println(jsx.toXML(jc));
+		
+//		JSAP jsap = jc.getJSAP();
+//		JSAPResult result = jsap.parse("-f abc");
+//		System.out.println(result.getString("flagged"));
+	}
+}
diff --git a/src/java/com/martiansoftware/jsap/xml/TryLoadXML.java b/src/java/com/martiansoftware/jsap/xml/TryLoadXML.java
new file mode 100644
index 0000000..6f3bd54
--- /dev/null
+++ b/src/java/com/martiansoftware/jsap/xml/TryLoadXML.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2002-2004, Martian Software, Inc.
+ * This file is made available under the LGPL as described in the accompanying
+ * LICENSE.TXT file.
+ */
+package com.martiansoftware.jsap.xml;
+
+import com.martiansoftware.jsap.JSAP;
+
+/**
+ * Loads a silly example JSAP from xml and displays its help message
+ * 
+ * @author <a href="http://www.martiansoftware.com/contact.html">Marty Lamb</a>
+ */
+public class TryLoadXML {
+
+	public static void main(String[] args) throws Exception {
+		JSAP jsap = new JSAP("com/martiansoftware/jsap/xml/silly-example.xml");
+		System.out.println(jsap.getHelp());
+	}
+}
diff --git a/src/java/com/martiansoftware/jsap/xml/UnflaggedOptionConfig.java b/src/java/com/martiansoftware/jsap/xml/UnflaggedOptionConfig.java
new file mode 100644
index 0000000..74ec96e
--- /dev/null
+++ b/src/java/com/martiansoftware/jsap/xml/UnflaggedOptionConfig.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2002-2004, Martian Software, Inc.
+ * This file is made available under the LGPL as described in the accompanying
+ * LICENSE.TXT file.
+ */
+package com.martiansoftware.jsap.xml;
+
+import com.martiansoftware.jsap.Parameter;
+import com.martiansoftware.jsap.UnflaggedOption;
+import com.martiansoftware.jsap.JSAP;
+
+/**
+ * Provides support for loading JSAP configurations at runtime
+ * via an xml file.  You don't need to access this class directly;
+ * instead, use JSAP's constructors that support xml.
+ * 
+ * @author <a href="http://www.martiansoftware.com/contact.html">Marty Lamb</a>
+ */
+class UnflaggedOptionConfig extends ParameterConfig {
+	
+	private boolean greedy = JSAP.NOT_GREEDY;
+	private boolean list = JSAP.NOT_LIST;
+	private char listSeparator = JSAP.DEFAULT_LISTSEPARATOR;
+	private StringParserConfig stringParser = null;
+	private boolean required = JSAP.NOT_REQUIRED;
+	private String usageName = null;
+	
+	public UnflaggedOptionConfig() {
+		super();
+	}
+
+	public boolean isGreedy() {
+		return greedy;
+	}
+
+	public void setGreedy(boolean greedy) {
+		this.greedy = greedy;
+	}
+
+	public boolean isList() {
+		return list;
+	}
+
+	public void setList(boolean list) {
+		this.list = list;
+	}
+
+	public char getListSeparator() {
+		return listSeparator;
+	}
+
+	public void setListSeparator(char listSeparator) {
+		this.listSeparator = listSeparator;
+	}
+
+	public boolean isRequired() {
+		return required;
+	}
+
+	public void setRequired(boolean required) {
+		this.required = required;
+	}
+
+	public StringParserConfig getStringParser() {
+		return stringParser;
+	}
+
+	public void setStringParser(StringParserConfig stringParser) {
+		this.stringParser = stringParser;
+	}
+	
+	public String getUsageName() {
+		return usageName;
+	}
+
+	public void setUsageName(String usageName) {
+		this.usageName = usageName;
+	}
+	
+	public Parameter getConfiguredParameter() {
+		UnflaggedOption result = new UnflaggedOption(getId());
+		super.configure(result);
+		result.setGreedy(isGreedy());
+		result.setListSeparator(getListSeparator());
+		result.setList(isList());
+		result.setRequired(isRequired());
+		result.setUsageName(getUsageName());
+
+		if (stringParser != null) {
+			result.setStringParser(stringParser.getConfiguredStringParser());
+		}
+		
+		return (result);
+	}
+}
diff --git a/src/java/com/martiansoftware/jsap/xml/silly-example.xml b/src/java/com/martiansoftware/jsap/xml/silly-example.xml
new file mode 100644
index 0000000..ca6e60d
--- /dev/null
+++ b/src/java/com/martiansoftware/jsap/xml/silly-example.xml
@@ -0,0 +1,47 @@
+<jsap>
+  <parameters>
+    <flaggedOption>
+      <allowMultipleDeclarations>false</allowMultipleDeclarations>
+      <list>false</list>
+      <listSeparator>:</listSeparator>
+      <required>false</required>
+      <shortFlag>f</shortFlag>
+      <id>flagged</id>
+      <help>This flag does something, but I'm not sure what.</help>
+      <defaults/>
+    </flaggedOption>
+    <switch>
+    	<longFlag>exampleSwitch</longFlag>
+    	<id>exampleswitch</id>
+    	<help>This is just a switch.</help>
+    </switch>
+    <qualifiedSwitch>
+    	<shortFlag>q</shortFlag>
+    	<id>qualified</id>
+    	<help>An example qualified switch</help>
+    	<allowMultipleDeclarations>true</allowMultipleDeclarations>
+    </qualifiedSwitch>
+    <unflaggedOption>
+      <help>Greedy unflagged option that uses a DateStringParser.</help>
+      <greedy>true</greedy>
+      <list>false</list>
+      <listSeparator>:</listSeparator>
+      <stringParser>
+        <classname>DateStringParser</classname>
+        <properties>
+          <property>
+            <name>DateFormat</name>
+            <value>MM/dd/yyyy</value>
+          </property>
+          <property>
+            <name>Another Property</name>
+            <value>123</value>
+          </property>
+        </properties>
+      </stringParser>
+      <required>false</required>
+      <id>unflagged</id>
+      <defaults/>
+    </unflaggedOption>
+  </parameters>
+</jsap>
diff --git a/src/java/com/martiansoftware/util/StringUtils.java b/src/java/com/martiansoftware/util/StringUtils.java
new file mode 100644
index 0000000..eff4f74
--- /dev/null
+++ b/src/java/com/martiansoftware/util/StringUtils.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2002-2004, Martian Software, Inc.
+ * This file is made available under the LGPL as described in the accompanying
+ * LICENSE.TXT file.
+ */
+
+package com.martiansoftware.util;
+
+import java.util.List;
+
+/**
+ * @author <a href="http://www.martiansoftware.com/contact.html">Marty Lamb</a>
+ */
+public final class StringUtils {
+
+    public static String noNull(String s) {
+        return (s == null ? "" : s);
+    }
+
+    public static String padRight(String s, int padCount) {
+        if (padCount < 0) {
+            throw (new IllegalArgumentException("padCount must be >= 0"));
+        }
+        StringBuffer buf = new StringBuffer(noNull(s));
+        for (int i = 0; i < padCount; ++i) {
+            buf.append(' ');
+        }
+        return (buf.toString());
+    }
+
+    public static String padRightToWidth(String s, int desiredWidth) {
+        String result = noNull(s);
+        if (result.length() < desiredWidth) {
+            result = padRight(result, desiredWidth - result.length());
+        }
+        return (result);
+    }
+
+    public static List wrapToList(String s, int width) {
+        List result = new java.util.LinkedList();
+        if ((s != null) && (s.length() > 0)) {
+            StringBuffer buf = new StringBuffer();
+            int lastSpaceBufIndex = -1;
+            for (int i = 0; i < s.length(); ++i) {
+                char c = s.charAt(i);
+                if (c == '\n') {
+                    result.add(buf.toString());
+                    buf.setLength(0);
+                    lastSpaceBufIndex = -1;
+                } else {
+                    if (c == ' ') {
+                        if (buf.length() >= width - 1) {
+                            result.add(buf.toString());
+                            buf.setLength(0);
+                            lastSpaceBufIndex = -1;
+                        }
+                        if (buf.length() > 0) {
+                            lastSpaceBufIndex = buf.length();
+                            buf.append(c);
+                        }
+                    } else {
+                    	if (buf.length() >= width) {
+                            if (lastSpaceBufIndex != -1) {
+                                result.add(buf.substring(0, lastSpaceBufIndex));
+                                buf.delete(0, lastSpaceBufIndex + 1);
+                                lastSpaceBufIndex = -1;
+                            }
+                        }
+                        buf.append(c);
+                    }
+                }
+            }
+            if (buf.length() > 0) {
+                result.add(buf.toString());
+            }
+        }
+        return (result);
+    }
+
+    public static void main(String[] args) {
+        String s =
+            "This is\n a test that I would like to word wrap at 15 characters.";
+
+        System.out.println("123456789012345");
+        List l = wrapToList(s, 15);
+        for (java.util.Iterator i = l.iterator(); i.hasNext();) {
+            String s1 = (String) i.next();
+            System.out.println(s1 + "|");
+        }
+    }
+}
\ No newline at end of file

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-java/jsap.git



More information about the pkg-java-commits mailing list