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

                                              Copyright  2014 Allen Baker

------------------------------------------------------------------------------------------------------------------------
File:        IteratingSubs
Originator:  Allen Baker (2014.06.02 17:38)
LayoutRev:   4
------------------------------------------------------------------------------------------------------------------------
$RCSfile$
$Revision$
$Date$
======================================================================================================================== */
/*
===============================================
Package
----------------------------------------------- */
package cosmicabyss.com.app;



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


import cosmicabyss.com.lib.*;



/*
=========================================================================== *//**
Description<PRE>
   This program makes string substitutions in each file specified by the
   command line arguments.
</PRE>

Usage<PRE>
   java cosmicabyss.com.app.IteratingSubs [-h -q -r -t -s] [-l [logFile]] {-x fileSpec} [-i] [-o oldString -n newString] {-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.

   -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.

   -i means IGNORE CASE.
      It causes the program to ignore the case of the strings being
      matched.

   -o means OLD STRING.
      It requires a string argument, and causes the program to replace all
      instances of the argument in the file with the argument of the -n
      control.

   -n means NEW STRING.
      It requires a string argument, and causes the program to replace all
      instances of the argument of the -o control in the file with the
      argument.

   -t means TRIM. It causes the program to trim the leading and trailing
      whitespace from each new and old string before substituting any new
      string for its corresponding old string. Another way of looking at it
      when the string pairs are being read from a file (see -f below) is
      that it trims all the whitespace from the beginning and ending of each
      line it reads from the file and all whitespace surrounding the
      delimiter character.

   -s means SQUEEZE. It causes the program to squeeze ALL the blank
      characters out of each new and old string before substituting any new
      string for its corresponding old string. Why would this ever be
      needed? Because it is often convenient to make a file (see -f below)
      more readable by adding blanks to the strings. One might want to be
      able to do this without the  extra blanks ending up as part of the
      substitutions.

   -f means FILE.
      It requires a string argument, and causes the program to read the
      file named by the string argument to find the old/new string pairs.
      If the -f option is on the command line, the -o and -n options become
      optional instead of required parameters.

      Each line of the file must contain 1 pair of strings, separated from
      each other by a comma.

      The file may contain blank lines (lines that are empty or that
      contain only SPACE characters). These blank lines are ignored.

         Note: A line containing any other whitespace character such as a
               tab, backspace, formfeed, etc., IS NOT A BLANK LINE. A blank
               line must be entirely empty or contain ONLY SPACES.

      The comma, known as the default string separator, may be changed to
      any other single character EXCEPT A SPACE.

         The string separator can ONLY be changed by placing a single
         non-space character on the FIRST non-blank line of the file. If
         the first non-blank line of the file is found to contain just one
         non-space character, that character is used as the string
         separator instead of the default, which is a comma.

      The file may contain comments. Comments are demarcated by a comment
      delimiter character. There is no default comment delimiter and
      consequently, by default, comments are not allowed in the file.

         The comment delimiter can ONLY be set by placing a single
         non-space character on the FIRST non-blank line of the file AFTER
         a string separator line. If the first non-blank line after a
         string separator line of the file is found to contain just one
         non-space character, that character is used as the comment
         delimiter.

            Notice: A comment delimiter may only be defined if a string
                    separator has already been defined in the file.

            When a comment delimiter is included in the file, comments may
            be placed anywhere else in the file. The span of a comment is
            from each comment delimiter [inclusive] to the end of the line
            on which the comment delimiter is found. Comments are ignored.

            Warning: If spaces are placed between the end of the second
                     string on a line and the Start of a comment, those
                     spaces will be considered part of the second string
                     [unless trim and/or squeeze are set to true].

      Metacharacters (escape sequences) may be include in the strings in
      the file. These are translated into the actual character when the
      string is read. These metacharacters are translated as follows:

         Note: In this table, {esc} represents the single backslash
               character.

         => +---------------+----------------------+
         => | Metacharacter | Is Translated Into a |
         => +---------------+----------------------+
         => |      {esc}n   |      newline         |
         => |      {esc}t   |      tab             |
         => |      {esc}b   |      backspace       |
         => |      {esc}f   |      formfeed        |
         => |      {esc}r   |      carriage return |
         => +---------------+----------------------+

      By setting the trim and/or squeeze command line parameters, space
      characters may be included in the file for formatting purposes to
      improve readability and yet not be considered part of either of the
      strings on any line.
         => the trim setting will strip away all leading and trailing
            spaces from each string in the File.
         => the squeeze setting will strip away all leading and trailing
            and interspersed spaces from each string in the file.

      Example File
         This is a file that redefines the string separator (comma) to be a
         colon instead and defines the hash as a comment delimiter. So,
         comments are allowed.

            :

            #

            replace:me,with me#comment

            old:new   # comment

            # old:new

         This file will result in the following string pairs being read
         from the file:

            => +------+-----------------+-----------------+
            => | Pair |  First String   |  Second String  |
            => +------+-----------------+-----------------+
            => |  1   | [replace]       | [me,with me]    |
            => |  2   | [old]           | [new   ]        |
            => +------+-----------------+-----------------+

   -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 IteratingSubs 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.IteratingSubs -o"out with the old" -n"in with the new" revolution.txt
         </PRE>
      </DD>
   </DT>
   <DT>
      <B>View Source:</B>
      <DD>
         <A href="IteratingSubs.java.html">
            IteratingSubs.java
         </A>
      </DD>
   </DT>
   <DT>
      <B>Author:</B>
      <DD>
         <A href="mailto:sourcecode.v01@cosmicabyss.com">
            Allen Baker
         </A>
      </DD>
   </DT>
</DL>
*//*
=========================================================================== */
public class IteratingSubs
   {



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


   @param
      pArgs contains the command line arguments with which the class was
      invoked as an application.
   *//*
   -------------------------------------------------------------- */
   public <Type> IteratingSubs(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.


   @return
      a reference to this object

   @param
      pConsole is a ConsoleStream through which this objects sends its
      output.
   *//*
   -------------------------------------------------------------- */
   public IteratingSubs 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.


   @return
      a reference to this object
   *//*
   -------------------------------------------------------------- */
   public IteratingSubs 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
      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(IteratingSubs.class.getName());
   private static final XString  DESCRIPTION = new XString
      (
      // <Description>  Preprocessor generated String definition, Don't mess with it.
      "This program makes string substitutions in each file specified by the                                                \n" +
      "command line arguments.                                                                                              \n" +
      ""// </Description>
      );
   private static final XString  USAGE       = new XString
      (
      // <Usage>        Preprocessor generated String definition, Don't mess with it.
      "java cosmicabyss.com.app.IteratingSubs [-h -q -r -t -s] [-l [logFile]] {-x fileSpec} [-i] [-o oldString -n newString] {-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" +
      "-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" +
      "-i means IGNORE CASE.                                                                                                \n" +
      "   It causes the program to ignore the case of the strings being                                                     \n" +
      "   matched.                                                                                                          \n" +
      "                                                                                                                     \n" +
      "-o means OLD STRING.                                                                                                 \n" +
      "   It requires a string argument, and causes the program to replace all                                              \n" +
      "   instances of the argument in the file with the argument of the -n                                                 \n" +
      "   control.                                                                                                          \n" +
      "                                                                                                                     \n" +
      "-n means NEW STRING.                                                                                                 \n" +
      "   It requires a string argument, and causes the program to replace all                                              \n" +
      "   instances of the argument of the -o control in the file with the                                                  \n" +
      "   argument.                                                                                                         \n" +
      "                                                                                                                     \n" +
      "-t means TRIM. It causes the program to trim the leading and trailing                                                \n" +
      "   whitespace from each new and old string before substituting any new                                               \n" +
      "   string for its corresponding old string. Another way of looking at it                                             \n" +
      "   when the string pairs are being read from a file (see -f below) is                                                \n" +
      "   that it trims all the whitespace from the beginning and ending of each                                            \n" +
      "   line it reads from the file and all whitespace surrounding the                                                    \n" +
      "   delimiter character.                                                                                              \n" +
      "                                                                                                                     \n" +
      "-s means SQUEEZE. It causes the program to squeeze ALL the blank                                                     \n" +
      "   characters out of each new and old string before substituting any new                                             \n" +
      "   string for its corresponding old string. Why would this ever be                                                   \n" +
      "   needed? Because it is often convenient to make a file (see -f below)                                              \n" +
      "   more readable by adding blanks to the strings. One might want to be                                               \n" +
      "   able to do this without the  extra blanks ending up as part of the                                                \n" +
      "   substitutions.                                                                                                    \n" +
      "                                                                                                                     \n" +
      "-f means FILE.                                                                                                       \n" +
      "   It requires a string argument, and causes the program to read the                                                 \n" +
      "   file named by the string argument to find the old/new string pairs.                                               \n" +
      "   If the -f option is on the command line, the -o and -n options become                                             \n" +
      "   optional instead of required parameters.                                                                          \n" +
      "                                                                                                                     \n" +
      "   Each line of the file must contain 1 pair of strings, separated from                                              \n" +
      "   each other by a comma.                                                                                            \n" +
      "                                                                                                                     \n" +
      "   The file may contain blank lines (lines that are empty or that                                                    \n" +
      "   contain only SPACE characters). These blank lines are ignored.                                                    \n" +
      "                                                                                                                     \n" +
      "      Note: A line containing any other whitespace character such as a                                               \n" +
      "            tab, backspace, formfeed, etc., IS NOT A BLANK LINE. A blank                                             \n" +
      "            line must be entirely empty or contain ONLY SPACES.                                                      \n" +
      "                                                                                                                     \n" +
      "   The comma, known as the default string separator, may be changed to                                               \n" +
      "   any other single character EXCEPT A SPACE.                                                                        \n" +
      "                                                                                                                     \n" +
      "      The string separator can ONLY be changed by placing a single                                                   \n" +
      "      non-space character on the FIRST non-blank line of the file. If                                                \n" +
      "      the first non-blank line of the file is found to contain just one                                              \n" +
      "      non-space character, that character is used as the string                                                      \n" +
      "      separator instead of the default, which is a comma.                                                            \n" +
      "                                                                                                                     \n" +
      "   The file may contain comments. Comments are demarcated by a comment                                               \n" +
      "   delimiter character. There is no default comment delimiter and                                                    \n" +
      "   consequently, by default, comments are not allowed in the file.                                                   \n" +
      "                                                                                                                     \n" +
      "      The comment delimiter can ONLY be set by placing a single                                                      \n" +
      "      non-space character on the FIRST non-blank line of the file AFTER                                              \n" +
      "      a string separator line. If the first non-blank line after a                                                   \n" +
      "      string separator line of the file is found to contain just one                                                 \n" +
      "      non-space character, that character is used as the comment                                                     \n" +
      "      delimiter.                                                                                                     \n" +
      "                                                                                                                     \n" +
      "         Notice: A comment delimiter may only be defined if a string                                                 \n" +
      "                 separator has already been defined in the file.                                                     \n" +
      "                                                                                                                     \n" +
      "         When a comment delimiter is included in the file, comments may                                              \n" +
      "         be placed anywhere else in the file. The span of a comment is                                               \n" +
      "         from each comment delimiter [inclusive] to the end of the line                                              \n" +
      "         on which the comment delimiter is found. Comments are ignored.                                              \n" +
      "                                                                                                                     \n" +
      "         Warning: If spaces are placed between the end of the second                                                 \n" +
      "                  string on a line and the Start of a comment, those                                                 \n" +
      "                  spaces will be considered part of the second string                                                \n" +
      "                  [unless trim and/or squeeze are set to true].                                                      \n" +
      "                                                                                                                     \n" +
      "   Metacharacters (escape sequences) may be include in the strings in                                                \n" +
      "   the file. These are translated into the actual character when the                                                 \n" +
      "   string is read. These metacharacters are translated as follows:                                                   \n" +
      "                                                                                                                     \n" +
      "      Note: In this table, {esc} represents the single backslash                                                     \n" +
      "            character.                                                                                               \n" +
      "                                                                                                                     \n" +
      "      => +---------------+----------------------+                                                                    \n" +
      "      => | Metacharacter | Is Translated Into a |                                                                    \n" +
      "      => +---------------+----------------------+                                                                    \n" +
      "      => |      {esc}n   |      newline         |                                                                    \n" +
      "      => |      {esc}t   |      tab             |                                                                    \n" +
      "      => |      {esc}b   |      backspace       |                                                                    \n" +
      "      => |      {esc}f   |      formfeed        |                                                                    \n" +
      "      => |      {esc}r   |      carriage return |                                                                    \n" +
      "      => +---------------+----------------------+                                                                    \n" +
      "                                                                                                                     \n" +
      "   By setting the trim and/or squeeze command line parameters, space                                                 \n" +
      "   characters may be included in the file for formatting purposes to                                                 \n" +
      "   improve readability and yet not be considered part of either of the                                               \n" +
      "   strings on any line.                                                                                              \n" +
      "      => the trim setting will strip away all leading and trailing                                                   \n" +
      "         spaces from each string in the File.                                                                        \n" +
      "      => the squeeze setting will strip away all leading and trailing                                                \n" +
      "         and interspersed spaces from each string in the file.                                                       \n" +
      "                                                                                                                     \n" +
      "   Example File                                                                                                      \n" +
      "      This is a file that redefines the string separator (comma) to be a                                             \n" +
      "      colon instead and defines the hash as a comment delimiter. So,                                                 \n" +
      "      comments are allowed.                                                                                          \n" +
      "                                                                                                                     \n" +
      "         :                                                                                                           \n" +
      "                                                                                                                     \n" +
      "         #                                                                                                           \n" +
      "                                                                                                                     \n" +
      "         replace:me,with me#comment                                                                                  \n" +
      "                                                                                                                     \n" +
      "         old:new   # comment                                                                                         \n" +
      "                                                                                                                     \n" +
      "         # old:new                                                                                                   \n" +
      "                                                                                                                     \n" +
      "      This file will result in the following string pairs being read                                                 \n" +
      "      from the file:                                                                                                 \n" +
      "                                                                                                                     \n" +
      "         => +------+-----------------+-----------------+                                                             \n" +
      "         => | Pair |  First String   |  Second String  |                                                             \n" +
      "         => +------+-----------------+-----------------+                                                             \n" +
      "         => |  1   | [replace]       | [me,with me]    |                                                             \n" +
      "         => |  2   | [old]           | [new   ]        |                                                             \n" +
      "         => +------+-----------------+-----------------+                                                             \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 IteratingSubs 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;
   private XString           iNew              = null;
   private XString           iOld              = null;
   private boolean           iCaseSensitive    = true;
   private boolean           iTrim             = false;
   private boolean           iSqueeze          = false;
   private XString[][]       iStrPairs         = null;



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


   @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.


   @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);
      /*
      ===============================================
      this method makes no sense on directory files, this must be a normal
      file
      ----------------------------------------------- */
      if (inFile.isDirectory()) return successCode;
      /*
      ===============================================
      do the string replacement.
      ----------------------------------------------- */
      if (iStrPairs != null)                inFile.iteratingReplace(iStrPairs,iCaseSensitive);
      if ((iOld != null) && (iNew != null)) inFile.iteratingReplace(iOld,iNew,iCaseSensitive);
//      /*
//      ===============================================
//      ----------------------------------------------- */
//      TextReaderIterator  inIter = inFile.iterator();
//      /*
//      ===============================================
//      ----------------------------------------------- */
//      while (inIter.hasNext())
//         {
//         XString  line = inIter.next();
//         }
      /*
      ===============================================
      tell 'em how it went
      ----------------------------------------------- */
      return successCode;
      }



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


   @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.


   @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(":hqrtsl::x:io:n:f:");
      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 (ctl.equals("-i"))
            {
            iCaseSensitive = false;
            }
         if (ctl.equals("-t"))
            {
            iTrim = true;
            }
         if (ctl.equals("-s"))
            {
            iSqueeze = true;
            }
         }
      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 (ctl.equals("-n"))
            {
            iNew = argStr;
            }
         if (ctl.equals("-o"))
            {
            iOld = argStr;
            }
         if (ctl.equals("-f"))
            {
            iStrPairs = UStringPairs.readStrings(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


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



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



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



   /*                  
   ==============================================================
   ============================================================== *//**
   This method runs the IteratingSubs 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.IteratingSubs [-h -q -r -t -s] [-l [logFile]] {-x fileSpec} [-i] [-o oldString -n newString] {-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.

   -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.

   -i means IGNORE CASE.
      It causes the program to ignore the case of the strings being
      matched.

   -o means OLD STRING.
      It requires a string argument, and causes the program to replace all
      instances of the argument in the file with the argument of the -n
      control.

   -n means NEW STRING.
      It requires a string argument, and causes the program to replace all
      instances of the argument of the -o control in the file with the
      argument.

   -t means TRIM. It causes the program to trim the leading and trailing
      whitespace from each new and old string before substituting any new
      string for its corresponding old string. Another way of looking at it
      when the string pairs are being read from a file (see -f below) is
      that it trims all the whitespace from the beginning and ending of each
      line it reads from the file and all whitespace surrounding the
      delimiter character.

   -s means SQUEEZE. It causes the program to squeeze ALL the blank
      characters out of each new and old string before substituting any new
      string for its corresponding old string. Why would this ever be
      needed? Because it is often convenient to make a file (see -f below)
      more readable by adding blanks to the strings. One might want to be
      able to do this without the  extra blanks ending up as part of the
      substitutions.

   -f means FILE.
      It requires a string argument, and causes the program to read the
      file named by the string argument to find the old/new string pairs.
      If the -f option is on the command line, the -o and -n options become
      optional instead of required parameters.

      Each line of the file must contain 1 pair of strings, separated from
      each other by a comma.

      The file may contain blank lines (lines that are empty or that
      contain only SPACE characters). These blank lines are ignored.

         Note: A line containing any other whitespace character such as a
               tab, backspace, formfeed, etc., IS NOT A BLANK LINE. A blank
               line must be entirely empty or contain ONLY SPACES.

      The comma, known as the default string separator, may be changed to
      any other single character EXCEPT A SPACE.

         The string separator can ONLY be changed by placing a single
         non-space character on the FIRST non-blank line of the file. If
         the first non-blank line of the file is found to contain just one
         non-space character, that character is used as the string
         separator instead of the default, which is a comma.

      The file may contain comments. Comments are demarcated by a comment
      delimiter character. There is no default comment delimiter and
      consequently, by default, comments are not allowed in the file.

         The comment delimiter can ONLY be set by placing a single
         non-space character on the FIRST non-blank line of the file AFTER
         a string separator line. If the first non-blank line after a
         string separator line of the file is found to contain just one
         non-space character, that character is used as the comment
         delimiter.

            Notice: A comment delimiter may only be defined if a string
                    separator has already been defined in the file.

            When a comment delimiter is included in the file, comments may
            be placed anywhere else in the file. The span of a comment is
            from each comment delimiter [inclusive] to the end of the line
            on which the comment delimiter is found. Comments are ignored.

            Warning: If spaces are placed between the end of the second
                     string on a line and the Start of a comment, those
                     spaces will be considered part of the second string
                     [unless trim and/or squeeze are set to true].

      Metacharacters (escape sequences) may be include in the strings in
      the file. These are translated into the actual character when the
      string is read. These metacharacters are translated as follows:

         Note: In this table, {esc} represents the single backslash
               character.

         => +---------------+----------------------+
         => | Metacharacter | Is Translated Into a |
         => +---------------+----------------------+
         => |      {esc}n   |      newline         |
         => |      {esc}t   |      tab             |
         => |      {esc}b   |      backspace       |
         => |      {esc}f   |      formfeed        |
         => |      {esc}r   |      carriage return |
         => +---------------+----------------------+

      By setting the trim and/or squeeze command line parameters, space
      characters may be included in the file for formatting purposes to
      improve readability and yet not be considered part of either of the
      strings on any line.
         => the trim setting will strip away all leading and trailing
            spaces from each string in the File.
         => the squeeze setting will strip away all leading and trailing
            and interspersed spaces from each string in the file.

      Example File
         This is a file that redefines the string separator (comma) to be a
         colon instead and defines the hash as a comment delimiter. So,
         comments are allowed.

            :

            #

            replace:me,with me#comment

            old:new   # comment

            # old:new

         This file will result in the following string pairs being read
         from the file:

            => +------+-----------------+-----------------+
            => | Pair |  First String   |  Second String  |
            => +------+-----------------+-----------------+
            => |  1   | [replace]       | [me,with me]    |
            => |  2   | [old]           | [new   ]        |
            => +------+-----------------+-----------------+

   -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 IteratingSubs do it.

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


   @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
      {
      IteratingSubs  thisIteratingSubs = new IteratingSubs(pArgs);
      }



   }  // class IteratingSubs



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