/* XBN Java: Generically useful, non-GUI Java code. http://sourceforge.net/projects/xbnjava Copyright (C) 1997-2003, Jeff Epstein All rights reserved. Modifications: No Redistribution in binary form, with or without modifications, are permitted provided that the following conditions are met: * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * If modifications are made to source code then this license should indicate that fact in the "Modifications" section above. * Neither the author, nor the contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. [NOTE: This license contains NO advertising clause.] */ package xbn.util; import xbn.XBNObject; import xbn.array.VWObject; import xbn.array.AOOValid; import xbn.array.AOOVCAll; import xbn.output.Outputter; import xbn.string.UtilString; import java.io.File; import java.io.FileFilter; /**

[Deprecated for DirScan] Represents a directory, and all files/directories/sub-directories/... therein. Every sub-directory in this one is another DirFile object.

Source code:  DirFile.java

This class is meant to be useful, not fast.

Example use

import xbn.util.DirFile;

public class XmplDirFile  {
   public static void main(String[] as_cmdLineParams)  {
      DirFile df = new DirFile(".");      //The directory you're executing this from

      System.out.println(df.toString());
      listFiles(df, "   ");
   }

   public static void listFiles(DirFile df, String s_dbgPrefix)  {
      for(int i = 0; i < df.getCountSubFiles(); i++)  {
         System.out.println(s_dbgPrefix + df.getSubFile(i).getFileObjectThis().getName());
      }

      for(int i = 0; i < df.getCountSubDirs(); i++)  {
         System.out.println(s_dbgPrefix + "[" + df.getSubDir(i).getFileObjectThis().getName() + "]");
         listFiles(df.getSubDir(i), (s_dbgPrefix + "   "));
      }
   }
}

@version 0.9b @author Jeff Epstein, http://sourceforge.net/projects/xbnjava. **/ public class DirFile extends XBNObject { //If this is a directory, were the contents of the //directories analyzed? private boolean bContentsAnalyzed = false; //How many sub-directory levels should be analyzed? private int iLevelsToAnalyze = -2; //How many levels below the sub directory is this file/dir? private int iLevelsBelowBaseDir = -1; private File fBaseDir = null; private File fThis = null; private String sRelDir = null; private DirFile[] adfSubDirectories = null; private DirFile[] adfSubFiles = null; /**

Create a DirFile.

Equal to DirFile(s_basePath, (new Outputter()))

**/ public DirFile(String s_basePath) { this(s_basePath, (new Outputter())); } /**

Create a DirFile.

Equal to DirFile(s_basePath, -1, o_utputter)

**/ public DirFile(String s_basePath, Outputter o_utputter) { this(s_basePath, -1, o_utputter); } /**

Create a DirFile.

Equal to DirFile(s_basePath, i_levelsToAnalyze, (new Outputter()))

**/ public DirFile(String s_basePath, int i_levelsToAnalyze) { this(s_basePath, i_levelsToAnalyze, (new Outputter())); } /**

Create a DirFile.

Equal to DirFile(s_basePath, i_levelsToAnalyze, null, o_utputter)

**/ public DirFile(String s_basePath, int i_levelsToAnalyze, Outputter o_utputter) { this(s_basePath, i_levelsToAnalyze, null, o_utputter); } /**

Create a DirFile.

Equal to DirFile(s_basePath, -1, file_filter)

Note: To pass in file_filter as null, use

(new DirFile("Base path", (FileFilter)file_filter))

**/ public DirFile(String s_basePath, FileFilter file_filter) { this(s_basePath, -1, file_filter); } /**

Create a DirFile.

Equal to DirFile(s_basePath, i_levelsToAnalyze, file_filter, (new Outputter())))

**/ public DirFile(String s_basePath, int i_levelsToAnalyze, FileFilter file_filter) { this(s_basePath, i_levelsToAnalyze, file_filter, (new Outputter())); } /**

Create a DirFile.

Equal to DirFile(s_basePath, -1, null)

**/ public DirFile(String s_basePath, FileFilter file_filter, Outputter o_utputter) { this(s_basePath, -1, file_filter, o_utputter); } /**

Create a DirFile.

@param s_basePath The full path (including the final File.separator) of the base directory. May not be null, and must represent a directory that exists. @param i_levelsToAnalyze The number of levels to analyze. must be greater than -2. See getLevelsToAnalyze. @param file_filter The java.io.FileFilter object to define the specific files and directories that should be retrieved. If all files are desired, set this to null. **/ public DirFile(String s_basePath, int i_levelsToAnalyze, FileFilter file_filter, Outputter o_utputter) { throwAXIfBadStr(s_basePath, "s_basePath", sCNSTR); if(i_levelsToAnalyze < -1) { throwAX("constructor: i_levelsToAnalyze must be greater than -2."); } //All parameters are valid. iLevelsToAnalyze = i_levelsToAnalyze; fBaseDir = new File(s_basePath); //There is no relative directory for the base directory, //nor for any files therein. sRelDir = sES; fThis = fBaseDir; if(!fThis.isDirectory() || !fThis.exists()) { throwAX("constructor: s_basePath ('" + s_basePath + "') must point to a directory, and that directory must exist."); } throwAXIfNull(o_utputter, "o_utputter", sCNSTR); //Since this is the base directory, this is -1 levels below //the base directory. Files *in* the base directory are //considered zero levels below. Items in sub-directories //are 1 level below... iLevelsBelowBaseDir = 0; o_utputter.writeNoln(true, getFileObjThis().getName()); //We need to analyze at least this directory. analyzeThisDirectory(file_filter, o_utputter); o_utputter.newln(); } /**

Create a DirFile.this file is below the base directory. This object may represent a file or directory.

f_baseDir   s_relDir   f_this
null              null                  not null          -->  Base directory
not null          null                  not null          -->  File/dir in base dir
not null          not null              not null          -->  File/dir in sub dir

All other configurations are bogus. f_this may never be null. If f_baseDir is null, then the base directory has not yet been determined, so s_relDir must also be null. If f_baseDir is not null, then s_relDir may or may not be null. If it is null, this is a file/dir directly in the base dir. If it's not null, this is a file/dir in a sub-directory.

@param f_baseDir The file object of the base directory (representing the DirFile object as created by the public constructor). If null, then this DirFile is the base directory. Must be a directory according to f_baseDir.isDirectory, and must end with File.separator. @param s_relDir A string representing the entire path of the directory in which this file is contained, not including f_baseDir.getPath. If not null, must end with File.separator. If null, then this DirFile object is definitely the base directory, or a file/directory (directly) contained in the base directory. @param f_this The file name of this file/directory. f_baseDir.getName() + s_relDir + f_this is equivalent to the entire and absolute path to this file. must be non-null, and at least one character in length. @param i_levelsToAnalyze The number of levels to analyze. If -1, then all sub-directories (and their files and sub directories) are analyzed. If 0, then only this directory is analyzed (directories are named, but no sub-files are analyzed). If greater than 0, then that number of sub-directories are analyzed, up to the number of sub-directory levels actually existing. **/ private DirFile(File f_baseDir, String s_relDir, File f_this, int i_levelsToAnalyze, int i_levelsBelowBaseDir, FileFilter file_filter, Outputter o_utputter) { //Validate and set internal objects...START if(f_baseDir == null || !f_baseDir.isDirectory()) { throwAX("constructor: f_baseDir may not be null, must refer to a directory, the directory must exist, and f_baseDir.getFileObjThis().getName() must end with a File.separator ('" + File.separator + "')."); } //f_baseDir is not null, exists, is a directory, and //ends with a slash. fBaseDir = f_baseDir; if(s_relDir == null) { throwAX("constructor: s_relDir may not be null If there is no relative directory (if this is a file/dir directly in the base path), then provide empty string."); } //s_relDir is definitiely not null. sRelDir = s_relDir; throwAXIfNull(f_this, "f_this", sCNSTR); fThis = f_this; if(i_levelsToAnalyze < -1) { throwAX("constructor: i_levelsToAnalyze must be greater than -2."); } if(i_levelsToAnalyze == -1) { iLevelsToAnalyze = -1; } else { iLevelsToAnalyze = i_levelsToAnalyze; } if(i_levelsBelowBaseDir < 0) { throwAX("constructor: i_levelsBelowBaseDir must be greater than -1."); } iLevelsBelowBaseDir = i_levelsBelowBaseDir; throwAXIfNull(o_utputter, "o_utputter", sCNSTR); //Validate and set internal objets...END if(o_utputter.getOConfig().isOn() && getFileObjThis().isDirectory()) { //Don't waste the time unless debugging is specifically requested... String sDebugPfx = (new UtilString()).getDuped(" ", getLevelsBelowBaseDir()); o_utputter.newln(); o_utputter.writeNoln(true, sDebugPfx + getFileObjThis().getName()); } analyzeThisDirectory(file_filter, o_utputter); } private void analyzeThisDirectory(FileFilter file_filter, Outputter o_utputter) { if(fThis.isFile()) { //No further analysis is necessary. return; } if(getLevelsToAnalyze() == 0) { return; } File[] aSubFiles = null; if(file_filter != null) { //Only retrieve those files conforming to the java.io.FileFilter aSubFiles = fThis.listFiles(file_filter); } else { //Retrieve all files. aSubFiles = fThis.listFiles(); } if(aSubFiles.length == 0) { //There are no objects in this directory. //Get outta here. adfSubFiles = null; adfSubDirectories = null; bContentsAnalyzed = true; return; } //There is at least one file/dir in this directory. FThenDArray aFTD = new FThenDArray(aSubFiles); //Get all sub-files/dirs...START VWObject acoSubDirs = new VWObject(); VWObject acoSubFiles = new VWObject(); DirFile df = null; String sRelDirForSub = null; if(aFTD.hasMoreFiles() || aFTD.hasMoreDirs()) { if(getRelDir().length() < 1) { sRelDirForSub = sES; } else { sRelDirForSub = getRelDir() + File.separator; } sRelDirForSub += getFileObjThis().getName(); } //First: Process all sub-files. while(aFTD.hasMoreFiles()) { File fFile = aFTD.getNextFile(); //This is a file. //A what? A file. A what? A file. Oh, a file. o_utputter.writeNoln("."); //The final "null" parameter is the FileFilter. We're //definitely not going to analyze sub-files (files don't //*have* sub-files!), so there's no need to provide a //FileFilter. df = new DirFile(getFileObjBaseDir(), sRelDirForSub, fFile, getLevelsToAnalyze(), (getLevelsBelowBaseDir() + 1), null, o_utputter); acoSubFiles.add(df); } //All sub-files have been processed. //Second, process all sub-directories. while(aFTD.hasMoreDirs()) { File fDir = aFTD.getNextDir(); //Do we need to analyze the contents of this //directory? int iLevelsToAnalyzeInSubObjects = -2; boolean bNeedToAnalyzeSubDir = false; if(getLevelsToAnalyze() > 0) { //We definitely need to analyze at least the //contents of this directory. iLevelsToAnalyzeInSubObjects = getLevelsToAnalyze() - 1; bNeedToAnalyzeSubDir = true; } else if(getLevelsToAnalyze() == -1) { //Analyze everything, no matter how deep. iLevelsToAnalyzeInSubObjects = -1; bNeedToAnalyzeSubDir = true; } if(bNeedToAnalyzeSubDir) { //We want to analyze this directory. Not //necessarily it's contents, but definitely *it*. df = new DirFile(getFileObjBaseDir(), sRelDirForSub, fDir, iLevelsToAnalyzeInSubObjects, (getLevelsBelowBaseDir() + 1), file_filter, o_utputter); acoSubDirs.add(df); } } //All directories have been processed. //We now have all the sub-thing-ees. Put them into the //DirFile arrays. Object[] ao = acoSubDirs.getAOObject(); adfSubDirectories = new DirFile[ao.length]; for(int i = 0; i < ao.length; i++) { adfSubDirectories[i] = (DirFile)ao[i]; } ao = acoSubFiles.getAOObject(); adfSubFiles = new DirFile[ao.length]; for(int i = 0; i < ao.length; i++) { adfSubFiles[i] = (DirFile)ao[i]; } //Get all sub-files/dirs...END } /**

Get the java.io.File representing this file/directory.

@return getFileObjThis() @deprecated Use getFileObjThis. **/ public File getFileObjectThis() { return getFileObjThis(); } /**

Get the java.io.File representing the base directory.

@return getFileObjBaseDir() @deprecated Use getFileObjBaseDir. **/ public File getFileObjectBaseDir() { return getFileObjBaseDir(); } /**

Get the relative path of this DirFile.

@return getRelDir() @deprecated Use getRelDir. **/ public String getRelativeDirectory() { return getRelDir(); } /**

Get the java.io.File representing the base directory.

If getFileObjThis equals getFileObjBaseDir, then this DirFile is the base directory.

**/ public File getFileObjBaseDir() { return fBaseDir; } /**

Get the java.io.File representing this file/directory.

If getFileObjBaseDir equals getFileObjThis, then this DirFile is the base directory.

**/ public File getFileObjThis() { return fThis; } /**

Get the relative path of this DirFile.

May be giving an extra parent directory. May be actually returning dir\\sub_dir\\another_sub_dir

@return "" If this is the base directory.
If this is not the base directory:  The full path of this DirFile, minus the path of the base directory and minus the name itself. It neither starts nor ends with a slash.

For example, if...

Then this function would return "sub_dir\\another_sub_dir"

**/ public String getRelDir() { return sRelDir; } /**

How many sub-directories exist in this directory?

@return 0 A number greater than 0 If this DirFile is a directory, and there is at least one directory contained in it. **/ public int getCountSubDirs() { if(adfSubDirectories == null) { return 0; } return adfSubDirectories.length; } /**

How many sub-files exist in this directory?

@return 0
A number greater than 0 If this DirFile is a directory, and there is at least one file contained in it. **/ public int getCountSubFiles() { if(adfSubFiles == null) { return 0; } return adfSubFiles.length; } /**

Get the sub file at the requested array index.

@param i_dx The array index of the sub file you wish to retrieve. Must range zero..[getCountSubFiles - 1], inclusive. @return A DirFile representing the sub-file (as contained in this directory) at array index i_dx. @exception AssertException **/ public DirFile getSubFile(int i_dx) { return getSubDirFile("File", adfSubFiles, i_dx); } /**

Get the sub directory at the requested array index.

@param i_dx The array index of the sub directory you wish to retrieve. Must range zero..[getCountSubDirs - 1], inclusive. @return A DirFile representing the sub-directory (as contained in this directory) at array index i_dx. @exception AssertException **/ public DirFile getSubDir(int i_dx) { return getSubDirFile("Dir", adfSubDirectories, i_dx); } private DirFile getSubDirFile(String s_dirOrFileForFuncName, DirFile[] a_dirFilesSubDirsOrFiles, int i_dx) { try { return a_dirFilesSubDirsOrFiles[i_dx]; } catch(NullPointerException npx) { String sErrorPrefix = "getSub" + s_dirOrFileForFuncName + ": "; if(isFile()) { throwAX(sErrorPrefix + "This is a file. Only directories can contain things."); } //This is a directory. if(!haveContentsBeenAnalyzed()) { throwAX(sErrorPrefix + "This is a directory, but the contents have not been analyzed. It is not known whether or not there are files/directories herein."); } //The contends of this directory have been analyzed. throwAX(sErrorPrefix + "This is a directory, but there are no files/directories herein."); } catch(ArrayIndexOutOfBoundsException aioobx) { //There is at least one sub-directory //(a_dirFilesSubDirsOrFiles is not null), but the //counter has exceeded the array's boundaries //The only other possibility is that i_dx is //greater than or equal to //a_dirFilesSubDirsOrFiles.length. throwAX("getSub" + s_dirOrFileForFuncName + ": i_dx (" + i_dx + ") is invalid. getCountSub" + s_dirOrFileForFuncName + "s()=" + a_dirFilesSubDirsOrFiles.length + "."); } //Never reached. Required for compile. return null; } /**

Does this DirFile object represent a directory?

@return true If this DirFile represents a directory.
false If this DirFile represents a file. **/ public boolean isDirectory() { return fThis.isDirectory(); } /**

Does this DirFile object represent a file?

@return true If this DirFile represents a file.
false If this DirFile represents a directory. **/ public boolean isFile() { return !isDirectory(); } /**

Does this DirFile represent a directory, and have its contents been analyzed?

Depending on the value of getLevelsToAnalyze, this directory (if it is a directory) may not be analyzed or recognized by this DirFile. In other words, this directory may be at the bottom level, in which case its contents are ignored. This is not a statement to whether the directory contains zero or [not-zero] objects in it.

@return true If this DirFile is a directory, and the contents (the files and directories) of it have been analyzed.
false If this DirFile is a file, or if it's a directory but the contents of it have not been analyzed. **/ public boolean haveContentsBeenAnalyzed() { if(!isFile()) { //Files have nothing to analyze. return false; } return bContentsAnalyzed; } /**

How many levels below the base directory is this file/dir?

@return 0 If this file/dir is directory in the base directory, or is the base directory.
1 If this file/dir is in a directory directly contained in the base directory.
2... If this file/dir is in a directory 2 (or more) levels below the base directory. **/ public int getLevelsBelowBaseDir() { return iLevelsBelowBaseDir; } /**

How many levels of sub-directories (and their contents) should be analyzed?

@return -1 Analyze everything. The contents of this directory, contents of any directories in this one, all their contents, all their sub-directories, ...
0 Analyze nothing further, even if this DirFile is a directory. This analyzes the current directory (file) itself, but nothing inside of it.
1 Analyze the contents of this directory, but not the contents of anything else, even if this directory contains another directory.
2 Same as 1, but analyze the contents of sub-directories (directories that are in this one). Do not analyze the contents of any sub-sub-directories.
3 Same as 2, but analyze the contents of the sub-sub-directories as well.
4 Same as 3, but analyze the contents of the sub-sub-sub-directories as well.
... Same as 4, but analyze the contents of the sub-sub-sub-...-directories as well. **/ public int getLevelsToAnalyze() { return iLevelsToAnalyze; } /**

Get some information about this DirFile.

**/ public String toString() { return this.getClass().getName() + ": getFileObjThis().getName()='" + getFileObjThis().getName() + "', getLevelsBelowBaseDir()=" + getLevelsBelowBaseDir() + ", getCountSubFiles()=" + getCountSubFiles() + ", getCountSubDirs()=" + getCountSubDirs() + ", haveContentsBeenAnalyzed()=" + haveContentsBeenAnalyzed() + ", getLevelsToAnalyze()=" + getLevelsToAnalyze(); } } /** This object corrects a problem where the debugging output was inaccurate. ====================================================== This replaces the array of Files. It acts like an enumeration that gets all files first, then dirs. It simply iterates through the array twice. **/ class FThenDArray extends XBNObject { private boolean bMoreFilesToGet = false; private int iArrIdx = -1; private File[] aFile = null; public FThenDArray(File[] a_file) { AOOValid aoov = new AOOValid(new AOOVCAll()); aoov.crashIfBad("DirFile.FThenDArray.constructor", "a_file", a_file); aFile = a_file; //Initialize internal variables bMoreFilesToGet = true; iArrIdx = 0; //Goto the first directory in the array. //If none go to the first file (element zero). gotoNext(); } public int getLength() { return aFile.length; } public boolean hasMoreDirs() { return (!hasMoreFiles() && iArrIdx < getLength()); } public File getNextDir() { if(!hasMoreDirs()) { throwAX("getNextDir: All files and directories have been retrieved. Both hasMoreDirs() and hasMoreFiles() equal true."); } File fDir = aFile[iArrIdx++]; if(iArrIdx < getLength()) { //There is at least one more element in the array. //If there is another directory, go to it. Otherwise, //go to the end of the array. gotoNext(); } return fDir; } public boolean hasMoreFiles() { return bMoreFilesToGet; } public File getNextFile() { if(!hasMoreFiles()) { if(!hasMoreDirs()) { throwAX("getNextFile: All files and directories have been retrieved."); } else { throwAX( "getNextFile: hasMoreFiles() equals false. No more files to retrieve. There is at least one more directory (hasMoreDirs() equals true). Try getNextDir()."); } } File fFile = aFile[iArrIdx++]; gotoNext(); return fFile; } private void gotoNext() { if(!bMoreFilesToGet && iArrIdx >= aFile.length) { throwAX("gotoNext: All sub-files and sub-directories have been retrieved."); } //There is at least one more object to retrieve. for(; iArrIdx < aFile.length; iArrIdx++) { if(bMoreFilesToGet && aFile[iArrIdx].isFile()) { //We've not yet completed retrieving files. We've //just found the next one. return; } else if(!bMoreFilesToGet && aFile[iArrIdx].isDirectory()) { //We've gotten all the files. We're now retrieving //the directories. This is the next one. return; } } if(iArrIdx >= aFile.length) { if(!bMoreFilesToGet) { //There is no more nothing to retrieve. return; } //There are no more files to retrieve. We've yet to //determine if there are any directories. bMoreFilesToGet = false; //Start again at the beginning, and go to the //next (first) *directory*. iArrIdx = 0; gotoNext(); } } } /* This class keeps track of the *next* class file to be retrieved in the provided directory. It skips over non-".class" files, as well as the directories themselves. We need to keep track of where we are in a recursive function so we can continue from that point Pretend we have this directory structure... xbn array primitive config db jdlcode named output placeholder programs string escape EscapeString.class EscapeUnescapeString.class ESConfig.class package.html UnescapeString.class UnescapeStringException.class USCIgnore.class USCISpecific.class USConfig.class USCSpecific.class padchop template util util ...and the file we want is... xbn/string/escape/UnescapeString.class ...and the DirFile's base (root) dir is xbn/ DIRECTORY LAYERS Layer zero: The root directory only: xbn Layer one: One directory level below the root: array, config, db, jdlcode, named, output, placeholder, programs, string, template, util Layer two: Two directory levels below the root: primitive, escape, padchop, util This number basically tells us how many recursions have occured. Since the file we want is in directory xbn/string/escape, the file is in *directory* layer two. "PATH" FROM ROOT TO THE DESIRED FILE [8, 0, 4] Directory string is the eighth directory element in xbn/. Directory escape is the zeroth directory element in xbn/string. File UnescapeString is the fourth *file* element in xbn/string/escape So the path is an array of numbers, every one being the directory element, except the last, which is the file element. CONCLUSION For this example, the directory layer containing the file is two, and the "path" to that file is [8, 0, 4]. */