/*
===========================================================================

                       Copyright  2008 Allen Baker

---------------------------------------------------------------------------
Class:         InflationData
Originator:    Allen Baker (2008.08.11 20:52)
---------------------------------------------------------------------------
$RCSfile$
$Revision$
$Date$
===========================================================================
*/



/*
===============================================
Package
----------------------------------------------- */
package cosmicabyss.com.app;



/*
===============================================
Imports
----------------------------------------------- */
import java.io.*;
import java.util.*;


import cosmicabyss.com.lib.*;



/*
=========================================================================== *//**
Description<PRE>
   This program processes a text file containing the historical
   consumer price index (CPI) and converts it into a csv file.  The source
   of this data is at
   ftp://ftp.bls.gov/pub/special.requests/cpi/cpiai.txt.

   To use this class:

      1) go to this URL
      (ftp://ftp.bls.gov/pub/special.requests/cpi/cpiai.txt) to download
      the BLS' text file containing monthly CPI data from 1913 to present.

      2) run the batch file InflationData.bat against the text file that
      was downloaded in step 1

      3) open the csv file created in step 2 and copy - paste its contents
      into the CPIData tab page in the InflationCalculator.xls file

</PRE>

Usage<PRE>
   java cosmicabyss.com.app.InflationData [-h -q -r -s] [-l [logFile]] {-x fileSpec} {-ifile fileName} {-xfile fileName} fileSpecs+
      Arguments can be placed in any order on the command line.

   -h means HELP.
      It causes this usage message to be displayed, and terminates the
      program.

   -q means QUIET.
      It stops the program from sending log messages to stdout. The program
      sends log messages to stdout by default; this option is the only way
      to stop it from doing that.

   -r means RECURSE.
      It causes the program to recurse down through the trees of
      subdirectories that are rooted at the directories specified in the
      file specifiers that are on the command line.

   -s means SUBDIRECTORIES.
      It causes the program to match subdirectory names that match the file
      specifiers on the command line.

   -l means LOGFILE.
      It takes an optional file name parameter, and causes the program to
      send log messages to the named file. If the file name is not present,
      the program generates a default file name. If the environment
      variable LOGDIRECTORY is defined, the program places the file in that
      directory. If not, the program places the file in the current
      directory. The program continues to send log messages to stdout too,
      unless the -q option is used.

   -x means EXCLUDE.
      It requires a fileSpec, and causes the program to not process any
      files that match the fileSpec. This control may be put on the command
      line as many times as needed to specify all the files that should be
      excluded from processing.

      Enclosing the fileSpec in quotes is REQUIRED for any fileSpec that
      contains embedded spaces or wildcard characters.

   -xfile means EXCLUDE FILE.
      It takes a required file name parameter, and causes the program to
      read each non-empty line in the named file and treat it as the
      filespec argument to a -x control. This control may be put on the
      command line as many times as needed to specify all the files that
      contain filespecs for files that should be excluded from processing.

   -ifile means INCLUDE FILE.
      It takes a required file name parameter, and causes the program to
      read each non-empty line in the named file and treat it as a fileSpec
      that identifies files to include in processing. This control may be
      put on the command line as many times as needed to specify all the
      files that contain filespecs for files that should be included in the
      processing.

   fileSpecs identifies the files to process.
      It is a required parameter and is one or more file specifiers that
      identify the files that the program will process.

      As many fileSpecs may be put on the command line as needed to specify
      all the files that should be included in processing.

      Enclosing the fileSpec in quotes is required for any file specifier
      that contains embedded spaces.

      Enclosing the fileSpec in quotes prevents the command line
      interpreter from trying to replace wildcard characters itself instead
      of letting InflationData do it.

      The quotes surrounding the fileSpec are not needed if it contains no
      wildcard characters or spaces.
</PRE>

<P>
<DL>
   <DT>
      <B>Example usage:</B>
      <DD>
         <PRE>
            *java cosmicabyss.com.app.InflationData -r -x"*.bat" "..\*.*"
         </PRE>
      </DD>
   </DT>
   <DT>
      <B>View Source:</B>
      <DD>
         <A href="InflationData.java.html">
            InflationData.java
         </A>
      </DD>
   </DT>
   <DT>
      <B>Author:</B>
      <DD>
         <A href="mailto:sourcecode.v01@cosmicabyss.com">
            Allen Baker
         </A>
      </DD>
   </DT>
</DL>
*//*
=========================================================================== */
public class InflationData
   {



   /*:                 
   ==============================================================
   ============================================================== *//**
   This method creates a InflationData class object and runs the
   application to completion.

   <P><B>Implementation: </B><A HREF="InflationData.java.html#000">View source</A>

   @param
      pArgs contains the command line arguments with which the class was
      invoked as an application.
   *//*
   -------------------------------------------------------------- */
   public <Type> InflationData(Type[] pArgs) throws Exception
      {
      boolean  successCode = true;

      successCode = initialize(pArgs);
      if (successCode)
         {
         successCode = processAllFiles();
         }
      cOut.println
         (
         CLASS_NAME.string() +
         (successCode? " completed successfully." : " completed with problems.")
         );
      cOut.println();
      cOut.stopLog();
      }



   /*:                 
   ==============================================================
   ============================================================== *//**
   This method sets the ConsoleStream for this object.

   <P><B>Implementation: </B><A HREF="InflationData.java.html#001">View source</A>

   @return
      a reference to this object

   @param
      pConsole is a ConsoleStream through which this objects sends its
      output.
   *//*
   -------------------------------------------------------------- */
   public InflationData setOut(ConsoleStream pConsole)
      {
      cOut = pConsole;
      return this;
      }



   /*:                 
   ==============================================================
   ============================================================== *//**
   This boilerplate method is used for testing an instantiated object of
   this class and may include any code the developer chooses.

   <P><B>Implementation: </B><A HREF="InflationData.java.html#002">View source</A>

   @return
      a reference to this object
   *//*
   -------------------------------------------------------------- */
   public InflationData test() throws Exception
      {
      cOut.titledPrintf
         (
         "\"HELLO WORLD!\"",
         "%s  %s  %s",
         "I'm an object of the", CLASS_NAME, "class, and I approved this message."
         );
      return this;
      }



   /*
   ===================================================================
   ===================================================================
                --------------- Protected ---------------
   =================================================================== */



   /*
   ===================================================================
   ===================================================================
                 --------------- Private ---------------
   =================================================================== */



   /*
   ===============================================
   Class Constants
      YEAR                   : index of the array element that will contain the year
      MONTH                  : index of the array element that will contain the month
      CPI                    : index of the array element that will contain the CPI
      DELTA_CPI_FROM_EARLIER : index of the array element that will contain the forward CPI delta
      DELTA_CPI_FROM_LATER   : index of the array element that will contain the backward CPI delta
      ARRAY_SIZE             : the size of the array
   ----------------------------------------------- */
   private static final int  YEAR                   = 0;
   private static final int  MONTH                  = 1;
   private static final int  CPI                    = 2;
   private static final int  DELTA_CPI_FROM_EARLIER = 3;
   private static final int  DELTA_CPI_FROM_LATER   = 4;
   private static final int  ARRAY_SIZE             = 5;
   /*
   ===============================================
   Class Constants
      CLASS_NAME  : the name of this class
      DESCRIPTION : the man page description of this application
      USAGE       : the man page usage information for this application
   ----------------------------------------------- */
   private static final XString  CLASS_NAME  = new XString(InflationData.class.getName());
   private static final XString  DESCRIPTION = new XString
      (
      // <Description>  Preprocessor generated String definition, Don't mess with it.
      "This program processes a text file containing the historical                                                         \n" +
      "consumer price index (CPI) and converts it into a csv file.  The source                                              \n" +
      "of this data is at                                                                                                   \n" +
      "ftp://ftp.bls.gov/pub/special.requests/cpi/cpiai.txt.                                                                \n" +
      "                                                                                                                     \n" +
      "To use this class:                                                                                                   \n" +
      "                                                                                                                     \n" +
      "   1) go to this URL                                                                                                 \n" +
      "   (ftp://ftp.bls.gov/pub/special.requests/cpi/cpiai.txt) to download                                                \n" +
      "   the BLS' text file containing monthly CPI data from 1913 to present.                                              \n" +
      "                                                                                                                     \n" +
      "   2) run the batch file InflationData.bat against the text file that                                                \n" +
      "   was downloaded in step 1                                                                                          \n" +
      "                                                                                                                     \n" +
      "   3) open the csv file created in step 2 and copy - paste its contents                                              \n" +
      "   into the CPIData tab page in the InflationCalculator.xls file                                                     \n" +
      "                                                                                                                     \n" +
      ""// </Description>
      );
   private static final XString  USAGE       = new XString
      (
      // <Usage>        Preprocessor generated String definition, Don't mess with it.
      "java cosmicabyss.com.app.InflationData [-h -q -r -s] [-l [logFile]] {-x fileSpec} {-ifile fileName} {-xfile fileName} fileSpecs+\n" +
      "   Arguments can be placed in any order on the command line.                                                         \n" +
      "                                                                                                                     \n" +
      "-h means HELP.                                                                                                       \n" +
      "   It causes this usage message to be displayed, and terminates the                                                  \n" +
      "   program.                                                                                                          \n" +
      "                                                                                                                     \n" +
      "-q means QUIET.                                                                                                      \n" +
      "   It stops the program from sending log messages to stdout. The program                                             \n" +
      "   sends log messages to stdout by default; this option is the only way                                              \n" +
      "   to stop it from doing that.                                                                                       \n" +
      "                                                                                                                     \n" +
      "-r means RECURSE.                                                                                                    \n" +
      "   It causes the program to recurse down through the trees of                                                        \n" +
      "   subdirectories that are rooted at the directories specified in the                                                \n" +
      "   file specifiers that are on the command line.                                                                     \n" +
      "                                                                                                                     \n" +
      "-s means SUBDIRECTORIES.                                                                                             \n" +
      "   It causes the program to match subdirectory names that match the file                                             \n" +
      "   specifiers on the command line.                                                                                   \n" +
      "                                                                                                                     \n" +
      "-l means LOGFILE.                                                                                                    \n" +
      "   It takes an optional file name parameter, and causes the program to                                               \n" +
      "   send log messages to the named file. If the file name is not present,                                             \n" +
      "   the program generates a default file name. If the environment                                                     \n" +
      "   variable LOGDIRECTORY is defined, the program places the file in that                                             \n" +
      "   directory. If not, the program places the file in the current                                                     \n" +
      "   directory. The program continues to send log messages to stdout too,                                              \n" +
      "   unless the -q option is used.                                                                                     \n" +
      "                                                                                                                     \n" +
      "-x means EXCLUDE.                                                                                                    \n" +
      "   It requires a fileSpec, and causes the program to not process any                                                 \n" +
      "   files that match the fileSpec. This control may be put on the command                                             \n" +
      "   line as many times as needed to specify all the files that should be                                              \n" +
      "   excluded from processing.                                                                                         \n" +
      "                                                                                                                     \n" +
      "   Enclosing the fileSpec in quotes is REQUIRED for any fileSpec that                                                \n" +
      "   contains embedded spaces or wildcard characters.                                                                  \n" +
      "                                                                                                                     \n" +
      "-xfile means EXCLUDE FILE.                                                                                           \n" +
      "   It takes a required file name parameter, and causes the program to                                                \n" +
      "   read each non-empty line in the named file and treat it as the                                                    \n" +
      "   filespec argument to a -x control. This control may be put on the                                                 \n" +
      "   command line as many times as needed to specify all the files that                                                \n" +
      "   contain filespecs for files that should be excluded from processing.                                              \n" +
      "                                                                                                                     \n" +
      "-ifile means INCLUDE FILE.                                                                                           \n" +
      "   It takes a required file name parameter, and causes the program to                                                \n" +
      "   read each non-empty line in the named file and treat it as a fileSpec                                             \n" +
      "   that identifies files to include in processing. This control may be                                               \n" +
      "   put on the command line as many times as needed to specify all the                                                \n" +
      "   files that contain filespecs for files that should be included in the                                             \n" +
      "   processing.                                                                                                       \n" +
      "                                                                                                                     \n" +
      "fileSpecs identifies the files to process.                                                                           \n" +
      "   It is a required parameter and is one or more file specifiers that                                                \n" +
      "   identify the files that the program will process.                                                                 \n" +
      "                                                                                                                     \n" +
      "   As many fileSpecs may be put on the command line as needed to specify                                             \n" +
      "   all the files that should be included in processing.                                                              \n" +
      "                                                                                                                     \n" +
      "   Enclosing the fileSpec in quotes is required for any file specifier                                               \n" +
      "   that contains embedded spaces.                                                                                    \n" +
      "                                                                                                                     \n" +
      "   Enclosing the fileSpec in quotes prevents the command line                                                        \n" +
      "   interpreter from trying to replace wildcard characters itself instead                                             \n" +
      "   of letting InflationData do it.                                                                                   \n" +
      "                                                                                                                     \n" +
      "   The quotes surrounding the fileSpec are not needed if it contains no                                              \n" +
      "   wildcard characters or spaces.                                                                                    \n" +
      ""// </Usage>
      );
   /*
   ===============================================
   Class variables
      cOut : console output.
   ----------------------------------------------- */
   private static ConsoleStream  cOut = ConsoleStream.getSingleton();
   /*
   ===============================================
   Instance variables
      iFileNameList     : the list of all file names that match the
                          filespecs given on the command line.
      iGlobalProperties : a copy of all the environment variables.
   ----------------------------------------------- */
   private FileNameList      iFileNameList     = null;
   private GlobalProperties  iGlobalProperties = null;



   /*:                 
   ==============================================================
   ============================================================== *//**
   This method performs the core processing functions of this application.

   <P><B>Implementation: </B><A HREF="InflationData.java.html#003">View source</A>

   @return
      a success code of true if all goes well, false otherwise.
   *//*
   -------------------------------------------------------------- */
   private boolean processAllFiles() throws Exception
      {
      boolean  successCode = true;
      String   fmtStr;
      /*
      ===============================================
      tell 'em what we're gonna do
      ----------------------------------------------- */
      cOut.println("Plan","Will process " + iFileNameList.size() + " files.");
      if      (iFileNameList.size() > 99999) fmtStr = "%06d";
      else if (iFileNameList.size() >  9999) fmtStr = "%05d";
      else if (iFileNameList.size() >   999) fmtStr = "%04d";
      else if (iFileNameList.size() >    99) fmtStr = "%03d";
      else if (iFileNameList.size() >     9) fmtStr = "%02d";
      else                                   fmtStr = "%d";
      /*
      ===============================================
      do it for each file in the FileNameList
      ----------------------------------------------- */
      int  filesProcessed = 0;
      for (XString  fileName : iFileNameList)
         {
         XFile    file          = new XFile(fileName);
         XString  canonicalPath = new XString(file.getCanonicalPath());
         /*
         ===============================================
         tell 'em which file is being processed next
         ----------------------------------------------- */
         filesProcessed++;
         cOut.printf("[" + fmtStr + "] Processing: %s\n",filesProcessed,canonicalPath);
         /*
         ===============================================
         process the file
         ----------------------------------------------- */
         successCode &= processThisFile(canonicalPath);
         }
      /*
      ===============================================
      tell 'em what we did
      ----------------------------------------------- */
      cOut.println("Result","Processed " + filesProcessed + " of an expected " + iFileNameList.size() + " files.");
      return successCode;
      }



   /*:                 
   ==============================================================
   ============================================================== *//**
   This method performs the core processing functions of this application
   on one file.

   <P><B>Implementation: </B><A HREF="InflationData.java.html#004">View source</A>

   @return
      a success code of true if all goes well, false otherwise.

   @param
      pFileName is the name of the file to process.
   *//*
   -------------------------------------------------------------- */
   private boolean processThisFile(XString pFileName) throws Exception
      {
      boolean     successCode = true;
      TextFile    inFile      = new TextFile(pFileName);
      TextFile    tmpFile     = new TextFile(inFile.getExtensionlessCanonicalPath().concat(".tmp"));
      TextWriter  outFile     = new TextWriter(inFile.getExtensionlessCanonicalPath().concat(".csv"));
      TextWriter  vbaFile     = new TextWriter(inFile.getExtensionlessCanonicalPath().concat(".bas"));
      /*
      ===============================================
      this method makes no sense on directory files, this must be a normal
      file
      ----------------------------------------------- */
      if (inFile.isDirectory()) return successCode;
      /*
      ===============================================
      dont modify the original file; instead, copy it to this tmp file
      and preprocess it
      ----------------------------------------------- */
      inFile.copyTo(tmpFile);
      tmpFile.trim();
      tmpFile.iteratingReplace("  "," ");
      tmpFile.tossBlankLines();
      /*
      ===============================================
      create a csv file to output to
      ----------------------------------------------- */
      DelimitedFile  dlmFile = new DelimitedFile(tmpFile);
      /*
      ===============================================
      the file we will read uses the double quote to enclose tokens and
      uses the blank as a delimiter.
      ----------------------------------------------- */
      dlmFile.setQuote('\"');
      dlmFile.setDelimiter(' ');
      /*
      ===============================================
      write the column headers out to the csv file
      ----------------------------------------------- */
//      outFile.println("year,month,CPI,% Delta CPI from Earlier,% Delta CPI from Later,Investment Value");
      /*
      ===============================================
      this is where we'll keep the data.  we will read the input file, store it in memory
      with each month in this ArrayList
      ----------------------------------------------- */
      ArrayList<double[]>  CPIdata = new ArrayList<double[]>();
      /*
      ===============================================
      parse each line of the delimited file into its separate tokens and
      print each line of tokens out on a line with each token separated by
      a comma.
      ----------------------------------------------- */
      boolean  start = false;
      while (dlmFile.hasNext())
         {
         ArrayList<XString>  tokens = dlmFile.parsedLine();
         /*
         ===============================================
         the first lines are junk; skip 'em up to and including the one
         that starts with the column header "year"
         ----------------------------------------------- */
         if (!start)
            {
            start = tokens.get(0).equals("Year");
            continue;
            }
         /*
         ===============================================
         each month of data will go into one array which will in-turn be
         put into the ArrayList.
         ----------------------------------------------- */
         double[]  array = null;
         /*
         ===============================================
         now parse the remaining lines and output into csv format
         ----------------------------------------------- */
         double  year = 0.0;
         int  tokenNum = 0;
         for (XString tkn : tokens)
            {
            switch (tokenNum)
               {
               default:
                  break;
               case 0:
                  year = UMath.decodeDecimalDouble(tkn);
                  break;
               case 1:
               case 2:
               case 3:
               case 4:
               case 5:
               case 6:
               case 7:
               case 8:
               case 9:
               case 10:
               case 11:
               case 12:
                  array = new double[ARRAY_SIZE];
                  array[YEAR ] = year;
                  array[MONTH] = (double)tokenNum;
                  array[CPI  ] = UMath.decodeDecimalDouble(tkn);
                  CPIdata.add(array);
                  break;
               }
            tokenNum++;
            }
         }
      /*
      ===============================================
      for each month, calc the delta from the month before and to the month after
      ----------------------------------------------- */
      deltaCPIfromEarlier(CPIdata);
      deltaCPIfromLater(CPIdata);
      /*
      ===============================================
      output one line for each month to the csv output file
      ----------------------------------------------- */
      for (double[] array : CPIdata)
         {
         outFile.println
            (
            array[YEAR                  ] + "," +
            array[MONTH                 ] + "," +
            array[CPI                   ] + "," +
            array[DELTA_CPI_FROM_EARLIER] + "," +
            array[DELTA_CPI_FROM_LATER  ]
            );
         }
      outFile.flush();
      outFile.close();
      outFile = null;
      /*
      ===============================================
      output one line for each month to the VBA output file
      ----------------------------------------------- */
      long    procedureNumber = 0;
      long    count           = 0;
      for (double[] array : CPIdata)
         {
         if ((count % 400) == 0)
            {
            if (count > 0)
               {
               vbaFile.println("End Sub");
               vbaFile.println();
               vbaFile.println();
               vbaFile.println();
               }
            vbaFile.format("Private Sub InitializeData%d()\n", procedureNumber++);
            }
         count++;
         vbaFile.format
            (
            "   "                                 +
            "mInflationData(%4d, 1) = %4d:  "     +
            "mInflationData(%4d, 2) = %2d:  "     +
            "mInflationData(%4d, 3) = %10.6f:  "  +
            "mInflationData(%4d, 4) = %28.25f:  " +
            "mInflationData(%4d, 5) = %28.25f\n",
            count,
            (int)array[YEAR                  ],
            count,
            (int)array[MONTH                 ],
            count,
            array[CPI                   ],
            count,
            array[DELTA_CPI_FROM_EARLIER],
            count,
            array[DELTA_CPI_FROM_LATER  ]
            );
         }
      vbaFile.println("End Sub");
      vbaFile.println();
      vbaFile.println();
      vbaFile.println();
      vbaFile.format("Private Const cDataArrayLength As Long = %d#\n", count);
      vbaFile.flush();
      vbaFile.close();
      vbaFile = null;
      /*
      ===============================================
      get rid of the tmp file
      ----------------------------------------------- */
      tmpFile.delete();
      /*
      ===============================================
      tell 'em how it went
      ----------------------------------------------- */
      return successCode;
      }



   /*:                 
   ==============================================================
   ============================================================== *//**
   This method runs through an array of CPI indexes and computes the delta
   from one to the next.

   <P><B>Implementation: </B><A HREF="InflationData.java.html#005">View source</A>

   @return
      nothing

   @param
      pCPIdata is an ArrayList of double[].  Each double[] contains the CPI
      index for one month.
   *//*
   -------------------------------------------------------------- */
   private void deltaCPIfromEarlier(ArrayList<double[]> pCPIdata)
      {
      double[]  lastArray = null;
      double[]  thisArray = null;
      double    lastCPI   = 0.0;
      double    thisCPI   = 0.0;
      double    deltaCPI  = 0.0;

      int  size = pCPIdata.size();
      for (int i = 0; i < size; i++)
         {
         if (i==0)
            {
            thisArray = pCPIdata.get(i);
            thisArray[DELTA_CPI_FROM_EARLIER] = 0.0;
            }
         else
            {
            lastArray = thisArray;
            thisArray = pCPIdata.get(i);

            lastCPI  = lastArray[CPI];
            thisCPI  = thisArray[CPI];
            deltaCPI = (thisCPI - lastCPI) / lastCPI;

            thisArray[DELTA_CPI_FROM_EARLIER] = deltaCPI;
            }
         }
      }



   /*:                 
   ==============================================================
   ============================================================== *//**
   This method runs through an array of CPI indexes and computes the delta
   from one to the next.

   <P><B>Implementation: </B><A HREF="InflationData.java.html#006">View source</A>

   @return
      nothing

   @param
      pCPIdata is an ArrayList of double[].  Each double[] contains the CPI
      index for one month.
   *//*
   -------------------------------------------------------------- */
   private void deltaCPIfromLater(ArrayList<double[]> pCPIdata)
      {
      double[]  lastArray = null;
      double[]  thisArray = null;
      double    lastCPI   = 0.0;
      double    thisCPI   = 0.0;
      double    deltaCPI  = 0.0;

      int  size = pCPIdata.size();
      for (int i = (size-1); i >= 0; i--)
         {
         if (i==(size-1))
            {
            thisArray = pCPIdata.get(i);
            thisArray[DELTA_CPI_FROM_LATER] = 0.0;
            }
         else
            {
            lastArray = thisArray;
            thisArray = pCPIdata.get(i);

            lastCPI  = lastArray[CPI];
            thisCPI  = thisArray[CPI];
            deltaCPI = (thisCPI - lastCPI) / lastCPI;

            thisArray[DELTA_CPI_FROM_LATER] = deltaCPI;
            }
         }
      }



   /*:                 
   ==============================================================
   ============================================================== *//**
   This method displays the command line arguments that this program was
   invoked with.

   <P><B>Implementation: </B><A HREF="InflationData.java.html#007">View source</A>

   @return
      nothing

   @param
      pArgs contains the command line arguments with which the class was
      invoked as an application.
   *//*
   -------------------------------------------------------------- */
   private <Type> void showArgs(Type[] pArgs) throws Exception
      {
      XString  str = new XString("");
      for (int i=0; i<pArgs.length; i++)
         {
         str = str.concat(UString.toString(pArgs[i]) + "   ");
         }
      cOut.println("Command Line Arguments",str);
      }



   /*:                 
   ==============================================================
   ============================================================== *//**
   This method processes the command line arguments and based on what it
   finds, sets the instance variables.

   <P><B>Implementation: </B><A HREF="InflationData.java.html#008">View source</A>

   @return
      a success code of true if all goes well, false otherwise.

   @param
      pArgs contains the command line arguments with which the class was
      invoked as an application.
   *//*
   -------------------------------------------------------------- */
   private <Type> boolean initialize(Type[] pArgs) throws Exception
      {
      boolean  successCode = true;
      /*
      ===============================================
      Greetings !
      ----------------------------------------------- */
      System.out.println();
      System.out.println();
      System.out.println();
      System.out.println();
      cOut.banner(CLASS_NAME);
      System.out.println();
      /*
      ===============================================
      tell the user what command line arges this program started with
      ----------------------------------------------- */
      showArgs(pArgs);
      /*
      ===============================================
      this cannot be done statically because it throws an exception.
      ----------------------------------------------- */
      iGlobalProperties = new GlobalProperties();
      /*
      ===============================================
      get ready to process the command line arguments
      ----------------------------------------------- */
      CommandLine  cmdLn = new CommandLine(CLASS_NAME,pArgs);
      /*
      ===============================================
      these are the valid command line controls
      ----------------------------------------------- */
      cmdLn.addControlsFromString(":hqrsl::x:");
      cmdLn.addControl("-xfile","required");
      cmdLn.addControl("-ifile","required");
      /*
      ===============================================
      list the valid controls
      ----------------------------------------------- */
      cmdLn.listControls();
      cOut.println();
      /*
      ===============================================
      interpret the arguments on the command line
      ----------------------------------------------- */
      HashSet<XString>          controlsWithoutArgs = new HashSet<XString>();
      HashMap<XString,XString>  controlsWithArgs    = new HashMap<XString,XString>();
      ArrayList<XString>        excludedFileSpecs   = new ArrayList<XString>();
      ArrayList<XString>        includedFileSpecs   = new ArrayList<XString>();
      boolean                   result              = cmdLn.commandLineArgs
         (
         iGlobalProperties  ,
         controlsWithoutArgs,
         controlsWithArgs   ,
         excludedFileSpecs  ,
         includedFileSpecs
         );
      /*
      ===============================================
      if the command line was messed up, abort the program
      ----------------------------------------------- */
      if (includedFileSpecs.isEmpty())
         {
         cOut.println("COMMAND LINE ERROR: MANDATORY control NOT on command line:  fileSpecs");
         }
      if ((result == false) || (includedFileSpecs.isEmpty()))
         {
         usage();
         cOut.titledSeverityPrintf
            (
            Const.HALT,
            CLASS_NAME,
            "Command line error."
            );
         cOut.stopLog();
         System.exit(-1);
         }
      /*
      ===============================================
      arguments for the FileNameList with their default values.  Some of
      these arguments may be modified by commandline controls.
      ----------------------------------------------- */
      boolean  recurse             = false;
      boolean  fullyQualifiedNames = true;
      boolean  matchSubdirs        = false;
      boolean  sort                = true;
      boolean  collapse            = false;
      /*
      ===============================================
      process commandline controls without arguments
      ----------------------------------------------- */
      for (XString  ctl : controlsWithoutArgs)
         {
         cOut.println(ctl.string() + " control is present.");
         if (ctl.equals("-h"))
            {
            usage();
            cOut.stopLog();
            System.exit(-1);
            }
         recurse      |= ctl.equals("-r");
         matchSubdirs |= ctl.equals("-s");
         }
      if (controlsWithoutArgs.size() > 0) cOut.println();
      /*
      ===============================================
      process commandline controls with arguments
      ----------------------------------------------- */
      Set<XString>  set = controlsWithArgs.keySet();
      for (XString  ctl : set)
         {
         XString argStr = controlsWithArgs.get(ctl);
         cOut.println(ctl + " control is present with the argument: " + argStr);
         }
      if (controlsWithArgs.size() > 0) cOut.println();
      /*
      ===============================================
      get a list of all the files names that will be processed
      ----------------------------------------------- */
      for (XString  fileSpec : includedFileSpecs)
         {
         cOut.println("Files matching this fileSpec will be INcluded: " + fileSpec);
         }
      if (includedFileSpecs.size() > 0) cOut.println();
      for (XString  fileSpec : excludedFileSpecs)
         {
         cOut.println("Files matching this fileSpec will be EXcluded: " + fileSpec);
         }
      if (excludedFileSpecs.size() > 0) cOut.println();
      iFileNameList = new FileNameList
         (
         includedFileSpecs,excludedFileSpecs,recurse,fullyQualifiedNames,matchSubdirs,sort,collapse
         );
      /*
      ===============================================
      ----------------------------------------------- */
      return successCode;
      }



   /*:                 
   ==============================================================
   ============================================================== *//**
   This method writes a usage message to the log

   <P><B>Implementation: </B><A HREF="InflationData.java.html#009">View source</A>

   @return
      nothing
   *//*
   -------------------------------------------------------------- */
   private void usage()
      {
      cOut.println("Program Description",DESCRIPTION);
      cOut.println("Usage",USAGE);
      }



   /*
   ===================================================================
   ===================================================================
              --------------- Inner Classes ---------------
   =================================================================== */



   /*
   ===================================================================
   ===================================================================
          --------------- Public Static Methods ---------------
   =================================================================== */



   /*:                 
   ==============================================================
   ============================================================== *//**
   This method runs the InflationData class as a standalone application.
   It's the method that's called when the class is invoked from the command
   line by using the java application launcher - "java".

   Usage<PRE>
   java cosmicabyss.com.app.InflationData [-h -q -r -s] [-l [logFile]] {-x fileSpec} {-ifile fileName} {-xfile fileName} fileSpecs+
      Arguments can be placed in any order on the command line.

   -h means HELP.
      It causes this usage message to be displayed, and terminates the
      program.

   -q means QUIET.
      It stops the program from sending log messages to stdout. The program
      sends log messages to stdout by default; this option is the only way
      to stop it from doing that.

   -r means RECURSE.
      It causes the program to recurse down through the trees of
      subdirectories that are rooted at the directories specified in the
      file specifiers that are on the command line.

   -s means SUBDIRECTORIES.
      It causes the program to match subdirectory names that match the file
      specifiers on the command line.

   -l means LOGFILE.
      It takes an optional file name parameter, and causes the program to
      send log messages to the named file. If the file name is not present,
      the program generates a default file name. If the environment
      variable LOGDIRECTORY is defined, the program places the file in that
      directory. If not, the program places the file in the current
      directory. The program continues to send log messages to stdout too,
      unless the -q option is used.

   -x means EXCLUDE.
      It requires a fileSpec, and causes the program to not process any
      files that match the fileSpec. This control may be put on the command
      line as many times as needed to specify all the files that should be
      excluded from processing.

      Enclosing the fileSpec in quotes is REQUIRED for any fileSpec that
      contains embedded spaces or wildcard characters.

   -xfile means EXCLUDE FILE.
      It takes a required file name parameter, and causes the program to
      read each non-empty line in the named file and treat it as the
      filespec argument to a -x control. This control may be put on the
      command line as many times as needed to specify all the files that
      contain filespecs for files that should be excluded from processing.

   -ifile means INCLUDE FILE.
      It takes a required file name parameter, and causes the program to
      read each non-empty line in the named file and treat it as a fileSpec
      that identifies files to include in processing. This control may be
      put on the command line as many times as needed to specify all the
      files that contain filespecs for files that should be included in the
      processing.

   fileSpecs identifies the files to process.
      It is a required parameter and is one or more file specifiers that
      identify the files that the program will process.

      As many fileSpecs may be put on the command line as needed to specify
      all the files that should be included in processing.

      Enclosing the fileSpec in quotes is required for any file specifier
      that contains embedded spaces.

      Enclosing the fileSpec in quotes prevents the command line
      interpreter from trying to replace wildcard characters itself instead
      of letting InflationData do it.

      The quotes surrounding the fileSpec are not needed if it contains no
      wildcard characters or spaces.
   </PRE>

   <P><B>Implementation: </B><A HREF="InflationData.java.html#010">View source</A>

   @return
      nothing

   @param
      pArgs contains the command line arguments with which the class was
      invoked as an application.
   *//*
   -------------------------------------------------------------- */
   public static void main(String[] pArgs) throws Exception
      {
      InflationData  thisInflationData = new InflationData(pArgs);
      }



   }  // class InflationData



   /*
   ===================================================================
   ===================================================================
              ------------------- Notes -------------------
   =================================================================== */