/**
  * Copyright John E. Lloyd, 2004. All rights reserved. Permission to use,
  * copy, modify and redistribute is granted, provided that this copyright
  * notice is retained and the author is given credit whenever appropriate.
  *
  * This  software is distributed "as is", without any warranty, including 
  * any implied warranty of merchantability or fitness for a particular
  * use. The author assumes no responsibility for, and shall not be liable
  * for, any special, indirect, or consequential damages, or any damages
  * whatsoever, arising out of or in connection with the use of this
  * software.
  */

import java.io.*;

import java.lang.reflect.Array;
import java.util.Vector;

/**
 * Testing class for the class ArgParser. Executing the <code>main</code>
 * method of this class will perform a suite of tests to help verify correct
 * operation of the parser class.
 *
 * @author John E. Lloyd, Fall 2004
 * @see ArgParser
 */
public class ArgParserTest
{
	ArgParser parser; 

	static final boolean CLOSED = true;
	static final boolean OPEN = false;

	static final boolean ONE_WORD = true;
	static final boolean MULTI_WORD = false;

	private static void verify (boolean ok, String msg)
	 { if (!ok)
	    { Throwable e = new Throwable();
	      System.out.println ("Verification failed:" + msg);
	      e.printStackTrace();
	      System.exit(1);
	    }
	 }

	private static String[] argsFromString (String s)
	 {
	   Vector vec = new Vector(100);
	   try
	    { ArgParser.stringToArgs (vec, s, /*allowQuotedStings=*/false);
	    }
	   catch (StringScanException e)
	    { e.printStackTrace();
	      System.exit (1);
	    }
	   String[] result = new String[vec.size()];
	   for (int i=0; i<vec.size(); i++)
	    { result[i] = (String)vec.get(i);
	    }
	   return result;
	 }

	static class RngCheck
	 {
	   ArgParser.RangePnt low = null;
	   ArgParser.RangePnt high = null;
	   int type; 

	   RngCheck (String s)
	    { low = new ArgParser.RangePnt (s, CLOSED);
	      type = 's';
	    }

	   RngCheck (double d)
	    { low = new ArgParser.RangePnt (d, CLOSED);
	      type = 'd';
	    }

	   RngCheck (long l)
	    { low = new ArgParser.RangePnt (l, CLOSED);
	      type = 'l';
	    }

	   RngCheck (boolean b)
	    { low = new ArgParser.RangePnt (b, CLOSED);
	      type = 'b';
	    }

	   RngCheck (String s1, boolean c1, String s2, boolean c2)
	    { low = new ArgParser.RangePnt (s1, c1);
	      high = new ArgParser.RangePnt (s2, c2);
	      type = 's';
	    }

	   RngCheck (double d1, boolean c1, double d2, boolean c2)
	    { low = new ArgParser.RangePnt (d1, c1);
	      high = new ArgParser.RangePnt (d2, c2);
	      type = 'd';
	    }

	   RngCheck (long l1, boolean c1, long l2, boolean c2)
	    { low = new ArgParser.RangePnt (l1, c1);
	      high = new ArgParser.RangePnt (l2, c2);
	      type = 'l';
	    }

	   void check (ArgParser.RangeAtom ra)
	    {
	      verify ((ra.low==null) == (low==null),
		 "(ra.low==null)=" + (ra.low==null) +
		 "(low==null)=" + (low==null));
	      verify ((ra.high==null) == (high==null),
		 "(ra.high==null)=" + (ra.high==null) +
		 "(high==null)=" + (high==null));

	      if (ra.low != null)
	       { switch (type)
		  { case 'l':
		     { verify (ra.low.lval==low.lval,
			     "ra.low=" + ra.low + " low=" + low);
		       break;
		     }
		    case 'd':
		     { verify (ra.low.dval==low.dval,
			     "ra.low=" + ra.low + " low=" + low);
		       break;
		     }
		    case 's':
		     { verify (ra.low.sval.equals (low.sval),
			     "ra.low=" + ra.low + " low=" + low);
		       break;
		     }
		    case 'b':
		     { verify (ra.low.bval==low.bval,
			     "ra.low=" + ra.low + " low=" + low);
		       break;
		     }
		  }
		 verify (ra.low.closed==low.closed,
			 "ra.low=" + ra.low + " low=" + low);
	       }
	      if (ra.high != null)
	       { switch (type)
		  { case 'l':
		     { verify (ra.high.lval==high.lval,
			     "ra.high=" + ra.high + " high=" + high);
		       break;
		     }
		    case 'd':
		     { verify (ra.high.dval==high.dval,
			     "ra.high=" + ra.high + " high=" + high);
		       break;
		     }
		    case 's':
		     { verify (ra.high.sval.equals (high.sval),
			     "ra.high=" + ra.high + " high=" + high);
		       break;
		     }
		    case 'b':
		     { verify (ra.high.bval==high.bval,
			     "ra.high=" + ra.high + " high=" + high);
		       break;
		     }
		  }
		 verify (ra.high.closed==high.closed,
			 "ra.high=" + ra.high + " high=" + high);
	       }
	    }
	 }

	ArgParserTest ()
	 { parser = new ArgParser("fubar");
	 }

	static void checkException (Exception e, String errmsg)
	 { if (errmsg != null)
	    { if (!e.getMessage().equals(errmsg))
	       { System.out.println (
"Expecting exception '" + errmsg + "' but got '" +
			e.getMessage() + "'");
		 e.printStackTrace();
		 (new Throwable()).printStackTrace();
		 System.exit(1);
	       }
	    }
	   else
	    { System.out.println (
"Unexpected exception '" + e.getMessage() + "'");
	      e.printStackTrace();
	      (new Throwable()).printStackTrace();
	      System.exit(1);
	    }
	 }

	void checkPrintHelp (String msg)
	 {
	   ByteArrayOutputStream buf = new ByteArrayOutputStream(0x10000);
	   PrintStream ps = new PrintStream(buf);
	   ps.println (parser.getHelpMessage());
	   System.out.print (buf.toString());	   
	 }

// 	void checkGetSynopsis (String msg)
// 	 {
// 	   ByteArrayOutputStream buf = new ByteArrayOutputStream(0x10000);
// 	   PrintStream ps = new PrintStream(buf);
// 	   parser.printSynopsis (ps, 80);
// 	   System.out.print (buf.toString());	   
// 	 }

	void checkAdd (String s, Object resHolder, String errmsg) 
	 {
	   checkAdd (s, resHolder, 0, 0, null, null, null, errmsg);
	 }

	void add (String s, Object resHolder)
	 { try
	    { parser.addOption (s, resHolder);
	    }
	   catch (Exception e)
	    { e.printStackTrace();
	      System.exit (1);
	    } 
	 }

	void checkStringArray (String msg, String[] strs, String[] check)
	 {
	   boolean dontMatch = false;
	   if (strs.length != check.length)
	    { dontMatch = true;
	    }
	   else
	    { for (int i=0; i<strs.length; i++)
	       { if (!strs[i].equals (check[i]))
		  { dontMatch = true;
		    break;
		  }
	       }
	    }
	   if (dontMatch)
	    { System.out.println (msg);
	      System.out.print ("Expected: ");
	      for (int i=0; i<check.length; i++)
	       { System.out.print ("'" + check[i] + "'");
		 if (i<check.length-1)
		  { System.out.print (" ");
		  }
	       }
	      System.out.println ("");
	      System.out.print ("Got: ");
	      for (int i=0; i<strs.length; i++)
	       { System.out.print ("'" + strs[i] + "'");
		 if (i<strs.length-1)
		  { System.out.print (" ");
		  }
	       }
	      System.out.println ("");
	      System.exit(1);
	    }
	 }

	void checkAdd (String s, Object resHolder, int code, int numValues,
		       Object names, RngCheck[] rngCheck,
		       String helpMsg, String errmsg)
	 {
	   boolean exceptionThrown = false;
	   String[] namelist = null;
	   try
	    { parser.addOption (s, resHolder);
	    }
	   catch (Exception e)
	    { exceptionThrown = true;
	      checkException (e, errmsg);
	    }
	   if (names instanceof String)
	    { namelist = new String[] { (String)names };
	    }
	   else
	    { namelist = (String[])names; 
	    }
	   if (!exceptionThrown)
	    { verify (errmsg == null, 
		      "Expecting exception " + errmsg);
	      ArgParser.Record rec = parser.lastMatchRecord();	   
	      verify (rec.convertCode==code, 
		      "code=" + rec.convertCode + ", expecting " + code);
	      ArgParser.NameDesc nd;
	      int i=0;
	      for (nd=rec.firstNameDesc(); nd!=null; nd=nd.next)
	       { i++;
	       }
	      verify (i==namelist.length,
		      "numNames=" + i + ", expecting " +namelist.length);
	      i=0;
	      for (nd=rec.firstNameDesc(); nd!=null; nd=nd.next)
	       { String ss;
		 if (!nd.oneWord)
		  { ss = new String(nd.name) + ' ';
		  }
		 else
		  { ss = nd.name;
		  }
		 verify (ss.equals(namelist[i]),
			 "have name '"+ss+"', expecting '"+namelist[i]+"'");
		 i++;
	       }
	      ArgParser.RangeAtom ra;
	      i=0;
	      for (ra=rec.firstRangeAtom(); ra!=null; ra=ra.next)
	       { i++;
	       }
	      int expectedRangeNum = 0;
	      if (rngCheck!=null)
	       { expectedRangeNum = rngCheck.length;
	       }
	      verify (i==expectedRangeNum,
		      "numRangeAtoms="+i+", expecting "+expectedRangeNum);
	      i=0;
	      for (ra=rec.firstRangeAtom(); ra!=null; ra=ra.next)
	       { rngCheck[i++].check(ra);
	       }
	      verify (rec.helpMsg.equals(helpMsg), 
		      "helpMsg="+rec.helpMsg+", expecting "+helpMsg);
	      verify (rec.numValues==numValues,
		      "numValues="+rec.numValues+", expecting "+numValues);
	    }
	 }

	double getDoubleValue (Object obj, int k)
	 {
	   if (obj instanceof DoubleHolder)
	    { return ((DoubleHolder)obj).value;
	    }
	   else if (obj instanceof FloatHolder)
	    { return ((FloatHolder)obj).value;
	    }
	   else if (obj instanceof double[])
	    { return ((double[])obj)[k];
	    }
	   else if (obj instanceof float[])
	    { return ((float[])obj)[k];
	    }
	   else
	    { verify (false, "object doesn't contain double values"); 
	      return 0;
	    }
	 }

	long getLongValue (Object obj, int k)
	 {
	   if (obj instanceof LongHolder)
	    { return ((LongHolder)obj).value;
	    }
	   else if (obj instanceof IntHolder)
	    { return ((IntHolder)obj).value;
	    }
	   else if (obj instanceof long[])
	    { return ((long[])obj)[k];
	    }
	   else if (obj instanceof int[])
	    { return ((int[])obj)[k];
	    }
	   else
	    { verify (false, "object doesn't contain long values"); 
	      return 0;
	    }
	 }

	String getStringValue (Object obj, int k)
	 {
	   if (obj instanceof StringHolder)
	    { return ((StringHolder)obj).value;
	    }
	   else if (obj instanceof String[])
	    { return ((String[])obj)[k];
	    }
	   else
	    { verify (false, "object doesn't contain String values");
	      return null;
	    }
	 }

	boolean getBooleanValue (Object obj, int k)
	 {
	   if (obj instanceof BooleanHolder)
	    { return ((BooleanHolder)obj).value;
	    }
	   else if (obj instanceof boolean[])
	    { return ((boolean[])obj)[k];
	    }
	   else
	    { verify (false, "object doesn't contain boolean values");
	      return false;
	    }
	 }

	char getCharValue (Object obj, int k)
	 {
	   if (obj instanceof CharHolder)
	    { return ((CharHolder)obj).value;
	    }
	   else if (obj instanceof char[])
	    { return ((char[])obj)[k];
	    }
	   else
	    { verify (false, "object doesn't contain char values");
	      return 0;
	    }
	 }

	static class MErr
	 {
	   int code;
	   String valStr;

	   MErr (int code, String valStr)
	    { this.code = code;
	      this.valStr = valStr;
	    }
	 }

	static class MTest
	 {
	   String args;
	   Object result;
	   int resultIdx;

	   MTest (String args, Object result)
	    { this (args, result, -1);
	    }

	   MTest (String args, Object result, int resultIdx)
	    { this.args = args;
	      this.result = result;
	      this.resultIdx = resultIdx;
	    }
	 };

	void checkMatch (String args[], int idx, String errMsg)
	 { getMatchResult (args, idx, -1, errMsg, -1); 
	 }

	void checkMatch (String args[], int idx, int cnt,
			 long check, int resultIdx)
	 { Object rholder = getMatchResult (args, idx, cnt, null, resultIdx);
	   long result = getLongValue(rholder,0);
	   verify (result==check, "result " + result + " vs. " + check);
	 }

	void checkMatch (String args[], int idx, int cnt,
			 double check, int resultIdx)
	 { Object rholder = getMatchResult (args, idx, cnt, null, resultIdx);
	   double result = getDoubleValue(rholder,0);
	   verify (result==check, "result " + result + " vs. " + check);
	 }

	void checkMatch (String args[], int idx, int cnt,
			 String check, int resultIdx)
	 { Object rholder = getMatchResult (args, idx, cnt, null, resultIdx);
	   String result = getStringValue(rholder,0);
	   verify (result.equals(check), "result " + result + " vs. " + check);
	 }

	void checkMatch (String args[], int idx, int cnt,
			 boolean check, int resultIdx)
	 { Object rholder = getMatchResult (args, idx, cnt, null, resultIdx);
	   boolean result = getBooleanValue(rholder,0);
	   verify (result==check, "result " + result + " vs. " + check);
	 }

	void checkMatch (String args[], int idx, int cnt,
			 char check, int resultIdx)
	 { Object rholder = getMatchResult (args, idx, cnt, null, resultIdx);
	   char result = getCharValue(rholder,0);
	   verify (result==check, "result " + result + " vs. " + check);
	 }

	void checkMatch (String args[], int idx, int cnt,
			 Object checkArray, int resultIdx)
	 { Object rholder = getMatchResult (args, idx, cnt, null, resultIdx);
	   if (!checkArray.getClass().isArray())
	    { verify (false, "check is not an array"); 
	    }
	   for (int i=0; i<Array.getLength(checkArray); i++)
	    { if (checkArray instanceof long[])
	       { long result = getLongValue(rholder,i);
		 long check = ((long[])checkArray)[i];
		 verify (result==check,
			 "result ["+i+"] " + result + " vs. " + check);
	       }
	      else if (checkArray instanceof double[])
	       { double result = getDoubleValue(rholder,i);
		 double check = ((double[])checkArray)[i];
		 verify (result==check, 
			 "result ["+i+"] " + result + " vs. " + check);
	       }
	      else if (checkArray instanceof String[])
	       { String result = getStringValue(rholder,i);
		 String check = ((String[])checkArray)[i];
		 verify (result.equals(check),
			 "result ["+i+"] " + result + " vs. " + check);
	       }
	      else if (checkArray instanceof boolean[])
	       { boolean result = getBooleanValue(rholder,i);
		 boolean check = ((boolean[])checkArray)[i];
		 verify (result==check, 
			 "result ["+i+"] " + result + " vs. " + check);
	       }
	      else if (checkArray instanceof char[])
	       { char result = getCharValue(rholder,i);
		 char check = ((char[])checkArray)[i];
		 verify (result==check, 
			 "result ["+i+"] " + result + " vs. " + check);
	       }
	      else 
	       { verify (false, "unknown type for checkArray");
	       }
	    }
	 }

	void checkMatch (MTest test, boolean oneWord)
	 { String[] argv;
	   if (oneWord)
	    { argv = new String[1];
	      argv[0] = test.args;
	    }
	   else
	    { argv = argsFromString(test.args);
	    }
	   if (test.result instanceof Long)
	    { checkMatch (argv, 0, argv.length,
			  ((Long)test.result).longValue(),
			  test.resultIdx);
	    }
	   else if (test.result instanceof Double)
	    { checkMatch (argv, 0, argv.length,
			  ((Double)test.result).doubleValue(),
			  test.resultIdx);
	    }
	   else if (test.result instanceof String)
	    { checkMatch (argv, 0, argv.length,
			  (String)test.result,
			  test.resultIdx);
	    }
	   else if (test.result instanceof Boolean)
	    { checkMatch (argv, 0, argv.length,
			  ((Boolean)test.result).booleanValue(),
			  test.resultIdx);
	    }
	   else if (test.result instanceof Character)
	    { checkMatch (argv, 0, argv.length,
			  ((Character)test.result).charValue(),
			  test.resultIdx);
	    }
	   else if (test.result.getClass().isArray())
	    { checkMatch (argv, 0, argv.length, test.result,
			  test.resultIdx);
	    }
	   else if (test.result instanceof MErr)
	    { MErr err = (MErr)test.result;
	      String argname = parser.getOptionName (argv[0]);
	      String msg = "";

	      switch (err.code)
	       { case 'c':
		  { msg = "requires a contiguous value";
		    break;
		  }
		 case 'm':
		  { msg = "malformed " + parser.getOptionTypeName(argv[0]) +
		       " '" + err.valStr + "'";
		    break;
		  }
		 case 'r':
		  { msg = "value '" + err.valStr + "' not in range " +
		       parser.getOptionRangeDesc(argv[0]);
		    break;
		  }
		 case 'v':
		  { msg = "requires " + err.valStr + " values";
		    break;
		  }
	       }
	      checkMatch (argv, 0, argname + ": " + msg);
	    }
	   else
	    { verify (false, "Unknown result type"); 
	    }
	 }

	void checkMatches (MTest[] tests, boolean oneWord)
	 { for (int i=0; i<tests.length; i++)
	    { checkMatch (tests[i], oneWord); 
	    }
	 }

	Object getMatchResult (String args[], int idx, int cnt,
			       String errMsg, int resultIdx)
	 {
	   boolean exceptionThrown = false;
	   int k = 0;
	   try
	    { k = parser.matchArg (args, idx);
	    }
	   catch (Exception e)
	    { exceptionThrown = true;
	      checkException (e, errMsg);
	    }
	   if (!exceptionThrown)
	    { verify (k==idx+cnt,
		      "Expecting result index " + (idx+cnt) + ", got " + k);
	      Object result = parser.getResultHolder(args[0]);
	      if (resultIdx >= 0)
	       { verify (result instanceof Vector,
			 "Expecting result to be stored in a vector");
		 Vector vec = (Vector)result;
		 verify (vec.size()==resultIdx+1, 
			 "Expecting result vector size " + (resultIdx+1));
		 return vec.get(resultIdx);
	       }
	      else
	       { return result;
	       }
	    }
	   else
	    { return null; 
	    }
	 }

	/**
	 * Runs a set of tests to verify correct operation of the
	 * ArgParser class. If all the tests run correctly, the
	 * program prints the message <code>Passed</code> and terminates.
	 * Otherwise, diagnostic information is printed at the first
	 * point of failure.
	 */
	public static void main (String[] args)
	 {
	   ArgParserTest test = new ArgParserTest();

	   BooleanHolder bh = new BooleanHolder();
	   boolean[] b3 = new boolean[3];
	   CharHolder ch = new CharHolder();
	   char[] c3 = new char[3];
	   IntHolder ih = new IntHolder();
	   int[] i3 = new int[3];
	   LongHolder lh = new LongHolder();
	   long[] l3 = new long[3];
	   FloatHolder fh = new FloatHolder();
	   float[] f3 = new float[3];
	   DoubleHolder dh = new DoubleHolder();
	   double[] d3 = new double[3];
	   StringHolder sh = new StringHolder();
	   String[] s3 = new String[3];

	   test.checkAdd ("-foo %i{[0,10)}X3 #sets the value of foo",
//			   0123456789012345
	      i3, 'i', 3, new String[] { "-foo " }, 
		new RngCheck[] { 
		   new RngCheck(0, CLOSED, 10, OPEN) },
	      "sets the value of foo", null);

	   test.checkAdd ("-arg1,,", null, "Null option name given");
	   test.checkAdd ("-arg1,,goo %f ", null, "Null option name given");
	   test.checkAdd ("  ", null, "Null option name given");
	   test.checkAdd ("", null, "Null option name given");
	   test.checkAdd ("  %v", null, "Null option name given");
	   test.checkAdd ("-foo  ", null, "No conversion character given");
	   test.checkAdd ("-foo %", null, "No conversion character given");
	   test.checkAdd ("foo, aaa   bbb ",null,"Names not separated by ','");
	   test.checkAdd (" foo aaa %d", null, "Names not separated by ','");
	   test.checkAdd ("-arg1,-b,", null, "Null option name given");
	   test.checkAdd ("-arg1,-b", null, "No conversion character given");
	   test.checkAdd ("-arg1 ", null, "No conversion character given");
	   test.checkAdd ("-arg1, %v", null, "Null option name given");
	   test.checkAdd ("-arg1,%v", null, "Null option name given");
	   test.checkAdd ("-foo %V", null,
			  "Conversion code 'V' not one of 'iodxcbfsvh'");
	   test.checkAdd ("-h %hX5",null,"Multipliers not supported for %h");
	   test.checkAdd ("-h %h{}",null,"Ranges not supported for %h");
	   test.checkAdd ("-help, -h %h #here is how we help you",
			  null, 'h', 1, new String[] {"-help ", "-h " },
			  null, "here is how we help you", null);

	   test.checkAdd (
	      "-arg1 ,-arg2=%d{0,3,(7,16]}X1 #x3 test",
	      l3, 'd', 1, new String[] { "-arg1 ", "-arg2=" },
	      new RngCheck[] 
	       { new RngCheck(0),
		 new RngCheck(3),
		 new RngCheck(7, OPEN, 16, CLOSED),
	       },
	      "x3 test", null);		   

	   test.checkAdd (
	      "bbb,ccc%x{[1,2]} #X3 x3 test",
	      l3, 'x', 1, new String[] { "bbb", "ccc" },
	      new RngCheck[] 
	       { new RngCheck(1, CLOSED, 2, CLOSED),
	       },
	      "X3 x3 test", null);		   

	   test.checkAdd (
	      " bbb ,ccc,  ddd  ,e   , f=%bX1 #x3 test",
	      b3, 'b', 1, new String[] { "bbb ", "ccc", "ddd ", "e ", "f=" },
	      null, 
	      "x3 test", null); 

	   test.checkAdd (
	      " bbb ,ccc,  ddd  ,e   , f= %bX3 #x3 test",
	      b3, 'b', 3, new String[] { "bbb ", "ccc ", "ddd ", "e ","f= " },
	      null, 
	      "x3 test", null); 

	   test.checkAdd (
	      "-b,--bar %s{[\"john\",\"jerry\"),fred,\"harry\"} #sets bar",
              sh, 's', 1, new String[] { "-b ", "--bar " },
	      new RngCheck[] {
		new RngCheck("jerry",OPEN,"john",CLOSED), 
		new RngCheck("fred"),
		new RngCheck("harry") },
	      "sets bar", null);

	   test.checkAdd (
	      "-c ,coven%f{0.0,9.0,(6,5],[-9.1,10.2]}  ",
	      dh, 'f', 1, new String[] { "-c ", "coven" },
	      new RngCheck[] {
		new RngCheck(0.0),
		new RngCheck(9.0),
		new RngCheck(5.0,CLOSED,6.0,OPEN),
		new RngCheck(-9.1,CLOSED,10.2,CLOSED) },
	      "", null);

	   test.checkAdd (
	      "-b %b #a boolean value  ",
	      bh, 'b', 1, new String[] { "-b "},
	      new RngCheck[] { },
	      "a boolean value  ", null);

	   test.checkAdd ("-a %i", ih, 'i', 1, "-a ", null, "", null);
	   test.checkAdd ("-a %o", lh, 'o', 1, "-a ", null, "", null);
	   test.checkAdd ("-a %d", i3, 'd', 1, "-a ", null, "", null);
	   test.checkAdd ("-a %x", l3, 'x', 1, "-a ", null, "", null);
	   test.checkAdd ("-a %c", ch, 'c', 1, "-a ", null, "", null);
	   test.checkAdd ("-a %c", c3, 'c', 1, "-a ", null, "", null);
	   test.checkAdd ("-a %v", bh, 'v', 1, "-a ", null, "", null);
	   test.checkAdd ("-a %b", b3, 'b', 1, "-a ", null, "", null);
	   test.checkAdd ("-a %f", fh, 'f', 1, "-a ", null, "", null);
	   test.checkAdd ("-a %f", f3, 'f', 1, "-a ", null, "", null);
	   test.checkAdd ("-a %f", dh, 'f', 1, "-a ", null, "", null);
	   test.checkAdd ("-a %f", d3, 'f', 1, "-a ", null, "", null);

	   test.checkAdd ("-a %i", fh, 'i', 1, "-a ", null, "",
			  "Invalid result holder for %i");
	   test.checkAdd ("-a %c", i3, 'c', 1, "-a ", null, "",
			  "Invalid result holder for %c");
	   test.checkAdd ("-a %v", d3, 'v', 1, "-a ", null, "",
			  "Invalid result holder for %v");
	   test.checkAdd ("-a %f", sh, 'f', 1, "-a ", null, "",
			  "Invalid result holder for %f");
	   test.checkAdd ("-a %s", l3, 's', 1, "-a ", null, "",
			  "Invalid result holder for %s");

	   test.checkAdd ("-foo %i{} ", ih, 'i', 1, "-foo ", null, "", null);
	   test.checkAdd ("-foo%i{}", ih, 'i', 1, "-foo", null, "", null);
	   test.checkAdd ("-foo%i{  }", ih, 'i', 1, "-foo", null, "", null);
	   test.checkAdd ("-foo%i{ }}", ih,
			  "Illegal character(s), expecting '#'");
	   test.checkAdd ("-foo%i{  ", ih,"Unterminated range specification");
	   test.checkAdd ("-foo%i{", ih, "Unterminated range specification");
	   test.checkAdd ("-foo%i{0,9", ih, "Unterminated range specification");
	   test.checkAdd ("-foo%i{1,2,3)", ih,
			  "Unterminated range specification");

	   test.checkAdd ("-b %f{0.9}", fh, 'f', 1, "-b ", 
			  new RngCheck[] { new RngCheck(0.9) },
			  "", null);
	   test.checkAdd ("-b %f{ 0.9 ,7, -0.5,-4 ,6 }", fh, 'f', 1, "-b ", 
			  new RngCheck[] { new RngCheck(0.9),
					   new RngCheck(7.0),
					   new RngCheck(-0.5),
					   new RngCheck(-4.0),
					   new RngCheck(6.0) },
			  "", null);
	   test.checkAdd ("-b %f{ [0.9,7), (-0.5,-4),[9,6] , (10,13.4] }",
			  fh, 'f', 1, "-b ", 
			  new RngCheck[] { new RngCheck(0.9,CLOSED,7.0,OPEN),
					   new RngCheck(-4.0,OPEN,-.5,OPEN),
					   new RngCheck(6.0,CLOSED,9.0,CLOSED),
					   new RngCheck(10.0,OPEN,13.4,CLOSED),
			  },
			  "", null);
	   test.checkAdd ("-b %f{(8 9]}", fh,
			  "Missing ',' in subrange specification");
	   test.checkAdd ("-b %f{(8,9,]}", fh, 
			  "Unterminated subrange");
	   test.checkAdd ("-b %f{(8,9 ,]}", fh, 
			  "Unterminated subrange");
	   test.checkAdd ("-b %f{(8,9  8]}", fh,
			  "Unterminated subrange");
	   test.checkAdd ("-b %f{8 9}", fh, 
			  "Range spec: ',' or '}' expected");
	   test.checkAdd ("-b %f{8 *}", fh, 
			  "Range spec: ',' or '}' expected");

	   test.checkAdd ("-b %f{8y}", fh,
			  "Range spec: ',' or '}' expected");
	   test.checkAdd ("-b %f{.}", fh,
			  "Malformed float '.}' in range spec");
	   test.checkAdd ("-b %f{1.0e}", fh,
			  "Malformed float '1.0e}' in range spec");
	   test.checkAdd ("-b %f{[*]}", fh,
			  "Malformed float '*' in range spec");
	   test.checkAdd ("-b %f{1.2e5t}", fh,
			  "Range spec: ',' or '}' expected");


	   test.checkAdd ("-b %i{8}", ih, 'i', 1, "-b ", 
			  new RngCheck[] { new RngCheck(8) },
			  "", null);
	   test.checkAdd ("-b %i{8, 9,10 }", ih, 'i', 1, "-b ", 
			  new RngCheck[] { new RngCheck(8),
					   new RngCheck(9),
					   new RngCheck(10) },
			  "", null);
	   test.checkAdd ("-b %i{8, [-9,10),[-17,15],(2,-33),(8,9] }",
			  ih, 'i', 1, "-b ", 
			  new RngCheck[] { new RngCheck(8),
					   new RngCheck(-9,CLOSED,10,OPEN),
					   new RngCheck(-17,CLOSED,15,CLOSED),
					   new RngCheck(-33,OPEN,2,OPEN),
				           new RngCheck(8,OPEN,9,CLOSED),
			  },
			  "", null);
	   test.checkAdd ("-b %i{8.7}", ih,
			  "Range spec: ',' or '}' expected");
	   test.checkAdd ("-b %i{6,[*]}", ih,
			  "Malformed integer '*' in range spec");
	   test.checkAdd ("-b %i{g76}", ih,
			  "Malformed integer 'g' in range spec");

	   test.checkAdd ("-b %s{foobar}", sh, 's', 1, "-b ", 
			  new RngCheck[] { new RngCheck("foobar") },
			  "", null);
	   test.checkAdd ("-b %s{foobar, 0x233,\"  \"}", sh, 's', 1, "-b ", 
			  new RngCheck[] { new RngCheck("foobar"),
					   new RngCheck("0x233"),
					   new RngCheck("  ") },
			  "", null);
	   test.checkAdd ("-b %s{foobar,(bb,aa], [\"01\",02]}",
			  sh, 's', 1, "-b ", 
			  new RngCheck[]
	                   { new RngCheck("foobar"),
			     new RngCheck("aa",CLOSED,"bb",OPEN),
			     new RngCheck("01",CLOSED,"02",CLOSED),
			   },
			  "", null);

	   test.checkAdd ("-b %c{'a'}", ch, 'c', 1, "-b ", 
			  new RngCheck[] { new RngCheck('a') },
			  "", null);
	   test.checkAdd ("-b %c{'\\n', '\\002', 'B'}", ch, 'c', 1, "-b ", 
			  new RngCheck[] { new RngCheck('\n'),
					   new RngCheck('\002'),
					   new RngCheck('B') },
			  "", null);
	   test.checkAdd ("-b %c{'q',('g','a'], ['\t','\\003']}",
			  ch, 'c', 1, "-b ", 
			  new RngCheck[]
	                   { new RngCheck('q'),
			     new RngCheck('a',CLOSED,'g',OPEN),
			     new RngCheck('\003',CLOSED,'\t',CLOSED),
			   },
			  "", null);

	   test.checkAdd ("-b %b{true}X2", b3, 'b', 2, "-b ", 
			  new RngCheck[] { new RngCheck(true) },
			  "", null);
	   test.checkAdd ("-b %b{ true , false, true }", bh, 'b', 1, "-b ", 
			  new RngCheck[] { new RngCheck(true),
					   new RngCheck(false),
					   new RngCheck(true) },
			  "", null);
	   test.checkAdd ("-b %v{true,[true,false)}", bh, 
			  "Sub ranges not supported for %b or %v");
	   test.checkAdd ("-b %v{true,[]}", bh,
			  "Sub ranges not supported for %b or %v");
	   test.checkAdd ("-b %b{tru}", bh,
			  "Malformed boolean 'tru}' in range spec");

	   test.checkAdd ("-b %iX2", i3, 'i', 2, "-b ", null, "", null);
	   test.checkAdd ("-b %vX3", b3, 'v', 3, "-b ", null, "", null);
	   test.checkAdd ("-b %v{ }X3", b3, 'v', 3, "-b ", null, "", null);

	   test.checkAdd ("-b=%iX2", i3, 'i', 2, "-b", null, "", 
"Multiplier value incompatible with one word option -b=");
	   test.checkAdd ("-b %iX0", i3, 'i', 0, "-b ", null, "", 
			  "Value multiplier number must be > 0");
	   test.checkAdd ("-b %iX-6", i3, 'i', 0, "-b ", null, "", 
			  "Value multiplier number must be > 0");
	   test.checkAdd ("-b %iXy", i3, 'i', 0, "-b ", null, "", 
			  "Malformed value multiplier");
	   test.checkAdd ("-b %iX4", i3, 'i', 4, "-b ", null, "", 
			  "Result holder array must have a length >= 4");
	   test.checkAdd ("-b %iX4", ih, 'i', 4, "-b ", null, "", 
"Multiplier requires result holder to be an array of length >= 4");

	   test.checkAdd ("-b %i #X4", ih, 'i', 1, "-b ", null, "X4", null);
	   test.checkAdd ("-b %i #[}X4",ih, 'i', 1, "-b ", null, "[}X4", null);

//	   test.checkPrintHelp("");
//	   test.checkPrintUsage("");

	   test = new ArgParserTest();

	   test.checkAdd (
	      "-intarg %i{1,2,(9,18],[22,27],[33,38),(45,48)} #test int arg",
	      ih, 'i', 1, "-intarg ",
	      new RngCheck[] 
	       { new RngCheck (1),
		 new RngCheck (2),
		 new RngCheck (9,OPEN,18,CLOSED),
		 new RngCheck (22,CLOSED,27,CLOSED),
		 new RngCheck (33,CLOSED,38,OPEN),
		 new RngCheck (45,OPEN,48,OPEN),
	       },
	      "test int arg", null);
	   
	   MTest[] tests;

	   tests = new MTest[]
	    { 
	      new MTest("-intarg 1", new Long(1) ),
	      new MTest("-intarg 3", new MErr ('r', "3") ),
	      new MTest("-intarg 9", new MErr ('r', "9") ),
	      new MTest("-intarg 11", new Long(11) ),
	      new MTest("-intarg 18", new Long(18)),
	      new MTest("-intarg 22", new Long(22)),
	      new MTest("-intarg 25", new Long(25)),
	      new MTest("-intarg 27", new Long(27)),
	      new MTest("-intarg 33", new Long(33)),
	      new MTest("-intarg 35", new Long(35)),
	      new MTest("-intarg 38", new MErr ('r', "38") ),
	      new MTest("-intarg 45", new MErr ('r', "45")),
	      new MTest("-intarg 46", new Long(46)),
	      new MTest("-intarg 48", new MErr ('r', "48")),
	      new MTest("-intarg 100", new MErr ('r', "100")),
	      new MTest("-intarg 0xbeef", new MErr ('r', "0xbeef")),
	      new MTest("-intarg 0x2f", new Long (0x2f)),
	      new MTest("-intarg 041", new Long(041) ),
	    };
	   test.checkMatches (tests, MULTI_WORD);
	   
	   test.checkAdd (
	      "-farg %f{1,2,(9,18],[22,27],[33,38),(45,48)} #test float arg",
	      dh, 'f', 1, "-farg ",
	      new RngCheck[] 
	       {   
		 new RngCheck (1.0),
		 new RngCheck (2.0),
		 new RngCheck (9.0,OPEN,18.0,CLOSED),
		 new RngCheck (22.0,CLOSED,27.0,CLOSED),
		 new RngCheck (33.0,CLOSED,38.0,OPEN),
		 new RngCheck (45.0,OPEN,48.0,OPEN),
	       },
	      "test float arg", null);

	   tests = new MTest[]
	    { 
	      new MTest("-farg 1", new Double(1)),
	      new MTest("-farg 3", new MErr('r', "3")),
	      new MTest("-farg 9", new MErr('r', "9")),
	      new MTest("-farg 9.0001", new Double(9.0001)),
	      new MTest("-farg 11", new Double(11)),
	      new MTest("-farg 18", new Double(18)),
	      new MTest("-farg 22", new Double(22)),
	      new MTest("-farg 25", new Double(25)),
	      new MTest("-farg 27", new Double(27)),
	      new MTest("-farg 33", new Double(33)),
	      new MTest("-farg 35", new Double(35)),
	      new MTest("-farg 37.9999",new Double(37.9999)),
	      new MTest("-farg 38", new MErr('r', "38")),
	      new MTest("-farg 45", new MErr('r', "45")),
	      new MTest("-farg 45.0001", new Double(45.0001)),
	      new MTest("-farg 46",new Double(46)),
	      new MTest("-farg 47.9999",new Double(47.9999)),
	      new MTest("-farg 48", new MErr('r', "48")),
	      new MTest("-farg 100", new MErr('r', "100")),
	      new MTest("-farg 0", new MErr('r', "0")),
	    };
	   test.checkMatches (tests, MULTI_WORD);

	   test.checkAdd (
	      "-sarg %s{1,2,(AA,AZ],[BB,BX],[C3,C8),(d5,d8)} #test string arg",
	      s3, 's', 1, "-sarg ",
	      new RngCheck[] 
	       { new RngCheck ("1"),
		 new RngCheck ("2"),
		 new RngCheck ("AA",OPEN,"AZ",CLOSED),
		 new RngCheck ("BB",CLOSED,"BX",CLOSED),
		 new RngCheck ("C3",CLOSED,"C8",OPEN),
		 new RngCheck ("d5",OPEN,"d8",OPEN),
	       },
	      "test string arg", null);

	   tests = new MTest[]
	    {
	      new MTest ("-sarg 1", "1"),
	      new MTest ("-sarg 3", new MErr('r',"3")),
	      new MTest ("-sarg AA", new MErr('r',"AA")),
	      new MTest ("-sarg AM", "AM"),
	      new MTest ("-sarg AZ", "AZ"),
	      new MTest ("-sarg BB", "BB"),
	      new MTest ("-sarg BL", "BL"),
	      new MTest ("-sarg BX", "BX"),
	      new MTest ("-sarg C3", "C3"),
	      new MTest ("-sarg C6", "C6"),
	      new MTest ("-sarg C8", new MErr('r',"C8")),
	      new MTest ("-sarg d5", new MErr('r',"d5")),
	      new MTest ("-sarg d6", "d6"),
	      new MTest ("-sarg d8", new MErr('r',"d8")),
	      new MTest ("-sarg zzz", new MErr('r',"zzz")),
	      new MTest ("-sarg 0", new MErr('r',"0")),
	    };
	   test.checkMatches (tests, MULTI_WORD);

	   test = new ArgParserTest();

	   test.checkAdd (
	      "-carg %c{1,2,(a,z],['A','Z'],['\\001',\\007),(4,8)}",
	      c3, 'c', 1, "-carg ",
	      new RngCheck[] 
	       { new RngCheck ('1'),
		 new RngCheck ('2'),
		 new RngCheck ('a',OPEN,'z',CLOSED),
		 new RngCheck ('A',CLOSED,'Z',CLOSED),
		 new RngCheck ('\001',CLOSED,'\007',OPEN),
		 new RngCheck ('4',OPEN,'8',OPEN),
	       },
	      "", null);

	   tests = new MTest[]
	    { 
	      new MTest ("-carg 1", new Character('1')),
              new MTest ("-carg 3", new MErr('r',"3")),
              new MTest ("-carg a", new MErr('r',"a")),
              new MTest ("-carg m", new Character('m')),
              new MTest ("-carg z", new Character('z')),
              new MTest ("-carg A", new Character('A')),
              new MTest ("-carg 'L'", new Character('L')),
              new MTest ("-carg 'Z'", new Character('Z')),
              new MTest ("-carg \\001", new Character('\001')),
              new MTest ("-carg \\005", new Character('\005')),
              new MTest ("-carg '\\007'", new MErr('r',"'\\007'")),
              new MTest ("-carg '4'", new MErr('r',"'4'")),
              new MTest ("-carg 6", new Character('6')),
              new MTest ("-carg 8", new MErr('r',"8")),
              new MTest ("-carg '\\012'", new MErr('r',"'\\012'")),
              new MTest ("-carg 0", new MErr('r',"0")),
	    };
	   test.checkMatches (tests, MULTI_WORD);

	   test.checkAdd (
	      "-foo=%i{[-50,100]}", ih, 'i', 1, "-foo=",
	      new RngCheck[] 
	       { new RngCheck (-50,CLOSED,100,CLOSED),
	       },
	      "", null);
	   
	   tests = new MTest[]
	    { 
	      new MTest ("-foo=-51", new MErr('r',"-51")),
	      new MTest ("-foo=-0x32", new Long(-0x32)),
	      new MTest ("-foo=-0x33", new MErr('r',"-0x33")),
	      new MTest ("-foo=-0777", new MErr('r',"-0777")),
	      new MTest ("-foo=-07", new Long(-07)),
	      new MTest ("-foo=0", new Long(0)),
	      new MTest ("-foo=100", new Long(100)),
	      new MTest ("-foo=0x5e", new Long(0x5e)),
	      new MTest ("-foo=066", new Long(066)),
	      new MTest ("-foo=06677", new MErr('r',"06677")),
	      new MTest ("-foo=0xbeef", new MErr('r',"0xbeef")),
	      new MTest ("-foo=foo", new MErr('m',"foo")),
	      new MTest ("-foo=-51d", new MErr('m',"-51d")),
	    };
	   test.checkMatches (tests, ONE_WORD);

	   test.checkAdd ("-foo2=%i", ih, 'i', 1, "-foo2=", null, "", null);
	   tests = new MTest[]
	    { 
	      new MTest ("-foo2=-51", new Long(-51)),
	      new MTest ("-foo2=-0x33", new Long(-0x33)),
	      new MTest ("-foo2=-0777", new Long(-0777)),
	      new MTest ("-foo2=06677", new Long(06677)),
	      new MTest ("-foo2=0xbeef", new Long(0xbeef)),
	      new MTest ("-foo2=foo", new MErr('m',"foo")),
	      new MTest ("-foo2=-51d", new MErr('m',"-51d")),
	      new MTest ("-foo2=-51", new Long(-51)),
	    };
	   test.checkMatches (tests, ONE_WORD);

	   test.checkAdd ("-foo3 %iX3", i3, 'i', 3, "-foo3 ", null, "", null);
	   tests = new MTest[]
	    { 
	      new MTest ("-foo3 -51 678 0x45",
			 new long[] { -51, 678, 0x45 }),
	      new MTest ("-foo3 55 16f 55", new MErr ('m', "16f")),
	      new MTest ("-foo3 55 16", new MErr ('v', "3")),
	    };
	   test.checkMatches (tests, MULTI_WORD);

	   Vector vec = new Vector(100);

	   test.checkAdd ("-foov3 %iX3", vec,'i',3,"-foov3 ",null,"",null);
	   tests = new MTest[]
	    { new MTest ("-foov3 -1 2 4", new long[] {-1, 2, 4}, 0),
	      new MTest ("-foov3 10 3 9", new long[] {10, 3, 9}, 1),
	      new MTest ("-foov3 123 1 0", new long[] {123, 1, 0}, 2),
	    };
	   vec.clear();
	   test.checkMatches (tests, MULTI_WORD);
	   test.checkAdd ("-foov %i", vec,'i',1,"-foov ",null,"",null);
	   tests = new MTest[]
	    { new MTest ("-foov 11", new Long(11), 0),
	      new MTest ("-foov 12", new Long(12), 1),
	      new MTest ("-foov 13", new Long(13), 2),
	    };
	   vec.clear();
	   test.checkMatches (tests, MULTI_WORD);

	   test.checkAdd (
	      "-foo4 %i{[-50,100]}X2", i3, 'i', 2, "-foo4 ",
	      new RngCheck[] 
	       { new RngCheck (-50,CLOSED,100,CLOSED),
	       },
	      "", null);
	   tests = new MTest[]
	    { 
	      new MTest ("-foo4 -49 78",
			 new long[] { -49, 78 }),
	      new MTest ("-foo4 -48 102", new MErr ('r', "102")),
	    };
	   test.checkMatches (tests, MULTI_WORD);

	   test.checkAdd (
	      "-oct=%o{[-062,0144]}", ih, 'o', 1, "-oct=",
	      new RngCheck[] 
	       { new RngCheck (-50,CLOSED,100,CLOSED),
	       },
	      "", null);
	   
	   tests = new MTest[]
	    {
	      new MTest ("-oct=-063", new MErr('r',"-063")),
	      new MTest ("-oct=-0x32", new MErr('m',"-0x32")),
	      new MTest ("-oct=-0777", new MErr('r',"-0777")),
	      new MTest ("-oct=-07", new Long(-07)),
	      new MTest ("-oct=0", new Long(0)),
	      new MTest ("-oct=100", new Long(64)),
	      new MTest ("-oct=0xae", new MErr('m',"0xae")),
	      new MTest ("-oct=66", new Long(066)),
	      new MTest ("-oct=06677", new MErr('r',"06677")),
	      new MTest ("-oct=0xbeef", new MErr('m',"0xbeef")),
	      new MTest ("-oct=foo", new MErr('m',"foo")),
	      new MTest ("-oct=-51d", new MErr('m',"-51d")),
	      new MTest ("-oct=78", new MErr('m',"78")),
	    };
	   test.checkMatches (tests, ONE_WORD);

	   test.checkAdd ("-oct2=%o", ih, 'o', 1, "-oct2=", null, "", null);
	   tests = new MTest[]
	    {
	      new MTest ("-oct2=-063", new Long(-063)),
	      new MTest ("-oct2=-0777", new Long(-0777)),
	      new MTest ("-oct2=06677", new Long(06677)),
	    };
	   test.checkMatches (tests, ONE_WORD);

	   test.checkAdd (
	      "-dec=%d{[-0x32,0x64]}", ih, 'd', 1, "-dec=",
	      new RngCheck[] 
	       { new RngCheck (-50,CLOSED,100,CLOSED),
	       },
	      "", null);
	   
	   tests = new MTest[]
	    { 
	      new MTest ("-dec=-063", new MErr('r',"-063")),
	      new MTest ("-dec=-0x32", new MErr('m',"-0x32")),
	      new MTest ("-dec=-0777", new MErr('r',"-0777")),
	      new MTest ("-dec=-07", new Long(-07)),
	      new MTest ("-dec=0", new Long(0)),
	      new MTest ("-dec=100", new Long(100)),
	      new MTest ("-dec=0xae", new MErr('m',"0xae")),
	      new MTest ("-dec=66", new Long(66)),
	      new MTest ("-dec=06677", new MErr('r',"06677")),
	      new MTest ("-dec=0xbeef", new MErr('m',"0xbeef")),
	      new MTest ("-dec=foo", new MErr('m',"foo")),
	      new MTest ("-dec=-51d", new MErr('m',"-51d")),
	    };
	   test.checkMatches (tests, ONE_WORD);

	   test.checkAdd ("-dec2=%d", ih, 'd', 1, "-dec2=", null, "", null);
	   tests = new MTest[]
	    { 
	      new MTest ("-dec2=-063", new Long(-63)),
	      new MTest ("-dec2=-0777", new Long(-777)),
	      new MTest ("-dec2=06677", new Long(6677)),
	    };
	   test.checkMatches (tests, ONE_WORD);

	   test.checkAdd (
	      "-hex=%x{[-0x32,0x64]}", ih, 'x', 1, "-hex=",
	      new RngCheck[] 
	       { new RngCheck (-50,CLOSED,100,CLOSED),
	       },
	      "", null);
	   
	   tests = new MTest[]
	    {
	      new MTest ("-hex=-06", new Long(-0x6)),
	      new MTest ("-hex=-0x3g2", new MErr('m',"-0x3g2")),
	      new MTest ("-hex=-0777", new MErr('r',"-0777")),
	      new MTest ("-hex=-017", new Long(-0x17)),
	      new MTest ("-hex=0", new Long(0)),
	      new MTest ("-hex=64", new Long(0x64)),
	      new MTest ("-hex=5e", new Long(0x5e)),
	      new MTest ("-hex=66", new MErr('r',"66")),
	      new MTest ("-hex=06677", new MErr('r',"06677")),
	      new MTest ("-hex=0xbeef", new MErr('m',"0xbeef")),
	      new MTest ("-hex=foo", new MErr('m',"foo")),
	      new MTest ("-hex=-51d", new MErr('r',"-51d")),
	      new MTest ("-hex=-51g", new MErr('m',"-51g")),
	      new MTest ("-hex=", new MErr('c',"")),
	    };
	   test.checkMatches (tests, ONE_WORD);

	   test.checkAdd ("-hex2=%x", ih, 'x', 1, "-hex2=", null, "", null);
	   tests = new MTest[]
	    {
	      new MTest ("-hex2=-0777", new Long(-0x777)),
	      new MTest ("-hex2=66", new Long(0x66)),
	      new MTest ("-hex2=06677", new Long(0x6677)),
	      new MTest ("-hex2=-51d", new Long(-0x51d)),
	    };
	   test.checkMatches (tests, ONE_WORD);

	   test.checkAdd (
	      "-char=%c{['b','m']}", ch, 'c', 1, "-char=",
	      new RngCheck[]
	       { new RngCheck ('b',CLOSED,'m',CLOSED),
	       },
	      "", null);

	   tests = new MTest[]
	    {
	      new MTest ("-char=a", new MErr('r',"a")),
	      new MTest ("-char=b", new Character('b')),
	      new MTest ("-char='b'", new Character('b')),
	      new MTest ("-char='\142'", new Character('b')),
	      new MTest ("-char='\141'", new MErr('r',"'\141'")),
	      new MTest ("-char=\142", new Character('b')),
	      new MTest ("-char=\141", new MErr('r',"\141")),
	      new MTest ("-char=m", new Character('m')),
	      new MTest ("-char=z", new MErr('r', "z")),
	      new MTest ("-char=bb", new MErr('m', "bb")),
	      new MTest ("-char='b", new MErr('m', "'b")),
	      new MTest ("-char='", new MErr('m', "'")),
	      new MTest ("-char=a'", new MErr('m', "a'")),
	    };
	   test.checkMatches (tests, ONE_WORD);

	   test.checkAdd ("-char2=%c", ch, 'c', 1, "-char2=",null,"",null);
	   tests = new MTest[]
	    {
	      new MTest ("-char2=a", new Character('a')),
	      new MTest ("-char2='\141'", new Character('\141')),
	      new MTest ("-char2=\141", new Character('\141')),
	      new MTest ("-char2=z", new Character('z')),
	    };
	   test.checkMatches (tests, ONE_WORD);

	   test.checkAdd ("-charv3 %cX3", vec,'c',3,"-charv3 ",null,"",null);
	   tests = new MTest[]
	    { new MTest ("-charv3 a b c", new char[] {'a', 'b', 'c'}, 0),
	      new MTest ("-charv3 'g' f '\\n'", new char[]{'g','f','\n'}, 1),
	      new MTest ("-charv3 1 \001 3", new char[] {'1', '\001', '3'}, 2),
	    };
	   vec.clear();
	   test.checkMatches (tests, MULTI_WORD);
	   test.checkAdd ("-charv=%c", vec,'c',1,"-charv=",null,"",null);
	   tests = new MTest[]
	    { new MTest ("-charv=d", new Character('d'), 0),
	      new MTest ("-charv='g'", new Character('g'), 1),
	      new MTest ("-charv=\111", new Character('\111'), 2),
	    };
	   vec.clear();
	   test.checkMatches (tests, ONE_WORD);

	   test.checkAdd (
	      "-bool=%b{true}", bh, 'b', 1, "-bool=",
	      new RngCheck[]
	       { new RngCheck (true),
	       },
	      "", null);
	   
	   tests = new MTest[]
	    {
	      new MTest ("-bool=true", new Boolean(true)),
	      new MTest ("-bool=false", new MErr('r', "false")),
	      new MTest ("-bool=fals", new MErr('m', "fals")),
	      new MTest ("-bool=falsem", new MErr('m', "falsem")),
	      new MTest ("-bool=truex", new MErr('m', "truex")),
	      new MTest ("-bool=foo", new MErr('m', "foo")),
	      new MTest ("-bool=1", new MErr('m', "1")),
	    };
	   test.checkMatches (tests, ONE_WORD);
	      
	   test.checkAdd (
	      "-boo2=%b{true,false}", bh, 'b', 1, "-boo2=",
	      new RngCheck[]
	       { new RngCheck (true),
		 new RngCheck (false),
	       },
	      "", null);
	   
	   tests = new MTest[]
	    {
	      new MTest ("-boo2=true", new Boolean(true)),
	      new MTest ("-boo2=false", new Boolean(false)),
	    };
	   test.checkMatches (tests, ONE_WORD);

	   test.checkAdd ("-boo3=%b", bh, 'b', 1, "-boo3=", null, "", null);
	   tests = new MTest[]
	    {
	      new MTest ("-boo3=true", new Boolean(true)),
	      new MTest ("-boo3=false", new Boolean(false)),
	    };
	   test.checkMatches (tests, ONE_WORD);

	   test.checkAdd ("-boo3 %bX3", b3, 'b', 3, "-boo3 ", null, "", null);
	   tests = new MTest[]
	    {
	      new MTest ("-boo3 true false true",
			 new boolean[] { true, false, true }),
	      new MTest ("-boo3 true fals true", new MErr ('m', "fals")),
	    };
	   test.checkMatches (tests, MULTI_WORD);

	   test.checkAdd ("-boov3 %bX3", vec,'b',3,"-boov3 ",null,"",null);
	   tests = new MTest[]
	    { new MTest ("-boov3 true true false",
			 new boolean [] { true, true, false }, 0),
	      new MTest ("-boov3 false false true",
			 new boolean [] { false, false, true }, 1),
	    };
	   vec.clear();
	   test.checkMatches (tests, MULTI_WORD);
	   test.checkAdd ("-boov %b", vec,'b',1,"-boov ",null,"",null);
	   tests = new MTest[]
	    { new MTest ("-boov true", new Boolean (true), 0),
	      new MTest ("-boov false", new Boolean (false), 1),
	      new MTest ("-boov true", new Boolean (true), 2),
	    };
	   vec.clear();
	   test.checkMatches (tests, MULTI_WORD);


	   test.checkAdd ("-v3 %vX2", b3, 'v', 2, "-v3 ", null, "", null);
	   tests = new MTest[]
	    { new MTest ("-v3", new boolean[] { true, true }),
	    };
	   test.checkMatches (tests, MULTI_WORD);

	   test.checkAdd (
	      "-vf %v{false,true}X2", b3, 'v', 2, "-vf ",
	      new RngCheck[]
	       { new RngCheck(false),
		 new RngCheck(true),
	       },
	      "", null);
	   tests = new MTest[]
	    { new MTest ("-vf", new boolean[] { false, false }),
	    };
	   test.checkMatches (tests, MULTI_WORD);

	   test.checkAdd (
	      "-str=%s{(john,zzzz]}", sh, 's', 1, "-str=",
	      new RngCheck[]
	       { new RngCheck ("john", OPEN, "zzzz", CLOSED),
	       },
	      "", null);
	   
	   tests = new MTest[]
	    {
	      new MTest ("-str=john", new MErr ('r', "john")),
	      new MTest ("-str=joho ", "joho "),
	      new MTest ("-str=joho ", "joho "),
	      new MTest ("-str=zzzz", "zzzz"),
	      new MTest ("-str= joho", new MErr ('r', " joho")),
	      new MTest ("-str=jnhn ", new MErr ('r', "jnhn ")),
	      new MTest ("-str=zzzzz", new MErr ('r', "zzzzz")),
	      new MTest ("-str=\"joho\"", new MErr ('r', "\"joho\"")),
	      new MTest ("-str=\"joho", new MErr('r', "\"joho")),
	      new MTest ("-str=joho j", "joho j"), // new MErr('m', "joho j")),
	    };
	   test.checkMatches (tests, ONE_WORD);

	   test.checkAdd ("-str2=%s", sh,'s',1,"-str2=",null,"",null);	   
	   tests = new MTest[]
	    {
	      new MTest ("-str2= jnhn", " jnhn"),
	      new MTest ("-str2=zzzzz", "zzzzz"),
	    };
	   test.checkMatches (tests, ONE_WORD);

	   test.checkAdd ("-str3 %sX3",s3,'s',3,"-str3 ",null,"",null);	   
	   tests = new MTest[]
	    {
	      new MTest ("-str3 foo bar johnny",
			 new String[] { "foo", "bar", "johnny" }),
	      new MTest ("-str3 zzzzz \"bad foo",
			 new String[] { "zzzzz", "\"bad", "foo"
			 }), // new MErr('m', "\"bad")),
	    };
	   test.checkMatches (tests, MULTI_WORD);

	   test.checkAdd ("-strv3 %sX3", vec,'s',3,"-strv3 ",null,"",null);
	   tests = new MTest[]
	    { new MTest ("-strv3 foo bar \"hihi\"",
			 new String[] {"foo", "bar", "\"hihi\""}, 0),
	      new MTest ("-strv3 a 123 gg",
			 new String[]{"a", "123", "gg"}, 1),
	    };
	   vec.clear();
	   test.checkMatches (tests, MULTI_WORD);
	   test.checkAdd ("-strv=%s", vec,'s',1,"-strv=",null,"",null);
	   tests = new MTest[]
	    { new MTest ("-strv=d", "d", 0),
	      new MTest ("-strv='g'", "'g'", 1),
	      new MTest ("-strv=\\111", "\\111", 2),
	    };
	   vec.clear();
	   test.checkMatches (tests, ONE_WORD);

	   test.checkAdd (
	      "-float=%f{(-0.001,1000.0]}", dh, 'f', 1, "-float=",
	      new RngCheck[]
	       { new RngCheck (-0.001, OPEN, 1000.0, CLOSED),
	       },
	      "", null);
	   
	   tests = new MTest[]
	    {
	      new MTest ("-float=-0.000999", new Double(-0.000999)),
	      new MTest ("-float=1e-3", new Double(0.001)),
	      new MTest ("-float=12.33e1", new Double(123.3)),
	      new MTest ("-float=1e3", new Double(1e3)),
	      new MTest ("-float=1000.000", new Double(1000.0)),
	      new MTest ("-float=-0.001", new MErr('r', "-0.001")),
	      new MTest ("-float=-1e-3", new MErr('r', "-1e-3")),
	      new MTest ("-float=1000.001", new MErr('r', "1000.001")),
	      new MTest ("-float=.", new MErr('m', ".")),
	      new MTest ("-float=  124.5 ", new Double (124.5)),
	      new MTest ("-float=124.5x", new MErr('m', "124.5x")),
	      new MTest ("-float= foo ", new MErr('m', " foo ")),
	      new MTest ("-float=1e1", new Double(10)),
	      new MTest ("-float=1e ", new MErr('m', "1e ")),
	    };
	   test.checkMatches (tests, ONE_WORD);

	   test.checkAdd ("-float2=%f", dh,'f',1,"-float2=",null,"",null);
	   tests = new MTest[]
	    {
	      new MTest ("-float2=-0.001", new Double(-0.001)),
	      new MTest ("-float2=-1e-3", new Double(-1e-3)),
	      new MTest ("-float2=1000.001", new Double(1000.001)),
	    };
	   test.checkMatches (tests, ONE_WORD);

	   test.checkAdd ("-f3 %fX3", d3,'f',3,"-f3 ",null,"",null);
	   tests = new MTest[]
	    {
	      new MTest ("-f3 -0.001 1.23e5 -9.88e-4",
			 new double[] { -0.001, 1.23e5, -9.88e-4 }),
	      new MTest ("-f3 7.88 foo 9.0", new MErr ('m', "foo")),
	      new MTest ("-f3 7.88 . 9.0", new MErr ('m', ".")),
	      new MTest ("-f3 7.88 3.0 9.0x", new MErr ('m', "9.0x")),
	    };
	   test.checkMatches (tests, MULTI_WORD);

	   test.checkAdd ("-fv3 %fX3", vec,'f',3,"-fv3 ",null,"",null);
	   tests = new MTest[]
	    { new MTest ("-fv3 1.0 3.444 6.7",
			 new double[] {1.0, 3.444, 6.7}, 0),
	      new MTest ("-fv3 13e-5 145.678 0.0001e45",
			 new double[]{13e-5, 145.678, 0.0001e45}, 1),
	      new MTest ("-fv3 11.11 3.1245 -1e-4",
			 new double[] {11.11, 3.1245, -1e-4}, 2),
	      new MTest ("-fv3 1.0 2 3",
			 new double[] { 1.0, 2.0, 3.0 }, 3),
	    };
	   vec.clear();
	   test.checkMatches (tests, MULTI_WORD);
	   test.checkAdd ("-fv %f", vec,'f',1,"-fv ",null,"",null);
	   tests = new MTest[]
	    { new MTest ("-fv -15.1234", new Double(-15.1234), 0),
	      new MTest ("-fv -1.234e-7", new Double(-1.234e-7), 1),
	      new MTest ("-fv 0.001111", new Double(0.001111), 2),
	    };
	   vec.clear();
	   test.checkMatches (tests, MULTI_WORD);


	   IntHolder intHolder = new IntHolder();
	   StringHolder strHolder = new StringHolder();

	   ArgParser parser = new ArgParser ("test");
	   parser.addOption ("-foo %d #an int", intHolder);
	   parser.addOption ("-bar %s #a string", strHolder);
	   args = new String[]
	      { "zzz", "-cat", "-foo", "123", "yyy", "-bar", "xxxx", "xxx" 
	      };

	   String[] unmatchedCheck = new String[]
	      { "zzz", "-cat", "yyy", "xxx"
	      };

	   String[] unmatched = parser.matchAllArgs (args, 0, 0);
	   test.checkStringArray (
	      "Unmatched args:", unmatched, unmatchedCheck);

	   vec.clear();
	   for (int i=0; i<args.length; )
	    { try
	       { i = parser.matchArg (args, i);
		 if (parser.getUnmatchedArgument() != null)
		  { vec.add (parser.getUnmatchedArgument());
		  }
	       }
	      catch (Exception e) 
	       { 
	       }
	    }
	   unmatched = (String[])vec.toArray(new String[0]);
	   test.checkStringArray (
	      "My unmatched args:", unmatched, unmatchedCheck);

	   System.out.println ("\nPassed\n");

	 }
}
