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

                                              Copyright 2010 Allen Baker

------------------------------------------------------------------------------------------------------------------------
Class:         RenameFilesByCreatedDate
Originator:    Allen Baker (2010.10.12 10:15)
LayoutRev:     4
------------------------------------------------------------------------------------------------------------------------
$RCSfile$
$Revision$
$Date$
========================================================================================================================
*/



package cosmicabyss.com.app;

import java.io.*;
import java.util.*;


import cosmicabyss.com.lib.*;



/*
======================================================================================================================== *//**
Description<PRE>
   This program renames the specified files and uses their created dates to generate the new file names.  Each
   file is renamed to a file name in the format illustrated by this example:  2009.08.23.0002.jpg  where '2009' is the
   year, '08' is the month, and '23' is the day that the file was created.  '0002' is a file count number that is
   used to make the file name unique in scenarios where multiple of the specified files were created on the same
   date.  The first file created on a particular date is given '0000' as its file count number.  If there is a
   second file created on that same date, it is given '0001' as its file count number, and so on.  In the example,
   'jpg' is the file extension that was on the original file.  A file's original file name extension is always retained
   for the new file name.
</PRE>

Usage<PRE>
   java cosmicabyss.com.app.RenameFilesByCreatedDate [-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 RenameFilesByCreatedDate 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.RenameFilesByCreatedDate -r -x"*.bat" "..\*.*"
         </PRE>
      </DD>
   </DT>
   <DT>
      <B>View Source:</B>
      <DD>
         <A href="RenameFilesByCreatedDate.java.html">
            RenameFilesByCreatedDate.java
         </A>
      </DD>
   </DT>
   <DT>
      <B>Author:</B>
      <DD>
         <A href="mailto:sourcecode.v01@cosmicabyss.com">
            Allen Baker
         </A>
      </DD>
   </DT>
</DL>
*//*
======================================================================================================================== */
public class RenameFilesByCreatedDate
   {



   /*
   =====================================================================================================================
   %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\               Public                /%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%/  Classes, Constants, and Variables  \%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   ===================================================================================================================== */



   /*
   =====================================================================================================================
   //////////////////////////////////////////////////     Public      \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
   \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\  Inner Classes  //////////////////////////////////////////////////
   ===================================================================================================================== */



   /*
   ================================================================================================
   for sorting a list of files by created date
   ------------------------------------------------------------------------------------------------ */
   class CreatedDateComparator implements Comparator<XFile>
      {
      public int compare(XFile p1, XFile p2)
         {
         long  p1Date = p1.created();
         long  p2Date = p2.created();
         if (p1Date < p2Date) return -1;
         if (p1Date > p2Date) return  1;
         return  0;
         }
      }



   /*
   =====================================================================================================================
   /////////////////////////////////////////////////      Public       \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
   \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\  Class Constants  /////////////////////////////////////////////////
   ===================================================================================================================== */
   /*
   =====================================================================================================================
   /////////////////////////////////////////////////      Public       \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
   \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\  Class Variables  /////////////////////////////////////////////////
   ===================================================================================================================== */
   /*
   =====================================================================================================================
   ///////////////////////////////////////////////        Public        \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
   \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\  Instance Constants  ////////////////////////////////////////////////
   ===================================================================================================================== */
   /*
   =====================================================================================================================
   ///////////////////////////////////////////////        Public        \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
   \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\  Instance Variables  ////////////////////////////////////////////////
   ===================================================================================================================== */



   /*
   =====================================================================================================================
   %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\              Protected              /%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%/  Classes, Constants, and Variables  \%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   ===================================================================================================================== */



   /*
   =====================================================================================================================
   //////////////////////////////////////////////////    Protected    \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
   \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\  Inner Classes  //////////////////////////////////////////////////
   ===================================================================================================================== */
   /*
   =====================================================================================================================
   /////////////////////////////////////////////////     Protected     \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
   \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\  Class Constants  /////////////////////////////////////////////////
   ===================================================================================================================== */
   /*
   =====================================================================================================================
   /////////////////////////////////////////////////     Protected     \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
   \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\  Class Variables  /////////////////////////////////////////////////
   ===================================================================================================================== */
   /*
   =====================================================================================================================
   ///////////////////////////////////////////////      Protected       \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
   \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\  Instance Constants  ////////////////////////////////////////////////
   ===================================================================================================================== */
   /*
   =====================================================================================================================
   ///////////////////////////////////////////////      Protected       \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
   \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\  Instance Variables  ////////////////////////////////////////////////
   ===================================================================================================================== */



   /*
   =====================================================================================================================
   %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\               Private               /%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%/  Classes, Constants, and Variables  \%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   ===================================================================================================================== */



   /*
   =====================================================================================================================
   //////////////////////////////////////////////////     Private     \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
   \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\  Inner Classes  //////////////////////////////////////////////////
   ===================================================================================================================== */
   /*
   =====================================================================================================================
   /////////////////////////////////////////////////      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(RenameFilesByCreatedDate.class.getName());
   private static final XString  DESCRIPTION = new XString
      (
      // <Description>  Preprocessor generated String definition, Don't mess with it.
      "This program renames the specified files and uses their created dates to generate the new file names.  Each          \n" +
      "file is renamed to a file name in the format illustrated by this example:  2009.08.23.0002.jpg  where '2009' is the  \n" +
      "year, '08' is the month, and '23' is the day that the file was created.  '0002' is a file count number that is       \n" +
      "used to make the file name unique in scenarios where multiple of the specified files were created on the same        \n" +
      "date.  The first file created on a particular date is given '0000' as its file count number.  If there is a          \n" +
      "second file created on that same date, it is given '0001' as its file count number, and so on.  In the example,      \n" +
      "'jpg' is the file extension that was on the original file.  A file's original file name extension is always retained \n" +
      "for the new file name.                                                                                               \n" +
      ""// </Description>
      );
   private static final XString  USAGE       = new XString
      (
      // <Usage>        Preprocessor generated String definition, Don't mess with it.
      "java cosmicabyss.com.app.RenameFilesByCreatedDate [-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 program.                                         \n" +
      "                                                                                                                     \n" +
      "-q means QUIET.                                                                                                      \n" +
      "   It stops the program from sending log messages to stdout. The program sends log messages to stdout by default;    \n" +
      "   this option is the only way to stop it from doing that.                                                           \n" +
      "                                                                                                                     \n" +
      "-r means RECURSE.                                                                                                    \n" +
      "   It causes the program to recurse down through the trees of subdirectories that are rooted at the directories      \n" +
      "   specified in the 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 specifiers on the command line.             \n" +
      "                                                                                                                     \n" +
      "-l means LOGFILE.                                                                                                    \n" +
      "   It takes an optional file name parameter, and causes the program to send log messages to the named file. If the   \n" +
      "   file name is not present, the program generates a default file name. If the environment variable LOGDIRECTORY is  \n" +
      "   defined, the program places the file in that directory. If not, the program places the file in the current        \n" +
      "   directory. The program continues to send log messages to stdout too, unless the -q option is used.                \n" +
      "                                                                                                                     \n" +
      "-x means EXCLUDE.                                                                                                    \n" +
      "   It requires a fileSpec, and causes the program to not process any files that match the fileSpec. This control may \n" +
      "   be put on the command line as many times as needed to specify all the files that should be excluded from          \n" +
      "   processing.                                                                                                       \n" +
      "                                                                                                                     \n" +
      "   Enclosing the fileSpec in quotes is REQUIRED for any fileSpec that contains embedded spaces or wildcard           \n" +
      "   characters.                                                                                                       \n" +
      "                                                                                                                     \n" +
      "-xfile means EXCLUDE FILE.                                                                                           \n" +
      "   It takes a required file name parameter, and causes the program to read each non-empty line in the named file and \n" +
      "   treat it as the filespec argument to a -x control. This control may be put on the command line as many times as   \n" +
      "   needed to specify all the files that 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 read each non-empty line in the named file and \n" +
      "   treat it as a fileSpec that identifies files to include in processing. This control may be put on the command line\n" +
      "   as many times as needed to specify all the 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 identify the files that the program will       \n" +
      "   process.                                                                                                          \n" +
      "                                                                                                                     \n" +
      "   As many fileSpecs may be put on the command line as needed to specify all the files that should be included in    \n" +
      "   processing.                                                                                                       \n" +
      "                                                                                                                     \n" +
      "   Enclosing the fileSpec in quotes is required for any file specifier that contains embedded spaces.                \n" +
      "                                                                                                                     \n" +
      "   Enclosing the fileSpec in quotes prevents the command line interpreter from trying to replace wildcard characters \n" +
      "   itself instead of letting RenameFilesByCreatedDate do it.                                                         \n" +
      "                                                                                                                     \n" +
      "   The quotes surrounding the fileSpec are not needed if it contains no wildcard characters or spaces.               \n" +
      "                                                                                                                     \n" +
      ""// </Usage>
      );



   /*
   =====================================================================================================================
   /////////////////////////////////////////////////      Private      \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
   \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\  Class Variables  /////////////////////////////////////////////////
   ===================================================================================================================== */



   /*
   ================================================================================================
   cOut : formatted and time-stamped console output.
   ------------------------------------------------------------------------------------------------ */
   private static ConsoleStream  cOut = ConsoleStream.getSingleton();



   /*
   =====================================================================================================================
   ///////////////////////////////////////////////       Private        \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
   \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\  Instance Constants  ////////////////////////////////////////////////
   ===================================================================================================================== */
   /*
   =====================================================================================================================
   ///////////////////////////////////////////////       Private        \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
   \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\  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         /%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%/  Class initialization  \%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   ===================================================================================================================== */



   static
      {
      /*
      ================================================================================================
      if required, make sure all the class constants and variables are initialized the first time the JVM loads this
      class code and before anything else in this class is accessed
      ------------------------------------------------------------------------------------------------ */
      initializeClassConstantsAndVariables();
      }



   /*
   =====================================================================================================================
   %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\  Private  /%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%/  Methods  \%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   ===================================================================================================================== */



   /*
   =====================================================================================================================
   //////////////////////////////////////////////////    Private     \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
   \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\  Constructors  ///////////////////////////////////////////////////
   ===================================================================================================================== */
   /*
   =====================================================================================================================
   ////////////////////////////////////////////////      Private       \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
   \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\  Instance Methods  /////////////////////////////////////////////////
   ===================================================================================================================== */



   /*:                                          
   ===============================================================================================================
   ===============================================================================================================
   =============================================================================================================== *//**
   This method initializes all the instance constants and variables

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

   *//*
   --------------------------------------------------------------------------------------------------------------- */
   private <Type> boolean initializeInstanceConstantsAndVariables(Type[] pArgs) throws Exception
      {
      boolean  successCode = true;

      successCode = processCommandLine(pArgs);
      if (successCode)
         {
         }
      return successCode;
      }



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

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

   @return
      a success code of true if all goes well, false otherwise.
   *//*
   --------------------------------------------------------------------------------------------------------------- */
   private boolean processAllFiles() throws Exception
      {
      boolean  successCode = true;
      /*
      ================================================================================================
      tell 'em what we're gonna do
      ------------------------------------------------------------------------------------------------ */
      String  fmtStr;
      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";
      /*
      ================================================================================================
      make a version of the file list that is sorted on the files' created date
      ------------------------------------------------------------------------------------------------ */
      XFile[]  curFiles = new XFile[iFileNameList.size()];
      int      fileIdx  = 0;
      for (XString  fileName : iFileNameList)
         {
         curFiles[fileIdx++] = new XFile(fileName);
         }
      Arrays.sort(curFiles, new CreatedDateComparator());
      /*
      ================================================================================================
      figure out what the longest file name in the list is.  This information will later be used for formatting
      messages. This code is enclosed in block delimeters on the theory that as soon as the block is exited, the array
      of file names goes out of scope and is freed up for garbage collection.
      ------------------------------------------------------------------------------------------------ */
      int  curLongest;
      {
      XString[]  curNames = new XString[curFiles.length];
      for (fileIdx = 0; fileIdx < curFiles.length; fileIdx++)
         {
         curNames[fileIdx] = new XString(curFiles[fileIdx].getCanonicalPath());
         }
      curLongest = UString.longestStringLength(curNames);
      }
      /*
      ================================================================================================
      We are going to rename all the files in the list from their current name to a temporary name first. Then we'll go
      through them again and rename them from their temporary name to their final name. Make 2 arrays, one containing
      the temporary names and one containing the new names for each file.
      ------------------------------------------------------------------------------------------------ */
      XFile[]  tmpFiles = new XFile[curFiles.length];
      XFile[]  newFiles = new XFile[curFiles.length];
      /*
      ================================================================================================
      fill in the temporary and new file name arrays
      ------------------------------------------------------------------------------------------------ */
      int      dateIdx = 0;
      XString  curDate = XString.EMPTY;
      for (fileIdx = 0; fileIdx < curFiles.length; fileIdx++)
         {
         /*
         ================================================================================================
         progress indicator is needed when file set is very large.
         ------------------------------------------------------------------------------------------------ */
         System.out.print(".");
         /*
         ================================================================================================
         get the temporary file name for this file
         ------------------------------------------------------------------------------------------------ */
         tmpFiles[fileIdx] = new XFile(curFiles[fileIdx].getCanonicalPath() + ".tmp");
         /*
         ================================================================================================
         get a calendar object that represents the created time
         ------------------------------------------------------------------------------------------------ */
         long               created  = curFiles[fileIdx].created();
         GregorianCalendar  calendar = new GregorianCalendar();
         calendar.setTimeInMillis(created);
         /*
         ================================================================================================
         pick out the elements of the calendar object that will be used in the new file name.
         ------------------------------------------------------------------------------------------------ */
         int  yyyy = calendar.get(Calendar.YEAR);
         int  mm   = calendar.get(Calendar.MONTH);
         int  dd   = calendar.get(Calendar.DAY_OF_MONTH);
         /*
         ================================================================================================
         Normal people number months 1..12. The numb-nuts at Sun number them 0..11. Make an adjustment from numb-nuts
         numbering to normal numbering.
         ------------------------------------------------------------------------------------------------ */
         mm = mm + 1;
         /*
         ================================================================================================
         build up the invariant components of a new file name
         ------------------------------------------------------------------------------------------------ */
         XString  directory = curFiles[fileIdx].getCanonicalParent();
         XString  rootName  = new XString
            (
            (new XString(yyyy)).alignRight(4,'0').string() +
             "."                                           +
            (new XString(mm)).alignRight(2,'0').string()   +
             "."                                           +
            (new XString(dd)).alignRight(2,'0').string()
            );
         XString  extension = curFiles[fileIdx].getExtension();
         /*
         ================================================================================================
         for each date, there is a sequence of numbers that starts at 0 for the first file on that date and that is
         incremented for each file of that same date.  The sequence number is appended to the file name for uniqueness.
         The sequence number is reset to 0 each time the date changes.
         ------------------------------------------------------------------------------------------------ */
         if ( ! curDate.equals(rootName))
            {
            curDate = rootName;
            dateIdx = 0;
            }
         else
            {
            dateIdx++;
            }
         XString  numberStr = (new XString(dateIdx)).alignRight(4,'0');
         /*
         ================================================================================================
         now put all the pieces of the new file name together into one string
         ------------------------------------------------------------------------------------------------ */
         newFiles[fileIdx] = new XFile
            (
            new XString
               (
               directory.string() +
               "/"                +
               rootName.string()  +
               "."                +
               numberStr.string() +
               "."                +
               extension.string()
               )
            );
         }
      /*
      ================================================================================================
      tell the user what files are going to be renamed to what
      ------------------------------------------------------------------------------------------------ */
      for (fileIdx = 0; fileIdx < curFiles.length; fileIdx++)
         {
         /*
         ================================================================================================
         display a message telling the user which file is being renamed to what this time through the loop.
         ------------------------------------------------------------------------------------------------ */
         cOut.println
            (
            UString.alignLeft(curFiles[fileIdx].getCanonicalPath() + " ", curLongest + 5, '-') +
            "> "                                                                               +
            newFiles[fileIdx]
            );
         }
      /*
      ================================================================================================
      rename all the files to their temporary name
      ------------------------------------------------------------------------------------------------ */
      for (fileIdx = 0; fileIdx < curFiles.length; fileIdx++)
         {
         if (! curFiles[fileIdx].renameTo(tmpFiles[fileIdx]))
            {
            cOut.printf("      !!! Renaming to temporary name failed !!!\n");
            throw new Exception("Cannot rename:  " + curFiles[fileIdx].getCanonicalPath() + "  to:  " + tmpFiles[fileIdx].getCanonicalPath());
            }
         }
      /*
      ================================================================================================
      rename all the files to their new name
      ------------------------------------------------------------------------------------------------ */
      for (fileIdx = 0; fileIdx < tmpFiles.length; fileIdx++)
         {
         if (! tmpFiles[fileIdx].renameTo(newFiles[fileIdx]))
            {
            cOut.printf("      !!! Renaming failed !!!\n");
            throw new Exception("Cannot rename:  " + tmpFiles[fileIdx].getCanonicalPath() + "  to:  " + newFiles[fileIdx].getCanonicalPath());
            }
         }
      /*
      ================================================================================================
      tell 'em what we did
      ------------------------------------------------------------------------------------------------ */
      cOut.println("Result","Processed " + fileIdx + " of an expected " + iFileNameList.size() + " files.");
      return successCode;
      }



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

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

   @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="RenameFilesByCreatedDate.java.html#003">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 processCommandLine(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="RenameFilesByCreatedDate.java.html#004">View source</A>

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



   /*
   =====================================================================================================================
   //////////////////////////////////////////////////     Private     \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
   \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\  Class Methods  //////////////////////////////////////////////////
   ===================================================================================================================== */



   /*:                                          
   ===============================================================================================================
   ===============================================================================================================
   =============================================================================================================== *//**
   This method is called from the class static initialization block to make sure all the class constants and variables
   are initialized the first time the JVM loads this class code and before anything else in this class is accessed

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

   *//*
   --------------------------------------------------------------------------------------------------------------- */
   private static void initializeClassConstantsAndVariables()
      {
      }



   /*
   =====================================================================================================================
   %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\  Protected  /%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%/   Methods   \%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   ===================================================================================================================== */



   /*
   =====================================================================================================================
   //////////////////////////////////////////////////   Protected    \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
   \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\  Constructors  ///////////////////////////////////////////////////
   ===================================================================================================================== */
   /*
   =====================================================================================================================
   ////////////////////////////////////////////////     Protected      \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
   \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\  Instance Methods  /////////////////////////////////////////////////
   ===================================================================================================================== */
   /*
   =====================================================================================================================
   //////////////////////////////////////////////////    Protected    \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
   \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\  Class Methods  //////////////////////////////////////////////////
   ===================================================================================================================== */



   /*
   =====================================================================================================================
   %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\  Public   /%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%/  Methods  \%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   ===================================================================================================================== */



   /*
   =====================================================================================================================
   //////////////////////////////////////////////////     Public     \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
   \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\  Constructors  ///////////////////////////////////////////////////
   ===================================================================================================================== */



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

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

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

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



   /*
   =====================================================================================================================
   ////////////////////////////////////////////////       Public       \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
   \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\  Instance Methods  /////////////////////////////////////////////////
   ===================================================================================================================== */



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

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

   @return
      a reference to this object

   @param
      pConsole is a ConsoleStream through which this objects sends its output.
   *//*
   --------------------------------------------------------------------------------------------------------------- */
   public RenameFilesByCreatedDate 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="RenameFilesByCreatedDate.java.html#008">View source</A>

   @return
      a reference to this object
   *//*
   --------------------------------------------------------------------------------------------------------------- */
   public RenameFilesByCreatedDate 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;
      }



   /*
   =====================================================================================================================
   //////////////////////////////////////////////////     Public      \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
   \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\  Class Methods  //////////////////////////////////////////////////
   ===================================================================================================================== */
   /*
   =====================================================================================================================
   //////////////////////////////////////////////////////  Main  \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
   \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\        ///////////////////////////////////////////////////////
   ===================================================================================================================== */



   /*:                                          
   ===============================================================================================================
   ===============================================================================================================
   =============================================================================================================== *//**
   This method runs the RenameFilesByCreatedDate 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.RenameFilesByCreatedDate [-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 RenameFilesByCreatedDate 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="RenameFilesByCreatedDate.java.html#009">View source</A>

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



   }  // class RenameFilesByCreatedDate



   /*
   =====================================================================================================================
   %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\  Notes  /%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%/         \%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   ===================================================================================================================== */