package org.cdlib.xtf.util;
/**
* Copyright (c) 2004, Regents of the University of California
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* - 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.
* - Neither the name of the University of California nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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.
*/
import java.io.*;
import org.cdlib.xtf.util.Path;
import org.cdlib.xtf.util.Trace;
////////////////////////////////////////////////////////////////////////////////
/** The <code>FileWalker</code> class is a utility class that simplifies
* traversing all the files in a file-system directory, and optionally, in
* any sub-directories. <br><br>
*
* To use this class, create a derived class that implements the abstract
* method {@link FileWalker#processFile(String,String,String,String) processFile() }.
* Then, create an instance of the derived class and call the
* {@link FileWalker#processFiles(String, boolean) processFiles() } method. <br><br>
*/
public abstract class FileWalker
{
//////////////////////////////////////////////////////////////////////////////
/** Process all the files in the specified directory, and optionally in
* all its sub-directories. <br><br>
*
* This method calls the derived {@link FileWalker#processFile(String,String,String,String) processFile() }
* to process any files found. <br><br>
*
* @param baseDir The base directory containing files to process. <br><br>
*
* @param subDirs A flag that indicates whether files in sub-directories
* should also be processed (<code>true</code>) or not
* (<code>false</code>). <br><br>
*/
public void processFiles(String baseDir, boolean subDirs)
throws IOException
{
try
{
// Convert the specified path to a file equivalent.
mBasePath = new File(Path.normalizePath(baseDir));
}
// Catch any problems that occurred during conversion.
catch (Exception e) {
Trace.error("*** FileWalker Exception: " + e.getClass());
Trace.error(" With message: " + e.getMessage());
Trace.error("");
return;
}
// Call the recursive file processing function to do the work.
mContinueProcessing = true;
processFiles(mBasePath, subDirs);
} // public processFiles( String baseDir, boolean subDirs )
//////////////////////////////////////////////////////////////////////////////
/** File processing function. <br><br>
*
* This function is called once for every file encountered in the specified
* base directory. This function is abstract, and requires a derived class
* to actually implement it. <br><br>
*
* @param basePath The base path under which the current file was found.
* This path will end in a forward slash (/) character to
* simplify the construction of a full path/file
* specification for the current file.<br><br>
*
* @param subPath The sub-path (if any) under which the current file was
* found. As with the base path, this sub-path will end in
* a forward slash (/) character to simplify the
* construction of a full path/file specification for
* the current file. <br><br>
*
* @param fileName The name of the current file (without the extension).
* <br><br>
*
* @param fileExt The extension of the current file (if any). If the
* current file has an extension, then this string will
* begin with a period (.), to simplify the construction
* of a full path/file specifiecation for the current file.
* <br><br>
*
* @return The derived function should return <code> true </code>
* if file processing should continue, or <code> false </code>
* if file processing shouild stop.
*/
protected abstract boolean processFile(String basePath, String subPath,
String fileName, String fileExt);
//////////////////////////////////////////////////////////////////////////////
/** Internal recursive directory/file iterating function. <br><br>
*
* This function is called recursively when a file encountered is a
* directory and sub-directory processing is enabled. <br><br>
*
* @param theFile The name of the current file/directory to process.
* <br><br>
*
* @param subDirs A flag indicating whether or not sub-directories
* should be processed. <br><br>
*
* @.notes
* This method calls itself recursively if sub-directory processing
* is enabled with the <code> subDirs </code> parameter. Once an
* actual file is encountered, this method calls the derived
* {@link FileWalker#processFile(String,String,String,String) processFile() }
* method to actually perform some work for the file found.
*/
private void processFiles(File theFile, boolean subDirs)
throws IOException
{
// If the derived class wants us to stop processing files, return now.
if (!mContinueProcessing)
return;
// If the file we were passed was in fact a directory...
if (theFile.isDirectory())
{
// Process each file in the current directory.
String[] subFiles = theFile.list();
for (int i = 0; i < subFiles.length; i++)
{
// Create a file object for the current file in the list.
File currFile = new File(theFile, subFiles[i]);
// If the current file is a sub-directory and we're not supposed
// to process sub-directories, skip it.
//
if (currFile.isDirectory() && !subDirs)
continue;
// Process the current file, which may or may not be a directory.
processFiles(currFile, subDirs);
}
// At this point we've processed the entire current directory. So
// return now to unwind the current level of recursion.
//
return;
}
// The current file is not a directory, so start by getting the absolute
// representation of the base path for the current file.
//
String basePath = Path.normalizePath(mBasePath.getAbsolutePath());
// Next, determine the name of the current file.
String fileName = theFile.getName();
// Now get the entire path to the file.
String subPath = Path.normalizeFileName(theFile.getAbsolutePath());
// And isolate the sub-path by subtracting out the base path and the name
// of the file.
//
subPath = subPath.substring(basePath.length(),
subPath.length() - fileName.length());
// Lastly, see if the file name has an extension.
String fileExt = "";
int fileExtIdx = fileName.lastIndexOf('.');
// If it does, separate the file name from the extension.
if (fileExtIdx > -1) {
fileExt = fileName.substring(fileExtIdx);
fileName = fileName.substring(0, fileExtIdx);
}
// One final check: If the file name is empty, but there's an extension,
// treat this as a file name with an initial '.' and no extension (for
// files like .login)
//
if (fileName.length() == 0 && fileExt.length() != 0) {
fileName = fileExt;
fileExt = "";
}
// Now call the derived file processing method for this file. Also, based
// on the return value, flag whether we should keep processing files or
// not.
//
mContinueProcessing = processFile(basePath, subPath, fileName, fileExt);
} // ProcessFiles( File theFile, boolean subDirs )
//////////////////////////////////////////////////////////////////////////////
/** Local copy of the path to the base directory to process (as passed into
* {@link FileWalker#processFiles(String,boolean) processFiles() }.
*/
private File mBasePath = null;
/** Flag indicating whether file processing should continue or stop (set by
* the value returned from the derived
* {@link FileWalker#processFile(String, String, String, String)}
* method.)
*/
private boolean mContinueProcessing = true;
} // public class FileWalker