/*::.
==================================================================================================================================
=================================================¦ Copyright © 2001 Allen Baker ¦=================================================
                                                 +------------------------------+
File:       FileNameList.java
Originator: Allen Baker (2001.09.02)
LayoutRev:  5
================================================================================================================================== */



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



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



/*::
======================================================================================================================== *//**
Instances of this class are ArrayLists that are instantiated from a XString that is a file path specifier. The path
specifier can contain wildcard file name expansion characters in its file name portion. The constructor for this class
searches the directory specified in the path specifier. It fills itself with the names of all the files in that
directory that match a file name portion of the path specifier. For example, if you instantiate a FileNameList like
this:
   <BLOCKQUOTE>
      <PRE id="unindent">
         ArrayList myList = new FileNameList("d:\test\bph*.i??", ... );
      </PRE>
   </BLOCKQUOTE>

MyList will contain all the file names in the directory "d:\test" that match the pattern "bph*.i??".

<P>
   <DL>
      <DT>
         <B>
            Example usage:
         </B>
         <DD>
            <BLOCKQUOTE>
               <PRE id="unindent">
                  ==============================================
                  create an object to recursively find all the java files starting at
                  the parent of the current directory.
                  ----------------------------------------------
                  ArrayList<String>  excludedFileSpecs = new ArrayList<String>();

                  ArrayList<String>  includedFileSpecs = new ArrayList<String>();
                  includedFileSpecs.add("../*.java");

                  FileNameList obj = new FileNameList(includedFileSpecs,excludedFileSpecs,true,true,true,true,false);

                  ==============================================
                  copy to sysout the names of all the files that match the file
                  specifiers.
                  ----------------------------------------------
                  for (XString str : obj)
                     {
                     cOut.println(str);
                     }
               </PRE>
            </BLOCKQUOTE>
         </DD>
      </DT>
      <DT>
         <B>
            View Source:
         </B>
         <DD>
            <A href="FileNameList.java.html">
               FileNameList.java
            </A>
         </DD>
      </DT>
      <DT>
         <B>
            Author:
         </B>
         <DD>
            <A href="mailto:sourcecode.v01@cosmicabyss.com">
               Allen Baker
            </A>
         </DD>
      </DT>
   </DL>
*//*
======================================================================================================================== */
public class FileNameList extends ArrayList<XString>
   {



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method builds a sorted ArrayList of all the files names that are specified by an ArrayList of
   file specifiers.

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

   @return
      The sorted ArrayList of file names

   @param
      pListOfFileSpecifiers is a list of file specifiers
   @param
      pRecurse specifies whether or not to recurse into subdirectories to build the file name list
   @param
      pFullyQualifiedNames specifies whether or not to generate file names in fully qualified form
   @param
      pMatchSubdirs specifies whether or not to match subdirectory names in addition to normal file
      names.
   @param
      pSort specifies whether or not the resulting FileNameList should be sorted into ascending lexical
      order.
   @param
      pCollapse specifies whether or not the resulting FileNameList should contain any pathnames that
      are subordinate to any subdirectory names that are found earlier in the FileNameList.
   @param
      pOnlyDirectoryFiles specifies whether or not the resulting FileNameList should include only the
      names of directories.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public <Type1,Type2>  FileNameList
      (
      ArrayList<Type1>  pIncludedFileSpecifiers,
      ArrayList<Type2>  pExcludedFileSpecifiers,
      boolean           pRecurse,
      boolean           pFullyQualifiedNames,
      boolean           pMatchSubdirs,
      boolean           pSort,
      boolean           pCollapse,
      boolean           pOnlyDirectoryFiles
      )
      throws Exception
      {
      /*.
      ==========================================================================================
      Initialize the instance
      ------------------------------------------------------------------------------------------ */
      iRecurse             = pRecurse            ;
      iFullyQualifiedNames = pFullyQualifiedNames;
      iMatchSubdirs        = pMatchSubdirs       ;
      iSort                = pSort               ;
      iCollapse            = pCollapse           ;
      iOnlyDirectoryFiles  = pOnlyDirectoryFiles ;
      /*.
      ==========================================================================================
      Collapse implies sort because the collapse method relies upon sorting having already been
      done to the list.
      ------------------------------------------------------------------------------------------ */
      iSort |= iCollapse;
      /*.
      ==========================================================================================
      Fill this list with all the files that match fileSpecs to be excluded.
      ------------------------------------------------------------------------------------------ */
      buildFromListOfFileSpecifiers(pExcludedFileSpecifiers);
      /*.
      ==========================================================================================
      Save that list somewhere else and clear out this list
      ------------------------------------------------------------------------------------------ */
      ArrayList<XString>  excluded = new ArrayList<XString>(this);
      this.clear();
      /*.
      ==========================================================================================
      Fill this list with all the files that match fileSpecs to be included.
      ------------------------------------------------------------------------------------------ */
      buildFromListOfFileSpecifiers(pIncludedFileSpecifiers);
      /*.
      ==========================================================================================
      Throw out all the files in this list that are also in the list of files to exclude
      ------------------------------------------------------------------------------------------ */
      boolean  any = false;
      for (XString  fileName : excluded)
         {
         if (this.remove(fileName))
            {
            any = true;
            cOut.println("Exclude: " + fileName);
            }
         }
      if (any) cOut.println();
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method builds a sorted ArrayList of all the files names that are specified by an ArrayList of
   file specifiers.

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

   @return
      The sorted ArrayList of file names

   @param
      pListOfFileSpecifiers is a list of file specifiers
   @param
      pRecurse specifies whether or not to recurse into subdirectories to build the file name list
   @param
      pFullyQualifiedNames specifies whether or not to generate file names in fully qualified form
   @param
      pMatchSubdirs specifies whether or not to match subdirectory names in addition to normal file
      names.
   @param
      pSort specifies whether or not the resulting FileNameList should be sorted into ascending lexical
      order.
   @param
      pCollapse specifies whether or not the resulting FileNameList should contain any pathnames that
      are subordinate to any subdirectory names that are found earlier in the FileNameList.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public <Type1,Type2>  FileNameList
      (
      ArrayList<Type1>  pIncludedFileSpecifiers,
      ArrayList<Type2>  pExcludedFileSpecifiers,
      boolean           pRecurse,
      boolean           pFullyQualifiedNames,
      boolean           pMatchSubdirs,
      boolean           pSort,
      boolean           pCollapse
      )
      throws Exception
      {
      thisb>
         (
         pIncludedFileSpecifiers,
         pExcludedFileSpecifiers,
         pRecurse,
         pFullyQualifiedNames,
         pMatchSubdirs,
         pSort,
         pCollapse,
         false
         );
      }



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

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

   @return
      A reference to this object

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

   *//*
   ---------------------------------------------------------------------------------------------------- */
   public FileNameList 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
      <BLOCKQUOTE>
         <PRE id="unindent">
            CLASS_NAME    : the name of this class
            DFLT_LINE_LEN : the default line length for word wrapping
         </PRE>
      </BLOCKQUOTE>
   ------------------------------------------------------------------------------------------ */
   private static final XString  CLASS_NAME    = new XString(FileNameList.class.getName());
   private static final int      DFLT_LINE_LEN = ConsoleMessage.defaultLineLength();
   /*.
   ==========================================================================================
   Class variables
      <BLOCKQUOTE>
         <PRE id="unindent">
            cOut     : console output.
            cVerbose : for turning on verbose testing output.
         </PRE>
      </BLOCKQUOTE>
   ------------------------------------------------------------------------------------------ */
   private static ConsoleStream  cOut     = ConsoleStream.getSingleton();
   private static boolean        cVerbose = false;
   /*.
   ==========================================================================================
   Instance variables
   ------------------------------------------------------------------------------------------ */
   private boolean  iRecurse             = false;
   private boolean  iFullyQualifiedNames = true;
   private boolean  iMatchSubdirs        = false;
   private boolean  iSort                = true;
   private boolean  iCollapse            = false;
   private boolean  iOnlyDirectoryFiles  = false;



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method builds a sorted ArrayList of all the files names that are specified by an ArrayList of
   file specifiers.

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

   @return
      The sorted ArrayList of file names

   @param
      pListOfFileSpecifiers is a list of file specifiers
   @param
      iRecurse specifies whether or not to recurse into subdirectories to build the file name list
   @param
      iFullyQualifiedNames specifies whether or not to generate file names in fully qualified form
   @param
      iMatchSubdirs specifies whether or not to match subdirectory names in addition to normal file
      names.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   private <Type> void buildFromListOfFileSpecifiers(ArrayList<Type>  pListOfFileSpecifiers) throws Exception
      {
      /*.
      ==========================================================================================
      ------------------------------------------------------------------------------------------ */
      ArrayList<XString>  listOfFileSpecifiers = XString.toXStringList(pListOfFileSpecifiers);
      /*.
      ==========================================================================================
      File names are case sensitive on some OSs and not others. Since we are going to sort the
      list of file names alphabetically, we need to use NCStrings instead of XStrings if the
      file names in this OS are not case sensitive and XStrings instead of NCStrings if they are
      case sensitive.
      ------------------------------------------------------------------------------------------ */
      boolean  isCaseSensitive = Util.fileNamesAreCaseSensitive();
      /*.
      ==========================================================================================
      A HashSet will be used to eliminate any duplicate file names.
      ------------------------------------------------------------------------------------------ */
      HashSet<XString>  setOfFileNames = new HashSet<XString>();
      /*.
      ==========================================================================================
      For each file specifier in the ArrayList ...
      ------------------------------------------------------------------------------------------ */
      for (XString fileSpecifier : listOfFileSpecifiers)
         {
         fileSpecifier = fileSpecifier.replace('\\',File.separatorChar);
         fileSpecifier = fileSpecifier.replace('/',File.separatorChar);
         /*.
         ==========================================================================================
         Get a list of all the files names that match this file specifier.
         ------------------------------------------------------------------------------------------ */
         ArrayList<XString>  listOfFileNames = matchAFileSpec(fileSpecifier);
         /*.
         ==========================================================================================
         Add all the names in this file name list to the HashSet. (This eliminates file names that
         are duplicated because they match multiple file specifiers).
         ------------------------------------------------------------------------------------------ */
         for (XString fileName : listOfFileNames)
            {
            XFile  file = new XFile(fileName);
            /*.
            ==========================================================================================
            If we're only interested in directories and this file isn't one, then skip it.
            ------------------------------------------------------------------------------------------ */
            if ((iOnlyDirectoryFiles) && (file.isFile())) continue;
            /*.
            ==========================================================================================
            Otherwise, add it to the list
            ------------------------------------------------------------------------------------------ */
            setOfFileNames.add
               (
               isCaseSensitive?
                  new XString(file.getCanonicalPath()) :
                  new NCString(file.getCanonicalPath())
               );
            }
         }
      /*.
      ==========================================================================================
      The HashSet has served its purpose -- it eliminated any duplicate file names. Now convert
      the HashSet to an ArrayList.
      ------------------------------------------------------------------------------------------ */
      ArrayList<XString>  temp = new ArrayList<XString>(setOfFileNames);
      /*.
      ==========================================================================================
      If sorting and/or collapsing were requested then do as asked.
      ------------------------------------------------------------------------------------------ */
      if (iSort)
         {
         temp = Util.sortedArrayList(temp);
         }
      if (iCollapse)
         {
         temp = collapsedFileNameList(temp);
         }
      /*.
      ==========================================================================================
      Done. Store intenal to this instance
      ------------------------------------------------------------------------------------------ */
      this.addAll(temp);
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   Get the directory name and file name specifier from the path name specifier

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

   @return
      A 2 element array of XStrings. Element 0 is the directory name and element 1 is the file name
      specifier.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   private XString[] interpretPathNameSpecifier(XString pPathNameSpecifier)
      {
      XString[]  result          = new XString[2];
      XString    directoryName;
      XString    fileNamePattern;
      /*.
      ==========================================================================================
      Fix up the path name specifier
      ------------------------------------------------------------------------------------------ */
      pPathNameSpecifier = fixedPathNameSpecifier(pPathNameSpecifier);
      /*.
      ==========================================================================================
      If the directory name turns out to be the empty XString, the user means for us to use the
      current directory. If the file name turns out to be the empty XString, the user means for
      us to find all the files in the diretory.

      BTW, it's assumptions like these that drives using this method's algorithm instead of
      using a File object to parse the XString into its parts.
      ------------------------------------------------------------------------------------------ */
      char[] c = pPathNameSpecifier.toCharArray();
      if (c.length == 0)
         {
         fileNamePattern = new XString("*");
         directoryName   = new XString(".");
         }
      /*.
      ==========================================================================================
      Scan backwards from the end of the path specifier until we run into a File.separatorChar
      such as '/' or '\'. Everything from there to the end of the path specifier is the file
      name portion. Everything from the beginning of the path specifier to there is the
      directory name.
      ------------------------------------------------------------------------------------------ */
      else
         {
         int i = c.length - 1;
         while (c[i] != File.separatorChar)
            {
            i--;
            if (i == (-1)) break;
            }
         /*.
         ==========================================================================================
         If the File.separatorChar is at the end of the path specifier, then the file name is empty
         and the whole path specifier is a directory name.
         ------------------------------------------------------------------------------------------ */
         if (i == (c.length - 1))
            {
            fileNamePattern = new XString("*");
            directoryName   = pPathNameSpecifier;
            }
         /*.
         ==========================================================================================
         If there is no File.separatorChar, then the directory name is empty and the whole path
         specifier is a file name.
         ------------------------------------------------------------------------------------------ */
         else if (i == (-1))
            {
            fileNamePattern = pPathNameSpecifier;
            directoryName   = new XString(".");
            }
         /*.
         ==========================================================================================
         Otherwise a File.separatorChar was found. Everything past there to the end of the path
         specifier is the file name portion. Everything from the beginning of the path specifier to
         there is the directory name.
         ------------------------------------------------------------------------------------------ */
         else
            {
            fileNamePattern = new XString(new String(c,i+1,c.length-i-1));
            directoryName   = new XString(new String(c,0,c.length-fileNamePattern.length()));
            }
         }
      /*.
      ==========================================================================================
      Windows interprets "*.*" at the end of a file name specifier the same as it interprets
      "*". To replicate that behavior, convert "*.*" to "*" if we're running on Windows.
      ------------------------------------------------------------------------------------------ */
      if (fileNamePattern.endsWith("*.*") && Util.isWindowsOS())
         {
         fileNamePattern = fileNamePattern.substring(0, fileNamePattern.length() - 2);
         }
      /*.
      ==========================================================================================
      If a specifier like "\test" was on the command line then this method to this point would
      have decided that the directory is "\" and the file name is "test". If there is a
      directory named "\test" though, this block of code will figure that out and change the
      directory to "\test" and the file name to "*".
      ------------------------------------------------------------------------------------------ */
      XString  str = directoryName.concat(fileNamePattern);
      if ((str.indexOf('*')==(-1)) && (str.indexOf('?')==(-1)))
         {
         try
            {
            File  file = new File(str.string());
            if (file.exists() && file.isDirectory())
               {
               fileNamePattern = new XString("*");
               directoryName   = new XString(file.getCanonicalPath());
               }
            }
         catch (Exception e)
            {
            }
         }
      /*.
      ==========================================================================================
      ------------------------------------------------------------------------------------------ */
      result[0] = directoryName;
      result[1] = fileNamePattern;
      return result;
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method fixes a problem that commonly occurs when the pathNameSpecifier originates on the
   command line. If the user enters a quote enclosed XString at the command line and the last char
   prior to the closing quote is a '\', the commandline interpreter removes the \ and passes the close
   quote as part of the XString. For example, this commandline:
      <BLOCKQUOTE>
         <PRE id="unindent">
            java cosmicabyss.com.lib.FileNameList "\test\"
         </PRE>
      </BLOCKQUOTE>

   Causes this XString to be passed into FileNameList:
      <BLOCKQUOTE>
         <PRE id="unindent">
            \test"
         </PRE>
      </BLOCKQUOTE>

   Instead of this XString which is what we wanted:
      <BLOCKQUOTE>
         <PRE id="unindent">
            \test\
         </PRE>
      </BLOCKQUOTE>

   This method will replace the trailing quote if there is one with a File.separatorChar.<P>

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

   @return
      A XString containing the fixed path name specifier.

   @param
      pPathNameSpecifier is the path name specifier that may need fixing.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   private XString fixedPathNameSpecifier(XString pPathNameSpecifier)
      {
      XString  str = new XString(pPathNameSpecifier);
      /*.
      ==========================================================================================
      Comming from the commandline, the file name specifiers sometimes have trailing whitespace.
      This gets rid of any leading or trailing whitespece.
      ------------------------------------------------------------------------------------------ */
      str = str.trim();
      /*.
      ==========================================================================================
      If the XString is empty there is no problem to correct so just return it
      ------------------------------------------------------------------------------------------ */
      if (str.length() == 0)
         {
         return str;
         }
      /*.
      ==========================================================================================
      If the user enters a quote enclosed XString at the command line and the last char prior to
      the closing quote is a '\', the commandline interpreter removes the \ and passes the close
      quote as part of the XString. This fixes that problem by removing the quote and putting
      the '\' back like the user intended.
      ------------------------------------------------------------------------------------------ */
      while (str.endsWith("\""))
         {
         str = str.trimRight("\"");
         str = new XString(str.toString() + File.separatorChar);
         }
      /*.
      ==========================================================================================
      ------------------------------------------------------------------------------------------ */
      return str;
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method returns a list of all the files names that match a single filespec (pathNameSpecifier).

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

   @return
      An arraylist of file names in XString form

   @param
      pPathNameSpecifier is the filespec to match and it includes the directory path as well.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   private <Type> ArrayList<XString> matchAFileSpec(Type pPathNameSpecifier) throws IOException
      {
      XString[]  result          = interpretPathNameSpecifier(XString.toXString(pPathNameSpecifier));
      XString    directoryName   = result[0];
      XString    fileNamePattern = result[1];
      /*.
      ==========================================================================================
      Describe what's going on.
      ------------------------------------------------------------------------------------------ */
      if (cVerbose)
         {
         cOut.pushWordWrap(false);
         cOut.print  ("pPathNameSpecifier   " +  UString.toString(pPathNameSpecifier) + "\n");
         cOut.print  ("directoryName        " +  directoryName    + "\n");
         cOut.print  ("fileNamePattern      " +  fileNamePattern  + "\n");
         cOut.print  ("iRecurse             " +  iRecurse         + "\n");
         cOut.print  ("iMatchSubdirs        " +  iMatchSubdirs    + "\n");
         cOut.println("iFullyQualifiedNames " +  iFullyQualifiedNames);
         cOut.popWordWrap();
         }
      /*.
      ==========================================================================================
      This is where the resulting list of matching file names goes
      ------------------------------------------------------------------------------------------ */
      ArrayList<XString>  listOfFileNames = new ArrayList<XString>();
      /*.
      ==========================================================================================
      This is the directory to match file names in.
      ------------------------------------------------------------------------------------------ */
      File  directory = new File(directoryName.string());
      /*.
      ==========================================================================================
      If recursion was requested, recursively descend into all of directory's subdirectories.
      The matching names found in those subdirectories will percolate up to here and be added to
      this FileNameList.
      ------------------------------------------------------------------------------------------ */
      if (iRecurse)
         {
         String[] listOfAll = directory.list();
         if (listOfAll==null)
            {
            cOut.println
               (
               Const.WARNING,
               "FileNameList.java (743)",
               "list() returns null for <"    +
               directory.getCanonicalPath()   +
               ">  I/O error or no such directory"
               );
            return listOfFileNames;
            }
         /*.
         ==========================================================================================
         For each subdirectory in this directory ...
         ------------------------------------------------------------------------------------------ */
         for (int j=0; j<listOfAll.length; j++)
            {
            File  file = new File(directory,listOfAll[j]);
            if (file.isDirectory())
               {
               ArrayList<XString>  subdirFnl = matchAFileSpec
                  (
                  new XString(file.getCanonicalPath() + File.separator + fileNamePattern)
                  );
               /*.
               ==========================================================================================
               Add all the names found in that subdirectory to the results list
               ------------------------------------------------------------------------------------------ */
               listOfFileNames.addAll(subdirFnl);
               }
            }
         }
      /*.
      ==========================================================================================
      Get the names of all the files in this directory that match the file name pattern.
      ------------------------------------------------------------------------------------------ */
      String[] listOfMatches = directory.list
         (
         new DirFilter
            (
            fileNamePattern,
            (iMatchSubdirs)? DirFilter.INC_SUBDIRS : DirFilter.NO_SUBDIRS
            )
         );
      /*.
      ==========================================================================================
      If the user wants fully qualified names, build 'em
      ------------------------------------------------------------------------------------------ */
      if (iFullyQualifiedNames)
         {
         for (int j=0; j<listOfMatches.length; j++)
            {
            File  file = new File(directory,listOfMatches[j]);
            listOfMatches[j] = file.getCanonicalPath();
            }
         }
      /*.
      ==========================================================================================
      Add all the names found in this directory to the results list
      ------------------------------------------------------------------------------------------ */
      listOfFileNames.addAll(Arrays.asList(XString.toXStringArray(listOfMatches)));
      return listOfFileNames;
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method collapses a file name list by sequentially scanning it and eliminating any files or
   subdirectories that are subordinate to any subdirectories that have already been scanned in the file
   list.

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

   @return
      A collapsed version of the file name list
   *//*
   ---------------------------------------------------------------------------------------------------- */
   private ArrayList<XString> collapsedFileNameList(ArrayList<XString> pFileNameList)
      {
      ArrayList<XString>  tmp  = new ArrayList<XString>(pFileNameList);
      ArrayList<XString>  rslt = new ArrayList<XString>();

      if (tmp.size() > 0)
         {
         XString  s1 = (XString)tmp.get(0);
         rslt.add(s1);

         while (true)
            {
            if (tmp.size()==0) break;

            XString  s2 = tmp.get(0);

            if ( ! s2.startsWith(s1))
               {
               rslt.add(s2);
               s1 = s2;
               }
            tmp.remove(0);
            }
         }
      return rslt;
      }



   /*:.
   ==============================================================================================================
   @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@[  Inner Classes  ]@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
   ============================================================================================================== */



   /*:.
   ==============================================================================================================
   @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@[  Public Static Methods  ]@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
   ============================================================================================================== */



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method turns verbose test mode on and off

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

   @param
      pVerbose when true, turns on verbose test mode. When false, it turns off verbose test mode.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public static void setVerbose(boolean pVerbose)
      {
      FileNameList.cVerbose = pVerbose;
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method allows this class file to be unit tested 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". Main() is not a required method, but the practice of putting one in each class and
   wrapping class test code within it allows easy unit testing of the class; and main does not need to
   be removed when testing is complete.


   <P>
      <DL>
         <DT>
            <B>
               Command line usage:
            </B>
            <DD>
               <BLOCKQUOTE>
                  <PRE id="unindent">
                     usage: java cosmicabyss.com.lib.FileNameList "pathSpecifier"

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

                       The quotes surronding pathSpecifier are not needed if
                       there are no wildcard characters in pathSpecifier.

                     Examples:

                       List all .txt files in current directory:
                         java cosmicabyss.com.lib.FileNameList "*.txt"

                       List all .c* files in \foo\bar\ directory:
                         java cosmicabyss.com.lib.FileNameList "\foo\bar\*.c*"

                       List file.txt file in c:\foo\ directory:
                         java cosmicabyss.com.lib.FileNameList c:\foo\file.txt
                  </PRE>
               </BLOCKQUOTE>
            </DD>
         </DT>
      </DL>

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

   @param
      pArgs contains the command line arguments with which this class was invoked as an application.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public static void main(String[] pArgs) throws Exception
      {
      /*.
      ==========================================================================================
      Greetings !
      ------------------------------------------------------------------------------------------ */
      cOut.banner(CLASS_NAME);
      /*.
      ==========================================================================================
      Stop right here if the command line is messed up
      ------------------------------------------------------------------------------------------ */
      if (pArgs.length < 1)
         {
         cOut.println
            (
            "\tusage: java cosmicabyss.com.lib.FileNameList \"pathSpecifier\"\n" +
            "\n"                                                                +
            "\t  Enclosing pathSpecifier in quotes prevents the command\n"      +
            "\t  line interpreter from trying to replace wildcard characters\n" +
            "\t  itself instead of letting FileNameList do it.\n"               +
            "\n"                                                                +
            "\t  The quotes surronding pathSpecifier are not needed if\n"       +
            "\t  there are no wildcard characters in pathSpecifier.\n"          +
            "\n"                                                                +
            "\tExamples:\n"                                                     +
            "\n"                                                                +
            "\t  List all .txt files in current directory:\n"                   +
            "\t    java cosmicabyss.com.lib.FileNameList \"*.txt\"\n"           +
            "\n"                                                                +
            "\t  List all .c* files in \\foo\\bar\\ directory:\n"               +
            "\t    java cosmicabyss.com.lib.FileNameList \"\\foo\\bar\\*.c*\"\n"+
            "\n"                                                                +
            "\t  List file.txt file in c:\\foo\\ directory:\n"                  +
            "\t    java cosmicabyss.com.lib.FileNameList c:\\foo\\file.txt\n"
            );
         System.exit(-1);
         }
      /*.
      ==========================================================================================
      Create an object and send its output to the ConsoleStream
      ------------------------------------------------------------------------------------------ */
      ArrayList<String>  fileSpecs = new ArrayList<String>();
      fileSpecs.add(pArgs[0]);
      FileNameList.setVerbose(true);
      FileNameList obj = new FileNameList(fileSpecs,new ArrayList<XString>(),true,true,true,true,false);
      /*.
      ==========================================================================================
      Test code
      ------------------------------------------------------------------------------------------ */
      obj.test();
      /*.
      ==========================================================================================
      Copy to sysout the names of all the files that match the file specifier - pArgs[0].
      ------------------------------------------------------------------------------------------ */
      for (XString str : obj)
         {
         cOut.println(str);
         }
      }



   }  // class FileNameList



   /*:.
   ==============================================================================================================
   @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@[  Notes  ]@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
   ============================================================================================================== */