package org.codehaus.mojo.wagon.shared;
/*
* 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.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import org.apache.maven.wagon.Wagon;
import org.apache.maven.wagon.WagonException;
import org.codehaus.plexus.util.StringUtils;
public class WagonDirectoryScanner
{
/**
* Patterns which should be excluded by default.
*
* @see #addDefaultExcludes()
*/
public static final String[] DEFAULTEXCLUDES = org.codehaus.plexus.util.DirectoryScanner.DEFAULTEXCLUDES;
/**
* The wagon
*/
private Wagon wagon;
/**
* Relative to wagon url
*/
private String directory;
/** The patterns for the wagon files to be included. */
private String[] includes;
/** The patterns for the wagon files to be excluded. */
private String[] excludes;
/**
* Whether or not the file system should be treated as a case sensitive one.
*/
private boolean isCaseSensitive = true;
/**
* The files which matched at least one include and at least one exclude and relative to
* directory
*/
private List filesIncluded = new ArrayList();
/**
* Sets the list of include patterns to use. All '/' and '\' characters are replaced by
* <code>File.separatorChar</code>, so the separator used need not match
* <code>File.separatorChar</code>.
* <p>
* When a pattern ends with a '/' or '\', "**" is appended.
*
* @param includes A list of include patterns. May be <code>null</code>, indicating that all
* files should be included. If a non-<code>null</code> list is given, all elements
* must be non-<code>null</code>.
*/
public void setIncludes( String[] includes )
{
if ( includes == null )
{
this.includes = null;
}
else
{
this.includes = new String[includes.length];
for ( int i = 0; i < includes.length; i++ )
{
String pattern = includes[i].trim();
if ( pattern.endsWith( "/" ) )
{
pattern += "**";
}
this.includes[i] = pattern;
}
}
}
/**
* Sets the list of exclude patterns to use. All '\' characters are replaced by '/'
* <p>
* When a pattern ends with a '/' orr '\', "**" is appended.
*
* @param excludes A list of exclude patterns. May be <code>null</code>, indicating that no
* files should be excluded. If a non-<code>null</code> list is given, all elements
* must be non-<code>null</code>.
*/
public void setExcludes( String[] excludes )
{
if ( excludes == null )
{
this.excludes = null;
}
else
{
this.excludes = new String[excludes.length];
for ( int i = 0; i < excludes.length; i++ )
{
String pattern = excludes[i].trim();
if ( pattern.endsWith( "/" ) )
{
pattern += "**";
}
this.excludes[i] = pattern;
}
}
}
/**
* Tests whether or not a name matches against at least one include pattern.
*
* @param name The name to match. Must not be <code>null</code>.
* @return <code>true</code> when the name matches against at least one include pattern, or
* <code>false</code> otherwise.
*/
private boolean isIncluded( String name )
{
for ( int i = 0; i < includes.length; i++ )
{
if ( matchPath( includes[i], name, isCaseSensitive ) )
{
return true;
}
}
return false;
}
/**
* Tests whether or not a name matches against at least one exclude pattern.
*
* @param name The name to match. Must not be <code>null</code>.
* @return <code>true</code> when the name matches against at least one exclude pattern, or
* <code>false</code> otherwise.
*/
protected boolean isExcluded( String name )
{
for ( int i = 0; i < excludes.length; i++ )
{
if ( matchPath( excludes[i], name, isCaseSensitive ) )
{
return true;
}
}
return false;
}
/**
* Tests whether or not a name matches the start of at least one include pattern.
*
* @param name The name to match. Must not be <code>null</code>.
* @return <code>true</code> when the name matches against the start of at least one include
* pattern, or <code>false</code> otherwise.
*/
protected boolean couldHoldIncluded( String name )
{
for ( int i = 0; i < includes.length; i++ )
{
if ( matchPatternStart( includes[i], name, isCaseSensitive ) )
{
return true;
}
}
return false;
}
/**
* Tests whether or not a given path matches the start of a given pattern up to the first "**".
* <p>
* This is not a general purpose test and should only be used if you can live with false
* positives. For example, <code>pattern=**\a</code> and <code>str=b</code> will yield
* <code>true</code>.
*
* @param pattern The pattern to match against. Must not be <code>null</code>.
* @param str The path to match, as a String. Must not be <code>null</code>.
* @param isCaseSensitive Whether or not matching should be performed case sensitively.
*
* @return whether or not a given path matches the start of a given pattern up to the first
* "**".
*/
protected static boolean matchPatternStart( String pattern, String str, boolean isCaseSensitive )
{
return SelectorUtils.matchPatternStart( pattern, str, isCaseSensitive );
}
/**
* Tests whether or not a given path matches a given pattern.
*
* @param pattern The pattern to match against. Must not be <code>null</code>.
* @param str The path to match, as a String. Must not be <code>null</code>.
* @param isCaseSensitive Whether or not matching should be performed case sensitively.
*
* @return <code>true</code> if the pattern matches against the string, or <code>false</code>
* otherwise.
*/
private static boolean matchPath( String pattern, String str, boolean isCaseSensitive )
{
return SelectorUtils.matchPath( pattern, str, isCaseSensitive );
}
public void scan()
throws WagonException
{
if ( wagon == null )
{
throw new IllegalStateException( "No wagon set" );
}
if ( StringUtils.isBlank( directory ) )
{
directory = "";
}
if ( includes == null )
{
// No includes supplied, so set it to 'matches all'
includes = new String[1];
includes[0] = "**";
}
if ( excludes == null )
{
excludes = new String[0];
}
filesIncluded = new ArrayList();
scandir( directory, "" );
Collections.sort( filesIncluded );
}
/**
* Adds default exclusions to the current exclusions set.
*/
public void addDefaultExcludes()
{
int excludesLength = excludes == null ? 0 : excludes.length;
String[] newExcludes;
newExcludes = new String[excludesLength + DEFAULTEXCLUDES.length];
if ( excludesLength > 0 )
{
System.arraycopy( excludes, 0, newExcludes, 0, excludesLength );
}
for ( int i = 0; i < DEFAULTEXCLUDES.length; i++ )
{
newExcludes[i + excludesLength] = DEFAULTEXCLUDES[i];
}
excludes = newExcludes;
}
////////////////////////////////////////////////////////////////////////////////////
/**
* Scans the given directory for files and directories. Found files are placed in a collection,
* based on the matching of includes, excludes, and the selectors. When a directory is found, it
* is scanned recursively.
*
* @throws WagonException
*
* @see #filesIncluded
*/
private void scandir( String dir, String vpath )
throws WagonException
{
List files = wagon.getFileList( dir );
for ( Iterator iterator = files.iterator(); iterator.hasNext(); )
{
String fileName = (String) iterator.next();
if ( fileName.endsWith( "." ) ) //including ".."
{
continue;
}
String file = fileName;
if ( !StringUtils.isBlank( dir ) )
{
if ( dir.endsWith( "/" ) )
{
file = dir + fileName;
}
else
{
file = dir + "/" + fileName;
}
}
String name = vpath + fileName;
if ( this.isDirectory( file ) )
{
if ( !name.endsWith( "/" ) )
{
name += "/";
}
if ( isIncluded( name ) )
{
if ( !isExcluded( name ) )
{
scandir( file, name );
}
else
{
if ( couldHoldIncluded( name ) )
{
scandir( file, name );
}
}
}
else
{
if ( couldHoldIncluded( name ) )
{
scandir( file, name );
}
}
}
else
{
if ( isIncluded( name ) )
{
if ( !isExcluded( name ) )
{
filesIncluded.add( name );
}
}
}
}
}
private boolean isDirectory( String existedRemotePath )
throws WagonException
{
if ( existedRemotePath.endsWith( "/" ) )
{
return true;
}
return wagon.resourceExists( existedRemotePath + "/" );
}
/////////////////////////////////////////////////////////////////////////////////
public List getFilesIncluded()
{
return filesIncluded;
}
public void setWagon( Wagon wagon )
{
this.wagon = wagon;
}
public void setCaseSensitive( boolean isCaseSensitive )
{
this.isCaseSensitive = isCaseSensitive;
}
public void setDirectory( String basePath )
{
this.directory = basePath;
}
}