package nxm.sys.lib;

import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;

import nxm.sys.inc.AsciiMap;
import nxm.sys.inc.DataTypes;
import nxm.sys.inc.Debug;
import nxm.sys.inc.InternalUseOnly;
import nxm.sys.inc.MidasReference;

/** Class for getting parameters and switches from the command line. <br>
    <br>
    Command line parameters and switches are specified using a key=value syntax.
    If the key= is omitted, the key is obtained from the command dictionary by
    its positionally dependent (PD) index.  Only PD args are counted when
    determining the PD index. Key=value or /key=value entries are ignored.
    See the examples below.
    Switches are normally placed directly after the command name before the
    first delimiter.  They may also appear in a parameter slot but do not affect
    the PD index mapping.  Switches are specified by preceding the name with
    a "/" and must be fully specified. Parameter names may be abbreviated. <br>
    <br>
    The PLOT command, for example, has the command dictionary entry:
    <pre>
           PLOT*  P,1+ ,FILE=,
    </pre>
    The command line:
    <pre>
           PLOT/id=psdplot/tl=1k input y1=-100 LINE=SOLID /name="My Plot"
    </pre>
    creates an arguments Table with the values:
    <pre>
            /ID=PSDPLOT
            /TL=1k
            /NAME=My Plot
            FILE=INPUT
            LINE=SOLID
            Y1=-100
    </pre>
    To access the arguments from inside a command:
    <pre>
           filename = MA.getS("FILE");
           tl = MA.getL("/TL");    or  tl = MA.getL("/TL",-1);
           ymin = MA.getD("Y1");   or  ymin = MA.getD("Y1",0.0);
    </pre>

    The most useful aspect of this class is that on retrieving a specified
    argument from the set of arguments it will automatically be converted into
    the desired type. For example using the {@link #getD(java.lang.String)} to
    get the argument named <code>OFFSET</code>
    (i.e. <code>getD("OFFSET")</code>) and convert it to the appropriate value
    as Midas type (double). <br>
    <br>
    <h2><a id="get_methods"> Common <code>get&lt;Type&gt;(...)</code> Methods </a></h2>
    For convenience the methods to get a specific argument (either by position
    or by name) provide internal type translation. The basic syntax for these
    methods is:
    <ul>
      <li><code>get&lt;Type&gt;(int <i>n</i>)</code>
          Gets the <code><i>n</i></code>th argument in the command set as a
          specific type, <code><i>Type</i></code>.</li>
      <li><code>get&lt;Type&gt;(String <i>name</i>)</code>
          Gets the argument named <code><i>name</i></code> from the command set
          as a specific type, <code><i>Type</i></code>.</li>
      <li><code>get&lt;Type&gt;(String <i>name</i>,
                                <i>&lt;Type&gt;</i> default)</code>
          Gets the argument named <code><i>name</i></code> from the command set
          as a specific type, <code><i>Type</i></code>. If the named
          argument does not exist then the default value,
          <code><i>default</i></code> is returned.</li>
    </ul>
    The common <code>get&lt;Type&gt;(...)</code> methods are summarized
    in the table below.
    <table style="margin-left:auto;margin-right:auto;width:80%;"  border="1">
      <caption style="visibility:hidden">""</caption>
      <tr>
        <td colspan="5" style="text-align:center"><b>Numeric Types</b></td>
      </tr>
      <tr>
        <td style="text-align:center;"><b>Type</b></td>
        <td style="text-align:center;"><b>Macro<br>Usage</b></td>
        <td style="text-align:center;"><b>By Position</b></td>
        <td style="text-align:center;"><b>By Name</b></td>
        <td style="text-align:center;"><b>By Name (With Default Value)</b></td>
      </tr>
      <tr>
        <td style="text-align:left"  >Byte (8-bit)</td>
        <td style="text-align:center;"><code>B:</code></td>
        <td style="text-align:center;">{@link #getB(int)}</td>
        <td style="text-align:center;">{@link #getB(String)}</td>
        <td style="text-align:center;">{@link #getB(String,byte)}</td>
      </tr>
      <tr>
        <td style="text-align:left">Integer (16-bit)</td>
        <td style="text-align:center;"><code>I:</code></td>
        <td style="text-align:center;">{@link #getI(int)}</td>
        <td style="text-align:center;">{@link #getI(String)}</td>
        <td style="text-align:center;">{@link #getI(String,short)}</td>
      </tr>
      <tr>
        <td style="text-align:left"  >Long (32-bit)</td>
        <td style="text-align:center"><code>L:</code></td>
        <td style="text-align:center">{@link #getL(int)}</td>
        <td style="text-align:center">{@link #getL(String)}</td>
        <td style="text-align:center">{@link #getL(String,int)}</td>
      </tr>
      <tr>
        <td style="text-align:left"  >eXtralong(64-bit)</td>
        <td style="text-align:center"><code>X:</code></td>
        <td style="text-align:center">{@link #getX(int)}</td>
        <td style="text-align:center">{@link #getX(String)}</td>
        <td style="text-align:center">{@link #getX(String,long)}</td>
      </tr>
      <tr>
        <td style="text-align:left"  >Float (32-bit)</td>
        <td style="text-align:center"><code>F:</code></td>
        <td style="text-align:center">{@link #getF(int)}</td>
        <td style="text-align:center">{@link #getF(String)}</td>
        <td style="text-align:center">{@link #getF(String,float)}</td>
      </tr>
      <tr>
        <td style="text-align:left"  >Double (64-bit)</td>
        <td style="text-align:center"><code>D:</code></td>
        <td style="text-align:center">{@link #getD(int)}</td>
        <td style="text-align:center">{@link #getD(String)}</td>
        <td style="text-align:center">{@link #getD(String,double)}</td>
      </tr>
      <tr>
        <td colspan="5" style="text-align:center"><br><b>String Types</b></td>
      </tr>
      <tr>
        <td style="text-align:center"><b>Type</b></td>
        <td style="text-align:center"><b>Macro<br>Usage</b></td>
        <td style="text-align:center"><b>By Position</b></td>
        <td style="text-align:center"><b>By Name</b></td>
        <td style="text-align:center"><b>By Name (With Default Value)</b></td>
      </tr>
      <tr>
        <td style="text-align:left"  >Case Sensitive</td>
        <td style="text-align:center"><code>CS:</code></td>
        <td style="text-align:center">{@link #getCS(int)}</td>
        <td style="text-align:center">{@link #getCS(String)}</td>
        <td style="text-align:center">{@link #getCS(String,String)}<br>
                           <i>{@link #getString(String,String,int)}</i></td>
      </tr>
      <tr>
        <td style="text-align:left"  >Quoted</td>
        <td style="text-align:center"><code>Q:</code></td>
        <td style="text-align:center">{@link #getQ(int)}</td>
        <td style="text-align:center">{@link #getQ(String)}</td>
        <td style="text-align:center">{@link #getQ(String,String)}</td>
      </tr>
      <tr>
        <td style="text-align:left"  >Normal</td>
        <td style="text-align:center"><code>S:</code></td>
        <td style="text-align:center">{@link #getS(int)}</td>
        <td style="text-align:center">{@link #getS(String)}</td>
        <td style="text-align:center">{@link #getS(String,String)}</td>
      </tr>
      <tr>
        <td style="text-align:left"  >Untranslated</td>
        <td style="text-align:center"><code>U:</code></td>
        <td style="text-align:center">{@link #getU(int)}</td>
        <td style="text-align:center">{@link #getU(String)}</td>
        <td style="text-align:center">{@link #getU(String,String)}</td>
      </tr>
      <tr>
        <td style="text-align:left"  >Unquoted</td>
        <td style="text-align:center"><code>&nbsp;</code></td>
        <td style="text-align:center">{@link #getUQ(int)}</td>
        <td style="text-align:center">{@link #getUQ(String)}</td>
        <td style="text-align:center">{@link #getUQ(String,String)}</td>
      </tr>
      <tr>
        <td colspan="5" style="text-align:center"><br><b>Boolean Types</b></td>
      </tr>
      <tr>
        <td style="text-align:center"><b>Type</b></td>
        <td style="text-align:center"><b>Macro<br>Usage</b></td>
        <td style="text-align:center"><b>By Position</b></td>
        <td style="text-align:center"><b>By Name</b></td>
        <td style="text-align:center"><b>By Name (With Default Value)</b></td>
      </tr>
      <tr>
        <td style="text-align:left"  >State</td>
        <td style="text-align:center"><code>Z:</code></td>
        <td style="text-align:center">{@link #getZ(int)}<br>{@link #getState(int)}</td>
        <td style="text-align:center">{@link #getZ(String)}<br>{@link #getState(String)}</td>
        <td style="text-align:center">{@link #getZ(String,boolean)}<br>{@link #getState(String,boolean)}</td>
      </tr>
      <tr>
        <td style="text-align:left"  >Inverse State</td>
        <td style="text-align:center"><code>&nbsp;</code></td>
        <td style="text-align:center">{@link #getNoState(int)}</td>
        <td style="text-align:center">{@link #getNoState(String)}</td>
        <td style="text-align:center">{@link #getNoState(String,boolean)}</td>
      </tr>
      <tr>
        <td colspan="5" style="text-align:center"><br><b>File Types</b></td>
      </tr>
      <tr>
        <td style="text-align:center"><b>Type</b></td>
        <td style="text-align:center"><b>Macro<br>Usage</b></td>
        <td style="text-align:center"><b>By Position</b></td>
        <td style="text-align:center"><b>By Name</b></td>
        <td style="text-align:center"><b>By Name (With Default Value)</b></td>
      </tr>
      <tr>
        <td style="text-align:left"  >File Name</td>
        <td style="text-align:center"><code>FN:</code></td>
        <td style="text-align:center">{@link #getFileName(int)}</td>
        <td style="text-align:center">{@link #getFileName(String)}</td>
        <td style="text-align:center">{@link #getFileName(String,String)}</td>
      </tr>
      <tr>
        <td style="text-align:left"  >Auto File</td>
        <td style="text-align:center"><code>&nbsp;</code></td>
        <td style="text-align:center">{@link #getFile(int)}</td>
        <td style="text-align:center">{@link #getFile(String)}</td>
        <td style="text-align:center">&nbsp;</td>
      </tr>
      <tr>
        <td style="text-align:left"  >Specific File</td>
        <td style="text-align:center"><code>&nbsp;</code></td>
        <td style="text-align:center">{@link #getFile(int,Class)}</td>
        <td style="text-align:center">{@link #getFile(String,Class)}</td>
        <td style="text-align:center">&nbsp;</td>
      </tr>
      <tr>
        <td style="text-align:left"  >Base File</td>
        <td style="text-align:center"><code>&nbsp;</code></td>
        <td style="text-align:center">{@link #getBaseFile(int)}</td>
        <td style="text-align:center">{@link #getBaseFile(String)}</td>
        <td style="text-align:center">&nbsp;</td>
      </tr>
      <tr>
        <td style="text-align:left"  >Data File</td>
        <td style="text-align:center"><code>&nbsp;</code></td>
        <td style="text-align:center">{@link #getDataFile(int)}</td>
        <td style="text-align:center">{@link #getDataFile(String)}</td>
        <td style="text-align:center"><i>
                  {@link #getDataFile(String,DataFile,int)}<br>
                  {@link #getDataFile(String,String,String,int)
                          getDataFile(String,String,...)}<br>
                  {@link #getDataFile(String,String,String,int,double,DataFile)
                          getDataFile(String,String,...)}
                  </i></td>
      </tr>
      <tr>
        <td style="text-align:left"  >Text File</td>
        <td style="text-align:center"><code>&nbsp;</code></td>
        <td style="text-align:center">{@link #getTextFile(int)}</td>
        <td style="text-align:center">{@link #getTextFile(String)}</td>
        <td style="text-align:center">{@link #getTextFile(String,TextFile)}</td>
      </tr>
      <tr>
        <td colspan="5" style="text-align:center"><br><b>Object Types</b></td>
      </tr>
      <tr>
        <td style="text-align:center"><b>Type</b></td>
        <td style="text-align:center"><b>Macro<br>Usage</b></td>
        <td style="text-align:center"><b>By Position</b></td>
        <td style="text-align:center"><b>By Name</b></td>
        <td style="text-align:center"><b>By Name (With Default Value)</b></td>
      </tr>
      <tr>
        <td style="text-align:left"  >Data</td>
        <td style="text-align:center"><code>&nbsp;</code></td>
        <td style="text-align:center">{@link #getData(int)}</td>
        <td style="text-align:center">{@link #getData(String)}</td>
        <td style="text-align:center">{@link #getData(String,Data)}</td>
      </tr>
      <tr>
        <td style="text-align:left"  >Message</td>
        <td style="text-align:center"><code>&nbsp;</code></td>
        <td style="text-align:center">{@link #getMessage(int)}</td>
        <td style="text-align:center">{@link #getMessage(String)}</td>
        <td style="text-align:center">{@link #getMessage(String,Message)}</td>
      </tr>
      <tr>
        <td style="text-align:left"  >Object</td>
        <td style="text-align:center"><code>O:</code></td>
        <td style="text-align:center">{@link #getO(int)}</td>
        <td style="text-align:center">{@link #getO(String)}</td>
        <td style="text-align:center">{@link #getO(String,Object)}</td>
      </tr>
      <tr>
        <td style="text-align:left"  >Table</td>
        <td style="text-align:center"><code>T:</code></td>
        <td style="text-align:center">{@link #getTable(int)}</td>
        <td style="text-align:center">{@link #getTable(String)}</td>
        <td style="text-align:center">{@link #getTable(String,Table)}</td>
      </tr>
      <tr>
        <td style="text-align:left"  >Time Value</td>
        <td style="text-align:center"><code>&nbsp;</code></td>
        <td style="text-align:center">{@link #getTime(int)}</td>
        <td style="text-align:center">{@link #getTime(String)}</td>
        <td style="text-align:center">{@link #getTime(String,Time)}</td>
      </tr>
    </table>

    @see nxm.sys.inc.DataTypes

    @author Jeff Schoen
 */
public class Args implements AsciiMap, MidasReference {
  private static final int I_NARGS = 3; // Positional index of the NumArgs field in the commands.cnf

  /** This is a pipe-separated (<code>|</code>) list of the logical states that
      indicate a true value. Note that this must start and end with a pipe
      character (<code>|</code>) in order to be compatible with the existing
      implementation.
      <br>
      True States = <code>{@value}</code>

      @see #falseStates
      @see #getState(String,boolean)
      @see #getNoState(String,boolean)
   */
  public static final String trueStates = nxm.sys.inc.DataTypes.trueStates+"1|PASS|SUCCESS|ABSC|ABS|AB|";

  /** This is a pipe-separated (<code>|</code>) list of the logical states that
      indicate a false value. Note that this must start and end with a pipe
      character (<code>|</code>) in order to be compatible with the existing
      implementation.
      <br>
      False States = <code>{@value}</code>

      @see #trueStates
      @see #getState(String,boolean)
      @see #getNoState(String,boolean)
   */
  public static final String falseStates = nxm.sys.inc.DataTypes.falseStates+"0|FAIL|INDEX|IND|IN|";


  /** The Midas Context to use. */
  public Midas M;

  /** The number of arguments. */
  public int nargs;

  /** The number of arguments plus switches. */
  public int nargsx;

  /** The command. */
  public Command cmd;

  /** The name of the command. */
  public String name;

  /** The option the command belongs to. */
  public String option;

  /** The command line, as entered (preserving case). */
  public String cmdline;

  /** The command's support. @see #getSupport() */
  public char support;

  /** The command's class name (if null determine based on option,area,support). */
  private String cmdClassName = null;

  /** Command line argument translation (bit-mask) flag value indicating that no flags are selected. */
  public static final int FLAGS_NONE=0x0;

  /** Command line argument translation flag indicating that carets should be resolved. */
  public static final int RESOLVE=0x1;

  /** Command line argument translation flag indicating that the quotes on a string should be kept. */
  public static final int KEEP_QUOTES=0x2;

  /** Command line argument translation flag indicating that the quotes on a
      string should not be kept, but the string should be converted to upper-case. */
  public static final int UPCASE_UNQUOTED=0x4;

  /** Command line argument translation flag indicating that the quotes on a
      string should be kept, and the string should be converted to upper-case. */
  public static final int UPCASE_QUOTED=0x8;

  /** Command line argument translation flag indicating that string should be
      converted to upper-case. I.e. UPCASE_UNQUOTED|UPCASE_QUOTED */
  public static final int UPCASE=0xC;

  /** Command line argument translation flag indicating that specified input
      argument is required (it must be specified) otherwise a MidasException
      will be thrown with an error message that the argument is required.
      @since NeXtMidas 2.9.3
   */
  public static final int REQUIRED=0x10;

  /** Command line argument translation flag indicating that no error check should
      done when trying to convert argument to desired type.
      e.g. flag is used by {@link #getTable(String, Table, int)}.
      @since NeXtMidas 2.7.1
   */
  public static final int NO_ERROR=0x20;

  /** Command line argument translation flag indicating that an empty state switch (switch without
      a value) should NOT return sdefval in {@link #getString(String, String, String, int)}, it will
      return {@link #STATE_SWITCH_VAL} instead. This flag is primarily for usage by
      {@link #getState(String)} to return true vs the default value of false for an empty state switch.
      @since NeXtMidas 3.1.1
   */
  public static final int STATE_CHECK=0x40;

  /** The value used for an empty State Switch (switch without a value) to allow caller to
      distinguish between an empty state switch (/switch) vs a switch assigned to an empty
      string (/switch="" or /switch=).
      @since NeXtMidas 2.9.0
   */
  @SuppressWarnings("all") // The use of 'new String("")' is essential here
  public static final String STATE_SWITCH_VAL = new String("");  // added for Bug 2285 to tell between regular ""

  /** The value used for an empty State Switch as a double. Since NeXtMidas 3.3.1. */
  @InternalUseOnly("Since NeXtMidas 3.3.1, this has always been meant for internal use only")
  public static final Double STATE_SWITCH_D = Double.valueOf(1.0);
  /** The value used for an empty State Switch as a float. Since NeXtMidas 3.3.1. */
  @InternalUseOnly("Since NeXtMidas 3.3.1, this has always been meant for internal use only")
  public static final Float STATE_SWITCH_F = Float.valueOf(1.0f);
  /** The value used for an empty State Switch as a long. Since NeXtMidas 3.3.1. */
  @InternalUseOnly("Since NeXtMidas 3.3.1, this has always been meant for internal use only")
  public static final Long STATE_SWITCH_X = Long.valueOf(1L);
  /** The value used for an empty State Switch as a int. Since NeXtMidas 3.3.1. */
  @InternalUseOnly("Since NeXtMidas 3.3.1, this has always been meant for internal use only")
  public static final Integer STATE_SWITCH_L = Integer.valueOf(1);
  /** The value used for an empty State Switch as a short. Since NeXtMidas 3.3.1. */
  @InternalUseOnly("Since NeXtMidas 3.3.1, this has always been meant for internal use only")
  public static final Short STATE_SWITCH_I = Short.valueOf((short)1);
  /** The value used for an empty State Switch as a byte. Since NeXtMidas 3.3.1. */
  @InternalUseOnly("Since NeXtMidas 3.3.1, this has always been meant for internal use only")
  public static final Byte STATE_SWITCH_B = Byte.valueOf((byte)1);

  /** The KeyVector holding all of the parameters/switches and their values. */
  private KeyVector kv;

  /** The line number in the macro this was read from (-1=NotApplicable) */
  private int lineNumber = -1;

  /** Initialize the Midas command context.
     @param M The Midas context to use.
  */
  public Args (Midas M) {
    this.M = M;
    kv = new KeyVector(32);
  }

  /** Initialize the Midas command context.
     @param M The Midas context to use.
     @param p The parser to use.
  */
  public Args (Midas M, Parser p) {
    this(M);
    put("all",p.getAll());
    for (nargs=0; p.more();) {
      nargs++;
      put("P"+nargs,p.get(nargs));
    }
  }

  //////////////////////////////////////////////////////////////////////////////
  // NUMERICAL ACCESSORS
  //////////////////////////////////////////////////////////////////////////////

  /** returns the proper numeric default for switches/parameters. */
  private int getNDef (String name) {
    return (name.charAt(0)==SLASH)? -1 : 0;
  }

  /** Gets the command line parameter at a given position as an 8-bit Byte.
      This is a convenience method equal to <code>getB(getKey(arg))</code>.
      @param arg The position index.
      @return The results of calling <code>getB(getKey(arg))</code>.
      @see #getB(String)
  */
  public byte getB (int arg) { return getB(getKey(arg)); }

  /** Gets the command line parameter with the given name as an 8-bit Byte.
      This is a convenience method equal to <code>getB(name,(byte)0)</code>.
      @param name The switch/parameter name.
      @return The results of calling <code>getB(name,(byte)0)</code>.
      @see #getB(String,byte)
  */
  public byte getB (String name) { return getB(name,(byte)getNDef(name)); }

  /** Gets the command line parameter with the given name as an 8-bit Byte.
      @param name  The switch/parameter name.
      @param def   The default value if not present.
      @return The command line parameter or 'def' as applicable.
  */
  public byte getB (String name, byte def) { return getB(name, (Byte)def); }

  /** Gets the command line parameter with the given name as a 8-bit Byte.
      @param name  The switch/parameter name.
      @param def   The default value if not present.
      @return The command line parameter or 'def' as applicable.
      @since NeXtMidas 3.3.1
   */
  public final Byte getB (String name, Byte def) { return getB(name, def, STATE_SWITCH_B); }

  /** Gets the command line parameter with the given name as a 8-bit Byte.
      @param name  The switch/parameter name.
      @param def   The default value if not present.
      @param sdef  The default value if only a state switch is present.
      @return The command line parameter or 'def' or 'sdef' as applicable.
      @since NeXtMidas 3.3.1
   */
  public final Byte getB (String name, Byte def, Byte sdef) {
    return (Byte)getNumber(name, 'B', def, sdef);
  }

  /** Gets the command line parameter at a given position as a 16-bit Midas Integer (Java short).
      @param arg The position index.
      @return The results of calling <code>getI(getKey(arg))</code>.
      @see #getI(String)
  */
  public short getI (int arg) { return getI(getKey(arg)); }

  /** Gets the command line parameter with the given name as a 16-bit Midas Integer (Java short).
      @param name The switch/parameter name.
      @return The results of calling <code>getI(name,(short)0)</code>.
      @see #getI(String,short)
  */
  public short getI (String name) { return getI(name,(short)getNDef(name)); }

  /** Gets the command line parameter with the given name as a 16-bit Midas Integer (Java short).
      @param name  The switch/parameter name.
      @param def   The default value for the switch/parameter.
      @return The command line parameter with the given switch/parameter name
              as a Midas (short) integer or <code><i>defvalue</i></code>
              if the parameter does not exist.
  */
  public short getI (String name, short def) { return getI(name, (Short)def); }

  /** Gets the command line parameter with the given name as a 16-bit Midas Integer (Java short).
      @param name  The switch/parameter name.
      @param def   The default value if not present.
      @return The command line parameter or 'def' as applicable.
      @since NeXtMidas 3.3.1
   */
  public final Short getI (String name, Short def) { return getI(name, def, STATE_SWITCH_I); }

  /** Gets the command line parameter with the given name as a 16-bit Midas Integer (Java short).
      @param name  The switch/parameter name.
      @param def   The default value if not present.
      @param sdef  The default value if only a state switch is present.
      @return The command line parameter or 'def' or 'sdef' as applicable.
      @since NeXtMidas 3.3.1
   */
  public final Short getI (String name, Short def, Short sdef) {
    return (Short)getNumber(name, 'I', def, sdef);
  }

  /** Gets the command line parameter at a given position as a 32-bit Midas Long.
      This is a convenience method equal to <code>getL(getKey(arg))</code>.
      @param arg The position index.
      @return The results of calling <code>getL(getKey(arg))</code>.
      @see #getL(int,int)
  */
  public int getL (int arg) { return getL(getKey(arg)); }

  /** Gets the command line parameter at a given position as a 32-bit Midas Long (Java int).
      This is a convenience method equal to <code>getL(getKey(arg),defValue)</code>.
      @param arg   The position index.
      @param def   The default value if not present.
      @return The results of calling <code>getL(getKey(arg),defValue)</code>.
      @see #getL(String,int)
  */
  public int getL (int arg, int def) { return getL(getKey(arg),def); }

  /** Gets the command line parameter with the given name as a 32-bit Midas Long (Java int).
      This is a convenience method equal to <code>getL(name,(int)0)</code>.
      @param name The switch/parameter name.
      @return The results of calling <code>getL(name,(int)0)</code>.
      @see #getL(String,int)
  */
  public int getL (String name) { return getL(name,getNDef(name)); }

  /** Gets the command line parameter with the given name as a 32-bit Midas Long (Java int).
      @param name  The switch/parameter name.
      @param def   The default value if not present.
      @return The command line parameter or 'def' as applicable.
  */
  public int getL (String name, int def) { return getL(name, (Integer)def); }

  /** Gets the command line parameter with the given name as a 32-bit Midas Long (Java int).
      @param name  The switch/parameter name.
      @param def   The default value if not present.
      @return The command line parameter or 'def' as applicable.
      @since NeXtMidas 3.3.1
   */
  public final Integer getL (String name, Integer def) { return getL(name, def, STATE_SWITCH_L); }

  /** Gets the command line parameter with the given name as a 32-bit Midas Long (Java int).
      @param name  The switch/parameter name.
      @param def   The default value if not present.
      @param sdef  The default value if only a state switch is present.
      @return The command line parameter or 'def' or 'sdef' as applicable.
      @since NeXtMidas 3.3.1
   */
  public final Integer getL (String name, Integer def, Integer sdef) {
    return (Integer)getNumber(name, 'L', def, sdef);
  }

  /** Gets the command line parameter at a given position as a 64-bit Midas eXtra long (Java long).
      @param arg The position index.
      @return The results of calling <code>getX(getKey(arg))</code>.
      @see  #getX(String)
  */
  public long getX (int arg) { return getX(getKey(arg)); }

  /** Gets the command line parameter at a given position as a 64-bit Midas eXtra long (Java long).
      @param arg The position index.
      @param def The default value.
      @return The command line parameter or 'def' or 'sdef' as applicable.
  */
  public long getX (int arg, long def) { return getX(getKey(arg), def); }

  /** Gets the command line parameter with the given name as 64-bit Midas eXtra long (Java long).
      @param name The switch/parameter name.
      @return The results of calling <code>getX(getKey(arg),(long)0)</code>.
      @see #getX(String,long)
  */
  public long getX (String name) { return getX(name,(long)getNDef(name)); }

  /** Gets the command line parameter with the given name as 64-bit Midas eXtra long (Java long).
      @param name  The switch/parameter name.
      @param def   The default value if not present.
      @return The command line parameter or 'def' as applicable.
  */
  public long getX (String name, long def) { return getX(name, (Long)def); }

  /** Gets the command line parameter with the given name as a 64-bit Midas eXtra long (Java long).
      @param name  The switch/parameter name.
      @param def   The default value if not present.
      @return The command line parameter or 'def' as applicable.
      @since NeXtMidas 3.3.1
   */
  public final Long getX (String name, Long def) { return getX(name, def, STATE_SWITCH_X); }

  /** Gets the command line parameter with the given name as a 64-bit Midas eXtra long (Java long).
      @param name  The switch/parameter name.
      @param def   The default value if not present.
      @param sdef  The default value if only a state switch is present.
      @return The command line parameter or 'def' or 'sdef' as applicable.
      @since NeXtMidas 3.3.1
   */
  public final Long getX (String name, Long def, Long sdef) {
    return (Long)getNumber(name, 'X', def, sdef);
  }

  /** Gets the command line parameter at a given position as a 32-bit Midas Float.
      This is a convenience method equal to <code>getF(getKey(arg))</code>.
      @param arg The position index.
      @return The results of calling <code>getF(getKey(arg))</code>.
      @see  #getF(String,float)
  */
  public float getF (int arg) { return getF(getKey(arg)); }

  /** Gets the command line parameter with the given name as a 32-bit Midas Float.
      This is a convenience method equal to <code>getF(name,(float)0)</code>.
      @param name The switch/parameter name.
      @return The results of calling <code>getF(name,(float)0)</code>.
      @see #getF(String,float)
  */
  public float getF (String name) { return getF(name,(float)getNDef(name)); }

  /** Gets the command line parameter with the given name as a 32-bit Midas Float.
      @param name  The switch/parameter name.
      @param def   The default value if not present.
      @return The command line parameter or 'def' as applicable.
  */
  public float getF (String name, float def) { return getF(name, (Float)def); }

  /** Gets the command line parameter with the given name as a 32-bit Midas Float.
      @param name  The switch/parameter name.
      @param def   The default value if not present.
      @return The command line parameter or 'def' as applicable.
      @since NeXtMidas 3.3.1
   */
  public final Float getF (String name, Float def) { return getF(name, def, STATE_SWITCH_F); }

  /** Gets the command line parameter with the given name as a 32-bit Midas Float.
      @param name  The switch/parameter name.
      @param def   The default value if not present.
      @param sdef  The default value if only a state switch is present.
      @return The command line parameter or 'def' or 'sdef' as applicable.
      @since NeXtMidas 3.3.1
   */
  public final Float getF (String name, Float def, Float sdef) {
    return (Float)getNumber(name, 'F', def, sdef);
  }

  /** Gets the command line parameter at a given position as a 64-bit Midas Double.
      This is a convenience method equal to <code>getD(getKey(arg))</code>.
      @param arg The position index.
      @return The results of calling <code>getD(getKey(arg))</code>.
      @see  #getD(String,double)
  */
  public double getD (int arg) { return getD(getKey(arg)); }

  /** Gets the command line parameter with the given name as a 64-bit Midas Double.
      This is a convenience method equal to <code>getD(name,(double)0)</code>.
      @param name The switch/parameter name.
      @return The results of calling <code>getD(name,(double)0)</code>.
      @see #getD(String,double)
  */
  public double getD (String name) { return getD(name,(double)getNDef(name)); }

  /** Gets the command line parameter with the given name as a 64-bit Midas Double.
      @param name     The switch/parameter name.
      @param def   The default value if not present.
      @return The command line parameter or 'def' as applicable.
   */
  public double getD (String name, double def) { return getD(name, (Double)def); }

  /** Gets the command line parameter with the given name as a 64-bit Midas Double.
      @param name  The switch/parameter name.
      @param def   The default value if not present.
      @return The command line parameter or 'def' as applicable.
      @since NeXtMidas 3.3.1
   */
  public final Double getD (String name, Double def) { return getD(name, def, STATE_SWITCH_D); }

  /** Gets the command line parameter with the given name as a 64-bit Midas Double.
      @param name  The switch/parameter name.
      @param def   The default value if not present.
      @param sdef  The default value if only a state switch is present.
      @return The command line parameter or 'def' or 'sdef' as applicable.
      @since NeXtMidas 3.3.1
   */
  public final Double getD (String name, Double def, Double sdef) {
    return (Double)getNumber(name, 'D', def, sdef);
  }

  /** Gets the command line parameter with the given name as a number.
      @param name  The switch/parameter name.
      @param type  The type (D, F, X, L, I, B).
      @param def   The default value if not present.
      @param sdef  The default value if only a state switch is present.
      @return The command line parameter or 'def' or 'sdef' as applicable.
      @since NeXtMidas 3.3.1
   */
  private Number getNumber (String name, char type, Number def, Number sdef) {
    String value = getRaw(name);
    if (value==null) return def;
    if (value.length()==0 && name.charAt(0)==SLASH) return sdef;
    value = value.toUpperCase();
    Object obj = ((type==DataTypes.BYTE)||(type==DataTypes.INT))?
                   Convert.s2o(value,(char)DataTypes.LONG,this)
                  : Convert.s2o(value,type,this);
    int lval;
    if (obj==null) throw new MidasException("Unable to convert "+name+" to type "+type);
    switch (type) {
      case DataTypes.DOUBLE: return Convert.o2d(obj);
      case DataTypes.FLOAT:  return Convert.o2f(obj);
      case DataTypes.XLONG:  return Convert.o2x(obj);
      case DataTypes.LONG:   return Convert.o2l(obj);
      case DataTypes.INT:
        lval = Convert.o2l(obj);
        if (lval < DataTypes.IMIN) throw new MidasException("Value "+lval+" out of I: range");
        if (lval > DataTypes.IMAX) throw new MidasException("Value "+lval+" out of I: range");
        return (short)lval;
      case DataTypes.BYTE:
        lval = Convert.o2l(obj);
        if (lval < DataTypes.BMIN) throw new MidasException("Value "+lval+" out of B: range");
        if (lval > DataTypes.BMAX) throw new MidasException("Value "+lval+" out of B: range");
        return (byte)lval;
    }
    throw new AssertionError("Unexpected type '"+type+"'");
  }

  //////////////////////////////////////////////////////////////////////////////
  // STRING ACCESSORS
  //////////////////////////////////////////////////////////////////////////////
  /** Gets the command line parameter at a given position as a case sensitive string.
      This is a convenience method equal to <code>getCS(getKey(arg))</code>.
      @param arg The position index.
      @return The results of calling <code>getCS(getKey(arg))</code>.
      @see  #getCS(String,String)
   */
  public String getCS (int arg) { return getCS(getKey(arg)); }

  /** Gets the command line parameter with the given name as a case sensitive string.
      This is a convenience method equal to <code>getCS(name,"")</code>.
      @param name The switch/parameter name.
      @return The results of calling <code>getCS(name,"")</code>.
      @see #getCS(String,String)
   */
  public String getCS (String name) { return getCS(name,""); }

  /** Gets the command line parameter with the given name as a case sensitive string.
      @param name     The switch/parameter name.
      @param defvalue The default value for the switch/parameter is not present.
      @return The command line parameter with the given switch/parameter name as a case sensitive string,
              or <code>defValue</code> if the parameter does not exist,
              or a blank string for a switch without a value.
   */
  public String getCS (String name, String defvalue) {
    return getString (name,defvalue,RESOLVE);
  }

  /** Gets the command line parameter with the given name as a case sensitive string.
      @param name     The switch/parameter name.
      @param defvalue The default value for the switch/parameter if NOT present.
      @param sdefval  The default value if only a state switch is present (e.g. /stateSwitch)
      @return The command line parameter with the given switch/parameter name as a case sensitive string,
              or <code>defValue</code> if the parameter does not exist,
              or <code>sdefval</code> for a switch without a value.
      @since NeXtMidas 3.1.3
   */
  public String getCS (String name, String defvalue, String sdefval) {
    return getString (name, defvalue, sdefval, RESOLVE);
  }

  /** Gets the command line parameter at a given position as a quoted string.
      This is a convenience method equal to <code>getQ(getKey(arg))</code>.
      @param arg The position index.
      @return The results of calling <code>getQ(getKey(arg))</code>.
      @see #getQ(String,String)
   */
  public String getQ (int arg) { return getQ(getKey(arg)); }

  /** Gets the command line parameter with the given name as a quoted string.
      This is a convenience method equal to <code>getQ(name,"")</code>.
      @param name The switch/parameter name.
      @return The results of calling <code>getQ(name,"")</code>.
      @see #getQ(String,String)
   */
  public String getQ (String name) { return getQ(name,""); }

  /** Gets the command line parameter with the given name as a quoted string.
      @param name     The switch/parameter name.
      @param defvalue The default value for the switch/parameter is not present.
      @return The command line parameter with the given switch/parameter name as an untranslated string,
              or <code>defValue</code> if the parameter does not exist,
              or a blank string for a switch without a value.
   */
  public String getQ (String name, String defvalue) {
    return getString (name,defvalue,0);
  }

  /** Gets the command line parameter with the given name as a quoted string.
      @param name     The switch/parameter name.
      @param defvalue The default value for the switch/parameter if NOT present.
      @param sdefval  The default value if only a state switch is present (e.g. /stateSwitch)
      @return The command line parameter with the given switch/parameter name as an untranslated string,
              or <code>defValue</code> if the parameter does not exist,
              or <code>sdefval</code> for a switch without a value.
      @since NeXtMidas 3.1.3
   */
  public String getQ (String name, String defvalue, String sdefval) {
    return getString (name, defvalue, sdefval, 0);
  }

  /** Gets the command line parameter at the given position as an uppercase string.
      This is a convenience method equal to <code>getS(getKey(arg))</code>.
      @param arg The position index.
      @return The results of calling <code>getS(getKey(arg))</code>.
      @see  #getS(String,String)
   */
  public String getS (int arg) { return getS(getKey(arg)); }

  /** Gets the command line parameter with the given name as an uppercase string.
      This is a convenience method equal to <code>getS(name,"")</code>.
      @param name The switch/parameter name.
      @return The results of calling <code>getS(name,"")</code>.
      @see #getS(String,String)
   */
  public String getS (String name) { return getS(name,""); }

  /** Gets the command line parameter with the given name as an uppercase string.
      @param name     The switch/parameter name.
      @param defvalue The default value for the switch/parameter is not present.
      @return The command line parameter with the given switch/parameter name as a string,
              or <code>defvalue</code> if the parameter does not exist,
              or a blank string for a switch without a value.
   */
  public String getS (String name, String defvalue) {
    return getString (name,defvalue,RESOLVE|UPCASE_UNQUOTED);
  }

  /** Gets the command line parameter with the given name as an uppercase string.
      @param name     The switch/parameter name.
      @param defvalue The default value for the switch/parameter if NOT present.
      @param sdefval  The default value if only a state switch is present (e.g. /stateSwitch)
      @return The command line parameter with the given switch/parameter name as a string,
              or <code>defvalue</code> if the parameter does not exist,
              or <code>sdefval</code> for a switch without a value.
      @since NeXtMidas 3.1.3
  */
  public String getS (String name, String defvalue, String sdefval) {
    return getString (name, defvalue, sdefval, RESOLVE|UPCASE_UNQUOTED);
  }

  /** Gets the command line parameter with the given name as a case sensitive string.
      See {@link #getString(String, String, String, int)} for details.<br>
      NOTE: When given a switch without a value (e.g. /stateSwitch), this will return a blank string.
            Use <code>getString(name,defval,sdefval,flags)</code>
            if you want to use a different state switch default value (sdefval).

      @param name     The switch/parameter name.
      @param defvalue The default value for the switch/parameter is not present.
      @param flags    The options to apply when getting the string.

      @return The command line parameter with the given switch/parameter name as a case sensitive string,
              or <code>defValue</code> if the parameter does not exist,
              or a blank string for a switch without a value.
      @throws MidasException if argument is not specified AND given the {@link #REQUIRED} flag.
      @see <a href="../../../nxm/sys/lib/Args.html#get_methods">Common<code>get&lt;Type&gt;(...)</code> Methods</a>
   */
  public String getString (String name, String defvalue, int flags) throws MidasException {
    return getString(name, defvalue, "", flags);
  }

  /** Gets the command line parameter with the given name as a case sensitive string. The following
      summarizes the recognized flags which can be used to alter the results (to include more than
      one flag, simply bitwise-or the flags together [e.g. <code>RESOLVE|KEEP_QUOTES</code>]):
       <ul>
        <li>{@link #RESOLVE}         - Do not resolve/translate any of the
                                       carets in the string (i.e. don't use
                                       {@link #getRaw(String)}).</li>
        <li>{@link #KEEP_QUOTES}     - Indicates if any quotes around the
                                       value should be removed.</li>
        <li>{@link #UPCASE_UNQUOTED} - Indicates that the unquoted part of the
                                       value should be converted to upper case.
                                       </li>
        <li>{@link #UPCASE_QUOTED}   - Indicates that the quoted part of the
                                       value should be converted to upper case.
                                       </li>
        <li>{@link #REQUIRED}        - This command-line parameter is required.
                                       A MidasException will be thrown if argument
                                       is NOT specified. Since 2.9.3.</li>
        <li>{@link #STATE_CHECK}     - Doing state check (called by <code>getState(..)</code>,
                                       so do NOT return sdefval for a switch without a value
                                       (e.g. /stateSwitch) instead return {@link #STATE_SWITCH_VAL}.
                                       Since 3.1.1.</li>
      </ul>
      Note that if <code>name</code> does not match a given switch/parameter
      name that has been given, then <code>defValue</code> will be used. In
      that event, <code>defvalue</code> will be converted based on the flags
      specified in <code>flags</code>.

      @param name     The switch/parameter name.
      @param defvalue The default value for the switch/parameter if NOT present
      @param sdefval  The default value if only a state switch is present (e.g. /stateSwitch)
      @param flags    The options to apply when getting the string.

      @return The command line parameter with the given switch/parameter name as a case sensitive string,
              or <code>defvalue</code> if the parameter does not exist,
              or <code>sdefval</code> for a switch without a value.
      @throws MidasException if argument is not specified AND given the {@link #REQUIRED} flag.
      @since NeXtMidas 3.1.3
      @see <a href="../../../nxm/sys/lib/Args.html#get_methods">Common
           <code>get&lt;Type&gt;(...)</code> Methods</a>
   */
  @SuppressWarnings("all") // The use of 'value==STATE_SWITCH_VAL' is essential here
  public String getString (String name, String defvalue, String sdefval, int flags) throws MidasException {
    String value = getRaw(name);
    if (value==null) { // null for not present
      if ((flags&REQUIRED) != 0) throw new MidasException("Argument '"+name+"=' is required.");
      value = defvalue;
      flags &= (~RESOLVE); // remove RESOLVE from flags since using default value
    } else if ((value==STATE_SWITCH_VAL) && (flags&STATE_CHECK)==0) { // since 3.1.3: if switch w/o value AND not doing state check
      value = sdefval;
      flags &= (~RESOLVE); // remove RESOLVE from flags since using state switch default value
    }
    if (value==null || value.length()==0) return value; // no need to resolve null or empty string value
    return resolveString(value,flags);
  }

  /** Resolves a string result. Mainly for the getString method
   *  @param value the string to be resolved
   *  @param flags the flags to use while resolving the string. possible values:
   *    {@link #RESOLVE},{@link #KEEP_QUOTES},{@link #UPCASE_UNQUOTED},{@link #UPCASE_QUOTED}
   *  @return the resolved string result.
   */
  public String resolveString (String value, int flags) {
    boolean resolve = (flags&RESOLVE)!=0;
    boolean quotes = (flags&KEEP_QUOTES)!=0;
    boolean upcase = (flags&UPCASE_UNQUOTED)!=0;
    boolean upcase_quoted = (flags&UPCASE_QUOTED)!=0;
    int ls = value.length();
    if (ls>0 && value.charAt(0)==SLASH && resolve) {
      int vls = Results.validNameLength(value,1);
      if (vls==ls-1) { // treat as switch
        String tmp = getRaw(value.toUpperCase());
        if (tmp==null) value="NULL"; else value=tmp;
        ls=value.length();
      }
    }
    if (ls==0) { }
    else if (value.charAt(0)==BTIC) {
      value = value.substring(1,ls-1);
    }
    else if (value.charAt(0)==QUOTE) {
      if (quotes) { } // leave them on as is
      else if (value.charAt(ls-1)==QUOTE) value = value.substring(1,ls-1);
      else value = value.substring(1,ls);
      if (upcase_quoted) value = Parser.toUpperCaseWithQuotes(value);
    } else {
      // The following block of code should be revisited, see Bug 1754
      if (resolve && Results.validNameLength(value,0) == ls) {
        // (BUG 2811) Argument passed to get() changed from value.toUpperCase() in line below. This is to remedy an issue with on-the-fly method invocations
        //            where  case sensitive arguments to said methods were being converted to uppercase (i.e. startswith() and endswith()).   -ADW
        Object resvalue = M.results.get(Parser.toUpperCaseWithQuotes(value));
        if (resvalue instanceof String) {
          // resolve and leave case unless forced
          value = resvalue.toString(); upcase = upcase_quoted;
        }
        else if ((resvalue instanceof Data) && ((Data)resvalue).isString()) {
          value = resvalue.toString();
        }
        else if ((resvalue instanceof XMValue) && (((XMValue)resvalue).getFormatType() == XMValue.ASCII)) {
          value = resvalue.toString();
        }
        else if ((resvalue instanceof Number) || (resvalue instanceof Boolean)) {
          value = resvalue.toString();
        }
      }
      if (upcase) value = Parser.toUpperCaseWithQuotes(value);
    }
    return value;
  }

  /** Gets the command line parameter at a given position as an untranslated string.
      This is a convenience method equal to <code>getU(getKey(arg))</code>.
      @param arg The position index.
      @return The results of calling <code>getU(getKey(arg))</code>.
      @see  #getU(String,String)
  */
  public String getU (int arg) { return getU(getKey(arg)); }

  /** Gets the command line parameter with the given name as an untranslated string.
      This is a convenience method equal to <code>getU(name,"")</code>.
      @param name The switch/parameter name.
      @return The results of calling <code>getU(name,"")</code>.
      @see #getU(String,String)
  */
  public String getU (String name) { return getU(name,""); }

  /** Gets the command line parameter with the given name as an untranslated string.
      @param name     The switch/parameter name.
      @param defvalue The default value for the switch/parameter.
      @return The command line parameter with the given switch/parameter name as
              an untranslated string, or <code>defValue</code> if the parameter does not exist,
              or a blank string if it is a state switch.
  */
  public String getU (String name, String defvalue) {
    return getString (name,defvalue,UPCASE_UNQUOTED);
  }

  /** Gets the command line parameter with the given name as an untranslated string.
      @param name     The switch/parameter name.
      @param defvalue The default value for the switch/parameter is not present.
      @param sdefval  The default value if only a state switch is present (e.g. /sw)
      @return The command line parameter with the given switch/parameter name as
              an untranslated string, or <code>defValue</code> if the parameter does not exist,
              or <code>sdefval</code> if it is a state switch.
      @since NeXtMidas 3.1.3
  */
  public String getU (String name, String defvalue, String sdefval) {
    return getString (name, defvalue, sdefval, UPCASE_UNQUOTED);
  }

  /** Gets the command line parameter at a given position as an untranslated quoted string.
      This is a convenience method equal to <code>getUQ(getKey(arg))</code>.
      @param arg The position index.
      @return The results of calling <code>getUQ(getKey(arg))</code>.
      @see #getUQ(String)
  */
  public String getUQ (int arg) { return getUQ(getKey(arg)); }

  /** Gets the command line parameter with the given name as an untranslated quoted string.
      This is a convenience method equal to <code>getUQ(name,null)</code>.
      @param name The switch/parameter name.
      @return The results of calling <code>getUQ(name,null)</code>.
      @see #getUQ(String,String)
  */
  public String getUQ (String name) {
    return getUQ(name,null);
  }

  /** Gets the command line parameter with the given name as an untranslated quoted string.
      @param name The switch/parameter name.
      @param defvalue The default value for the switch/parameter.
      @return The command line parameter with the given switch/parameter name as
              a string or defvalue if the parameter does not exist.
  */
  public String getUQ (String name, String defvalue) {
    return getString (name,defvalue,UPCASE_UNQUOTED|KEEP_QUOTES);
  }

  //////////////////////////////////////////////////////////////////////////////
  // STATE ACCESSORS
  //////////////////////////////////////////////////////////////////////////////
  /** Gets the boolean state of a specified result/parameter.
      This is a convenience method equal to <code>getState(arg)</code>.
      @param arg The position index.
      @return The results of calling <code>getState(getKey(arg),false)</code>.
      @see #getState(String,boolean)
      @since NeXtMidas 2.5.0
  */
  public final boolean getZ (int arg) { return getState(getKey(arg),false); }

  /** Gets the boolean state of a specified result/parameter.
      This is a convenience method equal to <code>getState(name)</code>.
      @param name The switch/parameter name.
      @return The results of calling <code>getState(name,false)</code>.
      @see #getState(String,boolean)
      @since NeXtMidas 2.5.0
  */
  public final boolean getZ (String name) { return getState(name,false); }

  /** Gets the boolean state of a specified result/parameter.
      This is a convenience method equal to <code>getState(name,defvalue)</code>.
      @param name     The switch/parameter name.
      @param defvalue The default value for the switch/parameter.
      @return The results of calling <code>getState(name,defvalue)</code>.
      @since NeXtMidas 2.5.0
  */
  public final boolean getZ (String name, boolean defvalue) { return getState(name,defvalue); }

  /** Gets the boolean state of a specified result/parameter.
      This is a convenience method equal to <code>getState(getKey(arg),false)</code>.
      @param arg The position index.
      @return The results of calling <code>getState(getKey(arg),false)</code>.
      @see #getState(String,boolean)
  */
  public boolean getState (int arg) { return getState(getKey(arg),false); }

  /** Gets the boolean state of a specified result/parameter.
      This is a convenience method equal to <code>getState(name,false)</code>.
      @param name The switch/parameter name.
      @return The results of calling <code>getState(name,false)</code>.
      @see #getState(String,boolean)
  */
  public boolean getState (String name) { return getState(name,false); }

  /** Gets the boolean state of a specified result/parameter. For the purposes
      of this method the following define the states:
      <ul>
        <li>True States  = Anything that matches a state in {@link #trueStates}
                           or a numerical value, <code>n</code> where
                           <code>n &gt; 0</code>.</li>
        <li>False States = Anything that matches a state in {@link #falseStates}
                           or a numerical value, <code>n</code> where
                           <code>n &lt;= 0</code>.</li>
      </ul>
      Note that the argument is parsed twice for one of these conditions. Once
      without results substitution and then once with results substitution. This
      eliminates problems where a macro has one of these tokens as a results
      parameter.
      @param name     The switch/parameter name.
      @param defvalue The default value for the switch/parameter.
      @return <code>true</code> if the command line parameter matches one of the
              "True States," <code>false</code> if the command line parameter
              matches one of the "False States," or <code>defValue</code> if the
              parameter (specified by <code>name</code>) does not exist.
      @see #trueStates
      @see #falseStates
      @see <a href="../../../nxm/sys/lib/Args.html#get_methods">Common
           <code>get&lt;Type&gt;(...)</code> Methods</a>
  */
  public boolean getState (String name, boolean defvalue) {
    String svalue, text;
    boolean isSwitch = name.startsWith("/");
    svalue = getString(name,null,UPCASE|STATE_CHECK);
    if (svalue==null||svalue.equals("NULL")) return defvalue; // not present
    if (svalue.length()==0 && isSwitch)      return true;     // empty state switch present
    text = "|"+svalue+"|";      // string entry
    if (trueStates.indexOf(text) >= 0)  return true;
    if (falseStates.indexOf(text) >= 0) return false;
    // no simple matches, now try to resolve the answer
    isSwitch = isSwitch || svalue.startsWith("/");
    svalue = getString(name,null,RESOLVE|UPCASE|STATE_CHECK);
    if (svalue==null||svalue.equals("NULL")) return defvalue; // not present
    if (svalue.length()==0 && isSwitch)      return true;     // empty state switch present
    text = "|"+svalue+"|";      // string entry
    if (trueStates.indexOf(text) >= 0)  return true;
    if (falseStates.indexOf(text) >= 0) return false;
    Data value = getData(name,'_');
    if (value==null) return defvalue;   // not present
    if (!value.isString()) return value.toD() > 0;   // numeric
    if (value.size==0) return false;    // state switch not present
    if (svalue.startsWith("(") && svalue.endsWith(")")) return logicalTest(this, svalue);
    throw new MidasException("Illegal state value: "+name+"="+value.toString());
  }

  /** Gets the inverse of the boolean state of a specified result/parameter.
      This is a convenience method equal to <code>getNoState(getKey(arg))</code>.
      @param arg The position index.
      @return The results of calling <code>getNoState(getKey(arg))</code>.
      @see #getNoState(String)
  */
  public boolean getNoState (int arg) { return getNoState(getKey(arg)); }

  /** Gets the inverse of the boolean state of a specified result/parameter.
      This is a convenience method equal to <code>getNoState(name,false)</code>.
      @param name The switch/parameter name.
      @return The results of calling <code>getNoState(name,false)</code>.
      @see #getState(String,boolean)
  */
  public boolean getNoState (String name) { return getNoState(name,false); }


  /** Gets the inverse of the boolean state of a specified result/parameter.
      This is a convenience method equal to <code>!getState(name,!defvalue)</code>.
      @param name     The switch/parameter name.
      @param defvalue The default value for the switch/parameter.
      @return The results of calling <code>!getState(name,!defvalue)</code>.
      @see #getState(String,boolean)
  */
  public boolean getNoState (String name, boolean defvalue) {
    return !getState(name,!defvalue);
  }


  //////////////////////////////////////////////////////////////////////////////
  // FILE ACCESSORS
  //////////////////////////////////////////////////////////////////////////////
  /** Gets the command line parameter at a given position as a FileName.
      This is a convenience method equal to <code>getFileName(getKey(arg))</code>.
      @param arg The position index indicating the position of the file name.
      @return  The results of calling <code>getFileName(getKey(arg))</code>.
      @see #getFileName(String)
  */
  public FileName getFileName (int arg) { return getFileName(getKey(arg)); }

  /** Gets the command line parameter at a given position as a file name.
      This is a convenience method equal to <code>getFileName(name,"")</code>.
      @param name The switch/parameter name.
      @return  The results of calling <code>getFileName(name,"")</code>.
      @see #getFileName(String,String)
  */
  public FileName getFileName (String name) {
    return getFileName (name, "", "");
  }

  /** Gets the command line parameter at a given position as a file name. This will include
      any trimmers, qualifiers and any results translation that may be specified.
      @param name     The switch/parameter name.
      @param defvalue The default value for the switch/parameter is not present.
      @return The command line parameter at the given position as a file name,
              or <code>new FileName(defvalue)</code> if the parameter does not exist,
              or <code>new FileName("")</code> for a switch without a value.
  */
  public FileName getFileName (String name, String defvalue) {
    return getFileName(name, defvalue, "");
  }

  /** Gets the command line parameter at a given position as a file name. This will include
      any trimmers, qualifiers and any results translation that may be specified.
      @param name     The switch/parameter name.
      @param defvalue The default value for the switch/parameter is not present.
      @param sdefval  The default value if only a state switch is present (e.g. /stateSwitch)
      @return The command line parameter at the given position as a file name,
              or <code>new FileName(defvalue)</code> if the parameter does not exist,
              or <code>new FileName(sdefval)</code> for a switch without a value.
      @since NeXtMidas 3.1.3
  */
  public FileName getFileName (String name, String defvalue, String sdefval) {
    String fname = getString(name, defvalue, sdefval, KEEP_QUOTES);
    return BaseFile.getFileNameFor(cmd,fname);
  }

  /** Gets the command line parameter at a given position as a file name. This
      will include any trimmers, qualifiers and any results translation that
      may be specified.
      @param name     The switch/parameter name.
      @param flags    The options to apply when getting the parameter (e.g. {@link #REQUIRED}).
      @return The command line parameter at the given position as a file name or
          <code>new FileName(defvalue)</code> if the parameter does not exist.
      @throws MidasException if argument is not specified AND given the {@link #REQUIRED} flag.
      @see #getString(String, String, int)
      @since NeXtMidas 2.9.3
   */
  public FileName getFileName (String name, int flags) {
    String fname = getString(name, null, KEEP_QUOTES|flags);
    return BaseFile.getFileNameFor(cmd,fname);
  }

  /** Gets the command line parameter at a given position as a file.
      This is a convenience method equal to <code>getFile(getKey(arg))</code>.
      @param arg The position index indicating the position of the file name.
      @return The results of calling <code>getFile(getKey(arg))</code>.
      @see #getFile(String)
   */
  public BaseFile getFile (int arg) { return getFile(getKey(arg)); }

  /** Get a sub-class of BaseFile for input/output. Unlike the {@link #getBaseFile(String)} method,
      this method will attempt to get the most specific type of file using the
      {@link BaseFile#getInstanceFor(Object,Object)} method. Thus this method will often return an
      instance of one of the subclasses of {@link BaseFile} rather than a direct instance of
      {@link BaseFile}. <br>
      <br>
      This is functionally identical to <code>getFile(name, BaseFile.class)</code>.
      @param name The name of the parameter/switch that indicates the file's name.
      @return The base file with the file name as provided by the user.
      @see #getFileName(String)
      @see BaseFile#getInstanceFor(Object,Object)
      @see #getFile(String,Class)
   */
  public BaseFile getFile (String name) {
    FileName fn = getFileName(name);
    BaseFile bf = BaseFile.getInstanceFor(cmd,fn);
    return bf;
  }

  /** Gets the command line parameter at a given position as a file.
      This is a convenience method equal to <code>getFile(getKey(arg),type)</code>.
      @param arg The position index indicating the position of the file name.
      @param type The file type to get.
      @param <T> This describes my type parameter.
      @return The results of calling <code>getFile(getKey(arg),type)</code>.
      @since NeXtMidas 2.7.0
      @see #getFile(String,Class)
   */
  public <T extends Object> T getFile (int arg, Class<T> type) { return getFile(getKey(arg), type); }

  /** Get a sub-class of {@link BaseFile} for input/output. Unlike the {@link #getBaseFile(String)}
      method, this method will attempt to get the most specific type of file using the
      {@link BaseFile#getInstanceFor(Object,Object)} method (which checks <code>REG.HANDLERS.FILE</code>).
      <br>
      <br>
      This method will then cast the file to the specified type, if that fails (i.e. because the
      file does not extend/implement <code>type</code>) an exception will be thrown indicating that.
      Note that this is a class-cast check, not an exactness check (e.g. if given <code>CsvFile.class</code>,
      the resulting file may be a sub-class of <code>CsvFile</code>). <br>
      <br>
      This relies on the handlers table (<code>REG.HANDLERS.FILE</code>) being up-to-date and does not
      do a <code>new ClassName(..)</code> (where <code>ClassName</code> matches the indicated <code>type</code>)
      to create the file.
      @param name The name of the parameter/switch that indicates the file's name.
      @param type The file type to get. This must be a subclass of {@link BaseFile} (e.g.
                  <code>CsvFile.class</code>) or an interface that is implemented by a sub-class of
                  {@link BaseFile} (e.g. <code>ListFile.class</code>).
      @param <T> This describes my type parameter.
      @return The name of the command-line parameter/switch where the user passes in the file name.
      @throws NullPointerException if <code>type</code> is null.
      @throws MidasException if the applicable file handler for the specified file does not
                             implement/extend <code>type</code>.
      @since NeXtMidas 2.7.0
      @see #getFileName(String)
      @see BaseFile#getInstanceFor(Object,Object)
   */
  @SuppressWarnings("unchecked") // for return (T)bf; has a try check block around it already
  public <T extends Object> T getFile (String name, Class<T> type) {
    if (type == null) throw new NullPointerException("Input parameter type can not be null.");
    FileName fn = getFileName(name);
    BaseFile bf = BaseFile.getInstanceFor(cmd,fn);

    try {
      return (T)bf;
    }
    catch (ClassCastException e) {
      throw new MidasException("Can not convert "+name+"="+fn+" to an instance of "+type.getName()+".");
    }
  }

  /** Gets the command line parameter at a given position as a BaseFile.
      This is a convenience method equal to <code>getBaseFile(getKey(arg))</code>.
      @param arg The position index indicating the position of the file name.
      @return The results of calling <code>getBaseFile(getKey(arg))</code>.
      @see #getBaseFile(String)
  */
  public BaseFile getBaseFile (int arg) { return getBaseFile(getKey(arg)); }

  /** Get a BaseFile for input/output. Unlike the {@link #getFile(String)}
      method, the file returned will be an instance of {@link BaseFile} and not
      an instance of one of the subclasses of {@link BaseFile}.
      @param name The name of the parameter/switch that indicates the file's name.
      @return The base file with the file name as provided by the user.
      @see #getFileName(String)
  */
  public BaseFile getBaseFile (String name) {
    FileName fn = getFileName(name);
    BaseFile bf = new BaseFile(cmd,fn);
    return bf;
  }

  /** Get a DataFile for any type of input.
      This is a convenience method equal to <code>getDataFile(getKey(arg))</code>.
      @param arg The position index indicating the position of the file name.
      @return The results of calling <code>getDataFile(getKey(arg))</code>.
      @see #getDataFile(String)
  */
  public DataFile getDataFile (int arg) { return getDataFile(getKey(arg)); }

  /** Get a DataFile for any type of input.
      This is a convenience method equal to <code>getDataFile(name,"","",0)</code>.
      @param name The name of the parameter/switch that indicates the file's name.
      @return The results of calling <code>getDataFile(name,"","",0)</code>.
      @see #getFileName(String)
      @see #getDataFile(String,String,String,int)
  */
  public DataFile getDataFile (String name) {
    return getDataFile (name,"","",0);
  }

  /** Get a DataFile with type/format checking.
      This is a convenience method equal to <code>getDataFile(name,types,formats,flags,-1.0,null)</code>.
      @param name    The name of the parameter/switch that indicates the file's name.
      @param types   The types for the data in the file.
      @param formats The formats for the data in the file.
      @param flags   Any flags to open the file with (e.g. {@link DataFile#OUTPUT}, {@link DataFile#APPEND}).
      @return Creates a new DataFile using the filename returned by <code>getFileName(name)</code>
              and the other options passed into this method. This never returns null.
      @see #getFileName(String)
      @see #getDataFile(String,String,String,int,double,DataFile)
  */
  public DataFile getDataFile(String name, String types, String formats, int flags) {
    return getDataFile(name,types,formats,flags,0.0,null);
  }

  /** Get a DataFile for output based on input DataFile.
      This is a convenience method equal to <code>getDataFile(name,"","",flags|DataFile.OUTPUT,-1.0,hcbin)</code>.
      @param name  The name of the parameter/switch that indicates the file's name.
      @param hcbin An existing file to copy the header from.
      @param flags Any flags to open the file with (e.g.  {@link DataFile#OUTPUT}, {@link DataFile#APPEND}).
      @return Creates a new DataFile using the filename returned by <code>getFileName(name)</code>.
              This propagates the header from the hcbin input DataFile. This never returns null.
      @see #getFileName(String)
      @see #getDataFile(String,String,String,int,double,DataFile)
      @see DataFile#OUTPUT
  */
  public DataFile getDataFile (String name, DataFile hcbin, int flags) {
    return getDataFile(name,"","",flags,0.0,hcbin);
  }

  /** Get a DataFile for input/output.
      @param name    The name of the parameter/switch that indicates the file's name.
      @param types   The types for the data in the file.
      @param formats The formats for the data in the file.
      @param flags   Any flags to open the file with (e.g. {@link DataFile#OUTPUT}, {@link DataFile#APPEND}).
      @param size    The size of the file (this is ignored if it is not greater than zero).
      @param hcbin   An existing file to copy the header from.
      @return The data file with the file name as provided by the user.

      @see DataTypes
      @see DataFile#init(MidasReference, Object, String, String, int, double, DataFile)
      @see DataFile#setSize(double)
      @see #getFileName(String)
  */
  public DataFile getDataFile (String name, String types, String formats,
                               int flags, double size, DataFile hcbin) {
//    DataFile df = new DataFile(cmd,getFileName(name),types,formats,flags,size,hcbin);
    FileName fn = getFileName(name);
    BaseFile bf = BaseFile.getInstanceFor(cmd,fn);
    DataFile df = (bf instanceof DataFile)? (DataFile)bf : new DataFile();
    df.init(cmd,fn,types,formats,flags,size,hcbin);
    if (cmd!=null) cmd.addProgressFeed(df);
    return df;
  }

  /** Get a TextFile for input/output by position.
      This is a convenience method equal to <code>getTextFile(getKey(arg))</code>.
      @param arg The position index indicating the position of the file name.
      @return The results of calling <code>getTextFile(getKey(arg))</code>.
      @see #getTextFile(String)
  */
  public TextFile getTextFile (int arg) {
    return getTextFile(getKey(arg));
  }

  /** Get a TextFile for input/output.
      This is a convenience method equal to <code>getTextFile(name,null)</code>.
      @param name The name of the parameter/switch that indicates the file's name.
      @return The results of calling <code>getTextFile(name,null)</code>.
      @see #getTextFile(String,TextFile)
  */
  public TextFile getTextFile (String name) {
    return getTextFile(name, null);
  }

  /** Get a TextFile for input/output.
      @param name The name of the parameter/switch that indicates the file's name.
      @param flags    The options to apply when getting the parameter (e.g. {@link #REQUIRED}).
      @return The text file with the file name as provided by the user
      @throws MidasException if argument is not specified AND given the {@link #REQUIRED} flag.
      @since NeXtMidas 2.9.3
   */
  public TextFile getTextFile (String name, int flags) {
    FileName fn = getFileName(name, flags);
    TextFile tf = new TextFile(cmd,fn);
    return tf;
  }

  /** Get a TextFile for input/output with a default TextFile.
      @param name   The name of the parameter/switch that indicates the file's name.
      @param defTF  The default value for the TextFile (TBD: currently not used)
      @return The text file with the file name as provided by the user or the default
      @see #getFileName(String)
  */
  public TextFile getTextFile (String name, TextFile defTF) {
    FileName fn = getFileName(name);
    TextFile tf = new TextFile(cmd,fn);
    if (tf==null) return defTF; // TODO: dead code!!!
    else          return tf;
  }

  //////////////////////////////////////////////////////////////////////////////
  // OBJECT ACCESSORS
  //////////////////////////////////////////////////////////////////////////////
  /** Gets the command line parameter at a given position as a Data object.
      This is a convenience method equal to <code>getData(getKey(arg))</code>.
      @param arg The position index.
      @return The results of calling <code>getData(getKey(arg))</code>.
      @see #getData(String)
  */
  public Data getData (int arg) { return getData(getKey(arg)); }

  /** Gets the command line parameter at a given position as a Data object.
      This is a convenience method equal to <code>getData(name,null)</code>.
      @param name The switch/parameter name.
      @return The results of calling <code>getData(name,null)</code>.
      @see #getData(String,Data)
  */
  public Data getData (String name) { return getData(name,null); }

  /** Gets the command line parameter at a given position as a Data object.
      @param name     The switch/parameter name.
      @param defvalue The default value for the switch/parameter.
      @return The command line parameter at the given position or {@code defvalue} if the parameter does not exist.
  */
  public Data getData (String name, Data defvalue) {
    Data value = getData(name,'_');
    if (value != null) return value;
    else return defvalue;
  }

  /** Gets the command line parameter at with the given name as Midas Data object of the given type.
      @param name The switch/parameter name.
      @param type The type character to use in converting the parameter to a
                  data type (e.g. <code>'F'</code> or <code>'L'</code>).
      @return The command line parameter with the given switch/parameter name as
              a Midas Data object or <code>null</code> if the parameter does not exist.
      @see DataTypes
  */
  private Data getData (String name, char type) {
    String value = getRaw(name);
    if (value==null) return null;
    value = Parser.toUpperCaseWithQuotes(value);
    return Data.fromString(value,type,this);
  }

  /** Gets the command line parameter at with the given name as a numeric Midas Data object.
      @param name The switch/parameter name.
      @return The command line parameter with the given switch/parameter name as
              a Midas Data object or <code>null</code> if the parameter does not exist.
      @since NeXtMidas 3.3.2
   */
  @InternalUseOnly("Since NeXtMidas 3.3.2, this has always been meant for internal use only, but may become provisional in the future") // <-- may make public in future (?)
  private Data getNumericData (String name) {
    String value = getRaw(name);
    if (value==null) return null;
    value = Parser.toUpperCaseWithQuotes(value);
    Data data = Data.fromString(value,'_',this);
    if (Convert.isNumericConversion(data.getFormatTypeChar())) {
      // Found a data value with a basic '_' conversion
      return data;
    }
    return Data.fromString(value,'D',this);
  }

  /** Get a message based on a message ID given on the command line.
      This is a convenience method equal to <code>getMessage(getKey(arg))</code>.
      @param arg The position index.
      @return The results of calling <code>getMessage(getKey(arg))</code>.
      @see #getMessage(String)
  */
  public Message getMessage (int arg) {
    return getMessage(getKey(arg));
  }

  /** Get a message based on a message ID given on the command line.
      This is a convenience method equal to <code>getMessage(name,null)</code>.
      @param name The name of the command line parameter which indicates the message's ID.
      @return The results of calling <code>getMessage(name,null)</code>.
      @see #getMessage(String,Message)
  */
  public Message getMessage (String name) {
    return getMessage(name, null);
  }

  /** Get a message based on a message ID given on the command line. The message
      ID will be resolved in the results table to determine the message.
      @param name   The name of the command line parameter which indicates the message's ID.
      @param defMsg The default message to return.
      @return The message or <code>defMsg</code> if the message entered is invalid.
      @see Midas#results
  */
  public Message getMessage (String name, Message defMsg) {
    String value = getU(name,"");
    Object obj = M.results.get(value);
    if (obj instanceof Message) return (Message)obj;
    return defMsg;
  }

  /**
   * Get command line parameter by index and return as list
   * @param index The position index.
   * @return list
   * @since NeXtMidas 3.7.1 for NXM-2945
   */
  public List<?> getList (int index) {
    return Convert.o2List(getO(index));
  }

  /**
   * TODO this will be added in KeyableByType interface when java 7 is no longer supported
   * Get command line parameter by key and return as list
   * @param key key of command line parameter
   * @return list
   * @since NeXtMidas 3.7.1 for NXM-2945
   */
  // @Override
  public List<?> getList (String key) {
    return getList(key,null); //Convert.o2List(getO(key));
  }

  /**
   * Get command line parameter by index and return as list
   * @param key key of command line parameter
   * @param list default list entered by user
   * @return list
   * @since NeXtMidas 3.7.1 for NXM-2945
   */
  public List<?> getList (String key,List<?> list) {
    Object obj = getO(key);
    if (obj != null) {
      return Convert.o2List(obj);
    } else {
      return list;
    }
  }

  /** Gets the command line parameter at a given position as an Object.
      This is a convenience method equal to <code>getO(getKey(arg)</code>.
      @param arg The position index.
      @return The results of calling <code>getO(getKey(arg))</code>.
      @see #getO(String,Object)
  */
  public Object getO (int arg) { return getO(getKey(arg)); }

  /** Gets the command line parameter with the given name as a Java Object.
      This is a convenience method equal to <code>getO(name,null)</code>.
      @param name The switch/parameter name.
      @return The results of calling <code>getO(name,null)</code>.
      @see #getO(String,Object)
  */
  public Object getO (String name) { return getO(name,null); }

  /** Gets the command line parameter with the given name as a Java Object.
      @param name     The switch/parameter name.
      @param defvalue The default value for the switch/parameter.
      @param uc       A boolean that indicates whether the return value should
                      be converted to upper case before being returned.
      @return The command line parameter with the given switch/parameter name as
              a Java object or <code><i>defvalue</i></code> if the parameter does not exist.

      @see DataTypes
  */
  public Object getO (String name, Object defvalue, boolean uc) {
    String value = getRaw(name);
    if (value==null)     return defvalue;
    if (value.isEmpty()) return value;  // Added for Bug 2285 to keep Object id intact
    int ls = value.length();
    if (ls>0 && value.charAt(0)==QUOTE) {
      if (value.charAt(ls-1)==QUOTE) value = value.substring(1,ls-1);
      else value = value.substring(1,ls);
      return value;
    }
    String ucvalue = Parser.toUpperCaseWithQuotes(value);
    Object obj = M.results.get(ucvalue);
    if (obj != null) return obj;
    if (uc) return Convert.s2o(ucvalue,'_',this);
    else return Convert.s2o(value,'_',this);
  }

  /** Gets the command line parameter with the given name as a Java Object.
      This is a convenience method equal to <code>getO(name,defvalue,true)</code>.
      @param name     The switch/parameter name.
      @param defvalue The default value for the switch/parameter.
      @return The command line parameter with the given switch/parameter name as
              a Java object or <code><i>defvalue</i></code> if the parameter does not exist.
      @see DataTypes
  */
  public Object getO (String name, Object defvalue) {
    return getO(name,defvalue,true);
  }

  /** Get a table off the command line by position.
      This is a convenience method equal to <code>getTable(getKey(arg))</code>.
      @param arg The name of the table to get.
      @return The results of calling <code>getTable(getKey(arg))</code>.
      @see #getTable(String)
  */
  public Table getTable (int arg) {
    return getTable(getKey(arg));
  }

  /** Get a table off the command line. Since NeXtMidas 2.7.1, this throws a
      MidasException if input is NOT a valid table.
      @param name The name of the table to get.
      @return The results of calling <code>getTable(name,null)</code> if input
              is a valid Table, null if input argument is not specified.
      @throws MidasException if input table is invalid (since NeXtMidas 2.7.1)
      @see #getTable(String,Table,int)
  */
  public Table getTable (String name) {
    return getTable(name,null,FLAGS_NONE);
  }

  /** Get a table off the command line with default.
      @param name The name of the table to get.
      @param defTab The default Table
      @return The table or default if a table entered is invalid.
      @throws MidasException if input table is invalid (since NeXtMidas 2.7.1)
      @see #getTable(String,Table,int)
  */
  public Table getTable (String name, Table defTab) {
    return getTable(name,defTab,FLAGS_NONE);
  }

  /** Get a table off the command line with default and specified flags.
      The following summarizes the recognized flags which can
      be used to alter the results (to include more than one flag, simply
      bitwise-or the flags together [e.g. <code>NO_ERROR|..</code>]):
       <ul>
        <li>{@link #NO_ERROR} - Do not cause an ERROR (i.e. throw MidasException),
                                if input is an invalid Table. User will take
                                care of their own error handling or have alternate
                                fall back logic.</li>
        <li>{@link #REQUIRED} - If input argument is NOT specified by user a
                                MidasException with required argument name is thrown.
                                (Since NeXtMidas 3.1.2).</li>
      </ul>

      @param name The name of the table to get.
      @param defTab The default Table (if argument is not specified)
      @param flags  The options to apply when getting the Table.
      @return The table or default if a table entered is invalid.
      @throws MidasException if input table is invalid and flags does not have NO_ERROR
      @since NeXtMidas 2.7.1
  */
  public Table getTable (String name, Table defTab, int flags) {
    String value = getU(name,"");
    if (value.length()<=0) {
      if ((flags&REQUIRED) != 0) throw new MidasException("Argument '"+name+"=' is required.");
      else return defTab;
    }
    Table table = Convert.o2t(value,this);
    if (table==null && (flags&NO_ERROR)==0) {
      throw new MidasException("Invalid Table for parameter: "+name+"="+value);
    }
    return table;
  }

  /** Gets the command line parameter at a given position as a time value.
      This is a convenience method equal to <code>getTime(getKey(arg))</code>.
      @param arg The position index.
      @return The results of calling <code>getTime(getKey(arg))</code>.
      @see #getTime(String)
  */
  public Time getTime (int arg) { return getTime(getKey(arg)); }

  /** Gets the command line parameter at a given position as a Time object.
      This is a convenience method equal to <code>getTime(name,null)</code>.
      @param name     The switch/parameter name.
      @return The results of calling <code>getTime(name,null)</code>.
      @see #getTime(String,Time)
  */
  public Time getTime (String name) {
    return getTime(name, null);
  }

  /** Gets the command line parameter at a given position as a Time object.
      @param name     The switch/parameter name.
      @param defvalue The default value for the switch/parameter.
      @return The command line parameter at the given position or {@code defvalue} if the parameter does not exist.
      @see Time#toTime(Object)
  */
  public Time getTime (String name, Time defvalue) {
    String value = getRaw(name);
    if (value==null) return defvalue;
    value = value.toUpperCase();
    // Special handling for X-Midas <wsec>|<fsec> syntax used by M$TPICK. This was
    // added in NeXtMidas 2.7.0 for backwards-compatibility with X-Midas but has always been deprecated.
    int i = value.indexOf('|');
    if (i > 0) {
      double wsec = Convert.o2d(value.substring(0,i), M);
      double fsec = Convert.o2d(value.substring(i+1), M);
      wsec = wsec - (wsec % 1.0); // strip off any fract seconds (matches X-Midas)
      fsec = fsec % 1.0;          // strip off any whole seconds (matches X-Midas)
      M.deprecate("Use of X-Midas WSEC|FSEC time syntax is deprecated, use (<wsec>,<fsec>).");
      return new Time(wsec, fsec);
    }
    // NeXtMidas Time Conversion
    Object obj = M.results.get(value);
    return (obj!=null)? Time.toTime(obj) : Time.toTime(value);
  }


  //////////////////////////////////////////////////////////////////////////////
  // METHODS FOR Shell.java
  //////////////////////////////////////////////////////////////////////////////

  /** Add an argument with its value to this set of arguments.
      <i>This method should only be used by the shell.</i>
     @param key   The argument key (or name).
     @param value The value of the argument.
  */
  public void put (String key, String value) {
    key = evaluateCarets(key);
    kv.put(key,value);
  }

  /** Gets an argument from this set of arguments.
      <i>This method should only be used by the shell.</i>
     @param key The key (or name) of the argument to get.
     @return The value of the argument or null if the provided key does not match any known argument.
  */
  public String get (String key) {
    return (String)kv.get(key);
  }

  /** Removes an argument from this set of arguments. This has no effect if the
      the provided key does not match any known arguments.
      <i>This method should only be used by the shell.</i>
      @param key The key (or name) of the argument to remove.
  */
  public void remove (String key) {
    kv.remove(key);
  }

  //////////////////////////////////////////////////////////////////////////////
  // UTILITY METHODS
  //////////////////////////////////////////////////////////////////////////////

  /** Gets the number of parameters (or arguments) on command line.
      This is the same as <code>size()</code>.
      @return The number of command line parameters.
      @see #size()
  */
  public int numberOf () { return nargs; }

  /** Gets the number of parameters (or arguments) on command line.
      This is the same as <code>numberOf()</code>.
     @return The number of command line parameters.
     @see #numberOf()
  */
  public int size () { return nargs; }

  /** Return original un-cleaned command with carets (<code>^</code>) evaluated.
      Note that the string returned will not have the letters converted to uppercase.
      <br>
      For example:
      <pre>
        Given the following at the command line or in a macro:

          nM&gt; RES s:foo "abc"
          nM&gt; RES l:bar 2
          nM&gt; MYCOMMAND "start" ^foo ^bar

        Calling getRaw() within the Java class that implements MYCOMMAND
        will return the String "<i>MYCOMMAND "start" "abc" 2</i>".
      </pre>

      @return The "un-cleaned" command line.

      @see #getRawArgs()
      @see #getRaw(String)
  */
  public String getRaw () {
    String value = cmdline;
    return evaluateCarets(value);
  }

  /** Return original un-cleaned command with carets (<code>^</code>) evaluated
      minus the command name. Note that the string returned will not have the
      letters converted to uppercase.
      <br>
      For example:
      <pre>
        Given the following at the command line or in a macro:

          nM&gt; RES s:foo "abc"
          nM&gt; RES l:bar 2
          nM&gt; MYCOMMAND "start" ^foo ^bar

        Calling getRaw() within the Java class that implements MYCOMMAND
        will return the String "<i>"start" "abc" 2</i>".
      </pre>

      @return The "un-cleaned" command line minus the command name.

      @see #getRaw()
      @see #getRaw(String)
  */
  public String getRawArgs () {
    // Prior to NeXtMidas 3.3.2 this simply looked for the first occurrence of
    // space (' ') or comma (',') and used that for the end of the switches. This
    // resulted in errors when switches included quoted values, tables, etc. and
    // did not handle end-of-line switches properly. (see Bug 2730)
    StringBuilder rawArgs = new StringBuilder();
    Parser        line    = new Parser(getRaw());  // Command line parser
    boolean       first   = true;
    line.setTreatBSlashAsEscape(!(M.io.isOptionSet(CoreIO.IOOptions.DisableEscapeSequences)));

    if (line.get(1).startsWith("$")) {
      // Special case for O/S commands like "$ls -al"
      rawArgs.append(line.get(1).substring(1));
      first = false;
    }

    for (int i = 2; i <= line.elements(); i++) {
      String value = line.get(i, !first);
      String val   = line.get(i, false);

      if (val.startsWith("/")) {
        int    equals     = val.indexOf('=');
        String switchName = (equals < 0)? val : val.substring(0,equals);
        if (find(switchName)) break; // skip trailing switches
      }
      rawArgs.append(value);
      first = false;
    }
    return rawArgs.toString();
  }

  /** Return a raw argument after switch resolution and caret (<code>^</code>) substitution.
      <br>
      For example:
      <pre>
        Given the following at the command line or in a macro:

          nM&gt; RES s:foo "abc"
          nM&gt; RES l:bar 2
          nM&gt; MYCOMMAND CMD="start" ^foo ^bar

        Calling getRaw("CMD") within the Java class that implements MYCOMMAND
        will return the String "<i>"start"</i>".
      </pre>

      @param name The name of the argument as specified in the {@code commands.cnf} file for the option.

      @return The argument corresponding to the argument name provided or {@code null}
              if the provided name does not match any of the fields for the command.

      @see #getRawArgs()
      @see #getRaw()
  */
  public String getRaw (String name) {
    String value=null;
    boolean isSwitch = (name.charAt(0)==SLASH);
    // handle switch precedence in Macros
    if (isSwitch && M.macro!=null) {
      boolean inherit = !name.equals("/BG") && !name.equals("/ID");  // never propagate /ID= or /BG switches
      boolean inheritFromParent = inherit && !name.equals("/MSGID"); // never propagate /MSGID= from parent (since 3.3.1)

      // 1st try from the Macro call line (e.g. cmdline from Macro's parent)
      if (value==null && M.macro.lines[0]!=null && inheritFromParent)
        value = M.macro.lines[0].get(name);
      // 2nd try from the Macro command call line (as typed inside Macro)
      if (value==null)
        value = this.get(name);
      // 3rd try from the Macro default (STARTMACRO) line
      if (value==null && M.macro.lines[1]!=null && inherit)
        value = M.macro.lines[1].get(name);
    }
    else value = get(name); // get switch value from cmdline
    if (value==null || (value.length()==0 && !isSwitch) || value.equalsIgnoreCase("NOTPRESENT"))
      return null;
    // now handle caret substitution
    value = evaluateCarets(value);
    return value;
  }

  /** This will evaluate carets in a string. This means that each item with a
     caret will be dereferenced. For example, if <code>A</code> is equal to
     <code>42</code> then the string <code>say "A = ^A"</code> will be
     transformed into <code>say "A = 42"</code>. Note that if an item with a
     caret does not match a known value then it will not be altered.
     @param value The original string with any carets in it.
     @return The string with any carets resolved.
  */
  public String evaluateCarets (String value) {
    if (value==null) return value;
    int i,j,k,kk; String tmp; Object obj;
    for (j=kk=value.length(); (i=value.lastIndexOf(CARET,j))>=0; j=i-1) {
      if (value.charAt(i+1)==LCURLY) {
        k = value.indexOf(RCURLY,i);
        // If the curly braces do not match (i.e. "{ ... }") then it is an
        //   error. This is the fix to DR 504098-1 where an expression such as
        //     nM> say "A = ^{A"
        //   would cause this method to die with an exception.
        if (k < i+1) {
          throw new MidasException(getName() + ": Syntax Error: Can not evaluate "
                                 + "carets: Missing '}' character in ( "+value+" ).");
        }
        tmp = value.substring(i+2,k).toUpperCase();
        obj = M.results.get(tmp);
        k++;
      }
      else if (value.charAt(i+1)==SLASH) {
        j = Results.validNameLength(value,i+2);
        if (j<1) continue; k = i+j+2;
        tmp = value.substring(i+1,k).toUpperCase();
        obj = getRaw(tmp);
        if (obj==null) obj = "NULL"; // not there for evaluation routines
      }
      else {
        j = Results.validNameLength(value,i+1);
        if (j<1) continue; k = i+j+1;
        tmp = value.substring(i+1,k).toUpperCase();
        obj = M.results.get(tmp);
      }
      if (obj==null) continue;
      tmp = obj.toString();
      if (k<kk) tmp = tmp+value.substring(k);
      if (i==0) return tmp;
      value = value.substring(0,i)+tmp;
      kk = value.length();
    }
    return value;
  }

  /** Gets the key for the argument at a specified index.
      @param arg The index position of the key to get.
      @return The value of the argument or <code>"BogusIndex"</code> if the provided index does not exist.
      @see #getValue(int)
  */
  public String getKey (int arg) {
    String value = kv.getKey(arg);
    if (value==null || arg>nargs) value = "BogusIndex";
    return value;
  }

  /** Gets an argument from this set of arguments at a specified index.
      @param arg The index position of the value to get.
      @return The value of the argument or <code>"BogusIndex"</code> if the provided index does not exist.
      @see #getKey(int)
      @see #get(java.lang.String)
  */
  public String getValue (int arg) {
    String value = (String)kv.get(arg);
    if (value==null) value = "BogusIndex";
    return value;
  }

  /** Gets the command line parameter with the given name as an ID value as
      resolved in the <b>NeXtMidas Registry</b>.

      @param name     The switch/parameter name.
      @param defvalue The default value for the switch/parameter.

      @return The command line parameter with the given switch/parameter name as
              an ID value upon being resolved in the NeXtMidas Registry or
              <code><i>defvalue</i></code> if either the parameter does not
              exist or does not match an entry in the registry.

      @see Midas#registry
  */
  public Object getID (String name, Object defvalue) {
    String value = getRaw(name);
    if (value==null) return defvalue;
    Object obj = M.registry.get(value.toUpperCase());
    if (obj==null) return defvalue;
    return obj;
  }

  /** Test whether an argument is present, and if so get its length.
      @param name The name of the argument.
      @return The length of the argument or <code>-1</code> if no argument is present.
  */
  public int getLength (String name) {
    String value = getRaw(name);
    if (value==null) {
      return -1;         // not present
    } else {
      return value.length();
    }
  }

  /** Tests whether an argument is present.
      This is the same as {@link #find(String)}.
      @param name The switch/parameter name.
      @return {@code true} if {@code name} matches one of the keys in the argument set, {@code false} otherwise.
      @see #find(String)
  */
  public boolean isPresent (String name) {
    return getRaw(name) != null;
  }

  /** Tests whether an argument is present.
      This is the same as <code>isPresent(name)</code>.
      @param name The switch/parameter name.
      @return The results of calling <code>isPresent(name)</code>.
      @see #find(String)
  */
  public boolean find (String name) {
    return this.isPresent(name);
  }

  /** Validate a user format input and convert to internal form.
      An error will be issued if user's input does not match up with the allowable format.
      @param name      The name of the format value.
      @param allowable The allowable format.
      @return The user's format input.
      @see #testFormat(String,String)
      @see DataFile#checkFormat(String,String)
  */
  public String getFormat (String name, String allowable) {
    return getFormat(name, allowable, "");
  }

  /** Validate a user format input and convert to internal form.
      An error will be issued if user's input does not match up with the allowable format.
      @param name      The name of the parameter/switch
      @param defvalue  The default value for the format if none is specified
      @param allowable The allowable format(s).
      @return The user's format input.
      @see #testFormat(String,String)
      @see DataFile#checkFormat(String,String)
      @since NeXtMidas 3.1.1
  */
  public String getFormat (String name, String defvalue, String allowable) {
    String format = getS(name, defvalue);
    return testFormat(format, allowable);
  }

  /** Validate a format code. Matches <code>M$TEST_FORMAT</code>.
      An error will be issued if user's input does not match up with the allowable format.
      Allowable is a list of valid input file data formats (e.g. "S#,-SB,CI,CF,V*,MD,NH,#A"), if the
      input format does not match one of the allowable formats, this will error out:
      <pre>
         - "#" in the second char (i.e. type) of the format digraph matches any numeric (F,D,L,I,etc.)
           In other words, use "M#" to allow any numeric format type where the mode is M (e.g. "C#" accepts "CF","CB","CI",...)
         - "*" in the second char (i.e type) of the format digraph matches any numeric or ASCII type (F,D,L,I,A,etc.)
           In other words, use "M*" to allow any format type where the mode is m (e.g. "S*" accepts "SA","SF","SB","SI",...)
         - Format "NH" are normally in type 3000/5000 file with records that are non-homogeneous.
         - '#' in the first char (i.e. mode) of the format digraph matches the numbers 1-9 for
           checking with ASCCI string format type.
           In other words, use "#A" to allow any mode (multiplier) of ASCCI strings (e.g. "#A" accepts "1A","2A","3A",...)
         - Use "-[format]" to disallow a specific format (e.g. "-SF")
      </pre>
      Note: If format contains one character, a scalar mode ("S") will be prepended to the
            format before checking the the allowable list (e.g. "F" becomes "SF" format).

      @param format    The input format code (as from command line parameter).
      @param allowable The allowable format (null for blank to allow any)
      @return The Midas rendition of the mode and type format string (two char digraph)
      @throws MidasException when format is not one of the allowable formats.
      @see DataFile#checkFormat(String,String)
      @since NeXtMidas 2.7.2
  */
  public String testFormat (String format, String allowable) {
    int formatLen = (format==null)? 0 : format.length();
    String fc = format; // Midas format code to return on success
         if (formatLen==2) { }              // standard mode and type format (most common case)
    else if (formatLen==1) fc = "S"+format; // type only, assume scalar
    else if (formatLen>=3) {
      // translate X-Midas style user input style format strings to internal Midas format
           if (format.equals("REAL") || format.equals("R*04") || format.startsWith("FLO")) fc = "SF";
      else if (format.startsWith("DOU") || format.equals("R*08"))                          fc = "SD";
      else if (format.startsWith("XLO") || format.equals("I*08"))                          fc = "SX";
      else if (format.startsWith("LON") || format.equals("I*04"))                          fc = "SL";
      else if (format.startsWith("INT") || format.equals("I*02"))                          fc = "SI";
      else if (format.startsWith("BYT") || format.equals("I*01") || format.equals("B*01")) fc = "SB";
      else if (format.startsWith("BIT") || format.startsWith("PAC"))                       fc = "SP";
      else if (format.startsWith("OFF") || format.equals("OB01"))                          fc = "SO";
      else if (format.startsWith("COM") || format.equals("C*08"))                          fc = "CF";
      else if (format.equals("C*16"))                                                      fc = "CD";
      else if (format.equals("C*04"))                                                      fc = "CI";
      else throw new MidasException("Unrecognized format: "+format);
    }
    else throw new MidasException("No format specified! "+format);
    if (!DataFile.checkFormat(fc,allowable)) {
      throw new MidasException("Format '"+format+"' not allowed in "+allowable);
    }
    return fc;
  }

  /** Validate a user input against a selection set and return the index by value.
      This is identical to <code>getSelectionIndex(name,selections,defsel)</code>.
      @param name       The name of the option as provided by the user.
      @param selections The set of all valid selections.
      @param defsel     The default selection to return if the user's input does
                        not match one of the selections.
      @return The results of calling <code>getSelectionIndex(name,selections,defsel)</code>.
      @see #getSelectionIndex(String,String,int)
  */
  public int getChoice (String name, String selections, int defsel) {
    return getSelectionIndex(name,selections,defsel);
  }

  /** Validate a user input against a selection set and return the index by value.
      This is identical to <code>getSelectionIndex(name,selections,defsel,0)</code>.
      @param name       The name of the option as provided by the user.
      @param selections The set of all valid selections.
      @param defsel     The default selection to return if the user's input does
                        not match one of the selections.
      @return The results of calling <code>getSelectionIndex(name,selections,defsel,0)</code>.
      @see #getSelectionIndex(String,String,int,int)
  */
  public int getSelectionIndex (String name, String selections, int defsel) {
    return getSelectionIndex(name,selections,defsel,0);
  }

  /** Validate a user input against a selection set and return the index by value.
      An error will be issued if the parser can not find the user's selection
      among the list of valid selections.
      @param name       The name of the option as provided by the user.
      @param selections The set of all valid selections.
      @param defsel     The default selection to return if the user's input does
                        not match one of the selections.
      @param offset     The offset into the selections to use.
      @return The index of the selection associated with the user's input or
              <code><i>defsel</i></code> if the user's input does not match one of the selections.
      @see #getSelectionIndex(String,String,int)
      @see #getSelectionIndex(String,String,int,int)
      @see #getOptionMask(String,String,int)
      @see Parser#find(String,String,int,int)
  */
  public int getSelectionIndex (String name, String selections, int defsel, int offset) {
    String select = getS(name);
    int item = Parser.find (selections,select,defsel,offset);
    if (item<offset && item!=defsel && !selections.startsWith("-")) {
      throw new MidasException("Selection "+select+" not in list "+selections);
    }
    return item;
  }

  /** Validate a user input against a selection set and return the string.
      An error will be issued if the parser can not find the user's selection
      among the list of valid selections.
      @param name       The name of the option as provided by the user.
      @param selections The set of all valid selections.
      @param defsel     The default selection to return if the user's input does
                        not match one of the selections.
      @return The selection associated with the user's input or
              <code><i>defsel</i></code> if the user's input does
              not match one of the selections.

      @see #getSelectionIndex(String,String,int)
      @see #getSelectionIndex(String,String,int,int)
      @see #getOptionMask(String,String,int)
      @see Parser#find(String,String,String)
  */
  public String getSelection (String name, String selections, String defsel) {
    String select = getS(name);
    String item = Parser.find (selections,select,defsel);
    if (item==null) throw new MidasException("Selection "+select+" not in list "+selections);
    return item;
  }

  /** Gets user input that is intended to match one of the choices in an enumerated list. This first
      tries a case-sensitive match. If that fails, a case-insensitive match is tried.
      @param name The name of the option as provided by the user.
      @param def  The default selection option.
      @param <E> the type of elements in this collection.
      @return The selection or <code>def</code> if not specified.
      @throws NullPointerException if <code>def</code> is null.
      @throws IllegalArgumentException If the selection specified by the user does not match one of
                                       the valid enumerated items.
      @since NeXtMidas 2.7.1
   */
  @SuppressWarnings("unchecked")
  public <E extends Enum<E>> E getSelection (String name, E def) {
    E[]    enums     = (E[])def.getClass().getEnumConstants();  // <-- Throws NPE if def is null
    String selection = getS(name);
    if ((selection == null) || (selection.length() == 0)) return def;

    // Try case-sensitive
    for (E e : enums) {
      if (e.toString().equals(selection)) return e;
    }

    // Try again case-insensitive
    for (E e : enums) {
      if (e.toString().equalsIgnoreCase(selection)) return e;
    }
    throw new IllegalArgumentException("Selection '"+selection+"' not found in "+StringUtil.join(enums));
  }

  /** Gets user input that is intended to match entries in a mask value. This gets the user's input
      string, then calls <code>MaskValue.fromString(&lt;input&gt;, type)</code>. However, if the
      user's input is <code>null</code> or <code>""</code> (empty string) a mask value with no flags set
      will be returned.
      @param name  The name of the option as provided by the user.
      @param type  The underlying enumerated type (cannot be null).
      @param <E> the type of elements in this collection.
      @return The mask value or <code>def</code> if not specified.
      @throws NullPointerException if <code>def</code> is null.
      @throws IllegalArgumentException If the any of the flags in the input specified by the user is invalid.
      @since NeXtMidas 2.7.1
   */
  @SuppressWarnings("unchecked")
  public <E extends Enum<E>> MaskValue<E> getMaskValue (String name, Class<E> type) {
    String maskStr = getS(name);
    if ((maskStr == null) || (maskStr.length() == 0)) return MaskValue.forType(type);
    return MaskValue.fromString(maskStr, type);
  }

  /** Gets user input that is intended to match entries in a mask value. This gets the user's input
      string, then calls <code>MaskValue.fromString(&lt;input&gt;, current, def)</code>. However, if the
      user's input is <code>null</code> or <code>""</code> (empty string) <code>def.copy()</code> will be returned.
      @param name    The name of the option as provided by the user.
      @param current The current mask value (null = same as default).
      @param def     The default mask value option (can not be null).
      @param <E> the type of elements in this collection.
      @return The mask value or <code>def</code> if not specified.
      @throws NullPointerException if <code>def</code> is null.
      @throws IllegalArgumentException If the any of the flags in the input specified by the user is invalid.
      @since NeXtMidas 2.7.1
   */
  public <E extends Enum<E>> MaskValue<E> getMaskValue (String name, MaskValue<E> current, MaskValue<E> def) {
    String maskStr = getS(name);
    if ((maskStr == null) || (maskStr.length() == 0)) return def.copy();
    return MaskValue.fromString(maskStr, current, def);
  }

  /** Validate a user input against a selection set and return the mask.
      @param name    The name of the option as provided by the user.
      @param options The set of all valid options.
      @param defmask The default mask to return if the user's input does not match one of the options.
      @return The option mask associated with the user's input or
              <code><i>defmask</i></code> if the user's input does not match one of the options.
      @see #getSelection(String,String,String)
      @see Parser#mask(String,String,int)
  */
  public int getOptionMask (String name, String options, int defmask) {
    String list = getS(name);
    int item = Parser.mask (options,list,defmask);
    return item;
  }

  /** Gets an argument. This is just for {@link #logicalTest()} strings as it
      will default to <code>"NULL"</code> for easy testing.
      This simply invokes {@link #getCSN(int,boolean)} with case-sensitivity set to <code>false</code>.
      @param arg The index of the argument to get.
      @return The argument at index <code><i>arg</i></code>.
      @see #getCSN(int,boolean)
  */
  private String getSN (int arg) {
    return getString (getKey(arg),"NULL", RESOLVE|UPCASE);
  }

  /** Gets an argument. This is just for {@link #logicalTest()} strings as it
      will default to <code>"NULL"</code> for easy testing.
      @param arg      The index of the argument to get.
      @return The case sensitive argument at index <code><i>arg</i></code>.
  */
  private String getCSN (int arg, boolean caseSen) {
    return getString(getKey(arg), "NULL", caseSen? RESOLVE : RESOLVE|UPCASE);
  }

  /** Used only by logicalTest() to compare numbers for equality at position i and j. */
  private boolean equals (int i, String test, int j, double relTol, double absTol) {
    // JC4: Restricting this to only numeric data seems silly, but it matches
    //      NeXtMidas 3.3.1 and earlier, hence I maintained that behavior when
    //      I removed the deprecated code and added in the support for absolute
    //      tolerance in NeXtMidas 3.3.2.
    Data data1 = getNumericData(getKey(i));
    Data data2 = getNumericData(getKey(j));
    return (absTol < 0)? data1.equals(data2, Data.parseTestFlag(test,false), relTol)
                       : data1.equals(data2, Data.parseTestFlag(test,true),  absTol);
  }

  /** Used only by logicalTest() to compare numbers for inequality at position i and j. */
  private int compare (int i, String test, int j) {
    // JC4: Restricting this to only numeric data seems silly, but it matches
    //      NeXtMidas 3.3.1 and earlier, hence I maintained that behavior when
    //      I removed the deprecated code and added in the support for absolute
    //      tolerance in NeXtMidas 3.3.2.
    Data data1 = getNumericData(getKey(i));
    Data data2 = getNumericData(getKey(j));
    return data1.compareTo(data2, Data.parseTestFlag(test,false));
  }

  /** Performs an in-line TEST.
      @param ref  The midas context or other reference.
      @param test The test in the syntax of <code>"TEST(...)"</code> or <code>(...)</code>.
      @return The results of the test.
      @since NeXtMidas 2.7.0
      @deprecated since NeXtMidas 3.5.0 use {@link #logicalTest(MidasReference, String)}
   */
  @Deprecated
  static boolean logicalTest (Object ref, String test) { // package-private only used by Convert
    if (ref instanceof MidasReference) {
      return logicalTest((MidasReference) ref, test);
    } else {
      return logicalTest(Convert.ref2Midas(ref), test);
    }
  }

  /** Performs an in-line TEST.
      @param ref  The midas context or reference. (Since NeXtMidas 3.5.0)
      @param test The test in the syntax of <code>"TEST(...)"</code> or <code>(...)</code>.
      @return The results of the test.
      @since NeXtMidas 3.5.0
   */
  static boolean logicalTest (MidasReference ref, String test) { // package-private only used by Convert
    Midas midas = Convert.ref2Midas(ref);
    int    off  = (test.startsWith("TEST("))? 5 : 1;
    int    end  = test.length()-1;
    Args   args = new Args(midas, new Parser(test.substring(off,end),true));
    if (ref instanceof Args) {
      ((Args)ref).copySwitchesTo(args);
      args.cmd = ((Args)ref).cmd;           // <-- Added in 3.3.1, see Bug 2695
    }
    return args.logicalTest();
  }

  /** Test a command line logical expression. This method is used by the macro
      control statements <code>IF</code>, <code>WHILE</code>, and <code>ASSERT</code>.
      @return true if the logical test on the command line evaluates to true and
              false if the logical test on the command line evaluates to false.
   */
  @InternalUseOnly("Since NeXtMidas 3.3.1, this has always been meant for internal use only")
  public boolean logicalTest () {
    if (nargs==0) return true;
    if (nargs==1) return getState(1);

    boolean result = false;
//  boolean cs     = isPresent("/CS");       // case sensitive strings? impl prior to 3.5.4 (TODO remove in 3.7+ if no issues)
    boolean cs     = getState("/CS", false); // case sensitive strings test, impl since 3.5.4 for BUG 2937
    double  relTol = getD("/TOL",   -1);  // relative numeric tolerance (EQ/T, FEQ/T)
    double  absTol = getD("/DELTA", -1);  // absolute numeric tolerance (EQ/T, FEQ/T) [since 3.3.2]

    try {
      for (int i=2;;i+=3) {
        boolean negate = false;
        String  test   = (i > nargs)? "" : getU(i);

        if (test.equals("NE") || test.equals("!=")) {
          test   = "EQ";
          negate = true;
        }
        else if (test.startsWith("NE/")) {
          test   = "EQ/"+test.substring(3); // change the "NE" to "EQ" but keep any modifiers
          negate = true;
        }
        else if (test.length()>1 && test.charAt(0)=='N' && Character.isLetter(test.charAt(1))) {
          test   = test.substring(1);          // skip past the 'N' character
          negate = true;
        }

        int    iSlash   = test.indexOf('/');
        String baseTest = (iSlash < 0)? test : test.substring(0,iSlash);

        if (test.startsWith("EQT") && !(test.equals("EQT") || test.equals("EQTOL"))) {
          // For some reason old versions were allowing anything starting with EQT, I believe
          // that was to allow EQTOL to work, but the explain file only ever advertised EQT.
          M.deprecate("Since NeXtMidas 2.7.0: Please use EQT or ~= in place of "+test+" in "+getRaw());
          test = "EQT";
        }

        // String tests
             if (test.equals("EQS"   )) result = getCSN(i+1,cs).equals(getCSN(i-1,cs));
        else if (test.equals("SUBS"  )) result = getCSN(i+1,cs).indexOf(getCSN(i-1,cs)) >= 0;
        else if (test.equals("STARTS")) result = getCSN(i-1,cs).startsWith(getCSN(i+1,cs)); // Since 3.5.0 (BUG 2806)
        else if (test.equals("ENDS"  )) result = getCSN(i-1,cs).endsWith(getCSN(i+1,cs));   // Since 3.1.2 (BUG 2603)
        else if (test.equals("SEQS") || test.equals("EQSS")) {
          String s1 = getCSN(i-1,cs);
          String s2 = getCSN(i+1,cs);
          result = s1.startsWith(s2) || s2.startsWith(s1);
        }
        // Numeric tests (since 2.5.0: added support for C language logical operators for BUG 975)
        else if (test.equals("EQT")    || test.equals("~=")) result = equals(i-1,"EQ/T",i+1,relTol,absTol); // alias for EQ/T
        else if (test.equals("EQTOL"))                       result = equals(i-1,"EQ/T",i+1,relTol,absTol); // alias for EQ/T
        else if (baseTest.equals("EQ") || test.equals("==")) result = equals(i-1,test,  i+1,relTol,absTol);
        else if (baseTest.equals("LE") || test.equals("<=")) result = compare(i-1,test,i+1) <= 0;
        else if (baseTest.equals("GE") || test.equals(">=")) result = compare(i-1,test,i+1) >= 0;
        else if (baseTest.equals("LT") || test.equals("<"))  result = compare(i-1,test,i+1) <  0;
        else if (baseTest.equals("GT") || test.equals(">"))  result = compare(i-1,test,i+1) >  0;
        // Inclusion tests
        else if (test.equals("ANYBITS"))  result = (getL(i-1)&getL(i+1)) != 0;
        else if (test.equals("ALLBITS"))  result = (getL(i-1)|getL(i+1)) == getL(i-1);
        else if (test.equals("CONTAINS") || test.equals("CONTAINS/K")) { // Map contains key (since 3.5.4 also support CONTAINS/K)
          Object obj = getO(i-1);
//        result = (obj instanceof Table) ? ((Table)obj).containsKey(getSN(i+1).toUpperCase()) : false; // prior to 3.5.4 (TODO: if no issues remove in 3.7+)
          if (obj instanceof Map) {  // since 3.5.4 - support any class that implements Map interface for BUG 2931
            Object key;
            if (obj instanceof Table) {
              key = getCSN(i+1, cs); // since 3.5.4, allow /CS switch to enable mix-cased keys on Table as part of BUG 2931
              // key = key.toUpperCase(); // to revert to behavior prior to 3.5.4 for Table (TODO: if no issues, remove in 3.9+)
            } else {
              key = getO(i+1);
            }
            result = ((Map<?, ?>) obj).containsKey(key);
          }
        }
        else if (test.equals("CONTAINS/V")) { // Map contains value - since 3.5.4 as part of BUG 2931
          Object obj = getO(i-1);
          if (obj instanceof Map) {
            Object value = getO(i+1);
            result = ((Map<?, ?>) obj).containsValue(value);
          }
        }
        else if (test.equals("TYPEOF"))     result = testTypeOf(getO(i-1), getS(i+1));
        else if (test.equals("INSTANCEOF")) result = testInstanceOf(getO(i-1), getCS(i+1));
        // Equality tests
        else if (test.equals("OEQ") || test.equals("EQO"))
          result = getO(i-1).equals( getO(i+1) );
        else if (test.startsWith("FEQ") || test.startsWith("EQF")) {
          BaseFile bf1 = getFile(i-1);
          BaseFile bf2 = getFile(i+1);
          int testFlags = BaseFile.parseFileTestFlag(test);
          if ((absTol > -1) && ((testFlags&BaseFile.EQ_TOL)!=0)) {
            testFlags = testFlags - BaseFile.EQ_TOL + BaseFile.EQ_ABSTOL;
          }
          result = bf1.equals(bf2, testFlags); // <-- change this to include tol
        }
        // Unary tests,  NOTE: must be included in isUnaryTest method
        else if (test.startsWith("AEX")) result = new java.io.File(getCS(--i)).exists(); // AEXISTS
        else if (test.startsWith("REX")) result = M.results.get(getU(--i)) != null;  // REXISTS
        else if (test.startsWith("FEX")) result = getFile(--i).find(-1);             // FEXISTS
        else if (test.startsWith("DEX")) result = getFile(--i).isDir();              // DEXISTS
        else if (test.startsWith("PEX")) result = M.macro.args.isPresent(getU(--i)); // PEXISTS
        else if (test.equals("ISTRUE"))  result = getState(--i);
        else if (test.equals("ISFALSE")) result = !getState(--i);
        else if (test.equals("ISNULL"))  result = StringUtil.isNull(getSN(--i));
        // infer state test ?
        else if ((test.length()==0 && getU(i-1).length()!=0) || test.equals("THEN") || isCompoundTest(test)) {
          result = getState(i-1); i-=2;
        }
        else throw new MidasException("Illegal logical test: "+test);

        if (negate) result = !result;

        if (++i >= nargs) break; // normal execution end

        test = getU(i+1);
             if (test.equals("THEN")) break; // normal execution end
        else if (test.equals("OR")) { if (result) return result; }
        else if (test.equals("AND")) { if (!result) return result; }
        else throw new MidasException("Illegal logical expression");
      }
    } catch (Exception e) {
      // since 2.7.3: Added code below to make a more meaningful error message for BUG 1895
      int    line;
      String text;
      if (M.macro != null) {
        line = M.macro.nextIndex-1;
        text = M.macro.lines[line].cmdline;  // get the text of the command since line numbers do not match
      } else {
        text = getRaw();
      }
      throw new MidasException("Args: logical test had an exception at command: '"+text+"'. "+e.getMessage(), e);
    }
    return result;
  }

  /** Tests to see if a given operator is a unary test operator. Currently
      the following are considered strings to be unary test operators:
      <code>"FEX..."</code>,     <code>"NFEX..."</code>,
      <code>"DEXIST..."</code>,  <code>"NDEXIST..."</code>,
      <code>"REX..."</code>,     <code>"NREX..."</code>,
      <code>"PEX..."</code>,     <code>"NPEX..."</code>,
      <code>"IS..."</code>,  and <code>"NIS..."</code>.
      @param op The operator.
      @return <code>true</code> if the given operator represents a compound test
              and <code>false</code> otherwise.
  */
  static boolean isUnaryTest (String op) {
    // Strip off negation to prevent tests for both
    // We must check for the IS cases explicitly to prevent ambiguity with a single line if's command
    if (op.startsWith("N")) op = op.substring(1);
    boolean unary = (op.startsWith("FEX") || op.startsWith("DEXIST") || op.startsWith("AEX")
                  || op.startsWith("REX") || op.startsWith("PEX")
                  || op.equals("IS")      || op.equals("ISNULL")
                  || op.equals("ISTRUE")  || op.equals("ISFALSE"));
    return unary;
  }

  /** Tests to see if a given operator is a compound test operator. Currently
      only the strings <code>"AND"</code> and <code>"OR"</code> are considered
      to be compound test operators.
      @param op The operator.
      @return <code>true</code> if the given operator represents a compound test
              and <code>false</code> otherwise.
  */
  static boolean isCompoundTest (String op) {
    return (op.equals("AND") || op.equals("OR"));
  }

  /** Tests to see if the given object matches the specified type specifier.
      @param obj  The object to test.
      @param type The type specifier.
      @return <code>true</code> if the type specifier appropriately matches
              the given object and <code>false</code> if it does not.
      @deprecated Since NeXtMidas 2.5.0: This method should be private.
   */
  @Deprecated public boolean testTypeOf (Object obj, String type) {
    if (type.equals("S")) return (obj instanceof String);
    if (type.length()==1 && obj instanceof Data)
      return (((Data)obj).type==(byte)type.charAt(0));
    if (type.length()==2 && obj instanceof Data)
      return (((Data)obj).getFormat().equals(type));
    return obj.getClass().getName().equalsIgnoreCase(type);
  }

  /** Tests to see if the given object is an instance of a named class.
      @param obj  The object to test.
      @param type The type specifier.
      @return true if obj is an instance of the named class.
   */
  private boolean testInstanceOf (Object obj, String type) {
    try {
      Class<?> clazz = Class.forName(type,false,Shell.class.getClassLoader());
      return clazz.isInstance(obj);
    }
    catch (ClassNotFoundException e) {
      throw new MidasException("Could not find class '"+type+"', note that class names "
                             + "are case sensitive.", e);
    }
  }

  /** This will return the arguments and switches in the parsed command line.
      @return The parsed command line.
   */
  @Override
  public String toString () {
    StringBuilder sb = new StringBuilder(128);
    String key,value;
    sb.append(support).append(':').append(name);
    for (int i=1; i<kv.size(); i++) {    // args and switches
      key = kv.getKey(i);
      value = getValue(i);
      sb.append(',').append(key).append('=').append(value);
    }
    if (support=='U' || support=='O') sb.append(cmdline);
    return sb.toString();
  }

  /** This will find the key matching the given abbreviation. This works by
      iterating through the list of known keys up to and including a limit key
      and seeing if the key starts with the same text as in the abbreviation.
      Note that if more than one key begins with the same letters as the
      abbreviation, a warning is given and the first one found is returned.
      No warning is given if the string is an exact match for one of the keys.
      @param abbr The abbreviation of a key.
      @param limit index of last key to check.
      @return The first key found that starts with the given abbreviation, or
              <code>null</code> if no keys with an index less than or equal to
              <code>limit</code> begin with the given abbreviation.
  */
  private String keyMatch (String abbr, int limit) {
    String match = null;
    for (int ii=0; ii<=limit; ii++)   // go through checking for exact matches
      if (abbr.equals(kv.getKey(ii))) return(abbr);
    for (int ii=0; ii<=limit; ii++) { // go through looking for abbreviations
      String key = kv.getKey(ii);
      if (key.startsWith(abbr)) {
        if (match == null) {
          match = key;
        } else {
          M.warning("Ambiguous tag abbreviation \"" + abbr + "\": \"" + match +
                    "\" selected, \"" + key + "\" ignored");
        }
      }
    }
    return match;
  }

  /** Gets the name of the command.
   *  @return the name of the command.
   */
  public String getName() { return name; }

  /** Get the option tree the command belongs to.
   *  @return the option tree the command belongs to.
   */
  public String getOption() { return option; }

  /** Get the command's support. The following are typical settings:
      <ul>
        <li>'I' - Intrinsic command (see {@link Intrinsic}).</li>
        <li>'M' - Macro command (see {@link Macro}).</li>
        <li>'O' - Operating system command (see {@link Foreign}).</li>
        <li>'P' - Primitive command (see {@link Primitive}).</li>
        <li>'U' - Unknown command.</li>
        <li>'H' - X-Midas "Host" command.</li>
      </ul>
      @return The command's support.
  */
  public char getSupport() { return support; }

  /** Gets the number of arguments.
   *  @return the number of arguments.
   */
  public int getNArgs() { return nargs; }

  /** Gets the KeyVector holding all of the parameters/switches and their associated values.
   *  @return the keyVector holding all of the parameters/switches and theirs associated values.
   */
  public KeyVector getKV() { return kv; }

  /** Dump the contents of the args table to {@link java.lang.System#out}.
      <b>This method is for debugging purposes and is likely to be changed or
      removed in the future.</b>
   */
  @InternalUseOnly("Since NeXtMidas 3.5.4, this has always been meant for internal use only")
  public void dump () {
    System.out.println("CmdLine = "+getValue(0));
    System.out.println("CmdName = "+getName());
    System.out.println("Support = "+getSupport());
    System.out.println("CmdClass= "+getCmdClassName());
    System.out.println("Positional Args = "+nargs);
    for (int i=1; i<=nargs; i++)
      System.out.println(" PI="+i+" Key="+kv.getKey(i)+" Value="+kv.get(i));
    int xargs = kv.size() - nargs - 1;
    System.out.println("Extra Args = "+xargs);
    for (int i=nargs+1; i<kv.size(); i++)
      System.out.println(" Key="+kv.getKey(i)+" Value="+kv.get(i));
  }

  /** Instantiates a new {@link SplitArg} instance and initialize it.
     @param string    The string with fields separated by the delimiter.
     @param delimiter The delimiter separating fields in the string.
     @return The new instance of {@link Args.SplitArg}.
  */
  public SplitArg getSplitArg (String string, char delimiter) {
    SplitArg sa = new SplitArg();
    sa.setString (string,delimiter);
    return sa;
  }

  /** Check to see if a line has any more parameters to parse.
      @param line The line being parsed.
      @param icur The current index position.
      @return <code>true</code> if there are no more parameters to parse and
              <code>false</code> if there are.
  */
  private static boolean noMoreParameters (Parser line, int icur) {
    boolean more = true;
    for (int i=icur+1; more && line.more(); i++) {
      String arg = line.get(i);                          // next token
      if (arg.indexOf('/')!=0                            // no switch syntax
        || arg.length()==1                               // an operator for calc
        || (arg.indexOf('/',1)>=0 && arg.indexOf('=')<0) // Unix filename or //
        ) more = false;
    }
    line.get(icur); // reset line.more() filter
    return more;
  }

  /** Get the user's macro configuration.
      @param M    The Midas context to use.
      @param name The name of the macro.
      @return The configuration of the user's macro.
      @see #parseArgs(Midas,Args,Parser,Parser)
  */
  private static Parser getUserMacroConfig (Midas M, String name, boolean isOutsideOptionTree) {
    String confstr = "USERMACRO,M,0+,";
    String mfn = name;
    if (!isOutsideOptionTree) mfn = Macro.getFileName(M,name,"SYS");
    else mfn = mfn.substring(1) + ".mm";
    TextFile file = new TextFile (M,mfn);
    if (file.open(TextFile.INPUT|TextFile.NOABORT)) { // Use NOABORT, allow Macro.java to issue error
      String text = file.readProper();
      Parser smp = new Parser (text);
      smp.clean();
      int nargs = smp.elements()-1;
      confstr = "USERMACRO,M,"+nargs+",";
      for (int i=1; i<=nargs; i++) {
        String arg = smp.get(i+1);
        String def = "";
        int ic = arg.indexOf(':');
//      if (ic>=0) arg = arg.substring(ic+1); // prior to 3.7.0
        int ib = arg.indexOf('[');
        if (ic>=0 && (ib<0 || ib>0 && ic<ib) ) { // since 3.7.0: add more checks for BUG 2934
          arg = arg.substring(ic+1);          // peel off <type>: from arg
          ib = arg.indexOf('[');              // need to update ib since args was updated
        }
        if (ib>=0) {
          def = arg.substring(ib+1,arg.length()-1);
          arg = arg.substring(0,ib);
        }
        else if (arg.indexOf('=')>=0) {  // added for Bug 2454 to check for args set on startmacro line
          int ie = arg.indexOf('=');
          def = arg.substring(ie+1,arg.length());
          arg = arg.substring(0,ie);
        }
        confstr += arg+"="+def+",";
      }
      file.close();
    }
    return new Parser (confstr);
  }

  /** This will copy the values of all switches in this set of arguments into
      another set of arguments.
      @param args The set of arguments to receive a copy of all arguments from this set.
      @see #propagateSwitchesTo(Args)
  */
  public void copySwitchesTo (Args args) {
    for (int i=nargs+1; i<kv.size(); i++) {
      String key = kv.getKey(i);
      if (key!=null && key.startsWith("/")) args.put(key,(String)kv.get(i));
    }
  }


  /** This will copy the values of all switches in this set of arguments into
      another set of arguments. Unlike {@link #copySwitchesTo(Args)} this will
      include any "extended arguments."
      @param args The set of arguments to receive a copy of all arguments from this set.
      @see #copySwitchesTo(Args)
  */
  public void propagateSwitchesTo (Args args) {
    for (int i=nargs+1; i<nargsx; i++) {
      String key = kv.getKey(i);
      if (key!=null && key.startsWith("/")) args.put(key,(String)kv.get(i));
    }
  }

  /** Finds the default support for an unconfigured command, used only by parseCommand.
      @param M      Midas context.
      @param fname  The command file name (e.g. "nxm.sys.test.foo").
      @param name   The command name (uppercase) not null.
      @param area   The command area (uppercase) or null.
      @param option The command option (uppercase) or null.
      @return The default support or "U" if unknown.
   */
  private static String findDefaultSupport (Midas M, String fname, String name, String area, String option) {
    String support = "U";
    if ((option != null) && (area != null)) {
      if (area.equals("HOST")) { support = "H"; }
      if (area.equals("INTR")) { support = "I"; }
      if (area.equals("PRIM")) { support = "P"; }
      if (area.equals("MCR" )) { support = "M"; }
    }

    if (support.equals("U")) {
      try {
        Class<?> clazz = Class.forName(fname);

             if (Primitive.class.isAssignableFrom(clazz)) { support = "P"; }
        else if (Intrinsic.class.isAssignableFrom(clazz)) { support = "I"; }

      } catch (ClassNotFoundException e) {
        // Probably not a class... just ignore this error.
        //e.printStackTrace();
      }
    }

    return support;
  }

  /** Finds the default support for an unconfigured command, used only by parseCommand.
      @param M      Midas context.
      @param fname  The command file name (e.g. "nxm.sys.test.foo").
      @param name   The command name (uppercase) not null.
      @param area   The command area (uppercase) or null.
      @param option The command option (uppercase) or null.
      @param support The default support or "U" if unknown.
      @return The configuration to use.
   */
  private static Parser findConfiguration (Midas M, String fname, String name, String area, String option, String support) {
    Parser conf  = null;

    if (!support.equals("U")) { // See if it is configured.
      conf = M.dictionary.findParser(name + ";" + option);

      if (conf != null) {
        if (conf.get(2).equals(support)) {
          // Is configured and supports match.
        } else if (conf.get(2).indexOf(support) > 0) {
          conf = null; // Support doesn't match... not sure what to do.
        } else {
          conf = null; // Not a match.
        }
      }
    }

    if (conf == null) {
      // ToDo: Try special .cnf file.
    }

    if (conf == null) {
      conf = new Parser(name+";"+option+":"+name.length()+","+support+",0+,"); // use the default.
    }

    return conf;
  }

  /** This will parse a command line into an argument set.
      @param M       The Midas context to use.
      @param cmdline The command line to parse.
      @return The argument set based on the given command line.
  */
  public static Args parseCommand (Midas M, String cmdline) {
    // Regular expression for command names (pre-compiled for better performance)
    // the regex pattern is for matching macro filename that may not contain blanks and dots.
    // any other character is valid.
    // NXM-1009
    Pattern macro_name_pattern = Pattern.compile("^[^ .]+");
    int i;
    Parser line    = new Parser(cmdline);  // Command line parser
    Parser conf    = null;                 // Configuration from commands.cnf
    String name    = null;                 // Command name (uppercase)
    String csName  = null;                 // Command name (case-sensitive)
    String support = "U";                  // Command support P=Prim, I=Intr, M=Macro, U=Unknown, ...
    String option  = "";                   // Command option tree
    Args   args    = new Args(M);          // Jeff???
    args.put("all",cmdline);
    line.setTreatBSlashAsEscape(!(M.io.isOptionSet(CoreIO.IOOptions.DisableEscapeSequences)));
    line.clean(false);                     // clean the command line
    csName = line.get(1);

    // The following is added in 3.9.2 for executing external macros
    boolean isOutsideNxmOptionTree = false;
    String macroExt = ".mm"; // must have a .mm extension
    int extLength = macroExt.length();
    String filePath = "";
    boolean isFilePath = false;
    String macroFileName = csName.split(" ")[0];
    if ((macroFileName.endsWith(macroExt) || macroFileName.contains(macroExt + "/"))
        && !M.io.isOptionSet(CoreIO.IOOptions.DoNotAllowExecutionOfMacroOutsideOfHomepath)) {
      filePath = macroFileName.substring(1,macroFileName.indexOf(macroExt) + extLength);
      if (Shell.isWindows()) {
        filePath = filePath.replaceAll("\\\\", "/");
        String windowsRegex = "^[a-zA-Z]:/[/S|*/S]?.*$"; // regex to match windows using linux file separators
        isFilePath = filePath.matches(windowsRegex);
      }
      else {
        isFilePath = filePath.startsWith("/"); // is it a good directory path for linux?
      }
      if (isFilePath) {
        isOutsideNxmOptionTree = true; // set to true for later processing
        args.put("/MFN", filePath); // need to add the switch MFN to tell macro it is external and not to try and find the path
        name = csName.substring(0,csName.indexOf(macroExt));
      }
    }
    // end external macro 3.9.2

    if (!isOutsideNxmOptionTree) i = csName.indexOf('/'); // is there a switch on the command name?
    else {
      i = csName.indexOf('/',csName.indexOf(macroExt) + extLength);
    }
    if (i > 0) {
      line.switchlen = csName.length();
      csName = csName.substring(0,i);
    }
    if (!isOutsideNxmOptionTree) name = csName.toUpperCase(); // do not use csName if we are using external macro - since 3.9.2
    if (csName.startsWith("nxm.")) {
      String area = null; // Area in option tree (uppercase)
      String cmd;         // Name of command without ';OPT' (uppercase)
      int    j;
      i=4;
      j = csName.indexOf('.',i);  if (j > 0) { option = csName.substring(i,j).toUpperCase(); i = j+1; }
      j = csName.indexOf('.',i);  if (j > 0) { area   = csName.substring(i,j).toUpperCase(); i = j+1; }
      j = csName.indexOf('.',i);  if (j > 0) { area   = null; }
      cmd  = csName.substring(csName.lastIndexOf('.')+1).toUpperCase();
      name = cmd + ";" + option;

      support = findDefaultSupport(M, csName, cmd, area, option);
      conf    = findConfiguration( M, csName, cmd, area, option, support);
    } else {
      conf   = M.dictionary.findParser(name);
      csName = null; // Done with this, now set it to null.
    }

    if (conf!=null) {                           // configured command
      name = conf.get(1);
      i = name.indexOf(';');
      option = name.substring(i+1);
      name = name.substring(0,i);
      i = option.indexOf(':');
      option = option.substring(0,i);
      if (support.equals("U")) { support = conf.get(2); } // set default support if not set
      if (support.equals("O")) { cmdline = "$"+cmdline; } // it's an OS command
      else Args.parseArgs(M,args,line,conf);
      args.setCmdClassName(csName);
    }
    else if (name.length()==0) { }
    else if (name.charAt(0)=='%') {             // unconfigured macro
      // NXM-1009 show better error when user includes macro extension
      if (name.endsWith(".MM") || name.endsWith(".MMP")) {
        throw new MidasException("Illegal macro name '"+name+"'. Do not include file "
                               + "extension when running a macro");
      }
      else if (!macro_name_pattern.matcher(name).matches()) {
        throw new MidasException("Illegal macro name '"+name+"'. Command names may contain "
            + "letters and symbols excluding spaces and dots");
      }
      support = "M";
      conf = getUserMacroConfig (M,name, isOutsideNxmOptionTree);
      Args.parseArgs(M,args,line,conf);
    }
    else if (name.charAt(0)=='$') {             // forced OS
      support = "O";
      // the $ is handled in Foreign
    }
    else {
      // If command is not found in dictionary, and is not one of the
      // above type of commands, it is unknown so return blank
      name = "";
    }
    int sIndex=0;
    String ss = args.getS("/S", null);
    if ((support.length()>1) || (ss != null) && (ss.length() > 0)) {
      if (ss!=null && ss.length()>0) sIndex = support.indexOf(ss.charAt(0));
      if (sIndex<0) sIndex=0;  // support not available for this command, use default
    }
    args.support = support.charAt(sIndex);
    args.name = name;
    args.option = option;
    args.cmdline = cmdline;
    if ((M.debug&Debug.ARGS)!=0) args.dump();
    return args;
  }

  /** Handle Strings escape sequence (if IOOptions.DisableEscapeSequences is NOT set
      in the passed in Midas context) by calling  {@link StringUtil#unescapeString(CharSequence)}
      when it is surrounded by double quotes otherwise return input String as is.
      @param midas Midas context with CoreIO.IOOptions flags and for deprecate message.
      @param argstr input String to unescape escape sequences (if enabled)
      @return unescaped all escape sequence if surrounded by double quotes and enabled,
              otherwise return input string untouched.
      @since NeXtMidas 2.9.0
    */
  private static String handleEscapesInArgString (Midas midas, String argstr) {
    // check for quotes at front and back, since we only parse escapes within quotes.
    if (argstr == null ||
        argstr.length() < 3 ||
        argstr.charAt(0)!='"' ||
        argstr.charAt(argstr.length()-1)!='"') {
      return argstr;
    }

    boolean disableEscapeSeq = midas.io.isOptionSet(CoreIO.IOOptions.DisableEscapeSequences);
    if (disableEscapeSeq) {
      if (argstr.indexOf('\\') != -1) {
        String msg = "Use of backslash in quoted strings are deprecated since 2.9.0.\n"+
                     "  In a future release, the default behavior will treat the backslash as the\n"+
                     "  escape character like in Java/C string escape sequences.\n"+
                     "  Run \"ENV set iooptions -DisableEscapeSequences\" to enable escape sequences.\n"+
                     "  argstr = [" + argstr + "]";
        midas.deprecate(msg);
      }
      return argstr;
    }

    argstr = StringUtil.unescapeString(argstr);
    return argstr;
  }

  /** Parse a line for the default arguments in a given parser configuration.
      @param M    The Midas context to use.
      @param args The arguments to parse the arguments into.
      @param line The line to parse.
      @param conf The default argument configuration.
   */
  public static void parseArgs (Midas M, Args args, Parser line, Parser conf) {
    int i,j,jj,k,kvSize=0;
    String key,tkey,value;
    String  arg = conf.get(I_NARGS);            // num args string (from configuration/dictionary)
    boolean xargs = arg.endsWith("+");          // extended args mode ?

    if (xargs) arg = arg.substring(0,arg.length()-1); // remove the +
    int nargs  = Integer.parseInt(arg);         // default number args
    int anargs = conf.elements()-I_NARGS;       // actual nargs in config

    for (i=1; i<=nargs; i++) {                  // parse config key[=default] (from configuration//dictionary)
      arg = conf.get(I_NARGS+i);                // starting after nargs entry
      j = arg.indexOf('=');                     // is it keyed ?
      if (j<0) {                                // just key name
        key = "P"+i;
        value = arg;
      } else {                                  // key=default
        key = arg.substring(0,j);
        value = arg.substring(j+1);
      }
      value = handleEscapesInArgString(M,value);// Handle escape sequences in quoted Strings (BUG 2292)
      args.put (key, value);                    // default value
      kvSize++;                                 // Keep track of tags in config
    } // end #1 for loop

    for (i=2,k=0; line.more(); i++) {           // parse command line
      arg = line.get(i);                        // next token
      arg = handleEscapesInArgString(M, arg);   // Handle escape sequences in quoted Strings (BUG 2292)
      j = arg.indexOf('=');                     // is it keyed ?
      jj = arg.indexOf('{');                    // is it tabled ?
      if (j>0 && jj>=0 && jj<j) j=-1;
      if (j>0 && arg.charAt(0)=='"') j=-1;
      if (j>0 && arg.charAt(0)=='/') { }
      else if (j>0 && Results.validNameLength(arg,0)!=j) j=-1;
      if (j>0) tkey = arg.substring(0,j).toUpperCase(); else tkey=null;
      value = arg.substring(j+1);               // get value from arg String
      if (j>0) value = handleEscapesInArgString(M,value);// Handle escape sequences in quoted Strings (BUG 2292)
      if (arg.indexOf('/')==0                   // its a switch
        && arg.length()>1                       //   and NOT an operator for calc
        && !(arg.indexOf('/',1)>=0 && j<0)      //   and NOT a Unix filename or //
        && (j>0 || noMoreParameters(line,i))) { //   and (NOT state switch OR
                                                //        after ALL cmdline parameters)
        if (j<0) args.put (arg.toUpperCase(),STATE_SWITCH_VAL); // state switch present
        else     args.put(tkey, value);         // value switch
      }
      else if (j>0) {                           // keyed parameter
        k++;
        key = args.keyMatch(tkey, kvSize);      // only abbreviate tags from config
        if (isDuplicate(line,i)) M.warning("Duplicate keys entered: " + line.getAll());
        if (key!=null) args.put (key, value);
        else if (xargs) {
          k = Math.max(k,nargs+1);
          args.put(tkey,value);
        }
        else M.warning("Bad Argument: "+arg);
      }
      else if (k < nargs) {                     // position parameter
        k++;
        key = args.getKV().getKey(k);
        if (arg.startsWith("//")) args.put(key, arg.substring(1));
        else if (arg.length()>0)  args.put(key, arg);
      }
      else if (xargs) {                         // position parameter
        k++;
        key = "P"+k;
        if (arg.startsWith("//")) args.put(key, arg.substring(1));
        else args.put(key, arg);
      }
      else { // too many arguments
        String cmdLine = line.getAll().toUpperCase();
        if (cmdLine.startsWith("ELSE,IF") || cmdLine.startsWith("ELSE;SYS,IF")) { // a common error
          throw new MidasException("Expected ELSEIF (no space): "+line.getAll());
        }
        else {
          M.warning("Too many arguments: "+line.getAll());
        }
      }
    } // end #2 for loop

    kvSize = parseConfiguredSwitches(M, args, conf, nargs, anargs, kvSize);

    if (k>nargs) nargs=k;
    args.nargs = nargs;
    args.nargsx = args.kv.size();

    if (line.switchlen>0) {                                    // handle switches right after command
      arg = line.get(0,line.switchlen);
      i = arg.indexOf('/');
      for (; i<line.switchlen; i+=k) {
        k = line.lengthd(i);                                   // len of next deliminator
        arg = line.get(i,k);
        j = arg.indexOf('=');                                  // is it keyed ?
        if (j<0) {
          args.put (arg.toUpperCase(),STATE_SWITCH_VAL);       // state switch present
        }
        else {
          key   = arg.substring(0,j).toUpperCase();
          value = arg.substring(j+1);                          // get value from arg string
          value = handleEscapesInArgString(M, value);          // Handle escape sequences in quoted Strings (BUG 2292)
          args.put(key, value);                                // value switch
        }
      } // end #3 for loop
    }
  }

  /** Return true if duplicate key entered.
   * @param   line     Command entered.
   * @param   curIndex current index
   * @return  True  if duplicate keys entered.
   * @since NeXtMidas 3.7.1
   */
  public static boolean isDuplicate (Parser line, int curIndex) {
    boolean more = true;
    int j = 0;
    String arg1 = line.get(curIndex);  // get current key
    int k = arg1.indexOf('=');
    String key = arg1.substring(0, k);

    for (int i=curIndex+1; more&&line.more(); i++) {
      String arg2 = line.get(i);    // get next key
      // line read from macro either has a space or carriage return at eol
      if (!arg2.isEmpty()) {
        j = arg2.indexOf('=');
        if (k>0 && j>0) {
          if (key.equals(arg2.substring(0, j))) {
            line.get(curIndex); // reset line.more() filter
            return true;
          }
       }
      }
    }
    line.get(curIndex); // reset line.more() filter
    return false;
  }

  /** Parse configured switches from command dictionary
      @param M       The Midas context to use.
      @param args    The arguments to parse the arguments into.
      @param conf    The default argument configuration.
      @param nargs   The default number args
      @param anargs  The actual nargs in config
      @param kvSize  Keep track of tags in config
      @return new kvSize for keeping track of tags in config
      @since NeXtMidas 2.7.1 Extracted from {@link #parseArgs(Midas, Args, Parser, Parser)}
   */
  private static int parseConfiguredSwitches (Midas M, Args args, Parser conf,
                                              int nargs, int anargs, int kvSize) {
    int i;
    int j;
    String arg;
    String key;
    String value;
    for (i=nargs+1; i<=anargs; i++) {           // parse config switches
      arg = conf.get(I_NARGS+i);
      int iSwitch = arg.indexOf('/');
      if (iSwitch >= 0) {
        j = arg.indexOf('=');
        if (j<0) {                              // just state switch
          key = arg;
          value = STATE_SWITCH_VAL;
        }
        else {                                  // /switch=default
          key = arg.substring(0,j);
          value = arg.substring(j+1);
        }
        args.put(key, value);                   // default value
        kvSize++;                               // Keep track of tags in config
      }
      else {
        if ((M.debug&Debug.ARGS)!=0) {
          //TBD -- we could trap this in configure but we need
          // some additional methods here in args to make it simpler
          M.warning("'"+conf.getAll()+"' has extra arg = '"+arg+
                    "' in config is not a switch");
        }
      }
    }
    return kvSize;
  }


  /** get all swtich names
   *  @return Set containing all switch names
   */
  public Set<String> getAllSwitchNames () {
    Set<String> names = new HashSet<>();
    addAllSwitchNames(names, this);
    if (M.macro != null) {
      // switches on the macro call line (same as used in the getRaw(String) method)
      if (M.macro.lines[0] != null) addAllSwitchNames(names,M.macro.lines[0]);

      // switches on the STARTMACRO line (same as used in getRaw(String) method)
      if (M.macro.lines[1] != null) addAllSwitchNames(names,M.macro.lines[1]);
    }
    return names;

  }

  /** Only called by {@link #getAllSwitchNames()} */
  private static void addAllSwitchNames (Set<String> names, Args MA) {
    // Switches on the command line (backwards compatible)
    for (int i = 1; i < MA.getKV().size(); i++) {
      String key = MA.getKV().getKey(i);
      if (key.charAt(0)== AsciiMap.SLASH) names.add(key);
    }
  }

  /** Sets the class name of the command. */
  void setCmdClassName(String str) {
    cmdClassName = str;
  }

  /** Gets the class name of the command.
      @return For primitives and intrinsics this is the fully-qualified
      class name of the command. For non-Java commands (macros, host primitives, etc) this returns
      the class name of the library used to execute the command. For unknown (SUPPORT='U') and
      operating system (SUPPORT='0') commands this returns null.

   */
  public String getCmdClassName() {
    String cname = null;

    if ((support == 'I') || (support == 'P')) {
      cname = cmdClassName; // Only allow override for intrinsics and primitives.
    }

    if (cname == null) {
      String opt = option.toLowerCase();
      String nameLC = name.toLowerCase();

      switch (support) {
        case 'I': cname = "nxm."+opt+"."+"intr."+name.substring(0,1)+nameLC.substring(1); break;
        case 'P': cname = "nxm."+opt+"."+"prim."+nameLC; break;  // Primitive
        case 'M': cname = "nxm.sys.lib.Macro";           break;  // Macro
        case 'H': cname = "nxm.xbc.lib.HPrimitive";      break;  // Host
        case 'D': cname = null;                          break;  // Deprecated
        case 'O': cname = null;                          break;  // Operating System
        case 'U': cname = null;                          break;  // Operating System
        default:  cname = null;                          break;  // Error
      }
    }

    return cname;
  }

  /** Gets the name of the source file for the command. This is a merely a guess based on normal
      option tree structure, the source file may or may not be present at the given location.
      @return The file name. May be in "nxm.opt..." syntax or may be in "nxm/opt/..." syntax.
   */
  public String getDefaultSourceName() {
    String fname;

    switch (support) {
      case 'I': fname = getCmdClassName() + ".java";          break;  // Intrinsic
      case 'P': fname = getCmdClassName() + ".java";          break;  // Primitive
      case 'M': fname = Macro.getFileName(M,name,option);     break;  // Macro
      case 'H': fname = "nxm."+option.toLowerCase()+".host."+name.toLowerCase()+".for";  break;
      case 'D': fname = null;                                 break;  // Deprecated
      case 'O': fname = null;                                 break;  // Operating System
      case 'U': fname = null;                                 break;  // Operating System
      default:  fname = null;                                 break;  // Error
    }

    return (fname == null)? null : (new FileName(fname)).toString(); // convert to path name
  }

  /** Sets the line number in the macro this was read from. */
  void setLineNumber (int line) {
    lineNumber = line;
  }

  /** Gets the line number in the macro this was read from. Returns -1 if not applicable. */
  int getLineNumber () {
    return lineNumber;
  }

  //////////////////////////////////////////////////////////////////////////////
  // SplitArg CLASS DEFINITION
  //////////////////////////////////////////////////////////////////////////////
  /** This class will split an argument string into the separate arguments. */
  public class SplitArg {
    Parser p;

    /** The number of fields in the string. */
    int fields;

    /** Indicates if quotes should be added. (Default is <code>false</code>). */
    boolean addQuotes=false;

    /** default constructor */
    public SplitArg () {}

    /** Set the string and the delimiter used by the string.
        @param string     The string with fields separated by the delimiter.
        @param delimiter The delimiter separating fields in the string.
    */
    public void setString (String string, char delimiter) {
      fields=0;
      if (string==null || string.length()==0) return;
      int ls = string.length(); // are there only quotes on the entire argument ?
      addQuotes = ls>2 && string.charAt(0)==QUOTE && string.indexOf(QUOTE,1)==ls-1;
      if (addQuotes) string = string.substring(1,ls-1);
      p = new Parser (string,delimiter);
      fields = p.elements();
    }

    /** Return a field from the string.
        @param index The index into the set of fields to get (0 based).
        @return The field in the original string at the specified index.
    */
    public String getField (int index) {
      if (index<0 || index>=fields) return null;
      String field = p.get(index+1);
      if (!addQuotes) return field;
      int lf = field.length();
      if (lf<1 || field.charAt(lf-1)!=QUOTE) field+=QUOTE;
      if (lf==0 || field.charAt(0)!=QUOTE) field=QUOTE+field;
      return field;
    }

    /** Get the total number of fields.
        @return The total number of fields.
    */
    public int getFields() { return fields; }
  }

  /**
    Gets the data addressing mode from the switches /ABSC, /ADDR or /ADDRESSING.
    The precedence are: <br>
    1. /addressing={@value nxm.sys.lib.DataFile#addressingModeList} <br>
    2. /addr={@value nxm.sys.lib.DataFile#addressingModeList} <br>
    3. /absc <br>
    see {@link DataFile#getIndex(String, int, double, int)} for valid values.
    @return the addressing mode.
    @see DataFile#addressingModeList
    @since NeXtMidas 2.9.1
   */
  public int getAddressing () {
    int mode = getState("/ABSC")? DataFile.ADDR_ABSCISSA_LEGACY : DataFile.ADDR_INDEX;
    mode = getSelectionIndex("/ADDR", DataFile.addressingModeList, mode);
    mode = getSelectionIndex("/ADDRESSING", DataFile.addressingModeList, mode);
    return mode;
  }

  /** Checks if user has specified data addressing mode from any of the switches
      used by {@link #getAddressing()}.
      @return true if any addressing mode switch is specified on the command line
              (e.g. any one of: /ABSC, /ADDR=, /ADDRESSING=), otherwise false.
      @since NeXtMidas 2.9.3
   */
  public boolean hasAddressing () {
    return isPresent("/ABSC") || isPresent("/ADDR") || isPresent("/ADDRESSING");
  }

  /** @since NeXtMidas 3.5.1 */
  @Override
  public Midas getMidas () {
    return M;
  }
}
