DirectoryWalker.java
package org.codehaus.plexus.util;
/*
* Copyright The Codehaus Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.io.File;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Stack;
/**
* DirectoryWalker
*
*
*/
public class DirectoryWalker {
/**
* DirStackEntry is an Item on the {@link DirectoryWalker#dirStack}
*/
class DirStackEntry {
/**
* Count of files in the directory.
*/
public int count;
/**
* Current Directory.
*/
public File dir;
/**
* Index (or offset) within the directory count.
*/
public int index;
/**
* Offset for percentage calculations. Based on parent DirStackEntry.
*/
public double percentageOffset;
/**
* Size of percentage space to work with.
*/
public double percentageSize;
/**
* Create a DirStackEntry.
*
* @param d the directory to track
* @param length the length of entries in the directory.
*/
public DirStackEntry(File d, int length) {
dir = d;
count = length;
}
/**
* Calculate the next percentage offset. Used by the next DirStackEntry.
*
* @return the value for the next percentage offset.
*/
public double getNextPercentageOffset() {
return percentageOffset + (index * (percentageSize / count));
}
/**
* Calculate the next percentage size. Used by the next DirStackEntry.
*
* @return the value for the next percentage size.
*/
public double getNextPercentageSize() {
return (percentageSize / count);
}
/**
* The percentage of the DirStackEntry right now. Based on count, index, percentageOffset, and percentageSize.
*
* @return the percentage right now.
*/
public int getPercentage() {
double percentageWithinDir = (double) index / (double) count;
return (int) Math.floor(percentageOffset + (percentageWithinDir * percentageSize));
}
@Override
public String toString() {
return "DirStackEntry[" + "dir=" + dir.getAbsolutePath() + ",count=" + count + ",index=" + index
+ ",percentageOffset=" + percentageOffset + ",percentageSize=" + percentageSize + ",percentage()="
+ getPercentage() + ",getNextPercentageOffset()=" + getNextPercentageOffset()
+ ",getNextPercentageSize()=" + getNextPercentageSize() + "]";
}
}
private File baseDir;
private int baseDirOffset;
private Stack<DirectoryWalker.DirStackEntry> dirStack;
private List<String> excludes;
private List<String> includes;
private boolean isCaseSensitive = true;
private List<DirectoryWalkListener> listeners;
private boolean debugEnabled = false;
public DirectoryWalker() {
includes = new ArrayList<String>();
excludes = new ArrayList<String>();
listeners = new ArrayList<DirectoryWalkListener>();
}
public void addDirectoryWalkListener(DirectoryWalkListener listener) {
listeners.add(listener);
}
public void addExclude(String exclude) {
excludes.add(fixPattern(exclude));
}
public void addInclude(String include) {
includes.add(fixPattern(include));
}
/**
* Add's to the Exclude List the default list of SCM excludes.
*/
public void addSCMExcludes() {
String scmexcludes[] = AbstractScanner.DEFAULTEXCLUDES;
for (String scmexclude : scmexcludes) {
addExclude(scmexclude);
}
}
private void fireStep(File file) {
DirStackEntry dsEntry = dirStack.peek();
int percentage = dsEntry.getPercentage();
for (Object listener1 : listeners) {
DirectoryWalkListener listener = (DirectoryWalkListener) listener1;
listener.directoryWalkStep(percentage, file);
}
}
private void fireWalkFinished() {
for (DirectoryWalkListener listener1 : listeners) {
listener1.directoryWalkFinished();
}
}
private void fireWalkStarting() {
for (DirectoryWalkListener listener1 : listeners) {
listener1.directoryWalkStarting(baseDir);
}
}
private void fireDebugMessage(String message) {
for (DirectoryWalkListener listener1 : listeners) {
listener1.debug(message);
}
}
private String fixPattern(String pattern) {
String cleanPattern = pattern;
if (File.separatorChar != '/') {
cleanPattern = cleanPattern.replace('/', File.separatorChar);
}
if (File.separatorChar != '\\') {
cleanPattern = cleanPattern.replace('\\', File.separatorChar);
}
return cleanPattern;
}
public void setDebugMode(boolean debugEnabled) {
this.debugEnabled = debugEnabled;
}
/**
* @return Returns the baseDir.
*/
public File getBaseDir() {
return baseDir;
}
/**
* @return Returns the excludes.
*/
public List<String> getExcludes() {
return excludes;
}
/**
* @return Returns the includes.
*/
public List<String> getIncludes() {
return includes;
}
private boolean isExcluded(String name) {
return isMatch(excludes, name);
}
private boolean isIncluded(String name) {
return isMatch(includes, name);
}
private boolean isMatch(List<String> patterns, String name) {
for (String pattern1 : patterns) {
if (SelectorUtils.matchPath(pattern1, name, isCaseSensitive)) {
return true;
}
}
return false;
}
private String relativeToBaseDir(File file) {
return file.getAbsolutePath().substring(baseDirOffset + 1);
}
/**
* Removes a DirectoryWalkListener.
*
* @param listener the listener to remove.
*/
public void removeDirectoryWalkListener(DirectoryWalkListener listener) {
listeners.remove(listener);
}
/**
* Performs a Scan against the provided {@link #setBaseDir(File)}
*/
public void scan() {
if (baseDir == null) {
throw new IllegalStateException("Scan Failure. BaseDir not specified.");
}
if (!baseDir.exists()) {
throw new IllegalStateException("Scan Failure. BaseDir does not exist.");
}
if (!baseDir.isDirectory()) {
throw new IllegalStateException("Scan Failure. BaseDir is not a directory.");
}
if (includes.isEmpty()) {
// default to include all.
addInclude("**");
}
if (debugEnabled) {
Iterator<String> it;
StringBuilder dbg = new StringBuilder();
dbg.append("DirectoryWalker Scan");
dbg.append("\n Base Dir: ").append(baseDir.getAbsolutePath());
dbg.append("\n Includes: ");
it = includes.iterator();
while (it.hasNext()) {
String include = it.next();
dbg.append("\n - \"").append(include).append("\"");
}
dbg.append("\n Excludes: ");
it = excludes.iterator();
while (it.hasNext()) {
String exclude = it.next();
dbg.append("\n - \"").append(exclude).append("\"");
}
fireDebugMessage(dbg.toString());
}
fireWalkStarting();
dirStack = new Stack<DirStackEntry>();
scanDir(baseDir);
fireWalkFinished();
}
private void scanDir(File dir) {
File[] files = dir.listFiles();
if (files == null) {
return;
}
DirectoryWalker.DirStackEntry curStackEntry = new DirectoryWalker.DirStackEntry(dir, files.length);
if (dirStack.isEmpty()) {
curStackEntry.percentageOffset = 0;
curStackEntry.percentageSize = 100;
} else {
DirectoryWalker.DirStackEntry previousStackEntry = dirStack.peek();
curStackEntry.percentageOffset = previousStackEntry.getNextPercentageOffset();
curStackEntry.percentageSize = previousStackEntry.getNextPercentageSize();
}
dirStack.push(curStackEntry);
for (int idx = 0; idx < files.length; idx++) {
curStackEntry.index = idx;
String name = relativeToBaseDir(files[idx]);
if (isExcluded(name)) {
fireDebugMessage(name + " is excluded.");
continue;
}
if (files[idx].isDirectory()) {
scanDir(files[idx]);
} else {
if (isIncluded(name)) {
fireStep(files[idx]);
}
}
}
dirStack.pop();
}
/**
* @param baseDir The baseDir to set.
*/
public void setBaseDir(File baseDir) {
this.baseDir = baseDir;
baseDirOffset = baseDir.getAbsolutePath().length();
}
/**
* @param entries The excludes to set.
*/
public void setExcludes(List<String> entries) {
excludes.clear();
if (entries != null) {
for (String entry : entries) {
excludes.add(fixPattern(entry));
}
}
}
/**
* @param entries The includes to set.
*/
public void setIncludes(List<String> entries) {
includes.clear();
if (entries != null) {
for (String entry : entries) {
includes.add(fixPattern(entry));
}
}
}
}