blob: 65fa2c62393d193c3dc23b64e91e0da973a48f3c [file] [log] [blame]
package org.apache.maven.shared.utils.io;
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.List;
import java.util.Stack;
/**
* DirectoryWalker
*
*/
class DirectoryWalker
{
/**
* DirStackEntry is an Item on the {@link DirectoryWalker#dirStack}
*/
static class DirStackEntry
{
/**
* Count of files in the directory.
*/
private final int count;
/**
* Current Directory.
*/
private final File dir;
/**
* Index (or offset) within the directory count.
*/
private int index;
/**
* Offset for percentage calculations. Based on parent DirStackEntry.
*/
private double percentageOffset;
/**
* Size of percentage space to work with.
*/
private double percentageSize;
/**
* Create a DirStackEntry.
*
* @param d the directory to track
* @param length the length of entries in the directory.
*/
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<DirStackEntry> dirStack;
private final List<String> excludes;
private final List<String> includes;
private final List<DirectoryWalkListener> listeners;
DirectoryWalker()
{
this.includes = new ArrayList<>();
this.excludes = new ArrayList<>();
this.listeners = new ArrayList<>();
}
public void addDirectoryWalkListener( DirectoryWalkListener listener )
{
this.listeners.add( listener );
}
void addExclude( String exclude )
{
this.excludes.add( fixPattern( exclude ) );
}
void addInclude( String include )
{
this.includes.add( fixPattern( include ) );
}
/**
* Add's to the Exclude List the default list of SCM excludes.
*/
public void addSCMExcludes()
{
String scmexcludes[] = DirectoryScanner.DEFAULTEXCLUDES;
for ( String scmexclude : scmexcludes )
{
addExclude( scmexclude );
}
}
private void fireStep( File file )
{
DirStackEntry dsEntry = dirStack.peek();
int percentage = dsEntry.getPercentage();
for ( DirectoryWalkListener listener : this.listeners )
{
listener.directoryWalkStep( percentage, file );
}
}
private void fireWalkFinished()
{
for ( Object listener1 : this.listeners )
{
DirectoryWalkListener listener = (DirectoryWalkListener) listener1;
listener.directoryWalkFinished();
}
}
private void fireWalkStarting()
{
for ( Object listener1 : this.listeners )
{
DirectoryWalkListener listener = (DirectoryWalkListener) listener1;
listener.directoryWalkStarting( this.baseDir );
}
}
private void fireDebugMessage( String message )
{
for ( Object listener1 : this.listeners )
{
DirectoryWalkListener listener = (DirectoryWalkListener) listener1;
listener.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;
}
private boolean isExcluded( String name )
{
return isMatch( this.excludes, name );
}
private boolean isIncluded( String name )
{
return isMatch( this.includes, name );
}
private boolean isMatch( List<String> patterns, String name )
{
for ( String pattern : patterns )
{
boolean caseSensitive = true;
if ( SelectorUtils.matchPath( pattern, name, caseSensitive ) )
{
return true;
}
}
return false;
}
private String relativeToBaseDir( File file )
{
return file.getAbsolutePath().substring( baseDirOffset + 1 );
}
/**
* 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 ( this.includes.isEmpty() )
{
// default to include all.
addInclude( "**" );
}
fireWalkStarting();
dirStack = new Stack<DirStackEntry>();
scanDir( this.baseDir );
fireWalkFinished();
}
private void scanDir( File dir )
{
File files[] = dir.listFiles();
if ( files == null )
{
return;
}
DirStackEntry curStackEntry = new DirStackEntry( dir, files.length );
if ( dirStack.isEmpty() )
{
curStackEntry.percentageOffset = 0;
curStackEntry.percentageSize = 100;
}
else
{
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;
this.baseDirOffset = baseDir.getAbsolutePath().length();
}
}