diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..28a027e1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,12 @@ +target/ +.project +.classpath +.settings/ +bin +*.iml +*.ipr +*.iws +*.idea +release.properties +.java-version + diff --git a/pom.xml b/pom.xml index 8039dbd6..e214eff9 100644 --- a/pom.xml +++ b/pom.xml @@ -22,21 +22,23 @@ limitations under the License. org.codehaus.plexus plexus - 2.0.6 - ../pom/pom.xml + 3.3.1 plexus-utils - 2.0.5 + 3.0.16 Plexus Common Utilities - A collection of various utility classes to ease working with strings, files, command lines, XML and more. + A collection of various utility classes to ease working with strings, files, command lines, XML and + more. + http://plexus.codehaus.org/plexus-utils - scm:svn:http://svn.codehaus.org/plexus/plexus-utils/tags/plexus-utils-2.0.5 - scm:svn:https://svn.codehaus.org/plexus/plexus-utils/tags/plexus-utils-2.0.5 - http://fisheye.codehaus.org/browse/plexus/plexus-utils/tags/plexus-utils-2.0.5 + scm:git:git@github.com:sonatype/plexus-utils.git + scm:git:git@github.com:sonatype/plexus-utils.git + http://github.com/sonatype/plexus-utils + plexus-utils-3.0.16 JIRA @@ -45,15 +47,6 @@ limitations under the License. - - org.apache.maven.plugins - maven-compiler-plugin - - - 1.3 - 1.3 - - org.apache.maven.plugins maven-surefire-plugin @@ -78,9 +71,30 @@ limitations under the License. org.apache.maven.plugins - maven-release-plugin + maven-enforcer-plugin + 1.1.1 + + + enforce-java + + enforce + + + + + 1.7.0 + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin - https://svn.codehaus.org/plexus/plexus-utils/tags/ + 1.6 + 1.6 diff --git a/src/main/java/org/codehaus/plexus/util/AbstractScanner.java b/src/main/java/org/codehaus/plexus/util/AbstractScanner.java index 23879e91..4477063f 100644 --- a/src/main/java/org/codehaus/plexus/util/AbstractScanner.java +++ b/src/main/java/org/codehaus/plexus/util/AbstractScanner.java @@ -19,8 +19,11 @@ import java.io.File; -public abstract class AbstractScanner - implements Scanner +/** + * Scan a directory tree for files, with specified inclusions and exclusions. + */ +public abstract class AbstractScanner + implements Scanner { /** * Patterns which should be excluded by default, like SCM files @@ -30,6 +33,7 @@ public abstract class AbstractScanner *
  • RCS: **/RCS, **/RCS/**
  • *
  • SCCS: **/SCCS, **/SCCS/**
  • *
  • VSSercer: **/vssver.scc
  • + *
  • MKS: **/project.pj
  • *
  • SVN: **/.svn, **/.svn/**
  • *
  • GNU: **/.arch-ids, **/.arch-ids/**
  • *
  • Bazaar: **/.bzr, **/.bzr/**
  • @@ -37,7 +41,7 @@ public abstract class AbstractScanner *
  • Mac: **/.DS_Store
  • *
  • Serena Dimension: **/.metadata, **/.metadata/**
  • *
  • Mercurial: **/.hg, **/.hg/**
  • - *
  • GIT: **/.git, **/.git/**
  • + *
  • GIT: **/.git, **/.gitignore, **/.gitattributes, **/.git/**
  • *
  • Bitkeeper: **/BitKeeper, **/BitKeeper/**, **/ChangeSet, **/ChangeSet/**
  • *
  • Darcs: **/_darcs, **/_darcs/**, **/.darcsrepo, **/.darcsrepo/****/-darcs-backup*, **/.darcs-temp-mail * @@ -46,85 +50,73 @@ public abstract class AbstractScanner */ public static final String[] DEFAULTEXCLUDES = { // Miscellaneous typical temporary files - "**/*~", - "**/#*#", - "**/.#*", - "**/%*%", - "**/._*", - + "**/*~", "**/#*#", "**/.#*", "**/%*%", "**/._*", + // CVS - "**/CVS", - "**/CVS/**", - "**/.cvsignore", - + "**/CVS", "**/CVS/**", "**/.cvsignore", + // RCS - "**/RCS", - "**/RCS/**", - + "**/RCS", "**/RCS/**", + // SCCS - "**/SCCS", - "**/SCCS/**", - + "**/SCCS", "**/SCCS/**", + // Visual SourceSafe "**/vssver.scc", - + + // MKS + "**/project.pj", + // Subversion - "**/.svn", - "**/.svn/**", - + "**/.svn", "**/.svn/**", + // Arch - "**/.arch-ids", - "**/.arch-ids/**", - + "**/.arch-ids", "**/.arch-ids/**", + //Bazaar - "**/.bzr", - "**/.bzr/**", - + "**/.bzr", "**/.bzr/**", + //SurroundSCM "**/.MySCMServerInfo", - + // Mac "**/.DS_Store", - + // Serena Dimensions Version 10 - "**/.metadata", - "**/.metadata/**", - + "**/.metadata", "**/.metadata/**", + // Mercurial - "**/.hg", - "**/.hg/**", - + "**/.hg", "**/.hg/**", + // git - "**/.git", - "**/.git/**", - + "**/.git", "**/.gitignore", "**/.gitattributes", "**/.git/**", + // BitKeeper - "**/BitKeeper", - "**/BitKeeper/**", - "**/ChangeSet", - "**/ChangeSet/**", - + "**/BitKeeper", "**/BitKeeper/**", "**/ChangeSet", "**/ChangeSet/**", + // darcs - "**/_darcs", - "**/_darcs/**", - "**/.darcsrepo", - "**/.darcsrepo/**", - "**/-darcs-backup*", - "**/.darcs-temp-mail" - }; - - /** The patterns for the files to be included. */ + "**/_darcs", "**/_darcs/**", "**/.darcsrepo", "**/.darcsrepo/**", "**/-darcs-backup*", "**/.darcs-temp-mail" }; + + /** + * The patterns for the files to be included. + */ protected String[] includes; - - /** The patterns for the files to be excluded. */ + + private MatchPatterns includesPatterns; + + /** + * The patterns for the files to be excluded. + */ protected String[] excludes; - + + private MatchPatterns excludesPatterns; + /** * Whether or not the file system should be treated as a case sensitive * one. */ protected boolean isCaseSensitive = true; - + /** * Sets whether or not the file system should be regarded as case sensitive. * @@ -135,11 +127,11 @@ public void setCaseSensitive( boolean isCaseSensitive ) { this.isCaseSensitive = isCaseSensitive; } - + /** * Tests whether or not a given path matches the start of a given * pattern up to the first "**". - *

    + *

    * This is not a general purpose test and should only be used if you * can live with false positives. For example, pattern=**\a * and str=b will yield true. @@ -148,39 +140,36 @@ public void setCaseSensitive( boolean isCaseSensitive ) * null. * @param str The path to match, as a String. Must not be * null. - * * @return whether or not a given path matches the start of a given - * pattern up to the first "**". + * pattern up to the first "**". */ protected static boolean matchPatternStart( String pattern, String str ) { return SelectorUtils.matchPatternStart( pattern, str ); } - + /** * Tests whether or not a given path matches the start of a given * pattern up to the first "**". - *

    + *

    * This is not a general purpose test and should only be used if you * can live with false positives. For example, pattern=**\a * and str=b will yield true. * - * @param pattern The pattern to match against. Must not be - * null. - * @param str The path to match, as a String. Must not be - * null. + * @param pattern The pattern to match against. Must not be + * null. + * @param str The path to match, as a String. Must not be + * null. * @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 "**". + * pattern up to the first "**". */ - protected static boolean matchPatternStart( String pattern, String str, - boolean isCaseSensitive ) + 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. * @@ -188,7 +177,6 @@ protected static boolean matchPatternStart( String pattern, String str, * null. * @param str The path to match, as a String. Must not be * null. - * * @return true if the pattern matches against the string, * or false otherwise. */ @@ -196,26 +184,24 @@ protected static boolean matchPath( String pattern, String str ) { return SelectorUtils.matchPath( pattern, str ); } - + /** * Tests whether or not a given path matches a given pattern. * - * @param pattern The pattern to match against. Must not be - * null. - * @param str The path to match, as a String. Must not be - * null. + * @param pattern The pattern to match against. Must not be + * null. + * @param str The path to match, as a String. Must not be + * null. * @param isCaseSensitive Whether or not matching should be performed * case sensitively. - * * @return true if the pattern matches against the string, * or false otherwise. */ - protected static boolean matchPath( String pattern, String str, - boolean isCaseSensitive ) + protected static boolean matchPath( String pattern, String str, boolean isCaseSensitive ) { return SelectorUtils.matchPath( pattern, str, isCaseSensitive ); } - + /** * Tests whether or not a string matches against a pattern. * The pattern may contain two special characters:
    @@ -226,7 +212,6 @@ protected static boolean matchPath( String pattern, String str, * Must not be null. * @param str The string which must be matched against the pattern. * Must not be null. - * * @return true if the string matches against the pattern, * or false otherwise. */ @@ -234,43 +219,40 @@ public static boolean match( String pattern, String str ) { return SelectorUtils.match( pattern, str ); } - + /** * Tests whether or not a string matches against a pattern. * The pattern may contain two special characters:
    * '*' means zero or more characters
    * '?' means one and only one character * - * @param pattern The pattern to match against. - * Must not be null. - * @param str The string which must be matched against the pattern. - * Must not be null. + * @param pattern The pattern to match against. + * Must not be null. + * @param str The string which must be matched against the pattern. + * Must not be null. * @param isCaseSensitive Whether or not matching should be performed * case sensitively. - * - * * @return true if the string matches against the pattern, * or false otherwise. */ - protected static boolean match( String pattern, String str, - boolean isCaseSensitive ) + protected static boolean match( String pattern, String str, boolean isCaseSensitive ) { return SelectorUtils.match( pattern, str, isCaseSensitive ); } - - + + /** * Sets the list of include patterns to use. All '/' and '\' characters * are replaced by File.separatorChar, so the separator used * need not match File.separatorChar. - *

    + *

    * When a pattern ends with a '/' or '\', "**" is appended. * * @param includes A list of include patterns. * May be null, indicating that all files * should be included. If a non-null * list is given, all elements must be - * non-null. + * non-null. */ public void setIncludes( String[] includes ) { @@ -287,12 +269,12 @@ public void setIncludes( String[] includes ) } } } - + /** * Sets the list of exclude patterns to use. All '/' and '\' characters * are replaced by File.separatorChar, so the separator used * need not match File.separatorChar. - *

    + *

    * When a pattern ends with a '/' or '\', "**" is appended. * * @param excludes A list of exclude patterns. @@ -318,7 +300,7 @@ public void setExcludes( String[] excludes ) /** * Normalizes the pattern, e.g. converts forward and backward slashes to the platform-specific file separator. - * + * * @param pattern The pattern to normalize, must not be null. * @return The normalized pattern, never null. */ @@ -360,16 +342,14 @@ private String normalizePattern( String pattern ) */ protected boolean isIncluded( String name ) { - for ( int i = 0; i < includes.length; i++ ) - { - if ( matchPath( includes[i], name, isCaseSensitive ) ) - { - return true; - } - } - return false; + return includesPatterns.matches( name, isCaseSensitive ); } - + + protected boolean isIncluded( String name, String[] tokenizedName ) + { + return includesPatterns.matches( name, tokenizedName, isCaseSensitive ); + } + /** * Tests whether or not a name matches the start of at least one include * pattern. @@ -380,16 +360,9 @@ protected boolean isIncluded( String name ) */ protected boolean couldHoldIncluded( String name ) { - for ( int i = 0; i < includes.length; i++ ) - { - if ( matchPatternStart( includes[i], name, isCaseSensitive ) ) - { - return true; - } - } - return false; + return includesPatterns.matchesPatternStart(name, isCaseSensitive); } - + /** * Tests whether or not a name matches against at least one exclude * pattern. @@ -400,16 +373,14 @@ protected boolean couldHoldIncluded( String name ) */ protected boolean isExcluded( String name ) { - for ( int i = 0; i < excludes.length; i++ ) - { - if ( matchPath( excludes[i], name, isCaseSensitive ) ) - { - return true; - } - } - return false; + return excludesPatterns.matches( name, isCaseSensitive ); + } + + protected boolean isExcluded( String name, String[] tokenizedName ) + { + return excludesPatterns.matches( name, tokenizedName, isCaseSensitive ); } - + /** * Adds default exclusions to the current exclusions set. */ @@ -428,8 +399,8 @@ public void addDefaultExcludes() } excludes = newExcludes; } - - protected void setupDefaultFilters() + + protected void setupDefaultFilters() { if ( includes == null ) { @@ -442,4 +413,10 @@ protected void setupDefaultFilters() excludes = new String[0]; } } + + protected void setupMatchPatterns() + { + includesPatterns = MatchPatterns.from( includes ); + excludesPatterns = MatchPatterns.from( excludes ); + } } diff --git a/src/main/java/org/codehaus/plexus/util/CollectionUtils.java b/src/main/java/org/codehaus/plexus/util/CollectionUtils.java index 4192b79f..1762b9ab 100644 --- a/src/main/java/org/codehaus/plexus/util/CollectionUtils.java +++ b/src/main/java/org/codehaus/plexus/util/CollectionUtils.java @@ -55,7 +55,7 @@ public class CollectionUtils * @param recessiveMap Recessive Map. * @return The result map with combined dominant and recessive values. */ - public static Map mergeMaps( Map dominantMap, Map recessiveMap ) + public static Map mergeMaps( Map dominantMap, Map recessiveMap ) { if ( dominantMap == null && recessiveMap == null ) @@ -68,21 +68,21 @@ public static Map mergeMaps( Map dominantMap, Map recessiveMap ) return dominantMap; } - if ( dominantMap == null && recessiveMap != null ) + if ( dominantMap == null) { return recessiveMap; } - Map result = new HashMap(); + Map result = new HashMap(); // Grab the keys from the dominant and recessive maps. - Set dominantMapKeys = dominantMap.keySet(); - Set recessiveMapKeys = recessiveMap.keySet(); + Set dominantMapKeys = dominantMap.keySet(); + Set recessiveMapKeys = recessiveMap.keySet(); // Create the set of keys that will be contributed by the // recessive Map by subtracting the intersection of keys // from the recessive Map's keys. - Collection contributingRecessiveKeys = + Collection contributingRecessiveKeys = CollectionUtils.subtract( recessiveMapKeys, CollectionUtils.intersection( dominantMapKeys, recessiveMapKeys ) ); @@ -90,9 +90,8 @@ public static Map mergeMaps( Map dominantMap, Map recessiveMap ) // Now take the keys we just found and extract the values from // the recessiveMap and put the key:value pairs into the dominantMap. - for ( Iterator i = contributingRecessiveKeys.iterator(); i.hasNext(); ) + for ( K key : contributingRecessiveKeys ) { - Object key = i.next(); result.put( key, recessiveMap.get( key ) ); } @@ -107,9 +106,9 @@ public static Map mergeMaps( Map dominantMap, Map recessiveMap ) * @param maps An array of Maps to merge. * @return Map The result Map produced after the merging process. */ - public static Map mergeMaps( Map[] maps ) + public static Map mergeMaps( Map[] maps ) { - Map result = null; + Map result; if ( maps.length == 0 ) { @@ -140,20 +139,21 @@ else if ( maps.length == 1 ) * will be equal to the minimum of the cardinality of that element * in the two given {@link Collection}s. * + * @param a The first collection + * @param b The second collection * @see Collection#retainAll + * @return The intersection of a and b, never null */ - public static Collection intersection( final Collection a, final Collection b ) + public static Collection intersection( final Collection a, final Collection b ) { - ArrayList list = new ArrayList(); - Map mapa = getCardinalityMap( a ); - Map mapb = getCardinalityMap( b ); - Set elts = new HashSet( a ); + ArrayList list = new ArrayList(); + Map mapa = getCardinalityMap( a ); + Map mapb = getCardinalityMap( b ); + Set elts = new HashSet( a ); elts.addAll( b ); - Iterator it = elts.iterator(); - while ( it.hasNext() ) + for ( E obj : elts ) { - Object obj = it.next(); - for ( int i = 0,m = Math.min( getFreq( obj, mapa ), getFreq( obj, mapb ) ); i < m; i++ ) + for ( int i = 0, m = Math.min( getFreq( obj, mapa ), getFreq( obj, mapb ) ); i < m; i++ ) { list.add( obj ); } @@ -167,15 +167,17 @@ public static Collection intersection( final Collection a, final Collection b ) * will be the cardinality of e in a minus the cardinality * of e in b, or zero, whichever is greater. * + * @param a The start collection + * @param b The collection that will be subtracted * @see Collection#removeAll + * @return The result of the subtraction */ - public static Collection subtract( final Collection a, final Collection b ) + public static Collection subtract( final Collection a, final Collection b ) { - ArrayList list = new ArrayList( a ); - Iterator it = b.iterator(); - while ( it.hasNext() ) + ArrayList list = new ArrayList( a ); + for ( T aB : b ) { - list.remove( it.next() ); + list.remove( aB ); } return list; } @@ -187,35 +189,35 @@ public static Collection subtract( final Collection a, final Collection b ) * in the {@link Collection}. * An entry that maps to null indicates that the * element does not appear in the given {@link Collection}. + * @param col The collection to count cardinalities for + * @return A map of counts, indexed on each element in the collection */ - public static Map getCardinalityMap( final Collection col ) + public static Map getCardinalityMap( final Collection col ) { - HashMap count = new HashMap(); - Iterator it = col.iterator(); - while ( it.hasNext() ) + HashMap count = new HashMap(); + for ( E obj : col ) { - Object obj = it.next(); - Integer c = (Integer) ( count.get( obj ) ); + Integer c = count.get( obj ); if ( null == c ) { - count.put( obj, new Integer( 1 ) ); + count.put( obj, 1 ); } else { - count.put( obj, new Integer( c.intValue() + 1 ) ); + count.put( obj, c + 1 ); } } return count; } - public static List iteratorToList( Iterator it ) + public static List iteratorToList( Iterator it ) { if ( it == null ) { throw new NullPointerException( "it cannot be null." ); } - List list = new ArrayList(); + List list = new ArrayList(); while ( it.hasNext() ) { @@ -229,23 +231,21 @@ public static List iteratorToList( Iterator it ) // // ---------------------------------------------------------------------- - private static final int getFreq( final Object obj, final Map freqMap ) + private static int getFreq( final E obj, final Map freqMap ) { try { - Object o = freqMap.get( obj ); + Integer o = freqMap.get( obj ); if ( o != null ) // minimize NullPointerExceptions { - return ( (Integer) o ).intValue(); + return o; } } - catch ( NullPointerException e ) + catch ( NullPointerException ignore ) { - // ignored } - catch ( NoSuchElementException e ) + catch ( NoSuchElementException ignore ) { - // ignored } return 0; } diff --git a/src/main/java/org/codehaus/plexus/util/DirectoryScanner.java b/src/main/java/org/codehaus/plexus/util/DirectoryScanner.java index b7155406..7a1511f4 100644 --- a/src/main/java/org/codehaus/plexus/util/DirectoryScanner.java +++ b/src/main/java/org/codehaus/plexus/util/DirectoryScanner.java @@ -56,17 +56,18 @@ import java.io.File; import java.io.IOException; +import java.util.ArrayList; import java.util.Vector; /** * Class for scanning a directory for files/directories which match certain * criteria. - *

    + *

    * These criteria consist of selectors and patterns which have been specified. * With the selectors you can select which files you want to have included. * Files which are not selected are excluded. With patterns you can include * or exclude files based on their filename. - *

    + *

    * The idea is simple. A given directory is recursively scanned for all files * and directories. Each file/directory is matched against a set of selectors, * including special support for matching against filenames with include and @@ -74,12 +75,12 @@ * pattern of the include pattern list or other file selector, and don't match * any pattern of the exclude pattern list or fail to match against a required * selector will be placed in the list of files/directories found. - *

    + *

    * When no list of include patterns is supplied, "**" will be used, which * means that everything will be matched. When no list of exclude patterns is * supplied, an empty list is used, such that nothing will be excluded. When * no selectors are supplied, none are applied. - *

    + *

    * The filename pattern matching is done as follows: * The name to be matched is split up in path segments. A path segment is the * name of a directory or file, which is bounded by @@ -87,11 +88,11 @@ * For example, "abc/def/ghi/xyz.java" is split up in the segments "abc", * "def","ghi" and "xyz.java". * The same is done for the pattern against which should be matched. - *

    + *

    * The segments of the name and the pattern are then matched against each * other. When '**' is used for a path segment in the pattern, it matches * zero or more path segments of the name. - *

    + *

    * There is a special case regarding the use of File.separators * at the beginning of the pattern and the string to match:
    * When a pattern starts with a File.separator, the string @@ -100,27 +101,27 @@ * string to match may not start with a File.separator. * When one of these rules is not obeyed, the string will not * match. - *

    + *

    * When a name path segment is matched against a pattern path segment, the * following special characters can be used:
    * '*' matches zero or more characters
    * '?' matches one character. - *

    + *

    * Examples: - *

    + *

    * "**\*.class" matches all .class files/dirs in a directory tree. - *

    + *

    * "test\a??.java" matches all files/dirs which start with an 'a', then two * more characters and then ".java", in a directory called test. - *

    + *

    * "**" matches everything in a directory tree. - *

    + *

    * "**\test\**\XYZ*" matches all files/dirs which start with "XYZ" and where * there is a parent directory called test (e.g. "abc\test\def\ghi\XYZ123"). - *

    + *

    * Case sensitivity may be turned off if necessary. By default, it is * turned on. - *

    + *

    * Example of usage: *

      *   String[] includes = {"**\\*.class"};
    @@ -141,56 +142,69 @@
      * files in all proper subdirectories of a directory called "modules"
      *
      * @author Arnout J. Kuiper
    - * ajkuiper@wxs.nl
    + *         ajkuiper@wxs.nl
      * @author Magesh Umasankar
      * @author Bruce Atherton
      * @author Antoine Levy-Lambert
      */
    -public class DirectoryScanner extends AbstractScanner
    +public class DirectoryScanner
    +    extends AbstractScanner
     {
     
    -    /** The base directory to be scanned. */
    +    /**
    +     * The base directory to be scanned.
    +     */
         protected File basedir;
     
    -    /** The files which matched at least one include and no excludes
    -     *  and were selected.
    +    /**
    +     * The files which matched at least one include and no excludes
    +     * and were selected.
          */
    -    protected Vector filesIncluded;
    +    protected Vector filesIncluded;
     
    -    /** The files which did not match any includes or selectors. */
    -    protected Vector filesNotIncluded;
    +    /**
    +     * The files which did not match any includes or selectors.
    +     */
    +    protected Vector filesNotIncluded;
     
         /**
          * The files which matched at least one include and at least
          * one exclude.
          */
    -    protected Vector filesExcluded;
    +    protected Vector filesExcluded;
     
    -    /** The directories which matched at least one include and no excludes
    -     *  and were selected.
    +    /**
    +     * The directories which matched at least one include and no excludes
    +     * and were selected.
          */
    -    protected Vector dirsIncluded;
    +    protected Vector dirsIncluded;
     
    -    /** The directories which were found and did not match any includes. */
    -    protected Vector dirsNotIncluded;
    +    /**
    +     * The directories which were found and did not match any includes.
    +     */
    +    protected Vector dirsNotIncluded;
     
         /**
          * The directories which matched at least one include and at least one
          * exclude.
          */
    -    protected Vector dirsExcluded;
    +    protected Vector dirsExcluded;
     
    -    /** The files which matched at least one include and no excludes and
    -     *  which a selector discarded.
    +    /**
    +     * The files which matched at least one include and no excludes and
    +     * which a selector discarded.
          */
    -    protected Vector filesDeselected;
    +    protected Vector filesDeselected;
     
    -    /** The directories which matched at least one include and no excludes
    -     *  but which a selector discarded.
    +    /**
    +     * The directories which matched at least one include and no excludes
    +     * but which a selector discarded.
          */
    -    protected Vector dirsDeselected;
    +    protected Vector dirsDeselected;
     
    -    /** Whether or not our results were built by a slow scan. */
    +    /**
    +     * Whether or not our results were built by a slow scan.
    +     */
         protected boolean haveSlowResults = false;
     
         /**
    @@ -200,9 +214,13 @@ public class DirectoryScanner extends AbstractScanner
          */
         private boolean followSymlinks = true;
     
    -    /** Whether or not everything tested so far has been included. */
    +    /**
    +     * Whether or not everything tested so far has been included.
    +     */
         protected boolean everythingIncluded = true;
     
    +    private final String[] tokenizedEmpty = MatchPattern.tokenizePathToString( "", File.separator );
    +
         /**
          * Sole constructor.
          */
    @@ -221,8 +239,7 @@ public DirectoryScanner()
          */
         public void setBasedir( String basedir )
         {
    -        setBasedir( new File( basedir.replace( '/', File.separatorChar ).replace(
    -            '\\', File.separatorChar ) ) );
    +        setBasedir( new File( basedir.replace( '/', File.separatorChar ).replace( '\\', File.separatorChar ) ) );
         }
     
         /**
    @@ -275,11 +292,12 @@ public boolean isEverythingIncluded()
          * pattern and don't match any exclude patterns. If there are selectors
          * then the files must pass muster there, as well.
          *
    -     * @exception IllegalStateException if the base directory was set
    -     *            incorrectly (i.e. if it is null, doesn't exist,
    -     *            or isn't a directory).
    +     * @throws IllegalStateException if the base directory was set
    +     *                               incorrectly (i.e. if it is null, doesn't exist,
    +     *                               or isn't a directory).
          */
    -    public void scan() throws IllegalStateException
    +    public void scan()
    +        throws IllegalStateException
         {
             if ( basedir == null )
             {
    @@ -287,29 +305,29 @@ public void scan() throws IllegalStateException
             }
             if ( !basedir.exists() )
             {
    -            throw new IllegalStateException( "basedir " + basedir
    -                                             + " does not exist" );
    +            throw new IllegalStateException( "basedir " + basedir + " does not exist" );
             }
             if ( !basedir.isDirectory() )
             {
    -            throw new IllegalStateException( "basedir " + basedir
    -                                             + " is not a directory" );
    +            throw new IllegalStateException( "basedir " + basedir + " is not a directory" );
             }
     
             setupDefaultFilters();
    -
    -        filesIncluded = new Vector();
    -        filesNotIncluded = new Vector();
    -        filesExcluded = new Vector();
    -        filesDeselected = new Vector();
    -        dirsIncluded = new Vector();
    -        dirsNotIncluded = new Vector();
    -        dirsExcluded = new Vector();
    -        dirsDeselected = new Vector();
    -
    -        if ( isIncluded( "" ) )
    +        setupMatchPatterns();
    +
    +        filesIncluded = new Vector();
    +        filesNotIncluded = new Vector();
    +        filesExcluded = new Vector();
    +        filesDeselected = new Vector();
    +        dirsIncluded = new Vector();
    +        dirsNotIncluded = new Vector();
    +        dirsExcluded = new Vector();
    +        dirsDeselected = new Vector();
    +
    +        if ( isIncluded( "", tokenizedEmpty ) )
             {
    -            if ( !isExcluded( "" ) )
    +
    +            if ( !isExcluded( "", tokenizedEmpty ) )
                 {
                     if ( isSelected( "", basedir ) )
                     {
    @@ -337,7 +355,7 @@ public void scan() throws IllegalStateException
          * list of excluded/included files/directories, whereas a fast scan
          * will only have full results for included files, as it ignores
          * directories which can't possibly hold any included files/directories.
    -     * 

    + *

    * Returns immediately if a slow scan has already been completed. */ protected void slowScan() @@ -353,21 +371,19 @@ protected void slowScan() String[] notIncl = new String[dirsNotIncluded.size()]; dirsNotIncluded.copyInto( notIncl ); - for ( int i = 0; i < excl.length; i++ ) + for ( String anExcl : excl ) { - if ( !couldHoldIncluded( excl[i] ) ) + if ( !couldHoldIncluded( anExcl ) ) { - scandir( new File( basedir, excl[i] ), - excl[i] + File.separator, false ); + scandir( new File( basedir, anExcl ), anExcl + File.separator, false ); } } - for ( int i = 0; i < notIncl.length; i++ ) + for ( String aNotIncl : notIncl ) { - if ( !couldHoldIncluded( notIncl[i] ) ) + if ( !couldHoldIncluded( aNotIncl ) ) { - scandir( new File( basedir, notIncl[i] ), - notIncl[i] + File.separator, false ); + scandir( new File( basedir, aNotIncl ), aNotIncl + File.separator, false ); } } @@ -385,8 +401,6 @@ protected void slowScan() * prevent problems with an absolute path when using * dir). Must not be null. * @param fast Whether or not this call is part of a fast scan. - * @throws IOException - * * @see #filesIncluded * @see #filesNotIncluded * @see #filesExcluded @@ -409,15 +423,14 @@ protected void scandir( File dir, String vpath, boolean fast ) * then???) */ - /* - * [jdcasey] (2) is apparently happening to me, as this is killing one of my tests... - * this is affecting the assembly plugin, fwiw. I will initialize the newfiles array as - * zero-length for now. - * - * NOTE: I can't find the problematic code, as it appears to come from a native method - * in UnixFileSystem... - */ + * [jdcasey] (2) is apparently happening to me, as this is killing one of my tests... + * this is affecting the assembly plugin, fwiw. I will initialize the newfiles array as + * zero-length for now. + * + * NOTE: I can't find the problematic code, as it appears to come from a native method + * in UnixFileSystem... + */ /* * [bentmann] A null array will also be returned from list() on NTFS when dir refers to a soft link or * junction point whose target is not existent. @@ -429,15 +442,15 @@ protected void scandir( File dir, String vpath, boolean fast ) if ( !followSymlinks ) { - Vector noLinks = new Vector(); - for ( int i = 0; i < newfiles.length; i++ ) + ArrayList noLinks = new ArrayList(); + for ( String newfile : newfiles ) { try { - if ( isSymbolicLink( dir, newfiles[i] ) ) + if ( isSymbolicLink( dir, newfile ) ) { - String name = vpath + newfiles[i]; - File file = new File( dir, newfiles[i] ); + String name = vpath + newfile; + File file = new File( dir, newfile ); if ( file.isDirectory() ) { dirsExcluded.addElement( name ); @@ -449,31 +462,31 @@ protected void scandir( File dir, String vpath, boolean fast ) } else { - noLinks.addElement( newfiles[i] ); + noLinks.add( newfile ); } } catch ( IOException ioe ) { - String msg = "IOException caught while checking " - + "for links, couldn't get cannonical path!"; + String msg = "IOException caught while checking " + "for links, couldn't get cannonical path!"; // will be caught and redirected to Ant's logging system System.err.println( msg ); - noLinks.addElement( newfiles[i] ); + noLinks.add( newfile ); } } - newfiles = new String[noLinks.size()]; - noLinks.copyInto( newfiles ); + newfiles = noLinks.toArray(new String[noLinks.size()]); } - for ( int i = 0; i < newfiles.length; i++ ) + for ( String newfile : newfiles ) { - String name = vpath + newfiles[i]; - File file = new File( dir, newfiles[i] ); + String name = vpath + newfile; + String[] tokenizedName = MatchPattern.tokenizePathToString( name, File.separator ); + File file = new File( dir, newfile ); if ( file.isDirectory() ) { - if ( isIncluded( name ) ) + + if ( isIncluded( name, tokenizedName ) ) { - if ( !isExcluded( name ) ) + if ( !isExcluded( name, tokenizedName ) ) { if ( isSelected( name, file ) ) { @@ -520,9 +533,9 @@ protected void scandir( File dir, String vpath, boolean fast ) } else if ( file.isFile() ) { - if ( isIncluded( name ) ) + if ( isIncluded( name, tokenizedName ) ) { - if ( !isExcluded( name ) ) + if ( !isExcluded( name, tokenizedName ) ) { if ( isSelected( name, file ) ) { @@ -584,7 +597,6 @@ public String[] getIncludedFiles() * * @return the names of the files which matched none of the include * patterns. - * * @see #slowScan */ public String[] getNotIncludedFiles() @@ -603,7 +615,6 @@ public String[] getNotIncludedFiles() * * @return the names of the files which matched at least one of the * include patterns and at at least one of the exclude patterns. - * * @see #slowScan */ public String[] getExcludedFiles() @@ -617,12 +628,11 @@ public String[] getExcludedFiles() /** *

    Returns the names of the files which were selected out and * therefore not ultimately included.

    - * + *

    *

    The names are relative to the base directory. This involves * performing a slow scan if one has not already been completed.

    * * @return the names of the files which were deselected. - * * @see #slowScan */ public String[] getDeselectedFiles() @@ -639,7 +649,7 @@ public String[] getDeselectedFiles() * The names are relative to the base directory. * * @return the names of the directories which matched at least one of the - * include patterns and none of the exclude patterns. + * include patterns and none of the exclude patterns. */ public String[] getIncludedDirectories() { @@ -654,8 +664,7 @@ public String[] getIncludedDirectories() * performing a slow scan if one has not already been completed. * * @return the names of the directories which matched none of the include - * patterns. - * + * patterns. * @see #slowScan */ public String[] getNotIncludedDirectories() @@ -673,8 +682,7 @@ public String[] getNotIncludedDirectories() * performing a slow scan if one has not already been completed. * * @return the names of the directories which matched at least one of the - * include patterns and at least one of the exclude patterns. - * + * include patterns and at least one of the exclude patterns. * @see #slowScan */ public String[] getExcludedDirectories() @@ -688,12 +696,11 @@ public String[] getExcludedDirectories() /** *

    Returns the names of the directories which were selected out and * therefore not ultimately included.

    - * + *

    *

    The names are relative to the base directory. This involves * performing a slow scan if one has not already been completed.

    * * @return the names of the directories which were deselected. - * * @see #slowScan */ public String[] getDeselectedDirectories() @@ -706,19 +713,24 @@ public String[] getDeselectedDirectories() /** * Checks whether a given file is a symbolic link. - * + *

    *

    It doesn't really test for symbolic links but whether the * canonical and absolute paths of the file are identical - this * may lead to false positives on some platforms.

    * * @param parent the parent directory of the file to test - * @param name the name of the file to test. - * + * @param name the name of the file to test. + * @return true if it's a symbolic link + * @throws java.io.IOException . * @since Ant 1.5 */ public boolean isSymbolicLink( File parent, String name ) throws IOException { + if ( Java7Detector.isJava7() ) + { + return Java7FileUtil.isSymLink( new File( parent, name ) ); + } File resolvedParent = new File( parent.getCanonicalPath() ); File toTest = new File( resolvedParent, name ); return !toTest.getAbsolutePath().equals( toTest.getCanonicalPath() ); diff --git a/src/main/java/org/codehaus/plexus/util/DirectoryWalkListener.java b/src/main/java/org/codehaus/plexus/util/DirectoryWalkListener.java index a3bb5e2c..a4af2527 100644 --- a/src/main/java/org/codehaus/plexus/util/DirectoryWalkListener.java +++ b/src/main/java/org/codehaus/plexus/util/DirectoryWalkListener.java @@ -19,8 +19,9 @@ import java.io.File; /** - * DirectoryWalkListener + * Observes the actions of a {@link DirectoryWalker}. * @version $Id$ + * @see DirectoryWalker */ public interface DirectoryWalkListener { diff --git a/src/main/java/org/codehaus/plexus/util/DirectoryWalker.java b/src/main/java/org/codehaus/plexus/util/DirectoryWalker.java index 6f621b09..0d7c76cb 100644 --- a/src/main/java/org/codehaus/plexus/util/DirectoryWalker.java +++ b/src/main/java/org/codehaus/plexus/util/DirectoryWalker.java @@ -320,7 +320,7 @@ public void scan() if ( debugEnabled ) { Iterator it; - StringBuffer dbg = new StringBuffer(); + StringBuilder dbg = new StringBuilder(); dbg.append( "DirectoryWalker Scan" ); dbg.append( "\n Base Dir: " ).append( this.baseDir.getAbsolutePath() ); dbg.append( "\n Includes: " ); diff --git a/src/main/java/org/codehaus/plexus/util/FileUtils.java b/src/main/java/org/codehaus/plexus/util/FileUtils.java index 28743811..e1100162 100644 --- a/src/main/java/org/codehaus/plexus/util/FileUtils.java +++ b/src/main/java/org/codehaus/plexus/util/FileUtils.java @@ -55,6 +55,9 @@ * */ +import org.codehaus.plexus.util.io.InputStreamFacade; +import org.codehaus.plexus.util.io.URLInputStreamFacade; + import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; @@ -64,22 +67,18 @@ import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; +import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.Reader; import java.io.Writer; import java.net.URL; +import java.nio.channels.FileChannel; import java.security.SecureRandom; import java.text.DecimalFormat; import java.util.ArrayList; import java.util.Arrays; -import java.util.Iterator; import java.util.List; import java.util.Random; -import java.util.Vector; - -import org.codehaus.plexus.util.io.FileInputStreamFacade; -import org.codehaus.plexus.util.io.InputStreamFacade; -import org.codehaus.plexus.util.io.URLInputStreamFacade; /** * This class provides basic facilities for manipulating files and file paths. @@ -140,14 +139,21 @@ public class FileUtils */ public static final int ONE_GB = ONE_KB * ONE_MB; - /** The vm line separator */ + /** + * The file copy buffer size (30 MB) + */ + private static final long FILE_COPY_BUFFER_SIZE = ONE_MB * 30; + + /** + * The vm file separator + */ public static String FS = System.getProperty( "file.separator" ); /** * Non-valid Characters for naming files, folders under Windows: ":", "*", "?", "\"", "<", ">", "|" * * @see - * http://support.microsoft.com/?scid=kb%3Ben-us%3B177506&x=12&y=13 + * http://support.microsoft.com/?scid=kb%3Ben-us%3B177506&x=12&y=13 */ private static final String[] INVALID_CHARACTERS_FOR_WINDOWS_FILE_NAME = { ":", "*", "?", "\"", "<", ">", "|" }; @@ -164,7 +170,7 @@ public static String[] getDefaultExcludes() * @return the default excludes pattern as list. * @see #getDefaultExcludes() */ - public static List getDefaultExcludesAsList() + public static List getDefaultExcludesAsList() { return Arrays.asList( getDefaultExcludes() ); } @@ -252,7 +258,7 @@ public static String basename( String filename ) * Matches the equally named unix command. * * @param filename the file path - * @param suffix the file suffix + * @param suffix the file suffix * @return the basename of the file */ public static String basename( String filename, String suffix ) @@ -300,7 +306,7 @@ public static String extension( String filename ) } } - if ( lastDot >= 0 && lastDot > lastSep) + if ( lastDot >= 0 && lastDot > lastSep ) { return filename.substring( lastDot + 1 ); } @@ -334,7 +340,7 @@ public static String fileRead( String file ) } /** - * @param file the file path + * @param file the file path * @param encoding the wanted encoding * @return the file content using the specified encoding. * @throws IOException if any @@ -355,11 +361,11 @@ public static String fileRead( String file, String encoding ) public static String fileRead( File file ) throws IOException { - return fileRead( file, null); + return fileRead( file, null ); } /** - * @param file the file path + * @param file the file path * @param encoding the wanted encoding * @return the file content using the specified encoding. * @throws IOException if any @@ -367,7 +373,7 @@ public static String fileRead( File file ) public static String fileRead( File file, String encoding ) throws IOException { - StringBuffer buf = new StringBuffer(); + StringBuilder buf = new StringBuilder(); Reader reader = null; @@ -401,13 +407,13 @@ public static String fileRead( File file, String encoding ) * Note: the data is written with platform encoding * * @param fileName The path of the file to write. - * @param data The content to write to the file. + * @param data The content to write to the file. * @throws IOException if any */ public static void fileAppend( String fileName, String data ) throws IOException { - fileAppend( fileName, null, data); + fileAppend( fileName, null, data ); } /** @@ -415,7 +421,7 @@ public static void fileAppend( String fileName, String data ) * * @param fileName The path of the file to write. * @param encoding The encoding of the file. - * @param data The content to write to the file. + * @param data The content to write to the file. * @throws IOException if any */ public static void fileAppend( String fileName, String encoding, String data ) @@ -425,7 +431,8 @@ public static void fileAppend( String fileName, String encoding, String data ) try { out = new FileOutputStream( fileName, true ); - if ( encoding != null ) { + if ( encoding != null ) + { out.write( data.getBytes( encoding ) ); } else @@ -444,7 +451,7 @@ public static void fileAppend( String fileName, String encoding, String data ) * Note: the data is written with platform encoding * * @param fileName The path of the file to write. - * @param data The content to write to the file. + * @param data The content to write to the file. * @throws IOException if any */ public static void fileWrite( String fileName, String data ) @@ -458,28 +465,60 @@ public static void fileWrite( String fileName, String data ) * * @param fileName The path of the file to write. * @param encoding The encoding of the file. - * @param data The content to write to the file. + * @param data The content to write to the file. * @throws IOException if any */ public static void fileWrite( String fileName, String encoding, String data ) throws IOException { - FileOutputStream out = null; + File file = ( fileName == null ) ? null : new File( fileName ); + fileWrite( file, encoding, data ); + } + + /** + * Writes data to a file. The file will be created if it does not exist. + * Note: the data is written with platform encoding + * + * @param file The file to write. + * @param data The content to write to the file. + * @throws IOException if any + * @since 2.0.6 + */ + public static void fileWrite( File file, String data ) + throws IOException + { + fileWrite( file, null, data ); + } + + /** + * Writes data to a file. The file will be created if it does not exist. + * + * @param file The file to write. + * @param encoding The encoding of the file. + * @param data The content to write to the file. + * @throws IOException if any + * @since 2.0.6 + */ + public static void fileWrite( File file, String encoding, String data ) + throws IOException + { + Writer writer = null; try { - out = new FileOutputStream( fileName ); + OutputStream out = new FileOutputStream( file ); if ( encoding != null ) { - out.write( data.getBytes( encoding ) ); + writer = new OutputStreamWriter( out, encoding ); } else { - out.write( data.getBytes() ); + writer = new OutputStreamWriter( out ); } + writer.write( data ); } finally { - IOUtil.close( out ); + IOUtil.close( writer ); } } @@ -509,8 +548,8 @@ public static boolean waitFor( String fileName, int seconds ) /** * Waits for NFS to propagate a file creation, imposing a timeout. * - * @param file The file. - * @param seconds The maximum time in seconds to wait. + * @param file The file. + * @param seconds The maximum time in seconds to wait. * @return True if file exists. */ public static boolean waitFor( File file, int seconds ) @@ -558,13 +597,13 @@ public static File getFile( String fileName ) *

    * The given extensions should be like "java" and not like ".java" * - * @param directory The path of the directory. + * @param directory The path of the directory. * @param extensions an array of expected extensions. * @return An array of files for the wanted extensions. */ public static String[] getFilesFromExtension( String directory, String[] extensions ) { - Vector files = new Vector(); + List files = new ArrayList(); File currentDir = new File( directory ); @@ -601,14 +640,14 @@ public static String[] getFilesFromExtension( String directory, String[] extensi String add = currentFile.getAbsolutePath(); if ( isValidFile( add, extensions ) ) { - files.addElement( add ); + files.add( add ); } } } //ok... move the Vector into the files list... String[] foundFiles = new String[files.size()]; - files.copyInto( foundFiles ); + files.toArray( foundFiles ); return foundFiles; } @@ -616,11 +655,11 @@ public static String[] getFilesFromExtension( String directory, String[] extensi /** * Private helper method for getFilesFromExtension() */ - private static Vector blendFilesToVector( Vector v, String[] files ) + private static List blendFilesToVector( List v, String[] files ) { for ( int i = 0; i < files.length; ++i ) { - v.addElement( files[i] ); + v.add( files[i] ); } return v; @@ -665,14 +704,11 @@ public static void mkdir( String dir ) { File file = new File( dir ); - if ( Os.isFamily( Os.FAMILY_WINDOWS ) ) + if ( Os.isFamily( Os.FAMILY_WINDOWS ) && !isValidWindowsFileName( file ) ) { - if ( !isValidWindowsFileName( file ) ) - { - throw new IllegalArgumentException( "The file (" + dir - + ") cannot contain any of the following characters: \n" - + StringUtils.join( INVALID_CHARACTERS_FOR_WINDOWS_FILE_NAME, " " ) ); - } + throw new IllegalArgumentException( + "The file (" + dir + ") cannot contain any of the following characters: \n" + StringUtils.join( + INVALID_CHARACTERS_FOR_WINDOWS_FILE_NAME, " " ) ); } if ( !file.exists() ) @@ -788,9 +824,9 @@ public static URL[] toURLs( final File[] files ) */ public static String removeExtension( final String filename ) { - String ext = extension(filename); + String ext = extension( filename ); - if ( "".equals(ext) ) + if ( "".equals( ext ) ) { return filename; } @@ -813,7 +849,7 @@ public static String removeExtension( final String filename ) */ public static String getExtension( final String filename ) { - return extension(filename); + return extension( filename ); } /** @@ -840,7 +876,7 @@ public static String removePath( final String filepath ) * a.txt --> a.txt *

    * - * @param filepath the path of the file + * @param filepath the path of the file * @param fileSeparatorChar the file separator character like / on Unix plateforms. * @return the filename minus path */ @@ -880,7 +916,7 @@ public static String getPath( final String filepath ) * a.txt --> "" * * - * @param filepath the filepath + * @param filepath the filepath * @param fileSeparatorChar the file separator character like / on Unix plateforms. * @return the filename minus path */ @@ -1009,8 +1045,8 @@ public static void copyFile( final File source, final File destination ) //if they are equal, we can exit the method without doing any work return; } - - copyStreamToFile( new FileInputStreamFacade( source ), destination); + mkdirsFor( destination ); + doCopyFile( source, destination ); if ( source.length() != destination.length() ) { @@ -1019,19 +1055,48 @@ public static void copyFile( final File source, final File destination ) } } + private static void doCopyFile( File source, File destination ) + throws IOException + { + FileInputStream fis = null; + FileOutputStream fos = null; + FileChannel input = null; + FileChannel output = null; + try + { + fis = new FileInputStream( source ); + fos = new FileOutputStream( destination ); + input = fis.getChannel(); + output = fos.getChannel(); + long size = input.size(); + long pos = 0; + long count = 0; + while ( pos < size ) + { + count = size - pos > FILE_COPY_BUFFER_SIZE ? FILE_COPY_BUFFER_SIZE : size - pos; + pos += output.transferFrom( input, pos, count ); + } + } + finally + { + IOUtil.close( output ); + IOUtil.close( fos ); + IOUtil.close( input ); + IOUtil.close( fis ); + } + } + /** * Copy file from source to destination only if source timestamp is later than the destination timestamp. * The directories up to destination will be created if they don't already exist. * destination will be overwritten if it already exists. * - * @param source An existing non-directory File to copy bytes from. + * @param source An existing non-directory File to copy bytes from. * @param destination A non-directory File to write bytes to (possibly - * overwriting). + * overwriting). * @return true if no problem occured * @throws IOException if source does not exist, destination cannot be - * written to, or an IO error occurs during copying. - * @throws FileNotFoundException if destination is a directory - * (use {@link #copyFileToDirectory}). + * written to, or an IO error occurs during copying. */ public static boolean copyFileIfModified( final File source, final File destination ) throws IOException @@ -1051,20 +1116,20 @@ public static boolean copyFileIfModified( final File source, final File destinat * The directories up to destination will be created if they don't already exist. * destination will be overwritten if it already exists. * - * @param source A URL to copy bytes from. + * @param source A URL to copy bytes from. * @param destination A non-directory File to write bytes to (possibly - * overwriting). + * overwriting). * @throws IOException if - *
      - *
    • source URL cannot be opened
    • - *
    • destination cannot be written to
    • - *
    • an IO error occurs during copying
    • - *
    + *
      + *
    • source URL cannot be opened
    • + *
    • destination cannot be written to
    • + *
    • an IO error occurs during copying
    • + *
    */ public static void copyURLToFile( final URL source, final File destination ) throws IOException { - copyStreamToFile( new URLInputStreamFacade( source ) , destination); + copyStreamToFile( new URLInputStreamFacade( source ), destination ); } /** @@ -1072,32 +1137,22 @@ public static void copyURLToFile( final URL source, final File destination ) * The directories up to destination will be created if they don't already exist. * destination will be overwritten if it already exists. * - * @param source An {@link InputStream} to copy bytes from. This stream is - * guaranteed to be closed. + * @param source An {@link InputStream} to copy bytes from. This stream is + * guaranteed to be closed. * @param destination A non-directory File to write bytes to (possibly - * overwriting). + * overwriting). * @throws IOException if - *
      - *
    • source URL cannot be opened
    • - *
    • destination cannot be written to
    • - *
    • an IO error occurs during copying
    • - *
    + *
      + *
    • source URL cannot be opened
    • + *
    • destination cannot be written to
    • + *
    • an IO error occurs during copying
    • + *
    */ public static void copyStreamToFile( final InputStreamFacade source, final File destination ) throws IOException { - //does destination directory exist ? - if ( destination.getParentFile() != null && !destination.getParentFile().exists() ) - { - destination.getParentFile().mkdirs(); - } - - //make sure we can write to destination - if ( destination.exists() && !destination.canWrite() ) - { - final String message = "Unable to open file " + destination + " for writing."; - throw new IOException( message ); - } + mkdirsFor( destination ); + checkCanWrite( destination ); InputStream input = null; FileOutputStream output = null; @@ -1114,6 +1169,27 @@ public static void copyStreamToFile( final InputStreamFacade source, final File } } + private static void checkCanWrite( File destination ) + throws IOException + { + //make sure we can write to destination + if ( destination.exists() && !destination.canWrite() ) + { + final String message = "Unable to open file " + destination + " for writing."; + throw new IOException( message ); + } + } + + private static void mkdirsFor( File destination ) + { + //does destination directory exist ? + File parentFile = destination.getParentFile(); + if ( parentFile != null && !parentFile.exists() ) + { + parentFile.mkdirs(); + } + } + /** * Normalize a path. * Eliminates "/../" and "/./" in a string. Returns null if the ..'s went past the @@ -1188,7 +1264,7 @@ public static String normalize( final String path ) * Thieved from Tomcat sources... * * @param lookupPath a path - * @param path the path to concatenate + * @param path the path to concatenate * @return The concatenated paths, or null if error occurs */ public static String catPath( final String lookupPath, final String path ) @@ -1225,7 +1301,7 @@ public static String catPath( final String lookupPath, final String path ) * baseFile, otherwise it is treated as a normal root-relative path. * * @param baseFile Where to resolve filename from, if filename is - * relative. + * relative. * @param filename Absolute or relative file path to resolve. * @return The canonical File of filename. */ @@ -1261,7 +1337,7 @@ public static File resolveFile( final File baseFile, String filename ) // FIXME: I'm almost certain this // removal is unnecessary, as getAbsoluteFile() strips // them. However, I'm not sure about this UNC stuff. (JT) final char[] chars = filename.toCharArray(); - final StringBuffer sb = new StringBuffer(); + final StringBuilder sb = new StringBuilder(); //remove duplicate file separators in succession - except //on win32 at start of filename as UNC filenames can @@ -1406,7 +1482,7 @@ public static void forceDeleteOnExit( final File file ) /** * Recursively schedule directory for deletion on JVM exit. * - * @param file a directory + * @param directory a directory * @throws IOException if any */ private static void deleteDirectoryOnExit( final File directory ) @@ -1416,15 +1492,15 @@ private static void deleteDirectoryOnExit( final File directory ) { return; } + directory.deleteOnExit(); // The hook reverses the list cleanDirectoryOnExit( directory ); - directory.deleteOnExit(); } /** * Clean a directory without deleting it. * - * @param file a directory + * @param directory a directory * @throws IOException if any */ private static void cleanDirectoryOnExit( final File directory ) @@ -1469,8 +1545,8 @@ private static void cleanDirectoryOnExit( final File directory ) * Make a directory. * * @param file not null - * @throws IOException If there already exists a file with specified name or - * the directory is unable to be created + * @throws IOException If there already exists a file with specified name or + * the directory is unable to be created * @throws IllegalArgumentException if the file contains illegal Windows characters under Windows OS. * @see #INVALID_CHARACTERS_FOR_WINDOWS_FILE_NAME */ @@ -1481,9 +1557,9 @@ public static void forceMkdir( final File file ) { if ( !isValidWindowsFileName( file ) ) { - throw new IllegalArgumentException( "The file (" + file.getAbsolutePath() - + ") cannot contain any of the following characters: \n" - + StringUtils.join( INVALID_CHARACTERS_FOR_WINDOWS_FILE_NAME, " " ) ); + throw new IllegalArgumentException( + "The file (" + file.getAbsolutePath() + ") cannot contain any of the following characters: \n" + + StringUtils.join( INVALID_CHARACTERS_FOR_WINDOWS_FILE_NAME, " " ) ); } } @@ -1532,6 +1608,14 @@ public static void deleteDirectory( final File directory ) return; } + /* try delete the directory before its contents, which will take + * care of any directories that are really symbolic links. + */ + if ( directory.delete() ) + { + return; + } + cleanDirectory( directory ); if ( !directory.delete() ) { @@ -1657,13 +1741,13 @@ public static long sizeOfDirectory( final File directory ) * including the directory name in each of the files * * @param directory the directory to scan - * @param includes the includes pattern, comma separated - * @param excludes the excludes pattern, comma separated + * @param includes the includes pattern, comma separated + * @param excludes the excludes pattern, comma separated * @return a list of File objects * @throws IOException - * @see #getFileNames( File, String, String, boolean ) + * @see #getFileNames(File, String, String, boolean) */ - public static List getFiles( File directory, String includes, String excludes ) + public static List getFiles( File directory, String includes, String excludes ) throws IOException { return getFiles( directory, includes, excludes, true ); @@ -1672,24 +1756,24 @@ public static List getFiles( File directory, String includes, String excludes ) /** * Return the files contained in the directory, using inclusion and exclusion Ant patterns * - * @param directory the directory to scan - * @param includes the includes pattern, comma separated - * @param excludes the excludes pattern, comma separated + * @param directory the directory to scan + * @param includes the includes pattern, comma separated + * @param excludes the excludes pattern, comma separated * @param includeBasedir true to include the base dir in each file * @return a list of File objects * @throws IOException - * @see #getFileNames( File, String, String, boolean ) + * @see #getFileNames(File, String, String, boolean) */ - public static List getFiles( File directory, String includes, String excludes, boolean includeBasedir ) + public static List getFiles( File directory, String includes, String excludes, boolean includeBasedir ) throws IOException { - List fileNames = getFileNames( directory, includes, excludes, includeBasedir ); + List fileNames = getFileNames( directory, includes, excludes, includeBasedir ); - List files = new ArrayList(); + List files = new ArrayList(); - for ( Iterator i = fileNames.iterator(); i.hasNext(); ) + for ( String filename : fileNames ) { - files.add( new File( (String) i.next() ) ); + files.add( new File( filename ) ); } return files; @@ -1699,14 +1783,14 @@ public static List getFiles( File directory, String includes, String excludes, b * Return a list of files as String depending options. * This method use case sensitive file name. * - * @param directory the directory to scan - * @param includes the includes pattern, comma separated - * @param excludes the excludes pattern, comma separated + * @param directory the directory to scan + * @param includes the includes pattern, comma separated + * @param excludes the excludes pattern, comma separated * @param includeBasedir true to include the base dir in each String of file * @return a list of files as String * @throws IOException */ - public static List getFileNames( File directory, String includes, String excludes, boolean includeBasedir ) + public static List getFileNames( File directory, String includes, String excludes, boolean includeBasedir ) throws IOException { return getFileNames( directory, includes, excludes, includeBasedir, true ); @@ -1715,16 +1799,16 @@ public static List getFileNames( File directory, String includes, String exclude /** * Return a list of files as String depending options. * - * @param directory the directory to scan - * @param includes the includes pattern, comma separated - * @param excludes the excludes pattern, comma separated + * @param directory the directory to scan + * @param includes the includes pattern, comma separated + * @param excludes the excludes pattern, comma separated * @param includeBasedir true to include the base dir in each String of file * @param isCaseSensitive true if case sensitive * @return a list of files as String * @throws IOException */ - public static List getFileNames( File directory, String includes, String excludes, boolean includeBasedir, - boolean isCaseSensitive ) + public static List getFileNames( File directory, String includes, String excludes, boolean includeBasedir, + boolean isCaseSensitive ) throws IOException { return getFileAndDirectoryNames( directory, includes, excludes, includeBasedir, isCaseSensitive, true, false ); @@ -1734,14 +1818,15 @@ public static List getFileNames( File directory, String includes, String exclude * Return a list of directories as String depending options. * This method use case sensitive file name. * - * @param directory the directory to scan - * @param includes the includes pattern, comma separated - * @param excludes the excludes pattern, comma separated + * @param directory the directory to scan + * @param includes the includes pattern, comma separated + * @param excludes the excludes pattern, comma separated * @param includeBasedir true to include the base dir in each String of file * @return a list of directories as String * @throws IOException */ - public static List getDirectoryNames( File directory, String includes, String excludes, boolean includeBasedir ) + public static List getDirectoryNames( File directory, String includes, String excludes, + boolean includeBasedir ) throws IOException { return getDirectoryNames( directory, includes, excludes, includeBasedir, true ); @@ -1750,16 +1835,16 @@ public static List getDirectoryNames( File directory, String includes, String ex /** * Return a list of directories as String depending options. * - * @param directory the directory to scan - * @param includes the includes pattern, comma separated - * @param excludes the excludes pattern, comma separated + * @param directory the directory to scan + * @param includes the includes pattern, comma separated + * @param excludes the excludes pattern, comma separated * @param includeBasedir true to include the base dir in each String of file * @param isCaseSensitive true if case sensitive * @return a list of directories as String * @throws IOException */ - public static List getDirectoryNames( File directory, String includes, String excludes, boolean includeBasedir, - boolean isCaseSensitive ) + public static List getDirectoryNames( File directory, String includes, String excludes, + boolean includeBasedir, boolean isCaseSensitive ) throws IOException { return getFileAndDirectoryNames( directory, includes, excludes, includeBasedir, isCaseSensitive, false, true ); @@ -1768,19 +1853,19 @@ public static List getDirectoryNames( File directory, String includes, String ex /** * Return a list of files as String depending options. * - * @param directory the directory to scan - * @param includes the includes pattern, comma separated - * @param excludes the excludes pattern, comma separated + * @param directory the directory to scan + * @param includes the includes pattern, comma separated + * @param excludes the excludes pattern, comma separated * @param includeBasedir true to include the base dir in each String of file * @param isCaseSensitive true if case sensitive - * @param getFiles true if get files + * @param getFiles true if get files * @param getDirectories true if get directories * @return a list of files as String * @throws IOException */ - public static List getFileAndDirectoryNames( File directory, String includes, String excludes, - boolean includeBasedir, boolean isCaseSensitive, boolean getFiles, - boolean getDirectories ) + public static List getFileAndDirectoryNames( File directory, String includes, String excludes, + boolean includeBasedir, boolean isCaseSensitive, + boolean getFiles, boolean getDirectories ) throws IOException { DirectoryScanner scanner = new DirectoryScanner(); @@ -1801,7 +1886,7 @@ public static List getFileAndDirectoryNames( File directory, String includes, St scanner.scan(); - List list = new ArrayList(); + List list = new ArrayList(); if ( getFiles ) { @@ -1843,7 +1928,7 @@ public static List getFileAndDirectoryNames( File directory, String includes, St /** * Copy a directory to an other one. * - * @param sourceDirectory the source dir + * @param sourceDirectory the source dir * @param destinationDirectory the target dir * @throws IOException if any */ @@ -1856,10 +1941,10 @@ public static void copyDirectory( File sourceDirectory, File destinationDirector /** * Copy a directory to an other one. * - * @param sourceDirectory the source dir + * @param sourceDirectory the source dir * @param destinationDirectory the target dir - * @param includes include pattern - * @param excludes exlucde pattern + * @param includes include pattern + * @param excludes exlucde pattern * @throws IOException if any * @see #getFiles(File, String, String) */ @@ -1872,12 +1957,10 @@ public static void copyDirectory( File sourceDirectory, File destinationDirector return; } - List files = getFiles( sourceDirectory, includes, excludes ); + List files = getFiles( sourceDirectory, includes, excludes ); - for ( Iterator i = files.iterator(); i.hasNext(); ) + for ( File file : files ) { - File file = (File) i.next(); - copyFileToDirectory( file, destinationDirectory ); } } @@ -1891,15 +1974,15 @@ public static void copyDirectory( File sourceDirectory, File destinationDirector *
  • The sourceDirectory must exists. * * - * @param sourceDirectory the source dir + * @param sourceDirectory the source dir * @param destinationDirectory the target dir - * @param includes include pattern - * @param excludes exlucde pattern - * @since 1.5.7 + * @param includes include pattern + * @param excludes exlucde pattern * @throws IOException if any + * @since 1.5.7 */ public static void copyDirectoryLayout( File sourceDirectory, File destinationDirectory, String[] includes, - String[] excludes ) + String[] excludes ) throws IOException { if ( sourceDirectory == null ) @@ -1932,7 +2015,7 @@ public static void copyDirectoryLayout( File sourceDirectory, File destinationDi } else { - scanner.setIncludes( new String[] { "**" } ); + scanner.setIncludes( new String[]{ "**" } ); } if ( excludes != null && excludes.length >= 1 ) @@ -1942,20 +2025,18 @@ public static void copyDirectoryLayout( File sourceDirectory, File destinationDi scanner.addDefaultExcludes(); scanner.scan(); - List includedDirectories = Arrays.asList( scanner.getIncludedDirectories() ); + List includedDirectories = Arrays.asList( scanner.getIncludedDirectories() ); - for (Iterator i = includedDirectories.iterator();i.hasNext();) + for ( String name : includedDirectories ) { - String name = (String)i.next(); - - File source = new File(sourceDirectory, name); + File source = new File( sourceDirectory, name ); if ( source.equals( sourceDirectory ) ) { continue; } - File destination = new File(destinationDirectory, name); + File destination = new File( destinationDirectory, name ); destination.mkdirs(); } } @@ -1969,7 +2050,7 @@ public static void copyDirectoryLayout( File sourceDirectory, File destinationDi *
  • The sourceDirectory must exists. * * - * @param sourceDirectory the source dir + * @param sourceDirectory the source dir * @param destinationDirectory the target dir * @throws IOException if any */ @@ -1988,7 +2069,7 @@ public static void copyDirectoryStructure( File sourceDirectory, File destinatio *
  • The sourceDirectory must exists. * * - * @param sourceDirectory the source dir + * @param sourceDirectory the source dir * @param destinationDirectory the target dir * @throws IOException if any */ @@ -2083,7 +2164,7 @@ else if ( file.isDirectory() ) * @param from the file to move * @param to the new file name * @throws IOException if anything bad happens during this process. - * Note that to may have been deleted already when this happens. + * Note that to may have been deleted already when this happens. */ public static void rename( File from, File to ) throws IOException @@ -2126,10 +2207,10 @@ public static void rename( File from, File to ) *

    To delete automatically the file created by this method, use the * {@link File#deleteOnExit()} method.

    * - * @param prefix prefix before the random number - * @param suffix file extension; include the '.' + * @param prefix prefix before the random number + * @param suffix file extension; include the '.' * @param parentDir Directory to create the temporary file in -java.io.tmpdir - * used if not specificed + * used if not specificed * @return a File reference to the new temporary file. */ public static File createTempFile( String prefix, String suffix, File parentDir ) @@ -2158,8 +2239,9 @@ public static File createTempFile( String prefix, String suffix, File parentDir /** * If wrappers is null or empty, the file will be copy only if to.lastModified() < from.lastModified() - * @param from the file to copy - * @param to the destination file + * + * @param from the file to copy + * @param to the destination file * @param encoding the file output encoding (only if wrappers is not empty) * @param wrappers array of {@link FilterWrapper} * @throws IOException if an IO error occurs during copying or filtering @@ -2177,12 +2259,13 @@ public static abstract class FilterWrapper /** * If wrappers is null or empty, the file will be copy only if to.lastModified() < from.lastModified() or if overwrite is true - * @param from the file to copy - * @param to the destination file - * @param encoding the file output encoding (only if wrappers is not empty) - * @param wrappers array of {@link FilterWrapper} + * + * @param from the file to copy + * @param to the destination file + * @param encoding the file output encoding (only if wrappers is not empty) + * @param wrappers array of {@link FilterWrapper} * @param overwrite if true and f wrappers is null or empty, the file will be copy - * enven if to.lastModified() < from.lastModified() + * enven if to.lastModified() < from.lastModified() * @throws IOException if an IO error occurs during copying or filtering * @since 1.5.2 */ @@ -2243,10 +2326,10 @@ public static void copyFile( File from, File to, String encoding, FilterWrapper[ * @return a List containing every every line not starting with # and not empty * @throws IOException if any */ - public static List loadFile( File file ) + public static List loadFile( File file ) throws IOException { - List lines = new ArrayList(); + List lines = new ArrayList(); if ( file.exists() ) { @@ -2277,7 +2360,7 @@ public static List loadFile( File file ) * * @param f not null file * @return false if the file path contains any of forbidden Windows characters, - * true if the Os is not Windows or if the file path respect the Windows constraints. + * true if the Os is not Windows or if the file path respect the Windows constraints. * @see #INVALID_CHARACTERS_FOR_WINDOWS_FILE_NAME * @since 1.5.2 */ @@ -2290,9 +2373,10 @@ public static boolean isValidWindowsFileName( File f ) return false; } - if ( f.getParentFile()!= null) + File parentFile = f.getParentFile(); + if ( parentFile != null ) { - return isValidWindowsFileName( f.getParentFile() ); + return isValidWindowsFileName( parentFile ); } } diff --git a/src/main/java/org/codehaus/plexus/util/IOUtil.java b/src/main/java/org/codehaus/plexus/util/IOUtil.java index 20cab90d..4045a7f4 100644 --- a/src/main/java/org/codehaus/plexus/util/IOUtil.java +++ b/src/main/java/org/codehaus/plexus/util/IOUtil.java @@ -67,6 +67,7 @@ import java.io.StringReader; import java.io.StringWriter; import java.io.Writer; +import java.nio.channels.Channel; /** * General IO Stream manipulation. @@ -152,7 +153,7 @@ public final class IOUtil { - private static final int DEFAULT_BUFFER_SIZE = 1024 * 4; + private static final int DEFAULT_BUFFER_SIZE = 1024 * 16; /** * Private constructor to prevent instantiation. @@ -745,6 +746,28 @@ public static void close( InputStream inputStream ) } } + /** + * Closes a channel. Channel can be null and any IOException's will be swallowed. + * + * @param channel The stream to close. + */ + public static void close( Channel channel ) + { + if ( channel == null ) + { + return; + } + + try + { + channel.close(); + } + catch( IOException ex ) + { + // ignore + } + } + /** * Closes the output stream. The output stream can be null and any IOException's will be swallowed. * diff --git a/src/main/java/org/codehaus/plexus/util/InterpolationFilterReader.java b/src/main/java/org/codehaus/plexus/util/InterpolationFilterReader.java index 8eec7e44..d765c0d5 100644 --- a/src/main/java/org/codehaus/plexus/util/InterpolationFilterReader.java +++ b/src/main/java/org/codehaus/plexus/util/InterpolationFilterReader.java @@ -61,6 +61,25 @@ import java.util.Map; /** + * A FilterReader which interpolates keyword values into a character stream. + * Keywords are recognized when enclosed between starting and ending delimiter + * strings. The keywords themselves, and their values, are fetched from a Map + * supplied to the constructor. + *

    + * When a possible keyword token is recognized (by detecting the starting and + * ending token delimiters): + *

    + *
      + *
    • if the enclosed string is found in the keyword Map, the delimiters and + * the keyword are effectively replaced by the keyword's value;
    • + *
    • if the enclosed string is found in the keyword Map, but its value has + * zero length, then the token (delimiters and keyword) is effectively removed + * from the character stream;
    • + *
    • if the enclosed string is not found in the keyword Map, then + * no substitution is made; the token text is passed through unaltered.
    • + *
    + * @see LineOrientedInterpolatingReader + * @see org.codehaus.plexus.interpolation */ public class InterpolationFilterReader extends FilterReader @@ -90,11 +109,20 @@ public class InterpolationFilterReader private int endTokenLength; /** Default begin token. */ - private static String DEFAULT_BEGIN_TOKEN = "${"; + private static final String DEFAULT_BEGIN_TOKEN = "${"; /** Default end token. */ - private static String DEFAULT_END_TOKEN = "}"; + private static final String DEFAULT_END_TOKEN = "}"; + /** + * Construct a Reader to interpolate values enclosed between the given + * delimiter tokens. + * + * @param in a Reader to be wrapped for interpolation. + * @param variables name/value pairs to be interpolated into the character stream. + * @param beginToken an interpolation target begins with this. + * @param endToken an interpolation target ends with this. + */ public InterpolationFilterReader( Reader in, Map variables, String beginToken, String endToken ) { super( in ); @@ -107,6 +135,13 @@ public InterpolationFilterReader( Reader in, Map variables, String beginToken, S endTokenLength = endToken.length(); } + /** + * Construct a Reader using the default interpolation delimiter tokens + * "${" and "}". + * + * @param in a Reader to be wrapped for interpolation. + * @param variables name/value pairs to be interpolated into the character stream. + */ public InterpolationFilterReader( Reader in, Map variables ) { this( in, variables, DEFAULT_BEGIN_TOKEN, DEFAULT_END_TOKEN ); @@ -201,7 +236,7 @@ public int read() throws IOException return ch; } - int ch = -1; + int ch; if ( previousIndex != -1 && previousIndex < endTokenLength ) { ch = endToken.charAt( previousIndex++ ); @@ -213,7 +248,7 @@ public int read() throws IOException if ( ch == beginToken.charAt( 0 ) ) { - StringBuffer key = new StringBuffer(); + StringBuilder key = new StringBuilder(); int beginTokenMatchPos = 1; diff --git a/src/main/java/org/codehaus/plexus/util/Java7Detector.java b/src/main/java/org/codehaus/plexus/util/Java7Detector.java new file mode 100644 index 00000000..64f71e4a --- /dev/null +++ b/src/main/java/org/codehaus/plexus/util/Java7Detector.java @@ -0,0 +1,48 @@ +package org.codehaus.plexus.util; + +/* + * Copyright 2011 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. + */ + +/** + * Java7 feature detection + * + * @author Kristian Rosenvold + */ +class Java7Detector +{ + + private static final boolean isJava7; + + static + { + boolean isJava7x = true; + try + { + Class.forName( "java.nio.file.Files" ); + } + catch ( Exception e ) + { + isJava7x = false; + } + isJava7 = isJava7x; + } + + + public static boolean isJava7() + { + return isJava7; + } +} diff --git a/src/main/java/org/codehaus/plexus/util/Java7FileUtil.java b/src/main/java/org/codehaus/plexus/util/Java7FileUtil.java new file mode 100644 index 00000000..df4d9ec3 --- /dev/null +++ b/src/main/java/org/codehaus/plexus/util/Java7FileUtil.java @@ -0,0 +1,32 @@ +package org.codehaus.plexus.util; + +/* + * Copyright 2007 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.nio.file.Files; + +/** + * Encapsulates use of java7 features + */ +public class Java7FileUtil +{ + public static boolean isSymLink( File file ) + { + return Files.isSymbolicLink( file.toPath() ); + } + +} \ No newline at end of file diff --git a/src/main/java/org/codehaus/plexus/util/LineOrientedInterpolatingReader.java b/src/main/java/org/codehaus/plexus/util/LineOrientedInterpolatingReader.java index 47a5ba2f..29b81acc 100644 --- a/src/main/java/org/codehaus/plexus/util/LineOrientedInterpolatingReader.java +++ b/src/main/java/org/codehaus/plexus/util/LineOrientedInterpolatingReader.java @@ -25,13 +25,40 @@ import java.io.Reader; import java.util.Collections; import java.util.HashSet; -import java.util.Iterator; import java.util.Map; import java.util.Set; import java.util.TreeMap; /** + * A FilterReader which interpolates keyword values into a character stream. + * Keywords are recognized when enclosed between starting and ending delimiter + * strings. The keywords themselves, and their values, are fetched from a Map + * supplied to the constructor. + *

    + * When a possible keyword token is recognized (by detecting the starting and + * ending token delimiters): + *

    + *
      + *
    • if the enclosed string is found in the keyword Map, the delimiters and + * the keyword are effectively replaced by the keyword's value;
    • + *
    • if the enclosed string is found in the keyword Map, but its value has + * zero length, then the token (delimiters and keyword) is effectively removed + * from the character stream;
    • + *
    • if the enclosed string is not found in the keyword Map, then + * no substitution is made; the token text is passed through unaltered.
    • + *
    + *

    + * A token in the incoming character stream may be escaped by + * prepending an "escape sequence" which is specified to the constructor. An + * escaped token is passed through as written, with the escape sequence removed. + * This allows things which would look like tokens to be read literally rather + * than interpolated. + *

    + * * @author jdcasey Created on Feb 3, 2005 + * + * @see InterpolationFilterReader + * @see org.codehaus.plexus.interpolation */ public class LineOrientedInterpolatingReader extends FilterReader @@ -48,7 +75,7 @@ public class LineOrientedInterpolatingReader private final PushbackReader pushbackReader; - private final Map context; + private final Map context; private final String startDelim; @@ -64,7 +91,17 @@ public class LineOrientedInterpolatingReader private String line; - public LineOrientedInterpolatingReader( Reader reader, Map context, String startDelim, String endDelim, + /** + * Construct an interpolating Reader, specifying token delimiters and the + * escape sequence. + * + * @param reader the Reader to be filtered. + * @param context keyword/value pairs for interpolation. + * @param startDelim character sequence which (possibly) begins a token. + * @param endDelim character sequence which ends a token. + * @param escapeSeq + */ + public LineOrientedInterpolatingReader( Reader reader, Map context, String startDelim, String endDelim, String escapeSeq ) { super( reader ); @@ -92,12 +129,27 @@ public LineOrientedInterpolatingReader( Reader reader, Map context, String start } } - public LineOrientedInterpolatingReader( Reader reader, Map context, String startDelim, String endDelim ) + /** + * Filters a Reader using the default escape sequence "\". + * + * @param reader the Reader to be filtered. + * @param context keyword/value pairs for interpolation. + * @param startDelim the character sequence which (possibly) begins a token. + * @param endDelim the character sequence which ends a token. + */ + public LineOrientedInterpolatingReader( Reader reader, Map context, String startDelim, String endDelim ) { this( reader, context, startDelim, endDelim, DEFAULT_ESCAPE_SEQ ); } - public LineOrientedInterpolatingReader( Reader reader, Map context ) + /** + * Filters a Reader using the default escape sequence "\" and token + * delimiters "${", "}". + * + * @param reader the Reader to be filtered. + * @param context keyword/value pairs for interpolation. + */ + public LineOrientedInterpolatingReader( Reader reader, Map context ) { this( reader, context, DEFAULT_START_DELIM, DEFAULT_END_DELIM, DEFAULT_ESCAPE_SEQ ); } @@ -190,10 +242,15 @@ private void readAndInterpolateLine() throws IOException } } + /* + * Read one line from the wrapped Reader. A line is a sequence of characters + * ending in CRLF, CR, or LF. The terminating character(s) will be included + * in the returned line. + */ private String readLine() throws IOException { - StringBuffer lineBuffer = new StringBuffer( 40 ); // half of the "normal" line maxsize - int next = -1; + StringBuilder lineBuffer = new StringBuilder( 40 ); // half of the "normal" line maxsize + int next; boolean lastWasCR = false; while ( ( next = pushbackReader.read() ) > -1 ) @@ -235,9 +292,9 @@ private String replaceWithInterpolatedValues( String rawLine, Map evaluatedExpre { String result = rawLine; - for ( Iterator it = evaluatedExpressions.entrySet().iterator(); it.hasNext(); ) + for ( Object o : evaluatedExpressions.entrySet() ) { - Map.Entry entry = (Map.Entry) it.next(); + Map.Entry entry = (Map.Entry) o; String expression = (String) entry.getKey(); @@ -251,14 +308,14 @@ private String replaceWithInterpolatedValues( String rawLine, Map evaluatedExpre private Map evaluateExpressions( Set expressions ) { - Map evaluated = new TreeMap(); + Map evaluated = new TreeMap(); - for ( Iterator it = expressions.iterator(); it.hasNext(); ) + for ( Object expression : expressions ) { - String rawExpression = (String) it.next(); + String rawExpression = (String) expression; - String realExpression = rawExpression.substring( startDelim.length(), rawExpression.length() - - endDelim.length() ); + String realExpression = + rawExpression.substring( startDelim.length(), rawExpression.length() - endDelim.length() ); String[] parts = realExpression.split( "\\." ); if ( parts.length > 0 ) @@ -297,7 +354,7 @@ private Map evaluateExpressions( Set expressions ) private Set parseForExpressions( String rawLine ) { - Set expressions = new HashSet(); + Set expressions = new HashSet(); if ( rawLine != null ) { @@ -342,7 +399,7 @@ private int findDelimiter( String rawLine, String delimiter, int lastPos ) { int placeholder = lastPos; - int position = -1; + int position; do { position = rawLine.indexOf( delimiter, placeholder ); @@ -371,7 +428,7 @@ private int findDelimiter( String rawLine, String delimiter, int lastPos ) private String findAndReplaceUnlessEscaped(String rawLine, String search, String replace) { - StringBuffer lineBuffer = new StringBuffer( (int)(rawLine.length() * 1.5) ); + StringBuilder lineBuffer = new StringBuilder( (int) ( rawLine.length() * 1.5 ) ); int lastReplacement = -1; @@ -385,7 +442,7 @@ private String findAndReplaceUnlessEscaped(String rawLine, String search, String lastReplacement = 0; } - lineBuffer.append( rawLine.substring( lastReplacement, nextReplacement ) ); + lineBuffer.append( rawLine, lastReplacement, nextReplacement ); int escIdx = rawLine.indexOf( escapeSeq, lastReplacement + 1 ); if(escIdx > -1 && escIdx + escapeSeq.length() == nextReplacement) @@ -409,7 +466,7 @@ private String findAndReplaceUnlessEscaped(String rawLine, String search, String if( lastReplacement < rawLine.length() ) { - lineBuffer.append( rawLine.substring( lastReplacement ) ); + lineBuffer.append( rawLine, lastReplacement, rawLine.length() ); } return lineBuffer.toString(); diff --git a/src/main/java/org/codehaus/plexus/util/MatchPattern.java b/src/main/java/org/codehaus/plexus/util/MatchPattern.java new file mode 100644 index 00000000..00f4d959 --- /dev/null +++ b/src/main/java/org/codehaus/plexus/util/MatchPattern.java @@ -0,0 +1,135 @@ +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.List; +import java.util.StringTokenizer; + +/** + * Describes a match target for SelectorUtils. + *

    + * Significantly more efficient than using strings, since re-evaluation and re-tokenizing is avoided. + * + * @author Kristian Rosenvold + */ +public class MatchPattern +{ + private final String source; + + private final String regexPattern; + + private final String separator; + + private final String[] tokenized; + private final char[][] tokenizedChar; + + private MatchPattern( String source, String separator ) + { + regexPattern = SelectorUtils.isRegexPrefixedPattern( source ) ? source.substring( + SelectorUtils.REGEX_HANDLER_PREFIX.length(), + source.length() - SelectorUtils.PATTERN_HANDLER_SUFFIX.length() ) : null; + this.source = + SelectorUtils.isAntPrefixedPattern( source ) + ? source.substring( SelectorUtils.ANT_HANDLER_PREFIX.length(), source.length() + - SelectorUtils.PATTERN_HANDLER_SUFFIX.length() ) + : source; + this.separator = separator; + tokenized = tokenizePathToString( this.source, separator ); + tokenizedChar = new char[tokenized.length][]; + for (int i = 0; i < tokenized.length; i++){ + tokenizedChar[i] = tokenized[i].toCharArray(); + } + + } + + + + public boolean matchPath( String str, boolean isCaseSensitive ) + { + if ( regexPattern != null ) + { + return str.matches( regexPattern ); + } + else + { + return SelectorUtils.matchAntPathPattern( this, str, separator, isCaseSensitive ); + } + } + + boolean matchPath( String str, char[][] strDirs, boolean isCaseSensitive ) + { + if ( regexPattern != null ) + { + return str.matches( regexPattern ); + } + else + { + return SelectorUtils.matchAntPathPattern( getTokenizedPathChars(), strDirs, isCaseSensitive ); + } + } + + public boolean matchPatternStart( String str, boolean isCaseSensitive ) + { + if ( regexPattern != null ) + { + // FIXME: ICK! But we can't do partial matches for regex, so we have to reserve judgement until we have + // a file to deal with, or we can definitely say this is an exclusion... + return true; + } + else + { + String altStr = source.replace( '\\', '/' ); + + return SelectorUtils.matchAntPathPatternStart( this, str, File.separator, isCaseSensitive ) + || SelectorUtils.matchAntPathPatternStart( this, altStr, "/", isCaseSensitive ); + } + } + + public String[] getTokenizedPathString() + { + return tokenized; + } + + public char[][] getTokenizedPathChars() + { + return tokenizedChar; + } + + public boolean startsWith( String string ) + { + return source.startsWith( string ); + } + + + static String[] tokenizePathToString( String path, String separator ) + { + List ret = new ArrayList(); + StringTokenizer st = new StringTokenizer( path, separator ); + while ( st.hasMoreTokens() ) + { + ret.add( st.nextToken() ); + } + return ret.toArray( new String[ret.size()] ); + } + + public static MatchPattern fromString( String source ) + { + return new MatchPattern( source, File.separator ); + } + +} diff --git a/src/main/java/org/codehaus/plexus/util/MatchPatterns.java b/src/main/java/org/codehaus/plexus/util/MatchPatterns.java new file mode 100644 index 00000000..bffb3ad7 --- /dev/null +++ b/src/main/java/org/codehaus/plexus/util/MatchPatterns.java @@ -0,0 +1,90 @@ +package org.codehaus.plexus.util; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +/** + * A list of patterns to be matched + * + * @author Kristian Rosenvold + */ +public class MatchPatterns +{ + private final MatchPattern[] patterns; + + private MatchPatterns( MatchPattern[] patterns ) + { + this.patterns = patterns; + } + + /** + * Checks these MatchPatterns against a specified string. + *

    + * Uses far less string tokenization than any of the alternatives. + * + * @param name The name to look for + * @param isCaseSensitive If the comparison is case sensitive + * @return true if any of the supplied patterns match + */ + public boolean matches( String name, boolean isCaseSensitive ) + { + String[] tokenized = MatchPattern.tokenizePathToString( name, File.separator ); + return matches( name, tokenized, isCaseSensitive ); + } + + public boolean matches( String name, String[] tokenizedName, boolean isCaseSensitive ) + { + char[][] tokenizedNameChar = new char[tokenizedName.length][]; + for(int i = 0; i < tokenizedName.length; i++){ + tokenizedNameChar[i] = tokenizedName[i].toCharArray(); + } + for ( MatchPattern pattern : patterns ) + { + if ( pattern.matchPath( name, tokenizedNameChar, isCaseSensitive ) ) + { + return true; + } + } + return false; + } + + public boolean matchesPatternStart( String name, boolean isCaseSensitive ) + { + for ( MatchPattern includesPattern : patterns ) + { + if ( includesPattern.matchPatternStart( name, isCaseSensitive ) ) + { + return true; + } + } + return false; + } + + public static MatchPatterns from( String... sources ) + { + final int length = sources.length; + MatchPattern[] result = new MatchPattern[length]; + for ( int i = 0; i < length; i++ ) + { + result[i] = MatchPattern.fromString( sources[i] ); + } + return new MatchPatterns( result ); + } + + public static MatchPatterns from( Iterable strings ) + { + return new MatchPatterns( getMatchPatterns( strings ) ); + } + + private static MatchPattern[] getMatchPatterns( Iterable items ) + { + List result = new ArrayList(); + for ( String string : items ) + { + result.add( MatchPattern.fromString( string ) ); + } + return result.toArray( new MatchPattern[result.size()] ); + } + +} diff --git a/src/main/java/org/codehaus/plexus/util/Os.java b/src/main/java/org/codehaus/plexus/util/Os.java index c8fc6b42..2f3af267 100644 --- a/src/main/java/org/codehaus/plexus/util/Os.java +++ b/src/main/java/org/codehaus/plexus/util/Os.java @@ -55,7 +55,6 @@ package org.codehaus.plexus.util; import java.util.HashSet; -import java.util.Iterator; import java.util.Locale; import java.util.Set; @@ -94,7 +93,7 @@ public class Os public static final String FAMILY_OPENVMS = "openvms"; // store the valid families - private static final Set validFamilies = setValidFamilies(); + private static final Set validFamilies = setValidFamilies(); // get the current info private static final String PATH_SEP = System.getProperty( "path.separator" ); @@ -136,9 +135,9 @@ public Os( String family ) /** * Initializes the set of valid families. */ - private static Set setValidFamilies() + private static Set setValidFamilies() { - Set valid = new HashSet(); + Set valid = new HashSet(); valid.add( FAMILY_DOS ); valid.add( FAMILY_MAC ); valid.add( FAMILY_NETWARE ); @@ -381,7 +380,7 @@ private static String getOsFamily() // in case the order of static initialization is // wrong, get the list // safely. - Set families = null; + Set families = null; if ( !validFamilies.isEmpty() ) { families = validFamilies; @@ -390,10 +389,8 @@ private static String getOsFamily() { families = setValidFamilies(); } - Iterator iter = families.iterator(); - while ( iter.hasNext() ) + for ( String fam : families ) { - String fam = (String) iter.next(); if ( Os.isFamily( fam ) ) { return fam; @@ -432,8 +429,8 @@ public static boolean isValidFamily( String theFamily ) * @return a copy of the valid families * @since 1.4.2 */ - public static Set getValidFamilies() + public static Set getValidFamilies() { - return new HashSet( validFamilies ); + return new HashSet( validFamilies ); } } diff --git a/src/main/java/org/codehaus/plexus/util/PathTool.java b/src/main/java/org/codehaus/plexus/util/PathTool.java index de52db8d..76301862 100644 --- a/src/main/java/org/codehaus/plexus/util/PathTool.java +++ b/src/main/java/org/codehaus/plexus/util/PathTool.java @@ -51,26 +51,26 @@ public class PathTool * PathTool.getRelativePath( "/usr/local/java/bin/java.sh", "/usr/local/" ) = "" * * - * @param basedir The base directory. + * @param basedir The base directory. * @param filename The filename that is relative to the base - * directory. + * directory. * @return The relative path of the filename from the base - * directory. This value is not terminated with a forward slash. - * A zero-length string is returned if: the filename is not relative to - * the base directory, basedir is null or zero-length, - * or filename is null or zero-length. + * directory. This value is not terminated with a forward slash. + * A zero-length string is returned if: the filename is not relative to + * the base directory, basedir is null or zero-length, + * or filename is null or zero-length. */ public static final String getRelativePath( String basedir, String filename ) { - basedir = uppercaseDrive(basedir); - filename = uppercaseDrive(filename); + basedir = uppercaseDrive( basedir ); + filename = uppercaseDrive( filename ); /* * Verify the arguments and make sure the filename is relative * to the base directory. */ - if ( basedir == null || basedir.length() == 0 || filename == null - || filename.length() == 0 || !filename.startsWith( basedir ) ) + if ( basedir == null || basedir.length() == 0 || filename == null || filename.length() == 0 + || !filename.startsWith( basedir ) ) { return ""; } @@ -108,13 +108,13 @@ public static final String getRelativePath( String basedir, String filename ) * * @param filename The filename to be parsed. * @return The relative path of the filename. This value is not - * terminated with a forward slash. A zero-length string is - * returned if: filename is null or zero-length. + * terminated with a forward slash. A zero-length string is + * returned if: filename is null or zero-length. * @see #getRelativeFilePath(String, String) */ public static final String getRelativePath( String filename ) { - filename = uppercaseDrive(filename); + filename = uppercaseDrive( filename ); if ( filename == null || filename.length() == 0 ) { @@ -155,8 +155,8 @@ public static final String getRelativePath( String filename ) * * @param filename The filename to be parsed. * @return The directory portion of the filename. If - * the filename does not contain a directory component, "." is - * returned. + * the filename does not contain a directory component, "." is + * returned. */ public static final String getDirectoryComponent( String filename ) { @@ -191,44 +191,52 @@ public static final String getDirectoryComponent( String filename ) * @param relativePath * @return String */ - public static final String calculateLink(String link, String relativePath) + public static final String calculateLink( String link, String relativePath ) { + if ( link == null ) + { + link = ""; + } + if ( relativePath == null ) + { + relativePath = ""; + } //This must be some historical feature - if (link.startsWith("/site/")) + if ( link.startsWith( "/site/" ) ) { - return link.substring(5); + return link.substring( 5 ); } //Allows absolute links in nav-bars etc - if (link.startsWith("/absolute/")) + if ( link.startsWith( "/absolute/" ) ) { - return link.substring(10); + return link.substring( 10 ); } // This traps urls like http:// - if (link.indexOf(":") >= 0) + if ( link.indexOf( ":" ) >= 0 ) { return link; } //If relativepath is current directory, just pass the link through - if (relativePath.equals(".")) + if ( StringUtils.equals( relativePath, "." ) ) { - if (link.startsWith("/")) + if ( link.startsWith( "/" ) ) { - return link.substring(1); + return link.substring( 1 ); } return link; } //If we don't do this, you can end up with ..//bob.html rather than ../bob.html - if (relativePath.endsWith("/") && link.startsWith("/")) + if ( relativePath.endsWith( "/" ) && link.startsWith( "/" ) ) { - return relativePath + "." + link.substring(1); + return relativePath + "." + link.substring( 1 ); } - if (relativePath.endsWith("/") || link.startsWith("/")) + if ( relativePath.endsWith( "/" ) || link.startsWith( "/" ) ) { return relativePath + link; } @@ -324,16 +332,16 @@ public static final String getRelativeFilePath( final String oldPath, final Stri // check for the presence of windows drives. No relative way of // traversing from one to the other. - if ( ( toPath.startsWith( ":", 1 ) && fromPath.startsWith( ":", 1 ) ) - && ( !toPath.substring( 0, 1 ).equals( fromPath.substring( 0, 1 ) ) ) ) + if ( ( toPath.startsWith( ":", 1 ) && fromPath.startsWith( ":", 1 ) ) && ( !toPath.substring( 0, 1 ).equals( + fromPath.substring( 0, 1 ) ) ) ) { // they both have drive path element but they dont match, no // relative path return null; } - if ( ( toPath.startsWith( ":", 1 ) && !fromPath.startsWith( ":", 1 ) ) - || ( !toPath.startsWith( ":", 1 ) && fromPath.startsWith( ":", 1 ) ) ) + if ( ( toPath.startsWith( ":", 1 ) && !fromPath.startsWith( ":", 1 ) ) || ( !toPath.startsWith( ":", 1 ) + && fromPath.startsWith( ":", 1 ) ) ) { // one has a drive path element and the other doesnt, no relative // path. @@ -359,26 +367,24 @@ public static final String getRelativeFilePath( final String oldPath, final Stri * within the filename (except the leading if present), append the * "../" string to the return value. * - * @param filename The filename to parse. + * @param filename The filename to parse. * @param separator The separator used within the filename. * @return The relative path of the filename. This value is not - * terminated with a forward slash. A zero-length string is - * returned if: the filename is zero-length. + * terminated with a forward slash. A zero-length string is + * returned if: the filename is zero-length. */ - private static final String determineRelativePath( String filename, - String separator ) + private static final String determineRelativePath( String filename, String separator ) { if ( filename.length() == 0 ) { return ""; } - /* - * Count the slashes in the relative filename, but exclude the - * leading slash. If the path has no slashes, then the filename - * is relative to the current directory. - */ + * Count the slashes in the relative filename, but exclude the + * leading slash. If the path has no slashes, then the filename + * is relative to the current directory. + */ int slashCount = StringUtils.countMatches( filename, separator ) - 1; if ( slashCount <= 0 ) { @@ -390,7 +396,7 @@ private static final String determineRelativePath( String filename, * that the file is within one or more directories. Thus, each * slash represents a "../" in the relative path. */ - StringBuffer sb = new StringBuffer(); + StringBuilder sb = new StringBuilder(); for ( int i = 0; i < slashCount; i++ ) { sb.append( "../" ); @@ -409,9 +415,9 @@ private static final String determineRelativePath( String filename, * often is returned as the separator. * * @param filename The filename parsed to determine the file - * separator. + * separator. * @return The file separator used within filename. - * This value is either a forward or backward slash. + * This value is either a forward or backward slash. */ private static final String determineSeparator( String filename ) { @@ -423,23 +429,24 @@ private static final String determineSeparator( String filename ) /** * Cygwin prefers lowercase drive letters, but other parts of maven use uppercase + * * @param path * @return String */ - static final String uppercaseDrive(String path) + static final String uppercaseDrive( String path ) { - if (path == null) + if ( path == null ) { return null; } - if (path.length() >= 2 && path.charAt(1) == ':') + if ( path.length() >= 2 && path.charAt( 1 ) == ':' ) { path = Character.toUpperCase( path.charAt( 0 ) ) + path.substring( 1 ); } return path; } - private static final String buildRelativePath( String toPath, String fromPath, final char separatorChar ) + private static final String buildRelativePath( String toPath, String fromPath, final char separatorChar ) { // use tokeniser to traverse paths and for lazy checking StringTokenizer toTokeniser = new StringTokenizer( toPath, String.valueOf( separatorChar ) ); diff --git a/src/main/java/org/codehaus/plexus/util/PropertyUtils.java b/src/main/java/org/codehaus/plexus/util/PropertyUtils.java index f327fd8a..0ed1aa48 100644 --- a/src/main/java/org/codehaus/plexus/util/PropertyUtils.java +++ b/src/main/java/org/codehaus/plexus/util/PropertyUtils.java @@ -24,7 +24,7 @@ import java.net.URL; /** - * + * Static methods to create Properties loaded from various sources. * * @author Jason van Zyl * @author Michal Maczka diff --git a/src/main/java/org/codehaus/plexus/util/ReaderFactory.java b/src/main/java/org/codehaus/plexus/util/ReaderFactory.java index c9fe9927..8eddfdf9 100644 --- a/src/main/java/org/codehaus/plexus/util/ReaderFactory.java +++ b/src/main/java/org/codehaus/plexus/util/ReaderFactory.java @@ -190,6 +190,8 @@ public static Reader newReader( InputStream in, String encoding ) /** * Create a new Reader with specified encoding. * + * Note that there is no buffering on this reader, which favours clients that read into large buffers (8K+). + * * @param file not null file. * @param encoding not null supported encoding. * @return a reader instance for the input file using the given encoding. diff --git a/src/main/java/org/codehaus/plexus/util/ReflectionUtils.java b/src/main/java/org/codehaus/plexus/util/ReflectionUtils.java index f19d4e19..53b2a24f 100644 --- a/src/main/java/org/codehaus/plexus/util/ReflectionUtils.java +++ b/src/main/java/org/codehaus/plexus/util/ReflectionUtils.java @@ -27,6 +27,8 @@ import java.util.Arrays; /** + * Operations on a class' fields and their setters. + * * @author Michal Maczka * @author Jesse McConnell * @author Trygve Laugstøl @@ -38,7 +40,7 @@ public final class ReflectionUtils // Field utils // ---------------------------------------------------------------------- - public static Field getFieldByNameIncludingSuperclasses( String fieldName, Class clazz ) + public static Field getFieldByNameIncludingSuperclasses( String fieldName, Class clazz ) { Field retValue = null; @@ -48,7 +50,7 @@ public static Field getFieldByNameIncludingSuperclasses( String fieldName, Class } catch ( NoSuchFieldException e ) { - Class superclass = clazz.getSuperclass(); + Class superclass = clazz.getSuperclass(); if ( superclass != null ) { @@ -59,11 +61,11 @@ public static Field getFieldByNameIncludingSuperclasses( String fieldName, Class return retValue; } - public static List getFieldsIncludingSuperclasses( Class clazz ) + public static List getFieldsIncludingSuperclasses( Class clazz ) { - List fields = new ArrayList( Arrays.asList( clazz.getDeclaredFields() ) ); + List fields = new ArrayList( Arrays.asList( clazz.getDeclaredFields() ) ); - Class superclass = clazz.getSuperclass(); + Class superclass = clazz.getSuperclass(); if ( superclass != null ) { @@ -85,16 +87,14 @@ public static List getFieldsIncludingSuperclasses( Class clazz ) * @param clazz The class to find the method in. * @return null or the method found. */ - public static Method getSetter( String fieldName, Class clazz ) + public static Method getSetter( String fieldName, Class clazz ) { - Method [] methods = clazz.getMethods(); + Method[] methods = clazz.getMethods(); fieldName = "set" + StringUtils.capitalizeFirstLetter( fieldName ); - for ( int i = 0; i < methods.length; i++ ) + for ( Method method : methods ) { - Method method = methods[i]; - if ( method.getName().equals( fieldName ) && isSetter( method ) ) { return method; @@ -107,16 +107,14 @@ public static Method getSetter( String fieldName, Class clazz ) /** * Finds all setters in the given class and super classes. */ - public static List getSetters( Class clazz ) + public static List getSetters( Class clazz ) { Method[] methods = clazz.getMethods(); - List list = new ArrayList(); + List list = new ArrayList(); - for ( int i = 0; i < methods.length; i++ ) + for ( Method method : methods ) { - Method method = methods[i]; - if ( isSetter( method ) ) { list.add( method ); @@ -131,7 +129,7 @@ public static List getSetters( Class clazz ) * * Will throw an RuntimeException if the method isn't a setter. */ - public static Class getSetterType( Method method ) + public static Class getSetterType( Method method ) { if ( !isSetter( method ) ) { @@ -191,9 +189,9 @@ public static Object getValueIncludingSuperclasses( String variable, Object obje public static Map getVariablesAndValuesIncludingSuperclasses( Object object ) throws IllegalAccessException { - HashMap map = new HashMap (); + HashMap map = new HashMap(); - gatherVariablesAndValuesIncludingSuperclasses(object, map); + gatherVariablesAndValuesIncludingSuperclasses( object, map ); return map; } @@ -220,7 +218,7 @@ private static void gatherVariablesAndValuesIncludingSuperclasses( Object object throws IllegalAccessException { - Class clazz = object.getClass(); + Class clazz = object.getClass(); Field[] fields = clazz.getDeclaredFields(); @@ -234,7 +232,7 @@ private static void gatherVariablesAndValuesIncludingSuperclasses( Object object } - Class superclass = clazz.getSuperclass(); + Class superclass = clazz.getSuperclass(); if ( !Object.class.equals( superclass ) ) { diff --git a/src/main/java/org/codehaus/plexus/util/Scanner.java b/src/main/java/org/codehaus/plexus/util/Scanner.java index efd96e11..d2af7b4d 100644 --- a/src/main/java/org/codehaus/plexus/util/Scanner.java +++ b/src/main/java/org/codehaus/plexus/util/Scanner.java @@ -18,6 +18,9 @@ import java.io.File; +/** + * Scan a directory tree for files, with specified inclusions and exclusions. + */ public interface Scanner { diff --git a/src/main/java/org/codehaus/plexus/util/SelectorUtils.java b/src/main/java/org/codehaus/plexus/util/SelectorUtils.java index ecdd40ce..4d1ebab1 100644 --- a/src/main/java/org/codehaus/plexus/util/SelectorUtils.java +++ b/src/main/java/org/codehaus/plexus/util/SelectorUtils.java @@ -55,8 +55,9 @@ package org.codehaus.plexus.util; import java.io.File; +import java.util.ArrayList; +import java.util.List; import java.util.StringTokenizer; -import java.util.Vector; /** *

    This is a utility class used by selectors and DirectoryScanner. The @@ -67,23 +68,23 @@ *

    This is a Singleton.

    * * @author Arnout J. Kuiper - * ajkuiper@wxs.nl + * ajkuiper@wxs.nl * @author Magesh Umasankar * @author Bruce Atherton - * @since 1.5 * @version $Id$ + * @since 1.5 */ public final class SelectorUtils { public static final String PATTERN_HANDLER_PREFIX = "["; - + public static final String PATTERN_HANDLER_SUFFIX = "]"; - + public static final String REGEX_HANDLER_PREFIX = "%regex" + PATTERN_HANDLER_PREFIX; - + public static final String ANT_HANDLER_PREFIX = "%ant" + PATTERN_HANDLER_PREFIX; - + private static SelectorUtils instance = new SelectorUtils(); /** @@ -104,7 +105,7 @@ public static SelectorUtils getInstance() /** * Tests whether or not a given path matches the start of a given * pattern up to the first "**". - *

    + *

    * This is not a general purpose test and should only be used if you * can live with false positives. For example, pattern=**\a * and str=b will yield true. @@ -113,9 +114,8 @@ public static SelectorUtils getInstance() * null. * @param str The path to match, as a String. Must not be * null. - * * @return whether or not a given path matches the start of a given - * pattern up to the first "**". + * pattern up to the first "**". */ public static boolean matchPatternStart( String pattern, String str ) { @@ -125,26 +125,23 @@ public static boolean matchPatternStart( String pattern, String str ) /** * Tests whether or not a given path matches the start of a given * pattern up to the first "**". - *

    + *

    * This is not a general purpose test and should only be used if you * can live with false positives. For example, pattern=**\a * and str=b will yield true. * - * @param pattern The pattern to match against. Must not be - * null. - * @param str The path to match, as a String. Must not be - * null. + * @param pattern The pattern to match against. Must not be + * null. + * @param str The path to match, as a String. Must not be + * null. * @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 "**". + * pattern up to the first "**". */ - public static boolean matchPatternStart( String pattern, String str, - boolean isCaseSensitive ) + public static boolean matchPatternStart( String pattern, String str, boolean isCaseSensitive ) { - if ( pattern.length() > ( REGEX_HANDLER_PREFIX.length() + PATTERN_HANDLER_SUFFIX.length() + 1 ) - && pattern.startsWith( REGEX_HANDLER_PREFIX ) && pattern.endsWith( PATTERN_HANDLER_SUFFIX ) ) + if ( isRegexPrefixedPattern( pattern ) ) { // FIXME: ICK! But we can't do partial matches for regex, so we have to reserve judgement until we have // a file to deal with, or we can definitely say this is an exclusion... @@ -152,50 +149,85 @@ public static boolean matchPatternStart( String pattern, String str, } else { - if ( pattern.length() > ( ANT_HANDLER_PREFIX.length() + PATTERN_HANDLER_SUFFIX.length() + 1 ) - && pattern.startsWith( ANT_HANDLER_PREFIX ) && pattern.endsWith( PATTERN_HANDLER_SUFFIX ) ) + if ( isAntPrefixedPattern( pattern ) ) { - pattern = - pattern.substring( ANT_HANDLER_PREFIX.length(), pattern.length() - PATTERN_HANDLER_SUFFIX.length() ); + pattern = pattern.substring( ANT_HANDLER_PREFIX.length(), + pattern.length() - PATTERN_HANDLER_SUFFIX.length() ); } String altStr = str.replace( '\\', '/' ); - + return matchAntPathPatternStart( pattern, str, File.separator, isCaseSensitive ) || matchAntPathPatternStart( pattern, altStr, "/", isCaseSensitive ); } } - - private static boolean matchAntPathPatternStart( String pattern, String str, String separator, boolean isCaseSensitive ) + + static boolean isAntPrefixedPattern( String pattern ) + { + return pattern.length() > ( ANT_HANDLER_PREFIX.length() + PATTERN_HANDLER_SUFFIX.length() + 1 ) + && pattern.startsWith( ANT_HANDLER_PREFIX ) && pattern.endsWith( PATTERN_HANDLER_SUFFIX ); + } + + @SuppressWarnings( "SimplifiableIfStatement" ) + static boolean matchAntPathPatternStart( MatchPattern pattern, String str, String separator, + boolean isCaseSensitive ) + { + if ( separatorPatternStartSlashMismatch( pattern, str, separator ) ) + { + return false; + } + + return matchAntPathPatternStart( pattern.getTokenizedPathString(), str, separator, isCaseSensitive ); + } + + static boolean matchAntPathPatternStart( String pattern, String str, String separator, boolean isCaseSensitive ) { // When str starts with a File.separator, pattern has to start with a // File.separator. // When pattern starts with a File.separator, str has to start with a // File.separator. - if ( str.startsWith( separator ) != - pattern.startsWith( separator ) ) + if ( separatorPatternStartSlashMismatch( pattern, str, separator ) ) { return false; } - Vector patDirs = tokenizePath( pattern, separator ); - Vector strDirs = tokenizePath( str, separator ); + String[] patDirs = tokenizePathToString( pattern, separator ); + return matchAntPathPatternStart( patDirs, str, separator, isCaseSensitive ); + } + + // When str starts with a File.separator, pattern has to start with a + // File.separator. + // When pattern starts with a File.separator, str has to start with a + // File.separator. + private static boolean separatorPatternStartSlashMismatch( String pattern, String str, String separator ) + { + return str.startsWith( separator ) != pattern.startsWith( separator ); + } + + private static boolean separatorPatternStartSlashMismatch( MatchPattern matchPattern, String str, String separator ) + { + return str.startsWith( separator ) != matchPattern.startsWith( separator ); + } + + + static boolean matchAntPathPatternStart( String[] patDirs, String str, String separator, boolean isCaseSensitive ) + { + String[] strDirs = tokenizePathToString( str, separator ); int patIdxStart = 0; - int patIdxEnd = patDirs.size() - 1; + int patIdxEnd = patDirs.length - 1; int strIdxStart = 0; - int strIdxEnd = strDirs.size() - 1; + int strIdxEnd = strDirs.length - 1; // up to first '**' while ( patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd ) { - String patDir = (String) patDirs.elementAt( patIdxStart ); + String patDir = patDirs[patIdxStart]; if ( patDir.equals( "**" ) ) { break; } - if ( !match( patDir, (String) strDirs.elementAt( strIdxStart ), - isCaseSensitive ) ) + if ( !match( patDir, strDirs[strIdxStart], isCaseSensitive ) ) { return false; } @@ -203,22 +235,7 @@ private static boolean matchAntPathPatternStart( String pattern, String str, Str strIdxStart++; } - if ( strIdxStart > strIdxEnd ) - { - // String is exhausted - return true; - } - else if ( patIdxStart > patIdxEnd ) - { - // String not exhausted, but pattern is. Failure. - return false; - } - else - { - // pattern now holds ** while string is not exhausted - // this will generate false positives but we can live with that. - return true; - } + return strIdxStart > strIdxEnd || patIdxStart <= patIdxEnd; } /** @@ -228,7 +245,6 @@ else if ( patIdxStart > patIdxEnd ) * null. * @param str The path to match, as a String. Must not be * null. - * * @return true if the pattern matches against the string, * or false otherwise. */ @@ -240,73 +256,88 @@ public static boolean matchPath( String pattern, String str ) /** * Tests whether or not a given path matches a given pattern. * - * @param pattern The pattern to match against. Must not be - * null. - * @param str The path to match, as a String. Must not be - * null. + * @param pattern The pattern to match against. Must not be + * null. + * @param str The path to match, as a String. Must not be + * null. * @param isCaseSensitive Whether or not matching should be performed * case sensitively. - * * @return true if the pattern matches against the string, * or false otherwise. */ - public static boolean matchPath( String pattern, String str, - boolean isCaseSensitive ) + public static boolean matchPath( String pattern, String str, boolean isCaseSensitive ) + { + return matchPath( pattern, str, File.separator, isCaseSensitive ); + } + + public static boolean matchPath( String pattern, String str, String separator, boolean isCaseSensitive ) { - if ( pattern.length() > ( REGEX_HANDLER_PREFIX.length() + PATTERN_HANDLER_SUFFIX.length() + 1 ) - && pattern.startsWith( REGEX_HANDLER_PREFIX ) && pattern.endsWith( PATTERN_HANDLER_SUFFIX ) ) + if ( isRegexPrefixedPattern( pattern ) ) { - pattern = pattern.substring( REGEX_HANDLER_PREFIX.length(), pattern.length() - - PATTERN_HANDLER_SUFFIX.length() ); + pattern = + pattern.substring( REGEX_HANDLER_PREFIX.length(), pattern.length() - PATTERN_HANDLER_SUFFIX.length() ); return str.matches( pattern ); } else { - if ( pattern.length() > ( ANT_HANDLER_PREFIX.length() + PATTERN_HANDLER_SUFFIX.length() + 1 ) - && pattern.startsWith( ANT_HANDLER_PREFIX ) && pattern.endsWith( PATTERN_HANDLER_SUFFIX ) ) + if ( isAntPrefixedPattern( pattern ) ) { - pattern = - pattern.substring( ANT_HANDLER_PREFIX.length(), pattern.length() - PATTERN_HANDLER_SUFFIX.length() ); + pattern = pattern.substring( ANT_HANDLER_PREFIX.length(), + pattern.length() - PATTERN_HANDLER_SUFFIX.length() ); } - - return matchAntPathPattern( pattern, str, isCaseSensitive ); + + return matchAntPathPattern( pattern, str, separator, isCaseSensitive ); } } - private static boolean matchAntPathPattern( String pattern, String str, boolean isCaseSensitive ) + static boolean isRegexPrefixedPattern( String pattern ) { - // When str starts with a File.separator, pattern has to start with a - // File.separator. - // When pattern starts with a File.separator, str has to start with a - // File.separator. - if ( str.startsWith( File.separator ) != - pattern.startsWith( File.separator ) ) + return pattern.length() > ( REGEX_HANDLER_PREFIX.length() + PATTERN_HANDLER_SUFFIX.length() + 1 ) + && pattern.startsWith( REGEX_HANDLER_PREFIX ) && pattern.endsWith( PATTERN_HANDLER_SUFFIX ); + } + + static boolean matchAntPathPattern( MatchPattern matchPattern, String str, String separator, + boolean isCaseSensitive ) + { + if ( separatorPatternStartSlashMismatch( matchPattern, str, separator ) ) + { + return false; + } + String[] patDirs = matchPattern.getTokenizedPathString(); + String[] strDirs = tokenizePathToString( str, separator ); + return matchAntPathPattern( patDirs, strDirs, isCaseSensitive ); + } + + static boolean matchAntPathPattern( String pattern, String str, String separator, boolean isCaseSensitive ) + { + if ( separatorPatternStartSlashMismatch( pattern, str, separator ) ) { return false; } + String[] patDirs = tokenizePathToString( pattern, separator ); + String[] strDirs = tokenizePathToString( str, separator ); + return matchAntPathPattern( patDirs, strDirs, isCaseSensitive ); - Vector patDirs = tokenizePath( pattern, File.separator ); - Vector strDirs = tokenizePath( str, File.separator ); + } + static boolean matchAntPathPattern( String[] patDirs, String[] strDirs, boolean isCaseSensitive ) + { int patIdxStart = 0; - int patIdxEnd = patDirs.size() - 1; + int patIdxEnd = patDirs.length - 1; int strIdxStart = 0; - int strIdxEnd = strDirs.size() - 1; + int strIdxEnd = strDirs.length - 1; // up to first '**' while ( patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd ) { - String patDir = (String) patDirs.elementAt( patIdxStart ); + String patDir = patDirs[patIdxStart]; if ( patDir.equals( "**" ) ) { break; } - if ( !match( patDir, (String) strDirs.elementAt( strIdxStart ), - isCaseSensitive ) ) + if ( !match( patDir, strDirs[strIdxStart], isCaseSensitive ) ) { - patDirs = null; - strDirs = null; return false; } patIdxStart++; @@ -317,10 +348,8 @@ private static boolean matchAntPathPattern( String pattern, String str, boolean // String is exhausted for ( int i = patIdxStart; i <= patIdxEnd; i++ ) { - if ( !patDirs.elementAt( i ).equals( "**" ) ) + if ( !patDirs[i].equals( "**" ) ) { - patDirs = null; - strDirs = null; return false; } } @@ -331,8 +360,6 @@ private static boolean matchAntPathPattern( String pattern, String str, boolean if ( patIdxStart > patIdxEnd ) { // String not exhausted, but pattern is. Failure. - patDirs = null; - strDirs = null; return false; } } @@ -340,16 +367,143 @@ private static boolean matchAntPathPattern( String pattern, String str, boolean // up to last '**' while ( patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd ) { - String patDir = (String) patDirs.elementAt( patIdxEnd ); + String patDir = patDirs[patIdxEnd]; if ( patDir.equals( "**" ) ) { break; } - if ( !match( patDir, (String) strDirs.elementAt( strIdxEnd ), - isCaseSensitive ) ) + if ( !match( patDir, strDirs[strIdxEnd], isCaseSensitive ) ) + { + return false; + } + patIdxEnd--; + strIdxEnd--; + } + if ( strIdxStart > strIdxEnd ) + { + // String is exhausted + for ( int i = patIdxStart; i <= patIdxEnd; i++ ) + { + if ( !patDirs[i].equals( "**" ) ) + { + return false; + } + } + return true; + } + + while ( patIdxStart != patIdxEnd && strIdxStart <= strIdxEnd ) + { + int patIdxTmp = -1; + for ( int i = patIdxStart + 1; i <= patIdxEnd; i++ ) + { + if ( patDirs[i].equals( "**" ) ) + { + patIdxTmp = i; + break; + } + } + if ( patIdxTmp == patIdxStart + 1 ) + { + // '**/**' situation, so skip one + patIdxStart++; + continue; + } + // Find the pattern between padIdxStart & padIdxTmp in str between + // strIdxStart & strIdxEnd + int patLength = ( patIdxTmp - patIdxStart - 1 ); + int strLength = ( strIdxEnd - strIdxStart + 1 ); + int foundIdx = -1; + strLoop: + for ( int i = 0; i <= strLength - patLength; i++ ) + { + for ( int j = 0; j < patLength; j++ ) + { + String subPat = patDirs[patIdxStart + j + 1]; + String subStr = strDirs[strIdxStart + i + j]; + if ( !match( subPat, subStr, isCaseSensitive ) ) + { + continue strLoop; + } + } + + foundIdx = strIdxStart + i; + break; + } + + if ( foundIdx == -1 ) + { + return false; + } + + patIdxStart = patIdxTmp; + strIdxStart = foundIdx + patLength; + } + + for ( int i = patIdxStart; i <= patIdxEnd; i++ ) + { + if ( !patDirs[i].equals( "**" ) ) + { + return false; + } + } + + return true; + } + + static boolean matchAntPathPattern( char[][] patDirs, char[][] strDirs, boolean isCaseSensitive ) + { + int patIdxStart = 0; + int patIdxEnd = patDirs.length - 1; + int strIdxStart = 0; + int strIdxEnd = strDirs.length - 1; + + // up to first '**' + while ( patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd ) + { + char[] patDir = patDirs[patIdxStart]; + if ( isDoubleStar( patDir ) ) + { + break; + } + if ( !match( patDir, strDirs[strIdxStart], isCaseSensitive ) ) + { + return false; + } + patIdxStart++; + strIdxStart++; + } + if ( strIdxStart > strIdxEnd ) + { + // String is exhausted + for ( int i = patIdxStart; i <= patIdxEnd; i++ ) + { + if ( !isDoubleStar( patDirs[i] ) ) + { + return false; + } + } + return true; + } + else + { + if ( patIdxStart > patIdxEnd ) + { + // String not exhausted, but pattern is. Failure. + return false; + } + } + + // up to last '**' + while ( patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd ) + { + char[] patDir = patDirs[patIdxEnd]; + if ( isDoubleStar( patDir ) ) + { + break; + } + if ( !match( patDir, strDirs[strIdxEnd], isCaseSensitive ) ) { - patDirs = null; - strDirs = null; return false; } patIdxEnd--; @@ -360,10 +514,8 @@ private static boolean matchAntPathPattern( String pattern, String str, boolean // String is exhausted for ( int i = patIdxStart; i <= patIdxEnd; i++ ) { - if ( !patDirs.elementAt( i ).equals( "**" ) ) + if ( !isDoubleStar( patDirs[i] ) ) { - patDirs = null; - strDirs = null; return false; } } @@ -375,7 +527,7 @@ private static boolean matchAntPathPattern( String pattern, String str, boolean int patIdxTmp = -1; for ( int i = patIdxStart + 1; i <= patIdxEnd; i++ ) { - if ( patDirs.elementAt( i ).equals( "**" ) ) + if ( isDoubleStar( patDirs[i] ) ) { patIdxTmp = i; break; @@ -393,26 +545,24 @@ private static boolean matchAntPathPattern( String pattern, String str, boolean int strLength = ( strIdxEnd - strIdxStart + 1 ); int foundIdx = -1; strLoop: - for ( int i = 0; i <= strLength - patLength; i++ ) - { - for ( int j = 0; j < patLength; j++ ) - { - String subPat = (String) patDirs.elementAt( patIdxStart + j + 1 ); - String subStr = (String) strDirs.elementAt( strIdxStart + i + j ); - if ( !match( subPat, subStr, isCaseSensitive ) ) - { - continue strLoop; - } - } - - foundIdx = strIdxStart + i; - break; - } + for ( int i = 0; i <= strLength - patLength; i++ ) + { + for ( int j = 0; j < patLength; j++ ) + { + char[] subPat = patDirs[patIdxStart + j + 1]; + char[] subStr = strDirs[strIdxStart + i + j]; + if ( !match( subPat, subStr, isCaseSensitive ) ) + { + continue strLoop; + } + } + + foundIdx = strIdxStart + i; + break; + } if ( foundIdx == -1 ) { - patDirs = null; - strDirs = null; return false; } @@ -422,10 +572,8 @@ private static boolean matchAntPathPattern( String pattern, String str, boolean for ( int i = patIdxStart; i <= patIdxEnd; i++ ) { - if ( !patDirs.elementAt( i ).equals( "**" ) ) + if ( !isDoubleStar( patDirs[i] ) ) { - patDirs = null; - strDirs = null; return false; } } @@ -433,6 +581,11 @@ private static boolean matchAntPathPattern( String pattern, String str, boolean return true; } + private static boolean isDoubleStar( char[] patDir ) + { + return patDir != null && patDir.length == 2 && patDir[0] == '*' && patDir[1] == '*'; + } + /** * Tests whether or not a string matches against a pattern. * The pattern may contain two special characters:
    @@ -443,7 +596,6 @@ private static boolean matchAntPathPattern( String pattern, String str, boolean * Must not be null. * @param str The string which must be matched against the pattern. * Must not be null. - * * @return true if the string matches against the pattern, * or false otherwise. */ @@ -458,22 +610,24 @@ public static boolean match( String pattern, String str ) * '*' means zero or more characters
    * '?' means one and only one character * - * @param pattern The pattern to match against. - * Must not be null. - * @param str The string which must be matched against the pattern. - * Must not be null. + * @param pattern The pattern to match against. + * Must not be null. + * @param str The string which must be matched against the pattern. + * Must not be null. * @param isCaseSensitive Whether or not matching should be performed * case sensitively. - * - * * @return true if the string matches against the pattern, * or false otherwise. */ - public static boolean match( String pattern, String str, - boolean isCaseSensitive ) + public static boolean match( String pattern, String str, boolean isCaseSensitive ) { char[] patArr = pattern.toCharArray(); char[] strArr = str.toCharArray(); + return match( patArr, strArr, isCaseSensitive); + } + + public static boolean match( char[] patArr, char[] strArr, boolean isCaseSensitive ) + { int patIdxStart = 0; int patIdxEnd = patArr.length - 1; int strIdxStart = 0; @@ -481,9 +635,9 @@ public static boolean match( String pattern, String str, char ch; boolean containsStar = false; - for ( int i = 0; i < patArr.length; i++ ) + for ( char aPatArr : patArr ) { - if ( patArr[i] == '*' ) + if ( aPatArr == '*' ) { containsStar = true; break; @@ -634,8 +788,8 @@ private static boolean equals( char c1, char c2, boolean isCaseSensitive ) if ( !isCaseSensitive ) { // NOTE: Try both upper case and lower case as done by String.equalsIgnoreCase() - if ( Character.toUpperCase( c1 ) == Character.toUpperCase( c2 ) || - Character.toLowerCase( c1 ) == Character.toLowerCase( c2 ) ) + if ( Character.toUpperCase( c1 ) == Character.toUpperCase( c2 ) + || Character.toLowerCase( c1 ) == Character.toLowerCase( c2 ) ) { return true; } @@ -643,28 +797,15 @@ private static boolean equals( char c1, char c2, boolean isCaseSensitive ) return false; } - /** - * Breaks a path up into a Vector of path elements, tokenizing on - * File.separator. - * - * @param path Path to tokenize. Must not be null. - * - * @return a Vector of path elements from the tokenized path - */ - public static Vector tokenizePath( String path ) - { - return tokenizePath( path, File.separator ); - } - - public static Vector tokenizePath( String path, String separator ) + private static String[] tokenizePathToString( String path, String separator ) { - Vector ret = new Vector(); + List ret = new ArrayList(); StringTokenizer st = new StringTokenizer( path, separator ); while ( st.hasMoreTokens() ) { - ret.addElement( st.nextToken() ); + ret.add( st.nextToken() ); } - return ret; + return ret.toArray( new String[ret.size()] ); } @@ -676,10 +817,10 @@ public static Vector tokenizePath( String path, String separator ) * false if the src file doesn't even exist, since how could the * target then be out of date. * - * @param src the original file - * @param target the file being compared against + * @param src the original file + * @param target the file being compared against * @param granularity the amount in seconds of slack we will give in - * determining out of dateness + * determining out of dateness * @return whether the target is out of date */ public static boolean isOutOfDate( File src, File target, int granularity ) @@ -709,7 +850,7 @@ public static boolean isOutOfDate( File src, File target, int granularity ) */ public static String removeWhitespace( String input ) { - StringBuffer result = new StringBuffer(); + StringBuilder result = new StringBuilder(); if ( input != null ) { StringTokenizer st = new StringTokenizer( input ); diff --git a/src/main/java/org/codehaus/plexus/util/StringUtils.java b/src/main/java/org/codehaus/plexus/util/StringUtils.java index 7e485e46..846f491f 100644 --- a/src/main/java/org/codehaus/plexus/util/StringUtils.java +++ b/src/main/java/org/codehaus/plexus/util/StringUtils.java @@ -58,8 +58,6 @@ import java.util.Locale; import java.util.Map; import java.util.StringTokenizer; -import java.util.regex.Matcher; -import java.util.regex.Pattern; /** *

    Common String manipulation routines.

    @@ -139,7 +137,7 @@ public static String trim( String str ) */ public static String deleteWhitespace( String str ) { - StringBuffer buffer = new StringBuffer(); + StringBuilder buffer = new StringBuilder(); int sz = str.length(); for ( int i = 0; i < sz; i++ ) { @@ -294,10 +292,10 @@ public static int indexOfAny( String str, String[] searchStrs ) // String's can't have a MAX_VALUEth index. int ret = Integer.MAX_VALUE; - int tmp = 0; - for ( int i = 0; i < sz; i++ ) + int tmp; + for ( String searchStr : searchStrs ) { - tmp = str.indexOf( searchStrs[i] ); + tmp = str.indexOf( searchStr ); if ( tmp == -1 ) { continue; @@ -328,12 +326,11 @@ public static int lastIndexOfAny( String str, String[] searchStrs ) { return -1; } - int sz = searchStrs.length; int ret = -1; - int tmp = 0; - for ( int i = 0; i < sz; i++ ) + int tmp; + for ( String searchStr : searchStrs ) { - tmp = str.lastIndexOf( searchStrs[i] ); + tmp = str.lastIndexOf( searchStr ); if ( tmp > ret ) { ret = tmp; @@ -576,7 +573,7 @@ public static String[] split( String text, String separator ) */ public static String[] split( String str, String separator, int max ) { - StringTokenizer tok = null; + StringTokenizer tok; if ( separator == null ) { // Null separator means we're using StringTokenizer's default @@ -596,7 +593,7 @@ public static String[] split( String str, String separator, int max ) String[] list = new String[listSize]; int i = 0; - int lastTokenBegin = 0; + int lastTokenBegin; int lastTokenEnd = 0; while ( tok.hasMoreTokens() ) { @@ -656,7 +653,7 @@ public static String join( Object[] array, String separator ) int arraySize = array.length; int bufSize = ( arraySize == 0 ? 0 : ( array[0].toString().length() + separator.length() ) * arraySize ); - StringBuffer buf = new StringBuffer( bufSize ); + StringBuilder buf = new StringBuilder( bufSize ); for ( int i = 0; i < arraySize; i++ ) { @@ -680,13 +677,13 @@ public static String join( Object[] array, String separator ) * @param separator the separator character to use * @return the joined String */ - public static String join( Iterator iterator, String separator ) + public static String join( Iterator iterator, String separator ) { if ( separator == null ) { separator = ""; } - StringBuffer buf = new StringBuffer( 256 ); // Java default is 16, probably too small + StringBuilder buf = new StringBuilder( 256 ); // Java default is 16, probably too small while ( iterator.hasNext() ) { buf.append( iterator.next() ); @@ -803,11 +800,11 @@ public static String replace( String text, String repl, String with, int max ) return text; } - StringBuffer buf = new StringBuffer( text.length() ); - int start = 0, end = 0; + StringBuilder buf = new StringBuilder( text.length() ); + int start = 0, end; while ( ( end = text.indexOf( repl, start ) ) != -1 ) { - buf.append( text.substring( start, end ) ).append( with ); + buf.append( text, start, end ).append( with ); start = end + repl.length(); if ( --max == 0 ) @@ -815,7 +812,7 @@ public static String replace( String text, String repl, String with, int max ) break; } } - buf.append( text.substring( start ) ); + buf.append( text, start, text.length()); return buf.toString(); } @@ -832,9 +829,9 @@ public static String replace( String text, String repl, String with, int max ) public static String overlayString( String text, String overlay, int start, int end ) { return new StringBuffer( start + overlay.length() + text.length() - end + 1 ) - .append( text.substring( 0, start ) ) + .append( text, 0, start ) .append( overlay ) - .append( text.substring( end ) ) + .append( text, end, text.length() ) .toString(); } @@ -1109,7 +1106,7 @@ public static String escape( String str ) // improved with code from cybertiger@cyberiantiger.org // unicode from him, and defaul for < 32's. int sz = str.length(); - StringBuffer buffer = new StringBuffer( 2 * sz ); + StringBuilder buffer = new StringBuilder( 2 * sz ); for ( int i = 0; i < sz; i++ ) { char ch = str.charAt( i ); @@ -1203,7 +1200,7 @@ else if ( ch < 32 ) */ public static String repeat( String str, int repeat ) { - StringBuffer buffer = new StringBuffer( repeat * str.length() ); + StringBuilder buffer = new StringBuilder( repeat * str.length() ); for ( int i = 0; i < repeat; i++ ) { buffer.append( str ); @@ -1480,7 +1477,7 @@ else if ( str.length() == 0 ) { return new StringBuffer( str.length() ) .append( Character.toLowerCase( str.charAt( 0 ) ) ) - .append( str.substring( 1 ) ) + .append( str, 1, str.length() ) .toString(); } } @@ -1506,9 +1503,9 @@ else if ( str.length() == 0 ) } else { - return new StringBuffer( str.length() ) + return new StringBuilder( str.length() ) .append( Character.toTitleCase( str.charAt( 0 ) ) ) - .append( str.substring( 1 ) ) + .append( str, 1, str.length() ) .toString(); } } @@ -1531,11 +1528,11 @@ public static String swapCase( String str ) return null; } int sz = str.length(); - StringBuffer buffer = new StringBuffer( sz ); + StringBuilder buffer = new StringBuilder( sz ); boolean whitespace = false; - char ch = 0; - char tmp = 0; + char ch; + char tmp; for ( int i = 0; i < sz; i++ ) { @@ -1588,7 +1585,7 @@ public static String capitaliseAllWords( String str ) return null; } int sz = str.length(); - StringBuffer buffer = new StringBuffer( sz ); + StringBuilder buffer = new StringBuilder( sz ); boolean space = true; for ( int i = 0; i < sz; i++ ) { @@ -1629,7 +1626,7 @@ public static String uncapitaliseAllWords( String str ) return null; } int sz = str.length(); - StringBuffer buffer = new StringBuffer( sz ); + StringBuilder buffer = new StringBuilder( sz ); boolean space = true; for ( int i = 0; i < sz; i++ ) { @@ -2132,9 +2129,9 @@ public static int differenceAt( String s1, String s2 ) return -1; } - public static String interpolate( String text, Map namespace ) + public static String interpolate( String text, Map namespace ) { - Iterator keys = namespace.keySet().iterator(); + Iterator keys = namespace.keySet().iterator(); while ( keys.hasNext() ) { @@ -2163,7 +2160,7 @@ public static String removeAndHump( String data, String replaceThis ) { String temp; - StringBuffer out = new StringBuffer(); + StringBuilder out = new StringBuilder(); temp = data; @@ -2199,7 +2196,7 @@ public static String lowercaseFirstLetter( String data ) public static String addAndDeHump( String view ) { - StringBuffer sb = new StringBuffer(); + StringBuilder sb = new StringBuilder(); for ( int i = 0; i < view.length(); i++ ) { @@ -2290,18 +2287,37 @@ public static String quoteAndEscape( String source, char escapeChar, boolean force ) { + return quoteAndEscape(source, quoteChar, escapedChars, quotingTriggers, escapeChar + "%s", force); + } + + /** + * @param source + * @param quoteChar + * @param escapedChars + * @param quotingTriggers + * @param escapePattern + * @param force + * @return the String quoted and escaped + * @since 3.0.4 + */ + public static String quoteAndEscape(String source, + char quoteChar, + final char[] escapedChars, + final char[] quotingTriggers, + String escapePattern, + boolean force) { if ( source == null ) { return null; } if ( !force && source.startsWith( Character.toString( quoteChar ) ) - && source.endsWith( Character.toString( quoteChar ) ) ) + && source.endsWith( Character.toString( quoteChar ) ) ) { return source; } - String escaped = escape( source, escapedChars, escapeChar ); + String escaped = escape( source, escapedChars, escapePattern ); boolean quote = false; if ( force ) @@ -2314,9 +2330,9 @@ else if ( !escaped.equals( source ) ) } else { - for ( int i = 0; i < quotingTriggers.length; i++ ) + for ( char quotingTrigger : quotingTriggers ) { - if ( escaped.indexOf( quotingTriggers[i] ) > -1 ) + if ( escaped.indexOf( quotingTrigger ) > -1 ) { quote = true; break; @@ -2340,6 +2356,18 @@ else if ( !escaped.equals( source ) ) * @since 1.5.1 */ public static String escape( String source, final char[] escapedChars, char escapeChar ) + { + return escape(source, escapedChars, escapeChar + "%s"); + } + + /** + * @param source + * @param escapedChars + * @param escapePattern + * @return the String escaped + * @since 3.0.4 + */ + public static String escape( String source, final char[] escapedChars, String escapePattern ) { if ( source == null ) { @@ -2350,9 +2378,8 @@ public static String escape( String source, final char[] escapedChars, char esca System.arraycopy( escapedChars, 0, eqc, 0, escapedChars.length ); Arrays.sort( eqc ); - StringBuffer buffer = new StringBuffer( source.length() ); + StringBuilder buffer = new StringBuilder( source.length() ); - int escapeCount = 0; for ( int i = 0; i < source.length(); i++ ) { final char c = source.charAt( i ); @@ -2360,11 +2387,12 @@ public static String escape( String source, final char[] escapedChars, char esca if ( result > -1 ) { - buffer.append( escapeChar ); - escapeCount++; + buffer.append( String.format(escapePattern, c) ); + } + else + { + buffer.append( c ); } - - buffer.append( c ); } return buffer.toString(); @@ -2380,11 +2408,18 @@ public static String escape( String source, final char[] escapedChars, char esca */ public static String removeDuplicateWhitespace( String s ) { - String patternStr = "\\s+"; - String replaceStr = " "; - Pattern pattern = Pattern.compile( patternStr ); - Matcher matcher = pattern.matcher( s ); - return matcher.replaceAll( replaceStr ); + StringBuilder result = new StringBuilder( ); + int length = s.length(); + boolean isPreviousWhiteSpace = false; + for (int i = 0; i < length; i++){ + char c = s.charAt( i ); + boolean thisCharWhiteSpace = Character.isWhitespace( c ); + if (!(isPreviousWhiteSpace && thisCharWhiteSpace)){ + result.append( c ); + } + isPreviousWhiteSpace = thisCharWhiteSpace; + } + return result.toString(); } /** @@ -2430,7 +2465,7 @@ public static String unifyLineSeparators( String s, String ls ) int length = s.length(); - StringBuffer buffer = new StringBuffer( length ); + StringBuilder buffer = new StringBuilder( length ); for ( int i = 0; i < length; i++ ) { if ( s.charAt( i ) == '\r' ) diff --git a/src/main/java/org/codehaus/plexus/util/WriterFactory.java b/src/main/java/org/codehaus/plexus/util/WriterFactory.java index 58095195..b730bc0e 100644 --- a/src/main/java/org/codehaus/plexus/util/WriterFactory.java +++ b/src/main/java/org/codehaus/plexus/util/WriterFactory.java @@ -118,7 +118,7 @@ public static XmlStreamWriter newXmlWriter( File file ) } /** - * Create a new Writer with default plaform encoding. + * Create a new Writer with default platform encoding. * * @param out not null output stream. * @return a writer instance for the output stream using the default platform charset. @@ -131,7 +131,7 @@ public static Writer newPlatformWriter( OutputStream out ) } /** - * Create a new Writer with default plaform encoding. + * Create a new Writer with default platform encoding. * * @param file not null file. * @return a writer instance for the output file using the default platform charset. diff --git a/src/main/java/org/codehaus/plexus/util/cli/AbstractStreamHandler.java b/src/main/java/org/codehaus/plexus/util/cli/AbstractStreamHandler.java new file mode 100644 index 00000000..2c7e8967 --- /dev/null +++ b/src/main/java/org/codehaus/plexus/util/cli/AbstractStreamHandler.java @@ -0,0 +1,59 @@ +package org.codehaus.plexus.util.cli; + +/* + * 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. + */ + +/** + * @author Kristian Rosenvold + */ +public class AbstractStreamHandler + extends Thread +{ + private boolean done; + + private volatile boolean disabled; + + public boolean isDone() + { + return done; + } + + public synchronized void waitUntilDone() + throws InterruptedException + { + while ( !isDone() ) + { + wait(); + } + } + + + protected boolean isDisabled() + { + return disabled; + } + + public void disable() + { + disabled = true; + } + + public void setDone() + { + done = true; + } + +} diff --git a/src/main/java/org/codehaus/plexus/util/cli/CommandLineCallable.java b/src/main/java/org/codehaus/plexus/util/cli/CommandLineCallable.java new file mode 100644 index 00000000..8a451607 --- /dev/null +++ b/src/main/java/org/codehaus/plexus/util/cli/CommandLineCallable.java @@ -0,0 +1,28 @@ +package org.codehaus.plexus.util.cli; + +/* + * 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.util.concurrent.Callable; + +/** + * Callable wrapper that exposes the proper exeception type to the client. + * @author Kristian Rosenvold + */ +public interface CommandLineCallable extends Callable +{ + public Integer call() throws CommandLineException; +} diff --git a/src/main/java/org/codehaus/plexus/util/cli/CommandLineUtils.java b/src/main/java/org/codehaus/plexus/util/cli/CommandLineUtils.java index 255a061d..fc73e515 100644 --- a/src/main/java/org/codehaus/plexus/util/cli/CommandLineUtils.java +++ b/src/main/java/org/codehaus/plexus/util/cli/CommandLineUtils.java @@ -16,24 +16,15 @@ * limitations under the License. */ -import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.Reader; -import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; -import java.util.Collections; -import java.util.HashMap; -import java.util.Iterator; import java.util.Locale; import java.util.Map; import java.util.Properties; import java.util.StringTokenizer; import java.util.Vector; - import org.codehaus.plexus.util.Os; -import org.codehaus.plexus.util.ReaderFactory; import org.codehaus.plexus.util.StringUtils; /** @@ -42,47 +33,6 @@ */ public abstract class CommandLineUtils { - private static Map processes = Collections.synchronizedMap( new HashMap() ); - - private static Thread shutdownHook = new Thread( "CommandlineUtil shutdown" ) - { - public void run() - { - if ( ( processes != null ) && ( processes.size() > 0 ) ) - { - System.err.println( "Destroying " + processes.size() + " processes" ); - for ( Iterator it = processes.values().iterator(); it.hasNext(); ) - { - System.err.println( "Destroying process.." ); - ( (Process) it.next() ).destroy(); - - } - System.err.println( "Destroyed " + processes.size() + " processes" ); - } - } - }; - - static - { - shutdownHook.setContextClassLoader( null ); - addShutdownHook(); - } - - public static void addShutdownHook() - { - Runtime.getRuntime().addShutdownHook( shutdownHook ); - } - - public static void removeShutdownHook( boolean execute ) - { - Runtime.getRuntime().removeShutdownHook( shutdownHook ); - - if ( execute ) - { - shutdownHook.run(); - } - } - public static class StringStreamConsumer implements StreamConsumer { @@ -101,6 +51,23 @@ public String getOutput() } } + private static class ProcessHook extends Thread { + private final Process process; + + private ProcessHook( Process process ) + { + super("CommandlineUtils process shutdown hook"); + this.process = process; + this.setContextClassLoader( null ); + } + + public void run() + { + process.destroy(); + } + } + + public static int executeCommandLine( Commandline cl, StreamConsumer systemOut, StreamConsumer systemErr ) throws CommandLineException { @@ -122,39 +89,56 @@ public static int executeCommandLine( Commandline cl, InputStream systemIn, Stre } /** - * @param cl The command line to execute - * @param systemIn The input to read from, must be thread safe - * @param systemOut A consumer that receives output, must be thread safe - * @param systemErr A consumer that receives system error stream output, must be thread safe + * @param cl The command line to execute + * @param systemIn The input to read from, must be thread safe + * @param systemOut A consumer that receives output, must be thread safe + * @param systemErr A consumer that receives system error stream output, must be thread safe * @param timeoutInSeconds Positive integer to specify timeout, zero and negative integers for no timeout. * @return A return value, see {@link Process#exitValue()} * @throws CommandLineException or CommandLineTimeOutException if time out occurs + * @noinspection ThrowableResultOfMethodCallIgnored */ public static int executeCommandLine( Commandline cl, InputStream systemIn, StreamConsumer systemOut, StreamConsumer systemErr, int timeoutInSeconds ) throws CommandLineException + { + final CommandLineCallable future = + executeCommandLineAsCallable( cl, systemIn, systemOut, systemErr, timeoutInSeconds ); + return future.call(); + } + + /** + * Immediately forks a process, returns a callable that will block until process is complete. + * @param cl The command line to execute + * @param systemIn The input to read from, must be thread safe + * @param systemOut A consumer that receives output, must be thread safe + * @param systemErr A consumer that receives system error stream output, must be thread safe + * @param timeoutInSeconds Positive integer to specify timeout, zero and negative integers for no timeout. + * @return A CommandLineCallable that provides the process return value, see {@link Process#exitValue()}. "call" must be called on + * this to be sure the forked process has terminated, no guarantees is made about + * any internal state before after the completion of the call statements + * @throws CommandLineException or CommandLineTimeOutException if time out occurs + * @noinspection ThrowableResultOfMethodCallIgnored + */ + public static CommandLineCallable executeCommandLineAsCallable( final Commandline cl, final InputStream systemIn, + final StreamConsumer systemOut, + final StreamConsumer systemErr, + final int timeoutInSeconds ) + throws CommandLineException { if ( cl == null ) { throw new IllegalArgumentException( "cl cannot be null." ); } - Process p; + final Process p = cl.execute(); - p = cl.execute(); - - processes.put( new Long( cl.getPid() ), p ); - - StreamFeeder inputFeeder = null; - - if ( systemIn != null ) - { - inputFeeder = new StreamFeeder( systemIn, p.getOutputStream() ); - } + final StreamFeeder inputFeeder = systemIn != null ? + new StreamFeeder( systemIn, p.getOutputStream() ) : null; - StreamPumper outputPumper = new StreamPumper( p.getInputStream(), systemOut ); + final StreamPumper outputPumper = new StreamPumper( p.getInputStream(), systemOut ); - StreamPumper errorPumper = new StreamPumper( p.getErrorStream(), systemErr ); + final StreamPumper errorPumper = new StreamPumper( p.getErrorStream(), systemErr ); if ( inputFeeder != null ) { @@ -165,93 +149,92 @@ public static int executeCommandLine( Commandline cl, InputStream systemIn, Stre errorPumper.start(); - try + final ProcessHook processHook = new ProcessHook( p ); + + ShutdownHookUtils.addShutDownHook( processHook ); + + return new CommandLineCallable() { - int returnValue; - if ( timeoutInSeconds <= 0 ) - { - returnValue = p.waitFor(); - } - else + public Integer call() + throws CommandLineException { - long now = System.currentTimeMillis(); - long timeoutInMillis = 1000L * timeoutInSeconds; - long finish = now + timeoutInMillis; - while ( isAlive( p ) && ( System.currentTimeMillis() < finish ) ) + try { - Thread.sleep( 10 ); - } - if ( isAlive( p ) ) - { - throw new InterruptedException( "Process timeout out after " + timeoutInSeconds + " seconds" ); - } - returnValue = p.exitValue(); - } + int returnValue; + if ( timeoutInSeconds <= 0 ) + { + returnValue = p.waitFor(); + } + else + { + long now = System.currentTimeMillis(); + long timeoutInMillis = 1000L * timeoutInSeconds; + long finish = now + timeoutInMillis; + while ( isAlive( p ) && ( System.currentTimeMillis() < finish ) ) + { + Thread.sleep( 10 ); + } + if ( isAlive( p ) ) + { + throw new InterruptedException( "Process timeout out after " + timeoutInSeconds + " seconds" ); + } + returnValue = p.exitValue(); + } - if ( inputFeeder != null ) - { - synchronized ( inputFeeder ) - { - while ( !inputFeeder.isDone() ) + waitForAllPumpers( inputFeeder, outputPumper, errorPumper ); + + if ( outputPumper.getException() != null ) { - inputFeeder.wait(); + throw new CommandLineException( "Error inside systemOut parser", outputPumper.getException() ); } - } - } - synchronized ( outputPumper ) - { - while ( !outputPumper.isDone() ) - { - outputPumper.wait(); - } - } + if ( errorPumper.getException() != null ) + { + throw new CommandLineException( "Error inside systemErr parser", errorPumper.getException() ); + } - synchronized ( errorPumper ) - { - while ( !errorPumper.isDone() ) + return returnValue; + } + catch ( InterruptedException ex ) { - errorPumper.wait(); + if ( inputFeeder != null ) + { + inputFeeder.disable(); + } + outputPumper.disable(); + errorPumper.disable(); + throw new CommandLineTimeOutException( "Error while executing external command, process killed.", ex ); } - } + finally + { + ShutdownHookUtils.removeShutdownHook( processHook ); - processes.remove( new Long( cl.getPid() ) ); + processHook.run(); - if ( outputPumper.getException() != null ) - { - throw new CommandLineException( "Error inside systemOut parser", outputPumper.getException() ); - } + if ( inputFeeder != null ) + { + inputFeeder.close(); + } - if ( errorPumper.getException() != null ) - { - throw new CommandLineException( "Error inside systemErr parser", errorPumper.getException() ); + outputPumper.close(); + + errorPumper.close(); + } } + }; + } - return returnValue; - } - catch ( InterruptedException ex ) + private static void waitForAllPumpers( StreamFeeder inputFeeder, StreamPumper outputPumper, + StreamPumper errorPumper ) + throws InterruptedException + { + if ( inputFeeder != null ) { - killProcess( cl.getPid() ); - throw new CommandLineTimeOutException( "Error while executing external command, process killed.", ex ); + inputFeeder.waitUntilDone(); } - finally - { - if ( inputFeeder != null ) - { - inputFeeder.close(); - } - - outputPumper.close(); - - errorPumper.close(); - p.destroy(); - - if ( processes.get( new Long( cl.getPid() ) ) != null ) - { - processes.remove( new Long( cl.getPid() ) ); - } - } + outputPumper.waitUntilDone(); + errorPumper.waitUntilDone(); } /** @@ -263,7 +246,7 @@ public static int executeCommandLine( Commandline cl, InputStream systemIn, Stre * @return The shell environment variables, can be empty but never null. * @throws IOException If the environment variables could not be queried from the shell. * @see System#getenv() System.getenv() API, new in JDK 5.0, to get the same result - * since 2.0.2 System#getenv() will be used if available in the current running jvm. + * since 2.0.2 System#getenv() will be used if available in the current running jvm. */ public static Properties getSystemEnvVars() throws IOException @@ -277,137 +260,25 @@ public static Properties getSystemEnvVars() * * @param caseSensitive Whether environment variable keys should be treated case-sensitively. * @return Properties object of (possibly modified) envar keys mapped to their values. - * @throws IOException + * @throws IOException . * @see System#getenv() System.getenv() API, new in JDK 5.0, to get the same result - * since 2.0.2 System#getenv() will be used if available in the current running jvm. + * since 2.0.2 System#getenv() will be used if available in the current running jvm. */ public static Properties getSystemEnvVars( boolean caseSensitive ) throws IOException { - - // check if it's 1.5+ run - - Method getenvMethod = getEnvMethod(); - if ( getenvMethod != null ) - { - try - { - return getEnvFromSystem( getenvMethod, caseSensitive ); - } - catch ( IllegalAccessException e ) - { - throw new IOException( e.getMessage() ); - } - catch ( IllegalArgumentException e ) - { - throw new IOException( e.getMessage() ); - } - catch ( InvocationTargetException e ) - { - throw new IOException( e.getMessage() ); - } - } - - Process p = null; - - try - { - Properties envVars = new Properties(); - - Runtime r = Runtime.getRuntime(); - - //If this is windows set the shell to command.com or cmd.exe with correct arguments. - boolean overriddenEncoding = false; - if ( Os.isFamily( Os.FAMILY_WINDOWS ) ) - { - if ( Os.isFamily( Os.FAMILY_WIN9X ) ) - { - p = r.exec( "command.com /c set" ); - } - else - { - overriddenEncoding = true; - // /U = change stdout encoding to UTF-16LE to avoid encoding inconsistency - // between command-line/DOS and GUI/Windows, see PLXUTILS-124 - p = r.exec( "cmd.exe /U /c set" ); - } - } - else - { - p = r.exec( "env" ); - } - - Reader reader = overriddenEncoding - ? new InputStreamReader( p.getInputStream(), ReaderFactory.UTF_16LE ) - : new InputStreamReader( p.getInputStream() ); - BufferedReader br = new BufferedReader( reader ); - - String line; - - String lastKey = null; - String lastVal = null; - - while ( ( line = br.readLine() ) != null ) - { - int idx = line.indexOf( '=' ); - - if ( idx > 0 ) - { - lastKey = line.substring( 0, idx ); - - if ( !caseSensitive ) - { - lastKey = lastKey.toUpperCase( Locale.ENGLISH ); - } - - lastVal = line.substring( idx + 1 ); - - envVars.setProperty( lastKey, lastVal ); - } - else if ( lastKey != null ) - { - lastVal += "\n" + line; - - envVars.setProperty( lastKey, lastVal ); - } - } - - return envVars; - } - finally + Properties envVars = new Properties(); + Map envs = System.getenv(); + for ( String key : envs.keySet() ) { - if ( p != null ) + String value = envs.get( key ); + if ( !caseSensitive) { - p.destroy(); + key = key.toUpperCase( Locale.ENGLISH ); } + envVars.put( key, value ); } - } - - /** - * Kill a process launched by executeCommandLine methods. - * Doesn't work correctly on windows, only the cmd process will be destroy but not the sub process (Bug ID 4770092) - * - * @param pid The pid of command return by Commandline.getPid() - */ - public static void killProcess( long pid ) - { - Process p = (Process) processes.get( new Long( pid ) ); - - if ( p != null ) - { - p.destroy(); - System.out.println( "Process " + pid + " is killed." ); - processes.remove( new Long( pid ) ); - } - else - { - System.out.println( "don't exist." ); - } - } - - public static boolean isAlive( long pid ) - { - return ( processes.get( new Long( pid ) ) != null ); + return envVars; } public static boolean isAlive( Process p ) @@ -443,8 +314,8 @@ public static String[] translateCommandline( String toProcess ) final int inDoubleQuote = 2; int state = normal; StringTokenizer tok = new StringTokenizer( toProcess, "\"\' ", true ); - Vector v = new Vector(); - StringBuffer current = new StringBuffer(); + Vector v = new Vector(); + StringBuilder current = new StringBuilder(); while ( tok.hasMoreTokens() ) { @@ -523,6 +394,7 @@ else if ( " ".equals( nextTok ) ) * {@link StringUtils#quoteAndEscape(String, char, char[], char, boolean)}, or * {@link StringUtils#quoteAndEscape(String, char)} instead. */ + @SuppressWarnings( { "JavaDoc", "deprecation" } ) public static String quote( String argument ) throws CommandLineException { @@ -541,6 +413,7 @@ public static String quote( String argument ) * {@link StringUtils#quoteAndEscape(String, char, char[], char, boolean)}, or * {@link StringUtils#quoteAndEscape(String, char)} instead. */ + @SuppressWarnings( { "JavaDoc", "UnusedDeclaration", "deprecation" } ) public static String quote( String argument, boolean wrapExistingQuotes ) throws CommandLineException { @@ -552,13 +425,14 @@ public static String quote( String argument, boolean wrapExistingQuotes ) * {@link StringUtils#quoteAndEscape(String, char, char[], char, boolean)}, or * {@link StringUtils#quoteAndEscape(String, char)} instead. */ + @SuppressWarnings( { "JavaDoc" } ) public static String quote( String argument, boolean escapeSingleQuotes, boolean escapeDoubleQuotes, boolean wrapExistingQuotes ) throws CommandLineException { - if ( argument.indexOf( "\"" ) > -1 ) + if ( argument.contains( "\"" ) ) { - if ( argument.indexOf( "\'" ) > -1 ) + if ( argument.contains( "\'" ) ) { throw new CommandLineException( "Can't handle single and double quotes in same argument" ); } @@ -574,7 +448,7 @@ else if ( wrapExistingQuotes ) } } } - else if ( argument.indexOf( "\'" ) > -1 ) + else if ( argument.contains( "\'" ) ) { if ( escapeDoubleQuotes ) { @@ -585,7 +459,7 @@ else if ( wrapExistingQuotes ) return '\"' + argument + '\"'; } } - else if ( argument.indexOf( " " ) > -1 ) + else if ( argument.contains( " " ) ) { if ( escapeDoubleQuotes ) { @@ -609,7 +483,7 @@ public static String toString( String[] line ) } // path containing one or more elements - final StringBuffer result = new StringBuffer(); + final StringBuilder result = new StringBuilder(); for ( int i = 0; i < line.length; i++ ) { if ( i > 0 ) @@ -627,39 +501,5 @@ public static String toString( String[] line ) } return result.toString(); } - - private static Method getEnvMethod() - { - try - { - return System.class.getMethod( "getenv", null ); - } - catch ( NoSuchMethodException e ) - { - return null; - } - catch ( SecurityException e ) - { - return null; - } - } - - private static Properties getEnvFromSystem( Method method, boolean caseSensitive ) - throws IllegalAccessException, IllegalArgumentException, InvocationTargetException - { - Properties envVars = new Properties(); - Map envs = (Map) method.invoke( null, null ); - Iterator iterator = envs.keySet().iterator(); - while ( iterator.hasNext() ) - { - String key = (String) iterator.next(); - String value = (String) envs.get( key ); - if ( !caseSensitive ) - { - key = key.toUpperCase( Locale.ENGLISH ); - } - envVars.put( key, value ); - } - return envVars; - } + } diff --git a/src/main/java/org/codehaus/plexus/util/cli/Commandline.java b/src/main/java/org/codehaus/plexus/util/cli/Commandline.java index 5e0d5af4..7346c7ef 100644 --- a/src/main/java/org/codehaus/plexus/util/cli/Commandline.java +++ b/src/main/java/org/codehaus/plexus/util/cli/Commandline.java @@ -139,6 +139,8 @@ public class Commandline * Create a new command line object. * Shell is autodetected from operating system * + * Shell usage is only desirable when generating code for remote execution. + * * @param toProcess */ public Commandline( String toProcess, Shell shell ) @@ -167,6 +169,8 @@ public Commandline( String toProcess, Shell shell ) /** * Create a new command line object. * Shell is autodetected from operating system + * + * Shell usage is only desirable when generating code for remote execution. */ public Commandline( Shell shell ) { @@ -174,8 +178,7 @@ public Commandline( Shell shell ) } /** - * Create a new command line object. - * Shell is autodetected from operating system + * Create a new command line object, given a command following POSIX sh quoting rules * * @param toProcess */ @@ -203,7 +206,6 @@ public Commandline( String toProcess ) /** * Create a new command line object. - * Shell is autodetected from operating system */ public Commandline() { @@ -253,7 +255,7 @@ public int getPosition() { if ( realPos == -1 ) { - realPos = ( getExecutable() == null ? 0 : 1 ); + realPos = ( getLiteralExecutable() == null ? 0 : 1 ); for ( int i = 0; i < position; i++ ) { Arg arg = (Arg) arguments.elementAt( i ); @@ -404,6 +406,21 @@ public void setExecutable( String executable ) this.executable = executable; } + /** + * @return Executable to be run, as a literal string (no shell quoting/munging) + */ + public String getLiteralExecutable() + { + return executable; + } + + /** + * Return an executable name, quoted for shell use. + * + * Shell usage is only desirable when generating code for remote execution. + * + * @return Executable to be run, quoted for shell interpretation + */ public String getExecutable() { String exec = shell.getExecutable(); @@ -483,7 +500,7 @@ public String[] getEnvironmentVariables() public String[] getCommandline() { final String[] args = getArguments(); - String executable = getExecutable(); + String executable = getLiteralExecutable(); if ( executable == null ) { @@ -497,6 +514,8 @@ public String[] getCommandline() /** * Returns the shell, executable and all defined arguments. + * + * Shell usage is only desirable when generating code for remote execution. */ public String[] getShellCommandline() { @@ -633,7 +652,7 @@ public Process execute() { if ( workingDir == null ) { - process = Runtime.getRuntime().exec( getShellCommandline(), environment ); + process = Runtime.getRuntime().exec( getCommandline(), environment, workingDir ); } else { @@ -648,7 +667,7 @@ else if ( !workingDir.isDirectory() ) + "\" does not specify a directory." ); } - process = Runtime.getRuntime().exec( getShellCommandline(), environment, workingDir ); + process = Runtime.getRuntime().exec( getCommandline(), environment, workingDir ); } } catch ( IOException ex ) @@ -669,7 +688,7 @@ private void verifyShellState() shell.setWorkingDirectory( workingDir ); } - if ( shell.getExecutable() == null ) + if ( shell.getOriginalExecutable() == null ) { shell.setExecutable( executable ); } @@ -684,6 +703,8 @@ public Properties getSystemEnvVars() /** * Allows to set the shell to be used in this command line. * + * Shell usage is only desirable when generating code for remote execution. + * * @param shell * @since 1.2 */ @@ -695,6 +716,7 @@ public void setShell( Shell shell ) /** * Get the shell to be used in this command line. * + * Shell usage is only desirable when generating code for remote execution. * @since 1.2 */ public Shell getShell() diff --git a/src/main/java/org/codehaus/plexus/util/cli/ShutdownHookUtils.java b/src/main/java/org/codehaus/plexus/util/cli/ShutdownHookUtils.java new file mode 100644 index 00000000..20873b07 --- /dev/null +++ b/src/main/java/org/codehaus/plexus/util/cli/ShutdownHookUtils.java @@ -0,0 +1,62 @@ +package org.codehaus.plexus.util.cli; + +/* + * 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.security.AccessControlException; + +/** + * A shutdown hook that does not throw any exceptions upon container startup/shutdown or security manager + * restrictions. + * + * Incorrect usage of the hook itself may still throw an exception. + * + * @author Kristian Rosenvold + */ +class ShutdownHookUtils +{ + + public static void addShutDownHook( Thread hook ) + { + try + { + Runtime.getRuntime().addShutdownHook( hook ); + } + catch ( IllegalStateException ignore ) + { + } + catch ( AccessControlException ignore ) + { + } + + + } + + public static void removeShutdownHook( Thread hook ) + { + try + { + Runtime.getRuntime().removeShutdownHook( hook ); + } + catch ( IllegalStateException ignore ) + { + } + catch ( AccessControlException ignore ) + { + } + } + +} diff --git a/src/main/java/org/codehaus/plexus/util/cli/StreamFeeder.java b/src/main/java/org/codehaus/plexus/util/cli/StreamFeeder.java index 4b6ab4cb..4259b656 100644 --- a/src/main/java/org/codehaus/plexus/util/cli/StreamFeeder.java +++ b/src/main/java/org/codehaus/plexus/util/cli/StreamFeeder.java @@ -26,14 +26,11 @@ * @author Trygve Laugstøl * @version $Id$ */ -public class StreamFeeder - extends Thread -{ +public class StreamFeeder extends AbstractStreamHandler { private InputStream input; private OutputStream output; - private boolean done; /** * Create a new StreamFeeder @@ -66,10 +63,9 @@ public void run() { close(); - synchronized ( this ) { - done = true; + setDone(); this.notifyAll(); } @@ -117,11 +113,6 @@ public void close() } } - public boolean isDone() - { - return done; - } - // ---------------------------------------------------------------------- // // ---------------------------------------------------------------------- @@ -131,14 +122,18 @@ private void feed() { int data = input.read(); - while ( !done && data != -1 ) + while ( !isDone() && data != -1 ) { synchronized ( output ) { - output.write( data ); + if ( !isDisabled()) + { + output.write( data ); + } data = input.read(); } } } + } diff --git a/src/main/java/org/codehaus/plexus/util/cli/StreamPumper.java b/src/main/java/org/codehaus/plexus/util/cli/StreamPumper.java index 67d54da6..e9d93f31 100644 --- a/src/main/java/org/codehaus/plexus/util/cli/StreamPumper.java +++ b/src/main/java/org/codehaus/plexus/util/cli/StreamPumper.java @@ -84,11 +84,11 @@ * * @author Florin Vancea * @author Paul Julius - * @since June 11, 2001 * @version $Id$ + * @since June 11, 2001 */ public class StreamPumper - extends Thread + extends AbstractStreamHandler { private final BufferedReader in; @@ -100,8 +100,6 @@ public class StreamPumper private static final int SIZE = 1024; - boolean done; - public StreamPumper( InputStream in ) { this( in, (StreamConsumer) null ); @@ -160,7 +158,7 @@ public void run() synchronized ( this ) { - done = true; + setDone(); this.notifyAll(); } @@ -180,11 +178,6 @@ public void close() IOUtil.close( out ); } - public boolean isDone() - { - return done; - } - public Exception getException() { return exception; @@ -192,7 +185,7 @@ public Exception getException() private void consumeLine( String line ) { - if ( consumer != null ) + if ( consumer != null && !isDisabled() ) { consumer.consumeLine( line ); } diff --git a/src/main/java/org/codehaus/plexus/util/cli/shell/BourneShell.java b/src/main/java/org/codehaus/plexus/util/cli/shell/BourneShell.java index 18e52838..9bf3a09f 100644 --- a/src/main/java/org/codehaus/plexus/util/cli/shell/BourneShell.java +++ b/src/main/java/org/codehaus/plexus/util/cli/shell/BourneShell.java @@ -17,7 +17,6 @@ */ import org.codehaus.plexus.util.Os; -import org.codehaus.plexus.util.StringUtils; import java.util.ArrayList; import java.util.List; @@ -29,37 +28,22 @@ public class BourneShell extends Shell { - private static final char[] BASH_QUOTING_TRIGGER_CHARS = { - ' ', - '$', - ';', - '&', - '|', - '<', - '>', - '*', - '?', - '(', - ')', - '[', - ']', - '{', - '}', - '`' }; public BourneShell() { - this( false ); + this(false); } public BourneShell( boolean isLoginShell ) { + setUnconditionalQuoting( true ); setShellCommand( "/bin/sh" ); setArgumentQuoteDelimiter( '\'' ); - setExecutableQuoteDelimiter( '\"' ); + setExecutableQuoteDelimiter( '\'' ); setSingleQuotedArgumentEscaped( true ); setSingleQuotedExecutableEscaped( false ); setQuotedExecutableEnabled( true ); + setArgumentEscapePattern("'\\%s'"); if ( isLoginShell ) { @@ -75,13 +59,13 @@ public String getExecutable() return super.getExecutable(); } - return unifyQuotes( super.getExecutable()); + return quoteOneItem( super.getOriginalExecutable(), true ); } - public List getShellArgsList() + public List getShellArgsList() { - List shellArgs = new ArrayList(); - List existingShellArgs = super.getShellArgsList(); + List shellArgs = new ArrayList(); + List existingShellArgs = super.getShellArgsList(); if ( ( existingShellArgs != null ) && !existingShellArgs.isEmpty() ) { @@ -122,49 +106,44 @@ protected String getExecutionPreamble() } String dir = getWorkingDirectoryAsString(); - StringBuffer sb = new StringBuffer(); + StringBuilder sb = new StringBuilder(); sb.append( "cd " ); - sb.append( unifyQuotes( dir ) ); + sb.append( quoteOneItem( dir, false ) ); sb.append( " && " ); return sb.toString(); } - protected char[] getQuotingTriggerChars() - { - return BASH_QUOTING_TRIGGER_CHARS; - } - /** *

    Unify quotes in a path for the Bourne Shell.

    * *
    -     * BourneShell.unifyQuotes(null)                       = null
    -     * BourneShell.unifyQuotes("")                         = (empty)
    -     * BourneShell.unifyQuotes("/test/quotedpath'abc")     = /test/quotedpath\'abc
    -     * BourneShell.unifyQuotes("/test/quoted path'abc")    = "/test/quoted path'abc"
    -     * BourneShell.unifyQuotes("/test/quotedpath\"abc")    = "/test/quotedpath\"abc"
    -     * BourneShell.unifyQuotes("/test/quoted path\"abc")   = "/test/quoted path\"abc"
    -     * BourneShell.unifyQuotes("/test/quotedpath\"'abc")   = "/test/quotedpath\"'abc"
    -     * BourneShell.unifyQuotes("/test/quoted path\"'abc")  = "/test/quoted path\"'abc"
    +     * BourneShell.quoteOneItem(null)                       = null
    +     * BourneShell.quoteOneItem("")                         = ''
    +     * BourneShell.quoteOneItem("/test/quotedpath'abc")     = '/test/quotedpath'"'"'abc'
    +     * BourneShell.quoteOneItem("/test/quoted path'abc")    = '/test/quoted pat'"'"'habc'
    +     * BourneShell.quoteOneItem("/test/quotedpath\"abc")    = '/test/quotedpath"abc'
    +     * BourneShell.quoteOneItem("/test/quoted path\"abc")   = '/test/quoted path"abc'
    +     * BourneShell.quoteOneItem("/test/quotedpath\"'abc")   = '/test/quotedpath"'"'"'abc'
    +     * BourneShell.quoteOneItem("/test/quoted path\"'abc")  = '/test/quoted path"'"'"'abc'
          * 
    * * @param path not null path. * @return the path unified correctly for the Bourne shell. */ - protected static String unifyQuotes( String path ) + protected String quoteOneItem( String path, boolean isExecutable ) { if ( path == null ) { return null; } - if ( path.indexOf( " " ) == -1 && path.indexOf( "'" ) != -1 && path.indexOf( "\"" ) == -1 ) - { - return StringUtils.escape( path ); - } + StringBuilder sb = new StringBuilder(); + sb.append( "'" ); + sb.append( path.replace( "'", "'\"'\"'" ) ); + sb.append( "'" ); - return StringUtils.quoteAndEscape( path, '\"', BASH_QUOTING_TRIGGER_CHARS ); + return sb.toString(); } } diff --git a/src/main/java/org/codehaus/plexus/util/cli/shell/CmdShell.java b/src/main/java/org/codehaus/plexus/util/cli/shell/CmdShell.java index 28a2149f..a96bb68a 100644 --- a/src/main/java/org/codehaus/plexus/util/cli/shell/CmdShell.java +++ b/src/main/java/org/codehaus/plexus/util/cli/shell/CmdShell.java @@ -77,9 +77,9 @@ public CmdShell() * appears to make Windows processes invoke successfully. *

    */ - public List getCommandLine( String executable, String[] arguments ) + public List getCommandLine( String executable, String[] arguments ) { - StringBuffer sb = new StringBuffer(); + StringBuilder sb = new StringBuilder(); sb.append( "\"" ); sb.append( super.getCommandLine( executable, arguments ).get( 0 ) ); sb.append( "\"" ); diff --git a/src/main/java/org/codehaus/plexus/util/cli/shell/Shell.java b/src/main/java/org/codehaus/plexus/util/cli/shell/Shell.java index 6e264cef..14fd62d6 100644 --- a/src/main/java/org/codehaus/plexus/util/cli/shell/Shell.java +++ b/src/main/java/org/codehaus/plexus/util/cli/shell/Shell.java @@ -44,10 +44,12 @@ public class Shell private String shellCommand; - private List shellArgs = new ArrayList(); + private List shellArgs = new ArrayList(); private boolean quotedArgumentsEnabled = true; + private boolean unconditionallyQuote = false; + private String executable; private String workingDir; @@ -66,6 +68,18 @@ public class Shell private char exeQuoteDelimiter = '\"'; + private String argumentEscapePattern = "\\%s"; + + /** + * Toggle unconditional quoting + * + * @param unconditionallyQuote + */ + public void setUnconditionalQuoting(boolean unconditionallyQuote) + { + this.unconditionallyQuote = unconditionallyQuote; + } + /** * Set the command to execute the shell (eg. COMMAND.COM, /bin/bash,...) * @@ -122,15 +136,28 @@ public String[] getShellArgs() * @param arguments arguments for the executable, not the shell * @return List with one String object with executable and arguments quoted as needed */ - public List getCommandLine( String executable, String[] arguments ) + public List getCommandLine( String executable, String[] arguments ) { return getRawCommandLine( executable, arguments ); } - protected List getRawCommandLine( String executable, String[] arguments ) + protected String quoteOneItem(String inputString, boolean isExecutable) { - List commandLine = new ArrayList(); - StringBuffer sb = new StringBuffer(); + char[] escapeChars = getEscapeChars( isSingleQuotedExecutableEscaped(), isDoubleQuotedExecutableEscaped() ); + return StringUtils.quoteAndEscape( + inputString, + isExecutable ? getExecutableQuoteDelimiter() : getArgumentQuoteDelimiter(), + escapeChars, + getQuotingTriggerChars(), + '\\', + unconditionallyQuote + ); + } + + protected List getRawCommandLine( String executable, String[] arguments ) + { + List commandLine = new ArrayList(); + StringBuilder sb = new StringBuilder(); if ( executable != null ) { @@ -142,9 +169,7 @@ protected List getRawCommandLine( String executable, String[] arguments ) if ( isQuotedExecutableEnabled() ) { - char[] escapeChars = getEscapeChars( isSingleQuotedExecutableEscaped(), isDoubleQuotedExecutableEscaped() ); - - sb.append( StringUtils.quoteAndEscape( getExecutable(), getExecutableQuoteDelimiter(), escapeChars, getQuotingTriggerChars(), '\\', false ) ); + sb.append( quoteOneItem( getOriginalExecutable(), true ) ); } else { @@ -160,9 +185,7 @@ protected List getRawCommandLine( String executable, String[] arguments ) if ( isQuotedArgumentsEnabled() ) { - char[] escapeChars = getEscapeChars( isSingleQuotedExecutableEscaped(), isDoubleQuotedExecutableEscaped() ); - - sb.append( StringUtils.quoteAndEscape( arguments[i], getArgumentQuoteDelimiter(), escapeChars, getQuotingTriggerChars(), '\\', false ) ); + sb.append( quoteOneItem( arguments[i], false ) ); } else { @@ -187,7 +210,7 @@ protected String getExecutionPreamble() protected char[] getEscapeChars( boolean includeSingleQuote, boolean includeDoubleQuote ) { - StringBuffer buf = new StringBuffer( 2 ); + StringBuilder buf = new StringBuilder( 2 ); if ( includeSingleQuote ) { buf.append( '\'' ); @@ -244,6 +267,15 @@ protected char getExecutableQuoteDelimiter() return exeQuoteDelimiter; } + protected void setArgumentEscapePattern(String argumentEscapePattern) + { + this.argumentEscapePattern = argumentEscapePattern; + } + + protected String getArgumentEscapePattern() { + return argumentEscapePattern; + } + /** * Get the full command line to execute, including shell command, shell arguments, * executable and executable arguments @@ -252,10 +284,10 @@ protected char getExecutableQuoteDelimiter() * @return List of String objects, whose array version is suitable to be used as argument * of Runtime.getRuntime().exec() */ - public List getShellCommandLine( String[] arguments ) + public List getShellCommandLine( String[] arguments ) { - List commandLine = new ArrayList(); + List commandLine = new ArrayList(); if ( getShellCommand() != null ) { @@ -267,13 +299,13 @@ public List getShellCommandLine( String[] arguments ) commandLine.addAll( getShellArgsList() ); } - commandLine.addAll( getCommandLine( getExecutable(), arguments ) ); + commandLine.addAll( getCommandLine( getOriginalExecutable(), arguments ) ); return commandLine; } - public List getShellArgsList() + public List getShellArgsList() { return shellArgs; } @@ -371,7 +403,7 @@ public String getOriginalExecutable() return executable; } - public List getOriginalCommandLine( String executable, String[] arguments ) + public List getOriginalCommandLine( String executable, String[] arguments ) { return getRawCommandLine( executable, arguments ); } @@ -395,5 +427,4 @@ protected void setSingleQuotedExecutableEscaped( boolean singleQuotedExecutableE { this.singleQuotedExecutableEscaped = singleQuotedExecutableEscaped; } - } diff --git a/src/main/java/org/codehaus/plexus/util/dag/CycleDetectedException.java b/src/main/java/org/codehaus/plexus/util/dag/CycleDetectedException.java index 55287d0d..76dd1883 100644 --- a/src/main/java/org/codehaus/plexus/util/dag/CycleDetectedException.java +++ b/src/main/java/org/codehaus/plexus/util/dag/CycleDetectedException.java @@ -22,9 +22,9 @@ public class CycleDetectedException extends Exception { - private List cycle; + private List cycle; - public CycleDetectedException( final String message, final List cycle ) + public CycleDetectedException( final String message, final List cycle ) { super( message ); @@ -32,8 +32,7 @@ public CycleDetectedException( final String message, final List cycle ) } - - public List getCycle() + public List getCycle() { return cycle; } @@ -43,9 +42,9 @@ public List getCycle() */ public String cycleToString() { - final StringBuffer buffer = new StringBuffer(); + final StringBuilder buffer = new StringBuilder(); - for ( Iterator iterator = cycle.iterator(); iterator.hasNext(); ) + for ( Iterator iterator = cycle.iterator(); iterator.hasNext(); ) { buffer.append( iterator.next() ); diff --git a/src/main/java/org/codehaus/plexus/util/dag/CycleDetector.java b/src/main/java/org/codehaus/plexus/util/dag/CycleDetector.java index 4f26c61b..ea285883 100644 --- a/src/main/java/org/codehaus/plexus/util/dag/CycleDetector.java +++ b/src/main/java/org/codehaus/plexus/util/dag/CycleDetector.java @@ -18,7 +18,6 @@ import java.util.Collections; import java.util.HashMap; -import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; @@ -37,18 +36,16 @@ public class CycleDetector private final static Integer VISITED = new Integer( 2 ); - public static List hasCycle( final DAG graph ) + public static List hasCycle( final DAG graph ) { - final List verticies = graph.getVerticies(); + final List verticies = graph.getVerticies(); - final Map vertexStateMap = new HashMap(); + final Map vertexStateMap = new HashMap(); - List retValue = null; + List retValue = null; - for ( final Iterator iter = verticies.iterator(); iter.hasNext(); ) + for ( Vertex vertex : verticies ) { - final Vertex vertex = ( Vertex ) iter.next(); - if ( isNotVisited( vertex, vertexStateMap ) ) { retValue = introducesCycle( vertex, vertexStateMap ); @@ -61,12 +58,10 @@ public static List hasCycle( final DAG graph ) } return retValue; - } - /** - * This method will be called when an egde leading to given vertex was added + * This method will be called when an edge leading to given vertex was added * and we want to check if introduction of this edge has not resulted * in apparition of cycle in the graph * @@ -74,9 +69,9 @@ public static List hasCycle( final DAG graph ) * @param vertexStateMap * @return */ - public static List introducesCycle( final Vertex vertex, final Map vertexStateMap ) + public static List introducesCycle( final Vertex vertex, final Map vertexStateMap ) { - final LinkedList cycleStack = new LinkedList(); + final LinkedList cycleStack = new LinkedList(); final boolean hasCycle = dfsVisit( vertex, cycleStack, vertexStateMap ); @@ -84,15 +79,15 @@ public static List introducesCycle( final Vertex vertex, final Map vertexStateMa { // we have a situation like: [b, a, c, d, b, f, g, h]. // Label of Vertex which introduced the cycle is at the first position in the list - // We have to find second occurence of this label and use its position in the list - // for getting the sublist of vertex labels of cycle paricipants + // We have to find second occurrence of this label and use its position in the list + // for getting the sublist of vertex labels of cycle participants // - // So in our case we are seraching for [b, a, c, d, b] - final String label = ( String ) cycleStack.getFirst(); + // So in our case we are searching for [b, a, c, d, b] + final String label = cycleStack.getFirst(); final int pos = cycleStack.lastIndexOf( label ); - final List cycle = cycleStack.subList( 0, pos + 1 ); + final List cycle = cycleStack.subList( 0, pos + 1 ); Collections.reverse( cycle ); @@ -103,13 +98,11 @@ public static List introducesCycle( final Vertex vertex, final Map vertexStateMa } - public static List introducesCycle( final Vertex vertex ) + public static List introducesCycle( final Vertex vertex ) { - - final Map vertexStateMap = new HashMap(); + final Map vertexStateMap = new HashMap(); return introducesCycle( vertex, vertexStateMap ); - } /** @@ -117,16 +110,11 @@ public static List introducesCycle( final Vertex vertex ) * @param vertexStateMap * @return */ - private static boolean isNotVisited( final Vertex vertex, final Map vertexStateMap ) + private static boolean isNotVisited( final Vertex vertex, final Map vertexStateMap ) { - if ( !vertexStateMap.containsKey( vertex ) ) - { - return true; - } + final Integer state = vertexStateMap.get( vertex ); - final Integer state = ( Integer ) vertexStateMap.get( vertex ); - - return NOT_VISTITED.equals( state ); + return ( state == null ) || NOT_VISTITED.equals( state ); } /** @@ -134,25 +122,22 @@ private static boolean isNotVisited( final Vertex vertex, final Map vertexStateM * @param vertexStateMap * @return */ - private static boolean isVisiting( final Vertex vertex, final Map vertexStateMap ) + private static boolean isVisiting( final Vertex vertex, final Map vertexStateMap ) { - final Integer state = ( Integer ) vertexStateMap.get( vertex ); + final Integer state = vertexStateMap.get( vertex ); return VISITING.equals( state ); } - private static boolean dfsVisit( final Vertex vertex, final LinkedList cycle, final Map vertexStateMap ) + private static boolean dfsVisit( final Vertex vertex, final LinkedList cycle, + final Map vertexStateMap ) { cycle.addFirst( vertex.getLabel() ); vertexStateMap.put( vertex, VISITING ); - final List verticies = vertex.getChildren(); - - for ( final Iterator iter = verticies.iterator(); iter.hasNext(); ) + for ( Vertex v : vertex.getChildren() ) { - final Vertex v = ( Vertex ) iter.next(); - if ( isNotVisited( v, vertexStateMap ) ) { final boolean hasCycle = dfsVisit( v, cycle, vertexStateMap ); @@ -174,9 +159,6 @@ else if ( isVisiting( v, vertexStateMap ) ) cycle.removeFirst(); return false; - } - - } \ No newline at end of file diff --git a/src/main/java/org/codehaus/plexus/util/dag/DAG.java b/src/main/java/org/codehaus/plexus/util/dag/DAG.java index 89c23b68..715f7624 100644 --- a/src/main/java/org/codehaus/plexus/util/dag/DAG.java +++ b/src/main/java/org/codehaus/plexus/util/dag/DAG.java @@ -28,7 +28,7 @@ * * @author Michal Maczka * @version $Id$ - * @todo this class should be reanmed from DAG to Dag + * @todo this class should be renamed from DAG to Dag */ public class DAG implements Cloneable, Serializable { @@ -36,18 +36,18 @@ public class DAG implements Cloneable, Serializable //Fields //------------------------------------------------------------ /** - * Nodes will be kept in two data strucures at the same time + * Nodes will be kept in two data structures at the same time * for faster processing */ /** * Maps vertex's label to vertex */ - private Map vertexMap = new HashMap(); + private Map vertexMap = new HashMap(); /** * Conatin list of all verticies */ - private List vertexList = new ArrayList(); + private List vertexList = new ArrayList(); // ------------------------------------------------------------ // Constructors @@ -68,17 +68,15 @@ public DAG() /** * @return */ - public List getVerticies() + public List getVerticies() { return vertexList; } - public Set getLabels() + public Set getLabels() { - final Set retValue = vertexMap.keySet(); - - return retValue; + return vertexMap.keySet(); } // ------------------------------------------------------------ @@ -100,7 +98,7 @@ public Vertex addVertex( final String label ) // check if vertex is alredy in DAG if ( vertexMap.containsKey( label ) ) { - retValue = ( Vertex ) vertexMap.get( label ); + retValue = vertexMap.get( label ); } else { @@ -130,7 +128,7 @@ public void addEdge( final Vertex from, final Vertex to ) throws CycleDetectedEx to.addEdgeFrom( from ); - final List cycle = CycleDetector.introducesCycle( to ); + final List cycle = CycleDetector.introducesCycle( to ); if ( cycle != null ) { @@ -185,7 +183,7 @@ public boolean hasEdge( final String label1, final String label2 ) * @param label * @return */ - public List getChildLabels( final String label ) + public List getChildLabels( final String label ) { final Vertex vertex = getVertex( label ); @@ -196,7 +194,7 @@ public List getChildLabels( final String label ) * @param label * @return */ - public List getParentLabels( final String label ) + public List getParentLabels( final String label ) { final Vertex vertex = getVertex( label ); @@ -241,16 +239,16 @@ public boolean isConnected( final String label ) * the label passed as parameter to this method. This label should * always be the last item in the list. */ - public List getSuccessorLabels( final String label ) + public List getSuccessorLabels( final String label ) { final Vertex vertex = getVertex( label ); - final List retValue; + final List retValue; //optimization. if ( vertex.isLeaf() ) { - retValue = new ArrayList( 1 ); + retValue = new ArrayList( 1 ); retValue.add( label ); } diff --git a/src/main/java/org/codehaus/plexus/util/dag/TopologicalSorter.java b/src/main/java/org/codehaus/plexus/util/dag/TopologicalSorter.java index 4a5f30d1..6c570a50 100644 --- a/src/main/java/org/codehaus/plexus/util/dag/TopologicalSorter.java +++ b/src/main/java/org/codehaus/plexus/util/dag/TopologicalSorter.java @@ -17,7 +17,6 @@ */ import java.util.HashMap; -import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; @@ -40,37 +39,30 @@ public class TopologicalSorter * @return List of String (vertex labels) */ - public static List sort( final DAG graph ) + public static List sort( final DAG graph ) { return dfs( graph ); } - public static List sort( final Vertex vertex ) + public static List sort( final Vertex vertex ) { // we need to use addFirst method so we will use LinkedList explicitly - final LinkedList retValue = new LinkedList(); + final List retValue = new LinkedList(); - final Map vertexStateMap = new HashMap(); - - dfsVisit( vertex, vertexStateMap, retValue ); + dfsVisit( vertex, new HashMap(), retValue ); return retValue; } - private static List dfs( final DAG graph ) + private static List dfs( final DAG graph ) { - final List verticies = graph.getVerticies(); - // we need to use addFirst method so we will use LinkedList explicitly - final LinkedList retValue = new LinkedList(); + final List retValue = new LinkedList(); + final Map vertexStateMap = new HashMap(); - final Map vertexStateMap = new HashMap(); - - for ( final Iterator iter = verticies.iterator(); iter.hasNext(); ) + for ( Vertex vertex : graph.getVerticies() ) { - final Vertex vertex = ( Vertex ) iter.next(); - if ( isNotVisited( vertex, vertexStateMap ) ) { dfsVisit( vertex, vertexStateMap, retValue ); @@ -85,28 +77,21 @@ private static List dfs( final DAG graph ) * @param vertexStateMap * @return */ - private static boolean isNotVisited( final Vertex vertex, final Map vertexStateMap ) + private static boolean isNotVisited( final Vertex vertex, final Map vertexStateMap ) { - if ( !vertexStateMap.containsKey( vertex ) ) - { - return true; - } - final Integer state = ( Integer ) vertexStateMap.get( vertex ); + final Integer state = vertexStateMap.get( vertex ); - return NOT_VISTITED.equals( state ); + return ( state == null ) || NOT_VISTITED.equals( state ); } - private static void dfsVisit( final Vertex vertex, final Map vertexStateMap, final LinkedList list ) + private static void dfsVisit( final Vertex vertex, final Map vertexStateMap, + final List list ) { vertexStateMap.put( vertex, VISITING ); - final List verticies = vertex.getChildren(); - - for ( final Iterator iter = verticies.iterator(); iter.hasNext(); ) + for ( Vertex v : vertex.getChildren() ) { - final Vertex v = ( Vertex ) iter.next(); - if ( isNotVisited( v, vertexStateMap ) ) { dfsVisit( v, vertexStateMap, list ); diff --git a/src/main/java/org/codehaus/plexus/util/dag/Vertex.java b/src/main/java/org/codehaus/plexus/util/dag/Vertex.java index 873c430c..a9f1324b 100644 --- a/src/main/java/org/codehaus/plexus/util/dag/Vertex.java +++ b/src/main/java/org/codehaus/plexus/util/dag/Vertex.java @@ -18,7 +18,6 @@ import java.io.Serializable; import java.util.ArrayList; -import java.util.Iterator; import java.util.List; /** @@ -32,9 +31,9 @@ public class Vertex implements Cloneable, Serializable //------------------------------------------------------------ private String label = null; - List children = new ArrayList(); + List children = new ArrayList(); - List parents = new ArrayList(); + List parents = new ArrayList(); // ------------------------------------------------------------ @@ -90,13 +89,11 @@ public void addEdgeFrom( final Vertex vertex ) public void removeEdgeFrom( final Vertex vertex ) { - parents.remove( vertex ); - } - public List getChildren() + public List getChildren() { return children; } @@ -107,14 +104,12 @@ public List getChildren() * * @return the labels used by the most direct children. */ - public List getChildLabels() + public List getChildLabels() { - final List retValue = new ArrayList( children.size() ); + final List retValue = new ArrayList( children.size() ); - for ( final Iterator iter = children.iterator(); iter.hasNext(); ) + for ( Vertex vertex : children ) { - final Vertex vertex = ( Vertex ) iter.next(); - retValue.add( vertex.getLabel() ); } return retValue; @@ -126,7 +121,7 @@ public List getChildLabels() * * @return list of parents */ - public List getParents() + public List getParents() { return parents; } @@ -137,14 +132,12 @@ public List getParents() * * @return the labels used parents */ - public List getParentLabels() + public List getParentLabels() { - final List retValue = new ArrayList( parents.size() ); + final List retValue = new ArrayList( parents.size() ); - for ( final Iterator iter = parents.iterator(); iter.hasNext(); ) + for ( Vertex vertex : parents ) { - final Vertex vertex = ( Vertex ) iter.next(); - retValue.add( vertex.getLabel() ); } return retValue; diff --git a/src/main/java/org/codehaus/plexus/util/introspection/ClassMap.java b/src/main/java/org/codehaus/plexus/util/introspection/ClassMap.java index a8b06765..1fb667c1 100644 --- a/src/main/java/org/codehaus/plexus/util/introspection/ClassMap.java +++ b/src/main/java/org/codehaus/plexus/util/introspection/ClassMap.java @@ -23,7 +23,7 @@ /** * A cache of introspection information for a specific class instance. - * Keys {@link java.lang.Method} objects by a concatenation of the + * Keys {@link java.lang.reflect.Method} objects by a concatenation of the * method name and the names of classes that make up the parameters. * * @author Jason van Zyl @@ -46,7 +46,7 @@ private static final class CacheMiss * the basis for the Method map. */ - private Class clazz; + private final Class clazz; /** * Cache of Methods, or CACHE_MISS, keyed by method @@ -187,9 +187,9 @@ private String makeMethodKey( Method method ) { Class[] parameterTypes = method.getParameterTypes(); - StringBuffer methodKey = new StringBuffer( method.getName() ); + StringBuilder methodKey = new StringBuilder( method.getName() ); - for ( int j = 0; j < parameterTypes.length; j++ ) + for ( Class parameterType : parameterTypes ) { /* * If the argument type is primitive then we want @@ -197,28 +197,44 @@ private String makeMethodKey( Method method ) * corresponding Object type so introspection for * methods with primitive types will work correctly. */ - if ( parameterTypes[j].isPrimitive() ) + if ( parameterType.isPrimitive() ) { - if ( parameterTypes[j].equals( Boolean.TYPE ) ) + if ( parameterType.equals( Boolean.TYPE ) ) + { methodKey.append( "java.lang.Boolean" ); - else if ( parameterTypes[j].equals( Byte.TYPE ) ) + } + else if ( parameterType.equals( Byte.TYPE ) ) + { methodKey.append( "java.lang.Byte" ); - else if ( parameterTypes[j].equals( Character.TYPE ) ) + } + else if ( parameterType.equals( Character.TYPE ) ) + { methodKey.append( "java.lang.Character" ); - else if ( parameterTypes[j].equals( Double.TYPE ) ) + } + else if ( parameterType.equals( Double.TYPE ) ) + { methodKey.append( "java.lang.Double" ); - else if ( parameterTypes[j].equals( Float.TYPE ) ) + } + else if ( parameterType.equals( Float.TYPE ) ) + { methodKey.append( "java.lang.Float" ); - else if ( parameterTypes[j].equals( Integer.TYPE ) ) + } + else if ( parameterType.equals( Integer.TYPE ) ) + { methodKey.append( "java.lang.Integer" ); - else if ( parameterTypes[j].equals( Long.TYPE ) ) + } + else if ( parameterType.equals( Long.TYPE ) ) + { methodKey.append( "java.lang.Long" ); - else if ( parameterTypes[j].equals( Short.TYPE ) ) + } + else if ( parameterType.equals( Short.TYPE ) ) + { methodKey.append( "java.lang.Short" ); + } } else { - methodKey.append( parameterTypes[j].getName() ); + methodKey.append( parameterType.getName() ); } } @@ -227,11 +243,11 @@ else if ( parameterTypes[j].equals( Short.TYPE ) ) private static String makeMethodKey( String method, Object[] params ) { - StringBuffer methodKey = new StringBuffer().append( method ); + StringBuilder methodKey = new StringBuilder().append( method ); - for ( int j = 0; j < params.length; j++ ) + for ( Object param : params ) { - Object arg = params[j]; + Object arg = param; if ( arg == null ) { @@ -287,9 +303,8 @@ private static Method[] getAccessibleMethods( Class clazz ) } int j = 0; - for ( int i = 0; i < methodInfos.length; ++i ) + for ( MethodInfo methodInfo : methodInfos ) { - MethodInfo methodInfo = methodInfos[i]; if ( methodInfo.upcast ) { methods[j++] = methodInfo.method; @@ -426,7 +441,7 @@ public static Method getPublicMethod( Method method ) * Looks up the method with specified name and signature in the first public * superclass or implemented interface of the class. * - * @param class the class whose method is sought + * @param clazz the class whose method is sought * @param name the name of the method * @param paramTypes the classes of method parameters */ diff --git a/src/main/java/org/codehaus/plexus/util/introspection/MethodMap.java b/src/main/java/org/codehaus/plexus/util/introspection/MethodMap.java index bf733097..1b6a27e5 100644 --- a/src/main/java/org/codehaus/plexus/util/introspection/MethodMap.java +++ b/src/main/java/org/codehaus/plexus/util/introspection/MethodMap.java @@ -42,28 +42,27 @@ public class MethodMap /** * Keep track of all methods with the same name. */ - Map methodByNameMap = new Hashtable(); + Map> methodByNameMap = new Hashtable>(); /** * Add a method to a list of methods by name. * For a particular class we are keeping track * of all the methods with the same name. + * @param method The method */ public void add(Method method) { String methodName = method.getName(); - List l = get( methodName ); + List l = get( methodName ); if ( l == null) { - l = new ArrayList(); + l = new ArrayList(); methodByNameMap.put(methodName, l); } l.add(method); - - return; } /** @@ -72,9 +71,9 @@ public void add(Method method) * @param key The name of the method. * @return List list of methods */ - public List get(String key) + public List get(String key) { - return (List) methodByNameMap.get(key); + return methodByNameMap.get(key); } /** @@ -145,7 +144,7 @@ public static class AmbiguousException extends Exception private static Method getMostSpecific(List methods, Class[] classes) throws AmbiguousException { - LinkedList applicables = getApplicables(methods, classes); + LinkedList applicables = getApplicables(methods, classes); if(applicables.isEmpty()) { @@ -154,7 +153,7 @@ private static Method getMostSpecific(List methods, Class[] classes) if(applicables.size() == 1) { - return (Method)applicables.getFirst(); + return applicables.getFirst(); } /* @@ -163,21 +162,18 @@ private static Method getMostSpecific(List methods, Class[] classes) * (the most specific method) otherwise we have ambiguity. */ - LinkedList maximals = new LinkedList(); + LinkedList maximals = new LinkedList(); - for (Iterator applicable = applicables.iterator(); - applicable.hasNext();) + for ( Method app : applicables ) { - Method app = (Method) applicable.next(); Class[] appArgs = app.getParameterTypes(); boolean lessSpecific = false; - for (Iterator maximal = maximals.iterator(); - !lessSpecific && maximal.hasNext();) + for ( Iterator maximal = maximals.iterator(); !lessSpecific && maximal.hasNext(); ) { Method max = (Method) maximal.next(); - switch(moreSpecific(appArgs, max.getParameterTypes())) + switch ( moreSpecific( appArgs, max.getParameterTypes() ) ) { case MORE_SPECIFIC: { @@ -205,9 +201,9 @@ private static Method getMostSpecific(List methods, Class[] classes) } } - if(!lessSpecific) + if ( !lessSpecific ) { - maximals.addLast(app); + maximals.addLast( app ); } } @@ -217,7 +213,7 @@ private static Method getMostSpecific(List methods, Class[] classes) throw new AmbiguousException(); } - return (Method)maximals.getFirst(); + return maximals.getFirst(); } /** @@ -282,17 +278,17 @@ private static int moreSpecific(Class[] c1, Class[] c2) * formal and actual arguments matches, and argument types are assignable * to formal types through a method invocation conversion). */ - private static LinkedList getApplicables(List methods, Class[] classes) + private static LinkedList getApplicables(List methods, Class[] classes) { - LinkedList list = new LinkedList(); + LinkedList list = new LinkedList(); - for (Iterator imethod = methods.iterator(); imethod.hasNext();) + for ( Object method1 : methods ) { - Method method = (Method) imethod.next(); + Method method = (Method) method1; - if(isApplicable(method, classes)) + if ( isApplicable( method, classes ) ) { - list.add(method); + list.add( method ); } } @@ -302,6 +298,9 @@ private static LinkedList getApplicables(List methods, Class[] classes) /** * Returns true if the supplied method is applicable to actual * argument types. + * @param method The method to check for applicability + * @param classes The arguments + * @return true if the method applies to the parameter types */ private static boolean isApplicable(Method method, Class[] classes) { diff --git a/src/main/java/org/codehaus/plexus/util/introspection/ReflectionValueExtractor.java b/src/main/java/org/codehaus/plexus/util/introspection/ReflectionValueExtractor.java index 2be103b5..53a124f2 100644 --- a/src/main/java/org/codehaus/plexus/util/introspection/ReflectionValueExtractor.java +++ b/src/main/java/org/codehaus/plexus/util/introspection/ReflectionValueExtractor.java @@ -16,66 +16,142 @@ * limitations under the License. */ +import java.lang.ref.WeakReference; +import java.lang.reflect.Array; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; -import java.util.Arrays; import java.util.List; -import java.util.WeakHashMap; import java.util.Map; -import java.util.StringTokenizer; -import java.util.regex.Matcher; -import java.util.regex.Pattern; +import java.util.WeakHashMap; import org.codehaus.plexus.util.StringUtils; /** - *

    Using simple dotted expressions to extract the values from an Object instance, - * For example we might want to extract a value like: project.build.sourceDirectory

    - * - *

    The implementation supports indexed, nested and mapped properties similar to the JSP way.

    - * + *

    + * Using simple dotted expressions to extract the values from an Object instance, For example we might want to extract a + * value like: project.build.sourceDirectory + *

    + *

    + * The implementation supports indexed, nested and mapped properties similar to the JSP way. + *

    + * * @author Jason van Zyl * @author Vincent Siveton * @version $Id$ - * @see http://struts.apache.org/1.x/struts-taglib/indexedprops.html + * @see http://struts.apache.org/1.x/struts-taglib/indexedprops.html */ public class ReflectionValueExtractor { - private static final Class[] CLASS_ARGS = new Class[0]; + private static final Class[] CLASS_ARGS = new Class[0]; private static final Object[] OBJECT_ARGS = new Object[0]; /** - * Use a WeakHashMap here, so the keys (Class objects) can be garbage collected. - * This approach prevents permgen space overflows due to retention of discarded - * classloaders. + * Use a WeakHashMap here, so the keys (Class objects) can be garbage collected. This approach prevents permgen + * space overflows due to retention of discarded classloaders. */ - private static final Map classMaps = new WeakHashMap(); + private static final Map, WeakReference> classMaps = + new WeakHashMap, WeakReference>(); - /** - * Indexed properties pattern, ie (\\w+)\\[(\\d+)\\] - */ - private static final Pattern INDEXED_PROPS = Pattern.compile( "(\\w+)\\[(\\d+)\\]" ); + static final int EOF = -1; - /** - * Indexed properties pattern, ie (\\w+)\\((.+)\\) - */ - private static final Pattern MAPPED_PROPS = Pattern.compile( "(\\w+)\\((.+)\\)" ); + static final char PROPERTY_START = '.'; + + static final char INDEXED_START = '['; + + static final char INDEXED_END = ']'; + + static final char MAPPED_START = '('; + + static final char MAPPED_END = ')'; + + static class Tokenizer + { + final String expression; + + int idx; + + public Tokenizer( String expression ) + { + this.expression = expression; + } + + public int peekChar() + { + return idx < expression.length() ? expression.charAt( idx ) : EOF; + } + + public int skipChar() + { + return idx < expression.length() ? expression.charAt( idx++ ) : EOF; + } + + public String nextToken( char delimiter ) + { + int start = idx; + + while ( idx < expression.length() && delimiter != expression.charAt( idx ) ) + { + idx++; + } + + // delimiter MUST be present + if ( idx <= start || idx >= expression.length() ) + { + return null; + } + + return expression.substring( start, idx++ ); + } + + public String nextPropertyName() + { + final int start = idx; + + while ( idx < expression.length() && Character.isJavaIdentifierPart( expression.charAt( idx ) ) ) + { + idx++; + } + + // property name does not require delimiter + if ( idx <= start || idx > expression.length() ) + { + return null; + } + + return expression.substring( start, idx ); + } + + public int getPosition() + { + return idx < expression.length() ? idx : EOF; + } + + // to make tokenizer look pretty in debugger + @Override + public String toString() + { + return idx < expression.length() ? expression.substring( idx ) : ""; + } + } private ReflectionValueExtractor() { } /** - *

    The implementation supports indexed, nested and mapped properties.

    - * + *

    + * The implementation supports indexed, nested and mapped properties. + *

    *
      *
    • nested properties should be defined by a dot, i.e. "user.address.street"
    • *
    • indexed properties (java.util.List or array instance) should be contains (\\w+)\\[(\\d+)\\] * pattern, i.e. "user.addresses[1].street"
    • - *
    • mapped properties should be contains (\\w+)\\((.+)\\) pattern, i.e. "user.addresses(myAddress).street"
    • + *
    • mapped properties should be contains (\\w+)\\((.+)\\) pattern, i.e. + * "user.addresses(myAddress).street"
    • *
        - * + * * @param expression not null expression * @param root not null object * @return the object defined by the expression @@ -88,30 +164,26 @@ public static Object evaluate( String expression, Object root ) } /** - *

        The implementation supports indexed, nested and mapped properties.

        - * + *

        + * The implementation supports indexed, nested and mapped properties. + *

        *
          *
        • nested properties should be defined by a dot, i.e. "user.address.street"
        • *
        • indexed properties (java.util.List or array instance) should be contains (\\w+)\\[(\\d+)\\] * pattern, i.e. "user.addresses[1].street"
        • - *
        • mapped properties should be contains (\\w+)\\((.+)\\) pattern, i.e. "user.addresses(myAddress).street"
        • + *
        • mapped properties should be contains (\\w+)\\((.+)\\) pattern, i.e. + * "user.addresses(myAddress).street"
        • *
            - * + * * @param expression not null expression * @param root not null object * @return the object defined by the expression * @throws Exception if any */ // TODO: don't throw Exception - public static Object evaluate( String expression, Object root, boolean trimRootToken ) + public static Object evaluate( final String expression, final Object root, final boolean trimRootToken ) throws Exception { - // if the root token refers to the supplied root object parameter, remove it. - if ( trimRootToken ) - { - expression = expression.substring( expression.indexOf( '.' ) + 1 ); - } - Object value = root; // ---------------------------------------------------------------------- @@ -119,128 +191,166 @@ public static Object evaluate( String expression, Object root, boolean trimRootT // MavenProject instance. // ---------------------------------------------------------------------- - StringTokenizer parser = new StringTokenizer( expression, "." ); + if ( StringUtils.isEmpty( expression ) || !Character.isJavaIdentifierStart( expression.charAt( 0 ) ) ) + { + return null; + } + + final Tokenizer tokenizer; + if ( trimRootToken ) + { + tokenizer = new Tokenizer( expression ); + tokenizer.nextPropertyName(); + } + else + { + tokenizer = new Tokenizer( "." + expression ); + } - while ( parser.hasMoreTokens() ) + int propertyPosition = tokenizer.getPosition(); + while ( value != null && tokenizer.peekChar() != EOF ) { - // if we have nothing, stop now - if ( value == null ) + switch ( tokenizer.skipChar() ) { - return null; + case INDEXED_START: + value = + getIndexedValue( expression, propertyPosition, tokenizer.getPosition(), value, + tokenizer.nextToken( INDEXED_END ) ); + break; + case MAPPED_START: + value = + getMappedValue( expression, propertyPosition, tokenizer.getPosition(), value, + tokenizer.nextToken( MAPPED_END ) ); + break; + case PROPERTY_START: + propertyPosition = tokenizer.getPosition(); + value = getPropertyValue( value, tokenizer.nextPropertyName() ); + break; + default: + // could not parse expression + return null; } + } - String token = parser.nextToken(); + return value; + } + private static Object getMappedValue( final String expression, final int from, final int to, final Object value, + final String key ) + throws Exception + { + if ( value == null || key == null ) + { + return null; + } + + if ( value instanceof Map ) + { + Object[] localParams = new Object[] { key }; ClassMap classMap = getClassMap( value.getClass() ); + Method method = classMap.findMethod( "get", localParams ); + return method.invoke( value, localParams ); + } + + final String message = + String.format( "The token '%s' at position '%d' refers to a java.util.Map, but the value seems is an instance of '%s'", + expression.subSequence( from, to ), from, value.getClass() ); + + throw new Exception( message ); + } - Method method; - Object[] localParams = OBJECT_ARGS; + private static Object getIndexedValue( final String expression, final int from, final int to, final Object value, + final String indexStr ) + throws Exception + { + try + { + int index = Integer.parseInt( indexStr ); - // do we have an indexed property? - Matcher matcher = INDEXED_PROPS.matcher( token ); - if ( matcher.find() ) + if ( value.getClass().isArray() ) { - String methodBase = StringUtils.capitalizeFirstLetter( matcher.group( 1 ) ); - String methodName = "get" + methodBase; - method = classMap.findMethod( methodName, CLASS_ARGS ); - value = method.invoke( value, OBJECT_ARGS ); - classMap = getClassMap( value.getClass() ); - - if ( classMap.getCachedClass().isArray() ) - { - value = Arrays.asList( (Object[]) value ); - classMap = getClassMap( value.getClass() ); - } - - if ( value instanceof List ) - { - // use get method on List interface - localParams = new Object[1]; - localParams[0] = Integer.valueOf( matcher.group( 2 ) ); - method = classMap.findMethod( "get", localParams ); - } - else - { - throw new Exception( "The token '" + token - + "' refers to a java.util.List or an array, but the value seems is an instance of '" - + value.getClass() + "'." ); - } + return Array.get( value, index ); } - else + + if ( value instanceof List ) { - // do we have a mapped property? - matcher = MAPPED_PROPS.matcher( token ); - if ( matcher.find() ) - { - String methodBase = StringUtils.capitalizeFirstLetter( matcher.group( 1 ) ); - String methodName = "get" + methodBase; - method = classMap.findMethod( methodName, CLASS_ARGS ); - value = method.invoke( value, OBJECT_ARGS ); - classMap = getClassMap( value.getClass() ); - - if ( value instanceof Map ) - { - // use get method on List interface - localParams = new Object[1]; - localParams[0] = matcher.group( 2 ); - method = classMap.findMethod( "get", localParams ); - } - else - { - throw new Exception( "The token '" + token - + "' refers to a java.util.Map, but the value seems is an instance of '" - + value.getClass() + "'." ); - } - } - else - { - String methodBase = StringUtils.capitalizeFirstLetter( token ); - String methodName = "get" + methodBase; - method = classMap.findMethod( methodName, CLASS_ARGS ); - - if ( method == null ) - { - // perhaps this is a boolean property?? - methodName = "is" + methodBase; - - method = classMap.findMethod( methodName, CLASS_ARGS ); - } - } + ClassMap classMap = getClassMap( value.getClass() ); + // use get method on List interface + Object[] localParams = new Object[] { index }; + Method method = classMap.findMethod( "get", localParams ); + return method.invoke( value, localParams ); } - - if ( method == null ) + } + catch ( NumberFormatException e ) + { + return null; + } + catch ( InvocationTargetException e ) + { + // catch array index issues gracefully, otherwise release + if ( e.getCause() instanceof IndexOutOfBoundsException ) { return null; } - try - { - value = method.invoke( value, localParams ); - } - catch ( InvocationTargetException e ) - { - // catch array index issues gracefully, otherwise release - if ( e.getCause() instanceof IndexOutOfBoundsException ) - { - return null; - } + throw e; + } - throw e; - } + final String message = + String.format( "The token '%s' at position '%d' refers to a java.util.List or an array, but the value seems is an instance of '%s'", + expression.subSequence( from, to ), from, value.getClass() ); + + throw new Exception( message ); + } + + private static Object getPropertyValue( Object value, String property ) + throws Exception + { + if ( value == null || property == null ) + { + return null; } - return value; + ClassMap classMap = getClassMap( value.getClass() ); + String methodBase = StringUtils.capitalizeFirstLetter( property ); + String methodName = "get" + methodBase; + Method method = classMap.findMethod( methodName, CLASS_ARGS ); + + if ( method == null ) + { + // perhaps this is a boolean property?? + methodName = "is" + methodBase; + + method = classMap.findMethod( methodName, CLASS_ARGS ); + } + + if ( method == null ) + { + return null; + } + + try + { + return method.invoke( value, OBJECT_ARGS ); + } + catch ( InvocationTargetException e ) + { + throw e; + } } - private static ClassMap getClassMap( Class clazz ) + private static ClassMap getClassMap( Class clazz ) { - ClassMap classMap = (ClassMap) classMaps.get( clazz ); - if ( classMap == null ) + WeakReference softRef = classMaps.get( clazz ); + + ClassMap classMap; + + if ( softRef == null || ( classMap = softRef.get() ) == null ) { classMap = new ClassMap( clazz ); - classMaps.put( clazz, classMap ); + classMaps.put( clazz, new WeakReference( classMap ) ); } return classMap; diff --git a/src/main/java/org/codehaus/plexus/util/io/RawInputStreamFacade.java b/src/main/java/org/codehaus/plexus/util/io/RawInputStreamFacade.java index 008ce6ac..c982b8fb 100644 --- a/src/main/java/org/codehaus/plexus/util/io/RawInputStreamFacade.java +++ b/src/main/java/org/codehaus/plexus/util/io/RawInputStreamFacade.java @@ -22,12 +22,10 @@ /** * Implementation of {@link InputStreamFacade} for raw input streams. */ +@SuppressWarnings( { "UnusedDeclaration" } ) public class RawInputStreamFacade implements InputStreamFacade { final InputStream stream; - /** - * Creates a new instance. - */ public RawInputStreamFacade( InputStream stream ) { this.stream = stream; diff --git a/src/main/java/org/codehaus/plexus/util/io/URLInputStreamFacade.java b/src/main/java/org/codehaus/plexus/util/io/URLInputStreamFacade.java index bd09669b..9bd53d9f 100644 --- a/src/main/java/org/codehaus/plexus/util/io/URLInputStreamFacade.java +++ b/src/main/java/org/codehaus/plexus/util/io/URLInputStreamFacade.java @@ -26,9 +26,6 @@ public class URLInputStreamFacade implements InputStreamFacade { private final URL url; - /** - * Creates a new instance. - */ public URLInputStreamFacade( URL url ) { this.url = url; diff --git a/src/main/java/org/codehaus/plexus/util/reflection/Reflector.java b/src/main/java/org/codehaus/plexus/util/reflection/Reflector.java index 1655803b..199c876a 100644 --- a/src/main/java/org/codehaus/plexus/util/reflection/Reflector.java +++ b/src/main/java/org/codehaus/plexus/util/reflection/Reflector.java @@ -56,7 +56,8 @@ public Reflector() * @throws ReflectorException * In case anything goes wrong here... */ - public Object newInstance( Class theClass, Object[] params ) + @SuppressWarnings( { "UnusedDeclaration" } ) + public T newInstance( Class theClass, Object[] params ) throws ReflectorException { if ( params == null ) @@ -73,19 +74,19 @@ public Object newInstance( Class theClass, Object[] params ) try { - Constructor con = getConstructor( theClass, paramTypes ); + Constructor con = getConstructor( theClass, paramTypes ); if ( con == null ) { - StringBuffer buffer = new StringBuffer(); + StringBuilder buffer = new StringBuilder(); buffer.append( "Constructor not found for class: " ); buffer.append( theClass.getName() ); buffer.append( " with specified or ancestor parameter classes: " ); - for ( int i = 0; i < paramTypes.length; i++ ) + for ( Class paramType : paramTypes ) { - buffer.append( paramTypes[i].getName() ); + buffer.append( paramType.getName() ); buffer.append( ',' ); } @@ -123,7 +124,8 @@ public Object newInstance( Class theClass, Object[] params ) * @throws ReflectorException * In case anything goes wrong here... */ - public Object getSingleton( Class theClass, Object[] initParams ) + @SuppressWarnings( { "UnusedDeclaration" } ) + public T getSingleton( Class theClass, Object[] initParams ) throws ReflectorException { Class[] paramTypes = new Class[initParams.length]; @@ -137,7 +139,8 @@ public Object getSingleton( Class theClass, Object[] initParams ) { Method method = getMethod( theClass, GET_INSTANCE_METHOD_NAME, paramTypes ); - return method.invoke( null, initParams ); + //noinspection unchecked + return (T) method.invoke( null, initParams ); } catch ( InvocationTargetException ex ) { @@ -163,6 +166,7 @@ public Object getSingleton( Class theClass, Object[] initParams ) * @throws ReflectorException * In case of an error looking up or invoking the method. */ + @SuppressWarnings( { "UnusedDeclaration" } ) public Object invoke( Object target, String methodName, Object[] params ) throws ReflectorException { @@ -184,14 +188,14 @@ public Object invoke( Object target, String methodName, Object[] params ) if ( method == null ) { - StringBuffer buffer = new StringBuffer(); + StringBuilder buffer = new StringBuilder(); buffer.append( "Singleton-producing method named '" ).append( methodName ) .append( "' not found with specified parameter classes: " ); - for ( int i = 0; i < paramTypes.length; i++ ) + for ( Class paramType : paramTypes ) { - buffer.append( paramTypes[i].getName() ); + buffer.append( paramType.getName() ); buffer.append( ',' ); } @@ -212,6 +216,7 @@ public Object invoke( Object target, String methodName, Object[] params ) } } + @SuppressWarnings( { "UnusedDeclaration" } ) public Object getStaticField( Class targetClass, String fieldName ) throws ReflectorException { @@ -239,6 +244,7 @@ public Object getStaticField( Class targetClass, String fieldName ) } } + @SuppressWarnings( { "UnusedDeclaration" } ) public Object getField( Object target, String fieldName ) throws ReflectorException { @@ -303,6 +309,7 @@ public Object getField( Object target, String fieldName, boolean breakAccessibil * @throws ReflectorException * In case of an error looking up or invoking the method. */ + @SuppressWarnings( { "UnusedDeclaration" } ) public Object invokeStatic( Class targetClass, String methodName, Object[] params ) throws ReflectorException { @@ -324,14 +331,15 @@ public Object invokeStatic( Class targetClass, String methodName, Object[] param if ( method == null ) { - StringBuffer buffer = new StringBuffer(); + StringBuilder buffer = new StringBuilder(); - buffer.append( "Singleton-producing method named \'" + methodName - + "\' not found with specified parameter classes: " ); + buffer.append( "Singleton-producing method named \'" ) + .append( methodName ) + .append( "\' not found with specified parameter classes: " ); - for ( int i = 0; i < paramTypes.length; i++ ) + for ( Class paramType : paramTypes ) { - buffer.append( paramTypes[i].getName() ); + buffer.append( paramType.getName() ); buffer.append( ',' ); } @@ -365,12 +373,12 @@ public Object invokeStatic( Class targetClass, String methodName, Object[] param * @throws ReflectorException * In case we can't retrieve the proper constructor. */ - public Constructor getConstructor( Class targetClass, Class[] params ) + public Constructor getConstructor( Class targetClass, Class[] params ) throws ReflectorException { - Map constructorMap = getConstructorMap( targetClass ); + Map> constructorMap = getConstructorMap( targetClass ); - StringBuffer key = new StringBuffer( 200 ); + StringBuilder key = new StringBuilder( 200 ); key.append( "(" ); @@ -387,17 +395,18 @@ public Constructor getConstructor( Class targetClass, Class[] params ) key.append( ")" ); - Constructor constructor = null; + Constructor constructor; String paramKey = key.toString(); synchronized ( paramKey.intern() ) { - constructor = (Constructor) constructorMap.get( paramKey ); + constructor = constructorMap.get( paramKey ); if ( constructor == null ) { - Constructor[] cands = targetClass.getConstructors(); + @SuppressWarnings( { "unchecked" } ) + Constructor[] cands = (Constructor[]) targetClass.getConstructors(); for ( int i = 0, len = cands.length; i < len; i++ ) { @@ -435,7 +444,7 @@ public Constructor getConstructor( Class targetClass, Class[] params ) public Object getObjectProperty( Object target, String propertyName ) throws ReflectorException { - Object returnValue = null; + Object returnValue; if ( propertyName == null || propertyName.trim().length() < 1 ) { @@ -494,7 +503,7 @@ public Object getObjectProperty( Object target, String propertyName ) else { returnValue = getField( target, propertyName, true ); - if ( method == null && returnValue == null ) + if ( returnValue == null ) { // TODO: Check if exception is the right action! Field exists, but contains null throw new ReflectorException( "Neither method: \'" + propertyName + "\' nor bean accessor: \'" @@ -535,9 +544,9 @@ public Method getMethod( Class targetClass, String methodName, Class[] params ) private Method _getMethod( Class targetClass, String methodName, Class[] params ) throws ReflectorException { - Map methodMap = getMethodMap( targetClass, methodName ); + Map methodMap = (Map) getMethodMap( targetClass, methodName ); - StringBuffer key = new StringBuffer( 200 ); + StringBuilder key = new StringBuilder( 200 ); key.append( "(" ); @@ -549,7 +558,7 @@ private Method _getMethod( Class targetClass, String methodName, Class[] params key.append( ")" ); - Method method = null; + Method method; String paramKey = key.toString(); @@ -604,10 +613,10 @@ private Method _getMethod( Class targetClass, String methodName, Class[] params * @throws ReflectorException * in case of a lookup error. */ - private Map getConstructorMap( Class theClass ) + private Map> getConstructorMap( Class theClass ) throws ReflectorException { - return getMethodMap( theClass, CONSTRUCTOR_METHOD_NAME ); + return (Map>) getMethodMap( theClass, CONSTRUCTOR_METHOD_NAME ); } /** @@ -621,10 +630,10 @@ private Map getConstructorMap( Class theClass ) * @throws ReflectorException * in case of a lookup error. */ - private Map getMethodMap( Class theClass, String methodName ) + private Map getMethodMap( Class theClass, String methodName ) throws ReflectorException { - Map methodMap = null; + Map methodMap; if ( theClass == null ) { @@ -635,14 +644,13 @@ private Map getMethodMap( Class theClass, String methodName ) synchronized ( className.intern() ) { - Map classMethods = (Map) classMaps.get( className ); + Map> classMethods = (Map>) classMaps.get( className ); if ( classMethods == null ) { classMethods = new HashMap(); - methodMap = new HashMap(); + methodMap = new HashMap(); classMethods.put( methodName, methodMap ); - classMaps.put( className, classMethods ); } else @@ -651,11 +659,11 @@ private Map getMethodMap( Class theClass, String methodName ) synchronized ( key.intern() ) { - methodMap = (Map) classMethods.get( methodName ); + methodMap = classMethods.get( methodName ); if ( methodMap == null ) { - methodMap = new HashMap(); + methodMap = new HashMap(); classMethods.put( methodName, methodMap ); } } diff --git a/src/main/java/org/codehaus/plexus/util/reflection/ReflectorException.java b/src/main/java/org/codehaus/plexus/util/reflection/ReflectorException.java index de564e2f..567b060c 100644 --- a/src/main/java/org/codehaus/plexus/util/reflection/ReflectorException.java +++ b/src/main/java/org/codehaus/plexus/util/reflection/ReflectorException.java @@ -26,9 +26,7 @@ public class ReflectorException extends Exception { - /** - * Create a new ReflectorException. - */ + @SuppressWarnings( { "UnusedDeclaration" } ) public ReflectorException() { } diff --git a/src/main/java/org/codehaus/plexus/util/xml/PrettyPrintXMLWriter.java b/src/main/java/org/codehaus/plexus/util/xml/PrettyPrintXMLWriter.java index ce8b3526..fae43373 100644 --- a/src/main/java/org/codehaus/plexus/util/xml/PrettyPrintXMLWriter.java +++ b/src/main/java/org/codehaus/plexus/util/xml/PrettyPrintXMLWriter.java @@ -206,30 +206,51 @@ private void writeText( String text, boolean escapeXml ) write( StringUtils.unifyLineSeparators( text, lineSeparator ) ); } + private static final Pattern amp = Pattern.compile( "&" ); + private static final Pattern lt = Pattern.compile( "<" ); + private static final Pattern gt = Pattern.compile( ">" ); + private static final Pattern dqoute = Pattern.compile( "\"" ); + private static final Pattern sqoute = Pattern.compile( "\'" ); + private static String escapeXml( String text ) { - text = text.replaceAll( "&", "&" ); - - text = text.replaceAll( "<", "<" ); + if (text.indexOf('&') >= 0){ + text = amp.matcher( text ).replaceAll( "&" ); + } + if (text.indexOf('<') >= 0){ + text = lt.matcher( text ).replaceAll( "<" ); + } + if (text.indexOf('>') >= 0){ + text = gt.matcher( text ).replaceAll( ">" ); + } + if (text.indexOf('"') >= 0){ + text = dqoute.matcher( text ).replaceAll( """ ); + } + if (text.indexOf('\'') >= 0){ + text = sqoute.matcher( text ).replaceAll( "'" ); + } - text = text.replaceAll( ">", ">" ); + return text; + } - text = text.replaceAll( "\"", """ ); + private static final String crlf_str = "\r\n"; - text = text.replaceAll( "\'", "'" ); + private static final Pattern crlf = Pattern.compile( crlf_str ); + private static final Pattern lowers = Pattern.compile( "([\000-\037])" ); - return text; - } private static String escapeXmlAttribute( String text ) { text = escapeXml( text ); // Windows - text = text.replaceAll( "\r\n", " " ); + Matcher crlfmatcher = crlf.matcher( text ); + if (text.contains( crlf_str )) + { + text = crlfmatcher.replaceAll( " " ); + } - Pattern pattern = Pattern.compile( "([\000-\037])" ); - Matcher m = pattern.matcher( text ); + Matcher m = lowers.matcher( text ); StringBuffer b = new StringBuffer(); while ( m.find() ) { diff --git a/src/main/java/org/codehaus/plexus/util/xml/XmlReader.java b/src/main/java/org/codehaus/plexus/util/xml/XmlReader.java index 7f15c96e..b489e219 100644 --- a/src/main/java/org/codehaus/plexus/util/xml/XmlReader.java +++ b/src/main/java/org/codehaus/plexus/util/xml/XmlReader.java @@ -732,7 +732,7 @@ private static String getXmlProlog( BufferedInputStream is, String guessedEnc ) { is.reset(); BufferedReader bReader = new BufferedReader( new StringReader( xmlProlog.substring( 0, firstGT + 1 ) ) ); - StringBuffer prolog = new StringBuffer(); + StringBuilder prolog = new StringBuilder(); String line = bReader.readLine(); while ( line != null ) { diff --git a/src/main/java/org/codehaus/plexus/util/xml/XmlWriterUtil.java b/src/main/java/org/codehaus/plexus/util/xml/XmlWriterUtil.java index c1a90c7a..913b5f27 100644 --- a/src/main/java/org/codehaus/plexus/util/xml/XmlWriterUtil.java +++ b/src/main/java/org/codehaus/plexus/util/xml/XmlWriterUtil.java @@ -206,17 +206,16 @@ public static void writeComment( XMLWriter writer, String comment, int indent, i String[] sentences = StringUtils.split( comment, LS ); StringBuffer line = new StringBuffer( indentation + " + + + + + + + + Facades to create {@code InputStream}s representing various kinds of + data sources, identically. + + diff --git a/src/main/javadoc/org/codehaus/plexus/util/package.html b/src/main/javadoc/org/codehaus/plexus/util/package.html index 79bf2c28..e4ff4b2c 100644 --- a/src/main/javadoc/org/codehaus/plexus/util/package.html +++ b/src/main/javadoc/org/codehaus/plexus/util/package.html @@ -1,3 +1,3 @@ -Misc. utilities. - \ No newline at end of file +Miscellaneous utilities, mainly dealing with I/O, strings, and filesystems. + diff --git a/src/site/site.xml b/src/site/site.xml index d339cb1b..38cbad8a 100644 --- a/src/site/site.xml +++ b/src/site/site.xml @@ -1,4 +1,17 @@ - + + - + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/java/org/codehaus/plexus/util/DirectoryScannerTest.java b/src/test/java/org/codehaus/plexus/util/DirectoryScannerTest.java index efe277a9..3f6cc58d 100644 --- a/src/test/java/org/codehaus/plexus/util/DirectoryScannerTest.java +++ b/src/test/java/org/codehaus/plexus/util/DirectoryScannerTest.java @@ -391,7 +391,7 @@ private void assertInclusionsAndExclusions( String[] files, String[] excludedPat } } - StringBuffer buffer = new StringBuffer(); + StringBuilder buffer = new StringBuilder(); if ( !failedToExclude.isEmpty() ) { buffer.append( "Should NOT have included:\n" ).append( diff --git a/src/test/java/org/codehaus/plexus/util/FileBasedTestCase.java b/src/test/java/org/codehaus/plexus/util/FileBasedTestCase.java index 84ea3aba..c7151e78 100644 --- a/src/test/java/org/codehaus/plexus/util/FileBasedTestCase.java +++ b/src/test/java/org/codehaus/plexus/util/FileBasedTestCase.java @@ -32,7 +32,6 @@ import junit.framework.AssertionFailedError; import junit.framework.TestCase; -import org.codehaus.plexus.util.FileUtils; /** * Base class for testcases doing tests with files. diff --git a/src/test/java/org/codehaus/plexus/util/FileUtilsTest.java b/src/test/java/org/codehaus/plexus/util/FileUtilsTest.java index 88b59de2..4fce7142 100644 --- a/src/test/java/org/codehaus/plexus/util/FileUtilsTest.java +++ b/src/test/java/org/codehaus/plexus/util/FileUtilsTest.java @@ -127,12 +127,12 @@ public void testToFileNull() public void testToURLs() throws Exception { - File[] files = new File[]{new File( "file1" ), new File( "file2" ),}; + File[] files = new File[]{ new File( "file1" ), new File( "file2" ), }; URL[] urls = FileUtils.toURLs( files ); - assertEquals( "The length of the generated URL's is not equals to the length of files. " + "Was " + files - .length + ", expected " + urls.length, files.length, urls.length ); + assertEquals( "The length of the generated URL's is not equals to the length of files. " + "Was " + files.length + + ", expected " + urls.length, files.length, urls.length ); for ( int i = 0; i < urls.length; i++ ) { @@ -147,7 +147,7 @@ public void testGetFilesFromExtension() // Non-existent files final String[] emptyFileNames = - FileUtils.getFilesFromExtension( getTestDirectory().getAbsolutePath(), new String[]{"java"} ); + FileUtils.getFilesFromExtension( getTestDirectory().getAbsolutePath(), new String[]{ "java" } ); assertTrue( emptyFileNames.length == 0 ); // Existing files @@ -178,7 +178,7 @@ public void testMkdir() FileUtils.mkdir( winFile.getAbsolutePath() ); assertTrue( false ); } - catch ( IllegalArgumentException e) + catch ( IllegalArgumentException e ) { assertTrue( true ); } @@ -307,7 +307,7 @@ public void testForceMkdir() FileUtils.forceMkdir( winFile ); assertTrue( false ); } - catch ( IllegalArgumentException e) + catch ( IllegalArgumentException e ) { assertTrue( true ); } @@ -379,6 +379,25 @@ public void testCopyFile2() assertTrue( "Check Full copy", destination.length() == testFile2Size ); } + /** + * ensure we create directory tree for destination + * + * @throws Exception + */ + public void testCopyFile3() + throws Exception + { + File destDirectory = new File( getTestDirectory(), "foo/bar/testcopy" ); + if ( destDirectory.exists() ) + { + destDirectory.delete(); + } + final File destination = new File( destDirectory, "copy2.txt" ); + FileUtils.copyFile( testFile1, destination ); + assertTrue( "Check Exist", destination.exists() ); + assertTrue( "Check Full copy", destination.length() == testFile2Size ); + } + // copyFileIfModified public void testCopyIfModifiedWhenSourceIsNewer() @@ -391,7 +410,7 @@ public void testCopyIfModifiedWhenSourceIsNewer() FileUtils.copyFile( testFile1, destination ); // Make sure source is newer - Thread.sleep( 1000 ); + reallySleep( 1000 ); // Place source File source = new File( getTestDirectory(), "copy1.txt" ); @@ -412,7 +431,7 @@ public void testCopyIfModifiedWhenSourceIsOlder() FileUtils.copyFile( testFile1, source ); // Make sure desintation is newer - Thread.sleep( 1000 ); + reallySleep( 1000 ); // Place destination File desintation = new File( getTestDirectory(), "/temp/copy1.txt" ); @@ -556,12 +575,14 @@ public void testResolveFileDot() public void testNormalize() throws Exception { - final String[] src = {"", "/", "///", "/foo", "/foo//", "/./", "/foo/./", "/foo/./bar", "/foo/../bar", - "/foo/../bar/../baz", "/foo/bar/../../baz", "/././", "/foo/./../bar", "/foo/.././bar/", "//foo//./bar", - "/../", "/foo/../../"}; + final String[] src = + { "", "/", "///", "/foo", "/foo//", "/./", "/foo/./", "/foo/./bar", "/foo/../bar", "/foo/../bar/../baz", + "/foo/bar/../../baz", "/././", "/foo/./../bar", "/foo/.././bar/", "//foo//./bar", "/../", + "/foo/../../" }; - final String[] dest = {"", "/", "/", "/foo", "/foo/", "/", "/foo/", "/foo/bar", "/bar", "/baz", "/baz", "/", - "/bar", "/bar/", "/foo/bar", null, null}; + final String[] dest = + { "", "/", "/", "/foo", "/foo/", "/", "/foo/", "/foo/bar", "/bar", "/baz", "/baz", "/", "/bar", "/bar/", + "/foo/bar", null, null }; assertEquals( "Oops, test writer goofed", src.length, dest.length ); @@ -574,7 +595,7 @@ public void testNormalize() private String replaceAll( String text, String lookFor, String replaceWith ) { - StringBuffer sb = new StringBuffer( text ); + StringBuilder sb = new StringBuilder( text ); while ( true ) { int idx = sb.indexOf( lookFor ); @@ -631,12 +652,8 @@ public void testFileUtils() public void testGetExtension() { final String[][] tests = - {{"filename.ext", "ext"} - , {"README", ""} - , {"domain.dot.com", "com"} - , {"image.jpeg", "jpeg"} - , {"folder" + File.separator + "image.jpeg", "jpeg"} - , {"folder" + File.separator + "README", ""}}; + { { "filename.ext", "ext" }, { "README", "" }, { "domain.dot.com", "com" }, { "image.jpeg", "jpeg" }, + { "folder" + File.separator + "image.jpeg", "jpeg" }, { "folder" + File.separator + "README", "" } }; for ( int i = 0; i < tests.length; i++ ) { @@ -650,14 +667,13 @@ public void testGetExtensionWithPaths() // Since the utilities are based on the separator for the platform // running the test, ensure we are using the right separator final String sep = File.separator; - final String[][] testsWithPaths = { - { sep + "tmp" + sep + "foo" + sep + "filename.ext", "ext"} - , {"C:" + sep + "temp" + sep + "foo" + sep + "filename.ext", "ext"} - , {"" + sep + "tmp" + sep + "foo.bar" + sep + "filename.ext", "ext"} - , {"C:" + sep + "temp" + sep + "foo.bar" + sep + "filename.ext", "ext"} - , {"" + sep + "tmp" + sep + "foo.bar" + sep + "README", ""} - , {"C:" + sep + "temp" + sep + "foo.bar" + sep + "README", ""} - , {".." + sep + "filename.ext", "ext"}, {"blabla", ""}}; + final String[][] testsWithPaths = { { sep + "tmp" + sep + "foo" + sep + "filename.ext", "ext" }, + { "C:" + sep + "temp" + sep + "foo" + sep + "filename.ext", "ext" }, + { "" + sep + "tmp" + sep + "foo.bar" + sep + "filename.ext", "ext" }, + { "C:" + sep + "temp" + sep + "foo.bar" + sep + "filename.ext", "ext" }, + { "" + sep + "tmp" + sep + "foo.bar" + sep + "README", "" }, + { "C:" + sep + "temp" + sep + "foo.bar" + sep + "README", "" }, { ".." + sep + "filename.ext", "ext" }, + { "blabla", "" } }; for ( int i = 0; i < testsWithPaths.length; i++ ) { assertEquals( testsWithPaths[i][1], FileUtils.getExtension( testsWithPaths[i][0] ) ); @@ -667,8 +683,8 @@ public void testGetExtensionWithPaths() public void testRemoveExtension() { - final String[][] tests = {{"filename.ext", "filename"}, {"first.second.third.ext", "first.second.third"}, - {"README", "README"}, {"domain.dot.com", "domain.dot"}, {"image.jpeg", "image"}}; + final String[][] tests = { { "filename.ext", "filename" }, { "first.second.third.ext", "first.second.third" }, + { "README", "README" }, { "domain.dot.com", "domain.dot" }, { "image.jpeg", "image" } }; for ( int i = 0; i < tests.length; i++ ) { @@ -684,20 +700,17 @@ public void testRemoveExtensionWithPaths() // running the test, ensure we are using the right separator final String sep = File.separator; final String[][] testsWithPaths = - {{sep + "tmp" + sep + "foo" + sep + "filename.ext" - , sep + "tmp" + sep + "foo" + sep + "filename"} - , {"C:" + sep + "temp" + sep + "foo" + sep + "filename.ext" - , "C:" + sep + "temp" + sep + "foo" + sep + "filename"} - , {sep + "tmp" + sep + "foo.bar" + sep + "filename.ext" - , sep + "tmp" + sep + "foo.bar" + sep + "filename"} - , {"C:" + sep + "temp" + sep + "foo.bar" + sep + "filename.ext" - , "C:" + sep + "temp" + sep + "foo.bar" + sep + "filename"} - , {sep + "tmp" + sep + "foo.bar" + sep + "README" - , sep + "tmp" + sep + "foo.bar" + sep + "README"} - , {"C:" + sep + "temp" + sep + "foo.bar" + sep + "README" - , "C:" + sep + "temp" + sep + "foo.bar" + sep + "README"} - , {".." + sep + "filename.ext" - , ".." + sep + "filename"}}; + { { sep + "tmp" + sep + "foo" + sep + "filename.ext", sep + "tmp" + sep + "foo" + sep + "filename" }, + { "C:" + sep + "temp" + sep + "foo" + sep + "filename.ext", + "C:" + sep + "temp" + sep + "foo" + sep + "filename" }, + { sep + "tmp" + sep + "foo.bar" + sep + "filename.ext", + sep + "tmp" + sep + "foo.bar" + sep + "filename" }, + { "C:" + sep + "temp" + sep + "foo.bar" + sep + "filename.ext", + "C:" + sep + "temp" + sep + "foo.bar" + sep + "filename" }, + { sep + "tmp" + sep + "foo.bar" + sep + "README", sep + "tmp" + sep + "foo.bar" + sep + "README" }, + { "C:" + sep + "temp" + sep + "foo.bar" + sep + "README", + "C:" + sep + "temp" + sep + "foo.bar" + sep + "README" }, + { ".." + sep + "filename.ext", ".." + sep + "filename" } }; for ( int i = 0; i < testsWithPaths.length; i++ ) { @@ -825,9 +838,9 @@ public void testCopyDirectoryStructureIfModified() FileUtils.copyDirectoryStructureIfModified( from, to ); - File files[] = {new File( to, "root.txt" ), new File( to, "2/2.txt" ), new File( to, "2/2_1/2_1.txt" )}; + File files[] = { new File( to, "root.txt" ), new File( to, "2/2.txt" ), new File( to, "2/2_1/2_1.txt" ) }; - long timestamps[] = {files[0].lastModified(), files[1].lastModified(), files[2].lastModified()}; + long timestamps[] = { files[0].lastModified(), files[1].lastModified(), files[2].lastModified() }; checkFile( fRoot, files[0] ); @@ -913,13 +926,13 @@ public void testFilteredFileCopy() filterProperties.setProperty( "s", "sample text" ); // test ${token} - FileUtils.FilterWrapper[] wrappers1 = new FileUtils.FilterWrapper[]{new FileUtils.FilterWrapper() + FileUtils.FilterWrapper[] wrappers1 = new FileUtils.FilterWrapper[]{ new FileUtils.FilterWrapper() { public Reader getReader( Reader reader ) { return new InterpolationFilterReader( reader, filterProperties, "${", "}" ); } - }}; + } }; File srcFile = new File( getTestDirectory(), "root.txt" ); FileUtils.fileWrite( srcFile.getAbsolutePath(), "UTF-8", "This is a test. Test ${s}\n" ); @@ -991,7 +1004,8 @@ public void testFilteredWithoutFilterAndOlderFileAndOverwrite() } - public void testFileRead() throws IOException + public void testFileRead() + throws IOException { File testFile = new File( getTestDirectory(), "testFileRead.txt" ); String testFileName = testFile.getAbsolutePath(); @@ -1018,7 +1032,8 @@ public void testFileRead() throws IOException testFile.delete(); } - public void testFileReadWithEncoding() throws IOException + public void testFileReadWithEncoding() + throws IOException { String encoding = "UTF-8"; File testFile = new File( getTestDirectory(), "testFileRead.txt" ); @@ -1041,7 +1056,8 @@ public void testFileReadWithEncoding() throws IOException testFile.delete(); } - public void testFileAppend() throws IOException + public void testFileAppend() + throws IOException { String baseString = "abc"; File testFile = new File( getTestDirectory(), "testFileAppend.txt" ); @@ -1064,7 +1080,8 @@ public void testFileAppend() throws IOException testFile.delete(); } - public void testFileAppendWithEncoding() throws IOException + public void testFileAppendWithEncoding() + throws IOException { String baseString = "abc"; String encoding = "UTF-8"; @@ -1088,7 +1105,8 @@ public void testFileAppendWithEncoding() throws IOException testFile.delete(); } - public void testFileWrite() throws IOException + public void testFileWrite() + throws IOException { File testFile = new File( getTestDirectory(), "testFileWrite.txt" ); String testFileName = testFile.getAbsolutePath(); @@ -1099,7 +1117,8 @@ public void testFileWrite() throws IOException testFile.delete(); } - public void testFileWriteWithEncoding() throws IOException + public void testFileWriteWithEncoding() + throws IOException { String encoding = "UTF-8"; File testFile = new File( getTestDirectory(), "testFileWrite.txt" ); @@ -1114,11 +1133,10 @@ public void testFileWriteWithEncoding() throws IOException /** * Workaround for the following Sun bugs. They are fixed in JDK 6u1 and JDK 5u11. * + * @throws Exception * @see Sun bug id=4403166 * @see Sun bug id=6182812 * @see Sun bug id=6481955 - * - * @throws Exception */ public void testDeleteLongPathOnWindows() throws Exception @@ -1133,7 +1151,7 @@ public void testDeleteLongPathOnWindows() File a1 = new File( a, "a" ); a1.mkdir(); - StringBuffer path = new StringBuffer( "" ); + StringBuilder path = new StringBuilder( "" ); for ( int i = 0; i < 100; i++ ) { path.append( "../a/" ); @@ -1177,17 +1195,11 @@ public void testExtensions() throws Exception { - String[][] values = { - { "fry.frozen", "frozen" }, - { "fry", "" }, - { "fry.", "" }, - { "/turanga/leela/meets.fry", "fry" }, - { "/3000/turanga.leela.fry/zoidberg.helps", "helps" }, - { "/3000/turanga.leela.fry/zoidberg.", "" }, - { "/3000/turanga.leela.fry/zoidberg", "" }, - { "/3000/leela.fry.bender/", "" }, - { "/3000/leela.fry.bdner/.", "" }, - { "/3000/leela.fry.bdner/foo.bar.txt", "txt" } }; + String[][] values = + { { "fry.frozen", "frozen" }, { "fry", "" }, { "fry.", "" }, { "/turanga/leela/meets.fry", "fry" }, + { "/3000/turanga.leela.fry/zoidberg.helps", "helps" }, { "/3000/turanga.leela.fry/zoidberg.", "" }, + { "/3000/turanga.leela.fry/zoidberg", "" }, { "/3000/leela.fry.bender/", "" }, + { "/3000/leela.fry.bdner/.", "" }, { "/3000/leela.fry.bdner/foo.bar.txt", "txt" } }; for ( int i = 0; i < values.length; i++ ) { @@ -1324,18 +1336,36 @@ public void testcopyDirectoryLayoutWithExcludesIncludes() /** * Be sure that {@link FileUtils#createTempFile(String, String, File)} is always unique. + * * @throws Exception if any */ public void testCreateTempFile() throws Exception { File last = FileUtils.createTempFile( "unique", ".tmp", null ); - for (int i = 0; i < 10; i ++ ) + for ( int i = 0; i < 10; i++ ) { File current = FileUtils.createTempFile( "unique", ".tmp", null ); - assertTrue( "No unique name: " + current.getName(), - !current.getName().equals( last.getName() ) ); + assertTrue( "No unique name: " + current.getName(), !current.getName().equals( last.getName() ) ); last = current; } } + + /** + * Because windows(tm) quite frequently sleeps less than the advertised time + * + * @param time The amount of time to sleep + * @throws InterruptedException + */ + private void reallySleep( int time ) + throws InterruptedException + { + long until = System.currentTimeMillis() + time; + Thread.sleep( time ); + while ( System.currentTimeMillis() < until ) + { + Thread.sleep( time / 10 ); + Thread.yield(); + } + } } diff --git a/src/test/java/org/codehaus/plexus/util/LineOrientedInterpolatingReaderTest.java b/src/test/java/org/codehaus/plexus/util/LineOrientedInterpolatingReaderTest.java index 9852b5bd..66ae435e 100644 --- a/src/test/java/org/codehaus/plexus/util/LineOrientedInterpolatingReaderTest.java +++ b/src/test/java/org/codehaus/plexus/util/LineOrientedInterpolatingReaderTest.java @@ -75,9 +75,7 @@ public void testShouldInterpolateExpressionAtEndOfDataWithInvalidEndToken() thro public void testDefaultInterpolationWithNonInterpolatedValueAtEnd() throws Exception { - Map m = new HashMap(); - m.put( "name", "jason" ); - m.put( "noun", "asshole" ); + Map m = getStandardMap(); String foo = "${name} is an ${noun}. ${not.interpolated}"; @@ -90,11 +88,17 @@ public void testDefaultInterpolationWithNonInterpolatedValueAtEnd() throws Excep assertEquals( "jason is an asshole. ${not.interpolated}", bar ); } - public void testDefaultInterpolationWithEscapedExpression() throws Exception + private Map getStandardMap() { - Map m = new HashMap(); + Map m = new HashMap(); m.put( "name", "jason" ); m.put( "noun", "asshole" ); + return m; + } + + public void testDefaultInterpolationWithEscapedExpression() throws Exception + { + Map m = getStandardMap(); String foo = "${name} is an ${noun}. \\${noun} value"; @@ -109,9 +113,7 @@ public void testDefaultInterpolationWithEscapedExpression() throws Exception public void testDefaultInterpolationWithInterpolatedValueAtEnd() throws Exception { - Map m = new HashMap(); - m.put( "name", "jason" ); - m.put( "noun", "asshole" ); + Map m = getStandardMap(); String foo = "${name} is an ${noun}"; @@ -126,9 +128,7 @@ public void testDefaultInterpolationWithInterpolatedValueAtEnd() throws Exceptio public void testInterpolationWithSpecifiedBoundaryTokens() throws Exception { - Map m = new HashMap(); - m.put( "name", "jason" ); - m.put( "noun", "asshole" ); + Map m = getStandardMap(); String foo = "@name@ is an @noun@. @not.interpolated@ baby @foo@. @bar@"; @@ -144,9 +144,7 @@ public void testInterpolationWithSpecifiedBoundaryTokens() throws Exception public void testInterpolationWithSpecifiedBoundaryTokensWithNonInterpolatedValueAtEnd() throws Exception { - Map m = new HashMap(); - m.put( "name", "jason" ); - m.put( "noun", "asshole" ); + Map m = getStandardMap(); String foo = "@name@ is an @foobarred@"; @@ -162,9 +160,7 @@ public void testInterpolationWithSpecifiedBoundaryTokensWithNonInterpolatedValue public void testInterpolationWithSpecifiedBoundaryTokensWithInterpolatedValueAtEnd() throws Exception { - Map m = new HashMap(); - m.put( "name", "jason" ); - m.put( "noun", "asshole" ); + Map m = getStandardMap(); String foo = "@name@ is an @noun@"; diff --git a/src/main/java/org/codehaus/plexus/util/io/FileInputStreamFacade.java b/src/test/java/org/codehaus/plexus/util/MatchPatternTest.java similarity index 54% rename from src/main/java/org/codehaus/plexus/util/io/FileInputStreamFacade.java rename to src/test/java/org/codehaus/plexus/util/MatchPatternTest.java index 70a452e1..ded393ea 100644 --- a/src/main/java/org/codehaus/plexus/util/io/FileInputStreamFacade.java +++ b/src/test/java/org/codehaus/plexus/util/MatchPatternTest.java @@ -1,5 +1,4 @@ -package org.codehaus.plexus.util.io; - +package org.codehaus.plexus.util; /* * Copyright The Codehaus Foundation. * @@ -16,28 +15,18 @@ * limitations under the License. */ -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; +import junit.framework.TestCase; /** - * Implementation of {@link InputStreamFacade} for files. + * @author Kristian Rosenvold */ -public class FileInputStreamFacade implements InputStreamFacade { - private final File file; - - /** - * Creates a new instance. - */ - public FileInputStreamFacade( File file ) +public class MatchPatternTest extends TestCase +{ + public void testMatchPath() + throws Exception { - this.file = file; - } + MatchPattern mp = MatchPattern.fromString( "ABC*" ); + assertTrue(mp.matchPath( "ABCD", true )); - public InputStream getInputStream() throws IOException { - return new FileInputStream( file ); } - - } diff --git a/src/test/java/org/codehaus/plexus/util/MatchPatternsTest.java b/src/test/java/org/codehaus/plexus/util/MatchPatternsTest.java new file mode 100644 index 00000000..b75b5b54 --- /dev/null +++ b/src/test/java/org/codehaus/plexus/util/MatchPatternsTest.java @@ -0,0 +1,32 @@ +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 junit.framework.TestCase; + +public class MatchPatternsTest + extends TestCase +{ + public void testMatches() + throws Exception + { + MatchPatterns from = MatchPatterns.from( "ABC**", "CDE**" ); + assertTrue( from.matches( "ABCDE", true ) ); + assertTrue( from.matches( "CDEF", true ) ); + assertFalse( from.matches( "XYZ", true ) ); + + } +} diff --git a/src/test/java/org/codehaus/plexus/util/PerfTest.java b/src/test/java/org/codehaus/plexus/util/PerfTest.java new file mode 100644 index 00000000..e4873548 --- /dev/null +++ b/src/test/java/org/codehaus/plexus/util/PerfTest.java @@ -0,0 +1,58 @@ +package org.codehaus.plexus.util; + +import junit.framework.TestCase; + +/* + * Copyright 2011 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. + */ + +public class PerfTest + extends TestCase +{ + + String src = "012345578901234556789012345678901234456789012345678901234567890"; + + private final int oops = 100; + + public void testSubString() + { + StringBuilder res = new StringBuilder(); + int len = src.length(); + for ( int cnt = 0; cnt < oops; cnt++ ) + { + for ( int i = 0; i < len - 5; i++ ) + { + res.append( src.substring( i, i+4 ) ); + } + } + int i = res.length(); + System.out.println( "i = " + i ); + } + + public void testResDir() + { + StringBuilder res = new StringBuilder(); + int len = src.length(); + for ( int cnt = 0; cnt < oops; cnt++ ) + { + for ( int i = 0; i < len - 5; i++ ) + { + res.append( src, i, i+4 ); + } + } + int i = res.length(); + System.out.println( "i = " + i ); + } +} diff --git a/src/test/java/org/codehaus/plexus/util/SelectorUtilsTest.java b/src/test/java/org/codehaus/plexus/util/SelectorUtilsTest.java new file mode 100644 index 00000000..aeef7a6f --- /dev/null +++ b/src/test/java/org/codehaus/plexus/util/SelectorUtilsTest.java @@ -0,0 +1,74 @@ +package org.codehaus.plexus.util; + +import java.io.File; + +import junit.framework.TestCase; + +/* + * 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. + */ + +public class SelectorUtilsTest + extends TestCase +{ + public void testMatchPath_DefaultFileSeparator() + { + String separator = File.separator; + + // Pattern and target start with file separator + assertTrue( SelectorUtils.matchPath( separator + "*" + separator + "a.txt", separator + "b" + separator + + "a.txt" ) ); + // Pattern starts with file separator, target doesn't + assertFalse( SelectorUtils.matchPath( separator + "*" + separator + "a.txt", "b" + separator + "a.txt" ) ); + // Pattern doesn't start with file separator, target does + assertFalse( SelectorUtils.matchPath( "*" + separator + "a.txt", separator + "b" + separator + "a.txt" ) ); + // Pattern and target don't start with file separator + assertTrue( SelectorUtils.matchPath( "*" + separator + "a.txt", "b" + separator + "a.txt" ) ); + } + + public void testMatchPath_UnixFileSeparator() + { + String separator = "/"; + + // Pattern and target start with file separator + assertTrue( SelectorUtils.matchPath( separator + "*" + separator + "a.txt", separator + "b" + separator + + "a.txt", separator, false ) ); + // Pattern starts with file separator, target doesn't + assertFalse( SelectorUtils.matchPath( separator + "*" + separator + "a.txt", "b" + separator + "a.txt", + separator, false ) ); + // Pattern doesn't start with file separator, target does + assertFalse( SelectorUtils.matchPath( "*" + separator + "a.txt", separator + "b" + separator + "a.txt", + separator, false ) ); + // Pattern and target don't start with file separator + assertTrue( SelectorUtils.matchPath( "*" + separator + "a.txt", "b" + separator + "a.txt", separator, false ) ); + } + + public void testMatchPath_WindowsFileSeparator() + { + String separator = "\\"; + + // Pattern and target start with file separator + assertTrue( SelectorUtils.matchPath( separator + "*" + separator + "a.txt", separator + "b" + separator + + "a.txt", separator, false ) ); + // Pattern starts with file separator, target doesn't + assertFalse( SelectorUtils.matchPath( separator + "*" + separator + "a.txt", "b" + separator + "a.txt", + separator, false ) ); + // Pattern doesn't start with file separator, target does + assertFalse( SelectorUtils.matchPath( "*" + separator + "a.txt", separator + "b" + separator + "a.txt", + separator, false ) ); + // Pattern and target don't start with file separator + assertTrue( SelectorUtils.matchPath( "*" + separator + "a.txt", "b" + separator + "a.txt", separator, false ) ); + } +} diff --git a/src/test/java/org/codehaus/plexus/util/StringUtilsTest.java b/src/test/java/org/codehaus/plexus/util/StringUtilsTest.java index 1f212129..e7c505ef 100644 --- a/src/test/java/org/codehaus/plexus/util/StringUtilsTest.java +++ b/src/test/java/org/codehaus/plexus/util/StringUtilsTest.java @@ -123,6 +123,17 @@ public void testQuote_EscapeEmbeddedSingleQuotes() assertEquals( check, result ); } + public void testQuote_EscapeEmbeddedSingleQuotesWithPattern() + { + String src = "This \'is a\' test"; + String check = "\'This pre'postis apre'post test\'"; + + char[] escaped = { '\'', '\"' }; + String result = StringUtils.quoteAndEscape( src, '\'', escaped, new char[]{ ' ' }, "pre%spost", false ); + + assertEquals( check, result ); + } + public void testQuote_EscapeEmbeddedDoubleQuotesAndSpaces() { String src = "This \"is a\" test"; @@ -221,6 +232,12 @@ public void testRemoveDuplicateWhitespace() assertEquals( "this is test ", StringUtils.removeDuplicateWhitespace( s ) ); s = "this \r\n is \n \r test "; assertEquals( "this is test ", StringUtils.removeDuplicateWhitespace( s ) ); + s = " this \r\n is \n \r test"; + assertEquals( " this is test", StringUtils.removeDuplicateWhitespace( s ) ); + s = "this \r\n is \n \r test \n "; + assertEquals( "this is test ", StringUtils.removeDuplicateWhitespace( s ) ); + + } public void testUnifyLineSeparators() diff --git a/src/test/java/org/codehaus/plexus/util/TestThreadManager.java b/src/test/java/org/codehaus/plexus/util/TestThreadManager.java index 20a24e06..bf80f370 100644 --- a/src/test/java/org/codehaus/plexus/util/TestThreadManager.java +++ b/src/test/java/org/codehaus/plexus/util/TestThreadManager.java @@ -99,7 +99,7 @@ public Object getNotifyObject() public boolean hasFailedThreads() { - if ( failedThreads.size() == 0 ) + if ( failedThreads.isEmpty() ) { return false; } diff --git a/src/test/java/org/codehaus/plexus/util/cli/CommandLineUtilsTest.java b/src/test/java/org/codehaus/plexus/util/cli/CommandLineUtilsTest.java index e37268fe..6e333870 100644 --- a/src/test/java/org/codehaus/plexus/util/cli/CommandLineUtilsTest.java +++ b/src/test/java/org/codehaus/plexus/util/cli/CommandLineUtilsTest.java @@ -16,15 +16,14 @@ * limitations under the License. */ +import junit.framework.TestCase; +import org.codehaus.plexus.util.Os; + import java.util.Arrays; -import java.util.Iterator; import java.util.Locale; import java.util.Properties; -import org.codehaus.plexus.util.Os; - -import junit.framework.TestCase; - +@SuppressWarnings( { "JavaDoc", "deprecation" } ) public class CommandLineUtilsTest extends TestCase { @@ -64,9 +63,9 @@ public void testGetSystemEnvVarsCaseInsensitive() throws Exception { Properties vars = CommandLineUtils.getSystemEnvVars( false ); - for ( Iterator it = vars.keySet().iterator(); it.hasNext(); ) + for ( Object o : vars.keySet() ) { - String variable = (String) it.next(); + String variable = (String) o; assertEquals( variable.toUpperCase( Locale.ENGLISH ), variable ); } } @@ -82,9 +81,9 @@ public void testGetSystemEnvVarsWindows() return; } Properties vars = CommandLineUtils.getSystemEnvVars(); - for ( Iterator it = vars.keySet().iterator(); it.hasNext(); ) + for ( Object o : vars.keySet() ) { - String variable = (String) it.next(); + String variable = (String) o; assertEquals( variable.toUpperCase( Locale.ENGLISH ), variable ); } } diff --git a/src/test/java/org/codehaus/plexus/util/cli/CommandlineTest.java b/src/test/java/org/codehaus/plexus/util/cli/CommandlineTest.java index b22814ba..7d9e3df8 100644 --- a/src/test/java/org/codehaus/plexus/util/cli/CommandlineTest.java +++ b/src/test/java/org/codehaus/plexus/util/cli/CommandlineTest.java @@ -16,6 +16,7 @@ * limitations under the License. */ +import junit.framework.TestCase; import org.codehaus.plexus.util.IOUtil; import org.codehaus.plexus.util.Os; import org.codehaus.plexus.util.StringUtils; @@ -23,15 +24,7 @@ import org.codehaus.plexus.util.cli.shell.CmdShell; import org.codehaus.plexus.util.cli.shell.Shell; -import java.io.File; -import java.io.FileWriter; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.Reader; -import java.io.StringWriter; -import java.io.Writer; - -import junit.framework.TestCase; +import java.io.*; public class CommandlineTest extends TestCase @@ -252,7 +245,7 @@ public void testGetShellCommandLineBash() assertEquals( "/bin/sh", shellCommandline[0] ); assertEquals( "-c", shellCommandline[1] ); - String expectedShellCmd = "/bin/echo \'hello world\'"; + String expectedShellCmd = "'/bin/echo' 'hello world'"; if ( Os.isFamily( Os.FAMILY_WINDOWS ) ) { expectedShellCmd = "\\bin\\echo \'hello world\'"; @@ -282,12 +275,12 @@ public void testGetShellCommandLineBash_WithWorkingDirectory() assertEquals( "/bin/sh", shellCommandline[0] ); assertEquals( "-c", shellCommandline[1] ); - String expectedShellCmd = "cd \"" + root.getAbsolutePath() - + "path with spaces\" && /bin/echo \'hello world\'"; + String expectedShellCmd = "cd '" + root.getAbsolutePath() + + "path with spaces' && '/bin/echo' 'hello world'"; if ( Os.isFamily( Os.FAMILY_WINDOWS ) ) { - expectedShellCmd = "cd \"" + root.getAbsolutePath() - + "path with spaces\" && \\bin\\echo \'hello world\'"; + expectedShellCmd = "cd '" + root.getAbsolutePath() + + "path with spaces' && '\\bin\\echo' 'hello world'"; } assertEquals( expectedShellCmd, shellCommandline[2] ); } @@ -311,7 +304,7 @@ public void testGetShellCommandLineBash_WithSingleQuotedArg() assertEquals( "/bin/sh", shellCommandline[0] ); assertEquals( "-c", shellCommandline[1] ); - String expectedShellCmd = "/bin/echo \'hello world\'"; + String expectedShellCmd = "'/bin/echo' ''\"'\"'hello world'\"'\"''"; if ( Os.isFamily( Os.FAMILY_WINDOWS ) ) { expectedShellCmd = "\\bin\\echo \'hello world\'"; @@ -341,7 +334,7 @@ public void testGetShellCommandLineNonWindows() } else { - assertEquals( "/usr/bin a b", shellCommandline[2] ); + assertEquals( "'/usr/bin' 'a' 'b'", shellCommandline[2] ); } } @@ -387,6 +380,18 @@ public void testQuotedPathWithSingleApostrophe() createAndCallScript( dir, "echo Quoted" ); } + /** + * Test an executable with shell-expandable content in its path. + * + * @throws Exception + */ + public void testPathWithShellExpansionStrings() + throws Exception + { + File dir = new File( System.getProperty( "basedir" ), "target/test/dollar$test" ); + createAndCallScript( dir, "echo Quoted" ); + } + /** * Test an executable with a single quotation mark \" in its path only for non Windows box. * @@ -631,7 +636,7 @@ private static void executeCommandLine( Commandline cmd ) if ( exitCode != 0 ) { - StringBuffer msg = new StringBuffer( "Exit code: " + exitCode + " - " + err.getOutput() ); + String msg = "Exit code: " + exitCode + " - " + err.getOutput(); throw new Exception( msg.toString() ); } } diff --git a/src/test/java/org/codehaus/plexus/util/cli/EnhancedStringTokenizerTest.java b/src/test/java/org/codehaus/plexus/util/cli/EnhancedStringTokenizerTest.java index c0f943a5..0b790d00 100644 --- a/src/test/java/org/codehaus/plexus/util/cli/EnhancedStringTokenizerTest.java +++ b/src/test/java/org/codehaus/plexus/util/cli/EnhancedStringTokenizerTest.java @@ -39,7 +39,7 @@ public void setUp() throws Exception public void test1() { EnhancedStringTokenizer est = new EnhancedStringTokenizer( "this is a test string" ); - StringBuffer sb = new StringBuffer(); + StringBuilder sb = new StringBuilder(); while ( est.hasMoreTokens() ) { sb.append( est.nextToken() ); diff --git a/src/test/java/org/codehaus/plexus/util/cli/StreamPumperTest.java b/src/test/java/org/codehaus/plexus/util/cli/StreamPumperTest.java index 4d21d810..c9ac1e6a 100644 --- a/src/test/java/org/codehaus/plexus/util/cli/StreamPumperTest.java +++ b/src/test/java/org/codehaus/plexus/util/cli/StreamPumperTest.java @@ -52,6 +52,8 @@ * limitations under the License. */ +import junit.framework.TestCase; + import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; @@ -60,12 +62,11 @@ import java.util.ArrayList; import java.util.List; -import junit.framework.TestCase; - /** * @author Paul Julius */ -public class StreamPumperTest extends TestCase +public class StreamPumperTest + extends TestCase { private String lineSeparator; @@ -80,7 +81,8 @@ public StreamPumperTest( String testName ) /* * @see TestCase#setUp() */ - public void setUp() throws Exception + public void setUp() + throws Exception { super.setUp(); lineSeparator = System.getProperty( "line.separator" ); @@ -91,8 +93,7 @@ public void testPumping() String line1 = "line1"; String line2 = "line2"; String lines = line1 + "\n" + line2; - ByteArrayInputStream inputStream = - new ByteArrayInputStream( lines.getBytes() ); + ByteArrayInputStream inputStream = new ByteArrayInputStream( lines.getBytes() ); TestConsumer consumer = new TestConsumer(); StreamPumper pumper = new StreamPumper( inputStream, consumer ); @@ -188,9 +189,9 @@ static class TestConsumer /** * Checks to see if this consumer consumed a particular line. This method will wait up to timeout number of * milliseconds for the line to get consumed. - * + * * @param testLine Line to test for. - * @param timeout Number of milliseconds to wait for the line. + * @param timeout Number of milliseconds to wait for the line. * @return true if the line gets consumed, else false. */ public boolean wasLineConsumed( String testLine, long timeout ) @@ -232,4 +233,22 @@ public void consumeLine( String line ) } } + public void testEnabled() + { + ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream( "AB\nCE\nEF".getBytes() ); + TestConsumer streamConsumer = new TestConsumer(); + StreamPumper streamPumper = new StreamPumper( byteArrayInputStream, streamConsumer ); + streamPumper.run(); + assertEquals( 3, streamConsumer.lines.size() ); + } + + public void testDisabled() + { + ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream( "AB\nCE\nEF".getBytes() ); + TestConsumer streamConsumer = new TestConsumer(); + StreamPumper streamPumper = new StreamPumper( byteArrayInputStream, streamConsumer ); + streamPumper.disable(); + streamPumper.run(); + assertEquals( 0, streamConsumer.lines.size() ); + } } diff --git a/src/test/java/org/codehaus/plexus/util/cli/shell/BourneShellTest.java b/src/test/java/org/codehaus/plexus/util/cli/shell/BourneShellTest.java index a4072495..0e06c639 100644 --- a/src/test/java/org/codehaus/plexus/util/cli/shell/BourneShellTest.java +++ b/src/test/java/org/codehaus/plexus/util/cli/shell/BourneShellTest.java @@ -16,14 +16,13 @@ * limitations under the License. */ +import junit.framework.TestCase; import org.codehaus.plexus.util.StringUtils; import org.codehaus.plexus.util.cli.Commandline; import java.util.Arrays; import java.util.List; -import junit.framework.TestCase; - public class BourneShellTest extends TestCase { @@ -42,7 +41,7 @@ public void testQuoteWorkingDirectoryAndExecutable() String executable = StringUtils.join( sh.getShellCommandLine( new String[]{} ).iterator(), " " ); - assertEquals( "/bin/sh -c cd /usr/local/bin && chmod", executable ); + assertEquals( "/bin/sh -c cd '/usr/local/bin' && 'chmod'", executable ); } public void testQuoteWorkingDirectoryAndExecutable_WDPathWithSingleQuotes() @@ -54,7 +53,7 @@ public void testQuoteWorkingDirectoryAndExecutable_WDPathWithSingleQuotes() String executable = StringUtils.join( sh.getShellCommandLine( new String[]{} ).iterator(), " " ); - assertEquals( "/bin/sh -c cd \"/usr/local/\'something else\'\" && chmod", executable ); + assertEquals( "/bin/sh -c cd '/usr/local/'\"'\"'something else'\"'\"'' && 'chmod'", executable ); } public void testQuoteWorkingDirectoryAndExecutable_WDPathWithSingleQuotes_BackslashFileSep() @@ -66,7 +65,7 @@ public void testQuoteWorkingDirectoryAndExecutable_WDPathWithSingleQuotes_Backsl String executable = StringUtils.join( sh.getShellCommandLine( new String[]{} ).iterator(), " " ); - assertEquals( "/bin/sh -c cd \"\\usr\\local\\\'something else\'\" && chmod", executable ); + assertEquals( "/bin/sh -c cd '\\usr\\local\\\'\"'\"'something else'\"'\"'' && 'chmod'", executable ); } public void testPreserveSingleQuotesOnArgument() @@ -82,7 +81,7 @@ public void testPreserveSingleQuotesOnArgument() String cli = StringUtils.join( shellCommandLine.iterator(), " " ); System.out.println( cli ); - assertTrue( cli.endsWith( args[0] ) ); + assertTrue( cli.endsWith("''\"'\"'some arg with spaces'\"'\"''")); } public void testAddSingleQuotesOnArgumentWithSpaces() @@ -101,6 +100,22 @@ public void testAddSingleQuotesOnArgumentWithSpaces() assertTrue( cli.endsWith( "\'" + args[0] + "\'" ) ); } + public void testEscapeSingleQuotesOnArgument() + { + Shell sh = newShell(); + + sh.setWorkingDirectory( "/usr/bin" ); + sh.setExecutable( "chmod" ); + + String[] args = { "arg'withquote" }; + + List shellCommandLine = sh.getShellCommandLine( args ); + + String cli = StringUtils.join( shellCommandLine.iterator(), " " ); + System.out.println( cli ); + assertEquals("cd '/usr/bin' && 'chmod' 'arg'\"'\"'withquote'", shellCommandLine.get(shellCommandLine.size() - 1)); + } + public void testArgumentsWithsemicolon() { @@ -130,7 +145,7 @@ public void testArgumentsWithsemicolon() assertEquals( "/bin/sh", lines[0] ); assertEquals( "-c", lines[1] ); - assertEquals( "chmod --password ';password'", lines[2] ); + assertEquals( "'chmod' '--password' ';password'", lines[2] ); commandline = new Commandline( newShell() ); commandline.setExecutable( "chmod" ); @@ -142,7 +157,7 @@ public void testArgumentsWithsemicolon() assertEquals( "/bin/sh", lines[0] ); assertEquals( "-c", lines[1] ); - assertEquals( "chmod --password ';password'", lines[2] ); + assertEquals( "'chmod' '--password' ';password'", lines[2] ); commandline = new Commandline( new CmdShell() ); commandline.getShell().setQuotedArgumentsEnabled( true ); @@ -190,7 +205,7 @@ public void testBourneShellQuotingCharacters() assertEquals( "/bin/sh", lines[0] ); assertEquals( "-c", lines[1] ); - assertEquals( "chmod ' ' '|' '&&' '||' ';' ';;' '&' '()' '<' '<<' '>' '>>' '*' '?' '[' ']' '{' '}' '`'", + assertEquals( "'chmod' ' ' '|' '&&' '||' ';' ';;' '&' '()' '<' '<<' '>' '>>' '*' '?' '[' ']' '{' '}' '`'", lines[2] ); } diff --git a/src/test/java/org/codehaus/plexus/util/dag/CycleDetectedExceptionTest.java b/src/test/java/org/codehaus/plexus/util/dag/CycleDetectedExceptionTest.java index 46942fb3..77f1d470 100644 --- a/src/test/java/org/codehaus/plexus/util/dag/CycleDetectedExceptionTest.java +++ b/src/test/java/org/codehaus/plexus/util/dag/CycleDetectedExceptionTest.java @@ -30,7 +30,7 @@ public class CycleDetectedExceptionTest { public void testException() { - final List cycle = new ArrayList(); + final List cycle = new ArrayList(); cycle.add( "a" ); diff --git a/src/test/java/org/codehaus/plexus/util/dag/CycleDetectorTest.java b/src/test/java/org/codehaus/plexus/util/dag/CycleDetectorTest.java index 7caaf933..bfa9424f 100644 --- a/src/test/java/org/codehaus/plexus/util/dag/CycleDetectorTest.java +++ b/src/test/java/org/codehaus/plexus/util/dag/CycleDetectorTest.java @@ -70,7 +70,7 @@ public void testCycyleDetection() catch ( CycleDetectedException e ) { - final List cycle = e.getCycle(); + final List cycle = e.getCycle(); assertNotNull( "Cycle should be not null", cycle ); @@ -129,7 +129,7 @@ public void testCycyleDetection() } catch ( CycleDetectedException e ) { - final List cycle = e.getCycle(); + final List cycle = e.getCycle(); assertNotNull( "Cycle should be not null", cycle ); @@ -177,7 +177,7 @@ public void testCycyleDetection() } catch ( CycleDetectedException e ) { - final List cycle = e.getCycle(); + final List cycle = e.getCycle(); assertNotNull( "Cycle should be not null", cycle ); diff --git a/src/test/java/org/codehaus/plexus/util/dag/DAGTest.java b/src/test/java/org/codehaus/plexus/util/dag/DAGTest.java index 50c1e50f..de599ad4 100644 --- a/src/test/java/org/codehaus/plexus/util/dag/DAGTest.java +++ b/src/test/java/org/codehaus/plexus/util/dag/DAGTest.java @@ -98,7 +98,7 @@ public void testDAG() assertFalse( dag.hasEdge( "d", "c" ) ); - final Set labels = dag.getLabels(); + final Set labels = dag.getLabels(); assertEquals( 4, labels.size() ); @@ -168,9 +168,9 @@ public void testGetPredessors() dag.addEdge( "f", "g" ); - final List actual = dag.getSuccessorLabels( "b" ); + final List actual = dag.getSuccessorLabels( "b" ); - final List expected = new ArrayList(); + final List expected = new ArrayList(); expected.add( "d" ); diff --git a/src/test/java/org/codehaus/plexus/util/dag/TopologicalSorterTest.java b/src/test/java/org/codehaus/plexus/util/dag/TopologicalSorterTest.java index 67a45722..a081b653 100644 --- a/src/test/java/org/codehaus/plexus/util/dag/TopologicalSorterTest.java +++ b/src/test/java/org/codehaus/plexus/util/dag/TopologicalSorterTest.java @@ -40,7 +40,7 @@ public void testDfs() dag1.addEdge( "b", "c" ); - final List expected1 = new ArrayList(); + final List expected1 = new ArrayList(); expected1.add( "c" ); @@ -48,7 +48,7 @@ public void testDfs() expected1.add( "a" ); - final List actual1 = TopologicalSorter.sort( dag1 ); + final List actual1 = TopologicalSorter.sort( dag1 ); assertEquals( "Order is different then expected", expected1, actual1 ); @@ -68,7 +68,7 @@ public void testDfs() dag2.addEdge( "c", "b" ); - final List expected2 = new ArrayList(); + final List expected2 = new ArrayList(); expected2.add( "a" ); @@ -76,7 +76,7 @@ public void testDfs() expected2.add( "c" ); - final List actual2 = TopologicalSorter.sort( dag2 ); + final List actual2 = TopologicalSorter.sort( dag2 ); assertEquals( "Order is different then expected", expected2, actual2 ); @@ -117,7 +117,7 @@ public void testDfs() dag3.addEdge( "f", "g" ); - final List expected3 = new ArrayList(); + final List expected3 = new ArrayList(); expected3.add( "d" ); @@ -133,7 +133,7 @@ public void testDfs() expected3.add( "a" ); - final List actual3 = TopologicalSorter.sort( dag3 ); + final List actual3 = TopologicalSorter.sort( dag3 ); assertEquals( "Order is different then expected", expected3, actual3 ); @@ -172,7 +172,7 @@ public void testDfs() dag4.addEdge( "e", "f" ); - final List expected4 = new ArrayList(); + final List expected4 = new ArrayList(); expected4.add( "d" ); @@ -186,7 +186,7 @@ public void testDfs() expected4.add( "a" ); - final List actual4 = TopologicalSorter.sort( dag4 ); + final List actual4 = TopologicalSorter.sort( dag4 ); assertEquals( "Order is different then expected", expected4, actual4 ); } diff --git a/src/test/java/org/codehaus/plexus/util/introspection/ReflectionValueExtractorTest.java b/src/test/java/org/codehaus/plexus/util/introspection/ReflectionValueExtractorTest.java index d09bce95..6e59aac0 100644 --- a/src/test/java/org/codehaus/plexus/util/introspection/ReflectionValueExtractorTest.java +++ b/src/test/java/org/codehaus/plexus/util/introspection/ReflectionValueExtractorTest.java @@ -16,15 +16,15 @@ * limitations under the License. */ -import junit.framework.Assert; -import junit.framework.TestCase; - +import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; -import java.util.ArrayList; import java.util.Map; +import junit.framework.Assert; +import junit.framework.TestCase; + /** * @author Jason van Zyl * @version $Id$ @@ -40,9 +40,9 @@ protected void setUp() super.setUp(); Dependency dependency1 = new Dependency(); - dependency1.setArtifactId("dep1"); + dependency1.setArtifactId( "dep1" ); Dependency dependency2 = new Dependency(); - dependency2.setArtifactId("dep2"); + dependency2.setArtifactId( "dep2" ); project = new Project(); project.setModelVersion( "4.0.0" ); @@ -55,6 +55,11 @@ protected void setUp() project.addDependency( dependency1 ); project.addDependency( dependency2 ); project.setBuild( new Build() ); + + // Build up an artifactMap + project.addArtifact( new Artifact("g0","a0","v0","e0","c0") ); + project.addArtifact( new Artifact("g1","a1","v1","e1","c1") ); + project.addArtifact( new Artifact("g2","a2","v2","e2","c2") ); } public void testValueExtraction() @@ -95,39 +100,39 @@ public void testValueExtraction() // ---------------------------------------------------------------------- // List - Dependency dependency = (Dependency)ReflectionValueExtractor.evaluate( "project.dependencies[0]", project ); + Dependency dependency = (Dependency) ReflectionValueExtractor.evaluate( "project.dependencies[0]", project ); Assert.assertNotNull( dependency ); - Assert.assertTrue( "dep1".equals(dependency.getArtifactId()) ); + Assert.assertTrue( "dep1".equals( dependency.getArtifactId() ) ); - String artifactId = (String)ReflectionValueExtractor.evaluate( "project.dependencies[1].artifactId", project ); + String artifactId = (String) ReflectionValueExtractor.evaluate( "project.dependencies[1].artifactId", project ); - Assert.assertTrue( "dep2".equals(artifactId) ); + Assert.assertTrue( "dep2".equals( artifactId ) ); // Array - dependency = (Dependency)ReflectionValueExtractor.evaluate( "project.dependenciesAsArray[0]", project ); + dependency = (Dependency) ReflectionValueExtractor.evaluate( "project.dependenciesAsArray[0]", project ); Assert.assertNotNull( dependency ); - Assert.assertTrue( "dep1".equals(dependency.getArtifactId()) ); + Assert.assertTrue( "dep1".equals( dependency.getArtifactId() ) ); - artifactId = (String)ReflectionValueExtractor.evaluate( "project.dependenciesAsArray[1].artifactId", project ); + artifactId = (String) ReflectionValueExtractor.evaluate( "project.dependenciesAsArray[1].artifactId", project ); - Assert.assertTrue( "dep2".equals(artifactId) ); + Assert.assertTrue( "dep2".equals( artifactId ) ); // Map - dependency = (Dependency)ReflectionValueExtractor.evaluate( "project.dependenciesAsMap(dep1)", project ); + dependency = (Dependency) ReflectionValueExtractor.evaluate( "project.dependenciesAsMap(dep1)", project ); Assert.assertNotNull( dependency ); - Assert.assertTrue( "dep1".equals(dependency.getArtifactId()) ); + Assert.assertTrue( "dep1".equals( dependency.getArtifactId() ) ); - artifactId = (String)ReflectionValueExtractor.evaluate( "project.dependenciesAsMap(dep2).artifactId", project ); + artifactId = (String) ReflectionValueExtractor.evaluate( "project.dependenciesAsMap(dep2).artifactId", project ); - Assert.assertTrue( "dep2".equals(artifactId) ); + Assert.assertTrue( "dep2".equals( artifactId ) ); // ---------------------------------------------------------------------- // Build @@ -146,6 +151,179 @@ public void testValueExtractorWithAInvalidExpression() Assert.assertNull( ReflectionValueExtractor.evaluate( "project.dependencies[0].foo", project ) ); } + public void testMappedDottedKey() + throws Exception + { + Map map = new HashMap(); + map.put( "a.b", "a.b-value" ); + + Assert.assertEquals( "a.b-value", ReflectionValueExtractor.evaluate( "h.value(a.b)", new ValueHolder( map ) ) ); + } + + public void testIndexedMapped() + throws Exception + { + Map map = new HashMap(); + map.put( "a", "a-value" ); + List list = new ArrayList(); + list.add( map ); + + Assert.assertEquals( "a-value", ReflectionValueExtractor.evaluate( "h.value[0](a)", new ValueHolder( list ) ) ); + } + + public void testMappedIndexed() + throws Exception + { + List list = new ArrayList(); + list.add( "a-value" ); + Map map = new HashMap(); + map.put( "a", list ); + Assert.assertEquals( "a-value", ReflectionValueExtractor.evaluate( "h.value(a)[0]", new ValueHolder( map ) ) ); + } + + public void testMappedMissingDot() + throws Exception + { + Map map = new HashMap(); + map.put( "a", new ValueHolder( "a-value" ) ); + Assert.assertNull( ReflectionValueExtractor.evaluate( "h.value(a)value", new ValueHolder( map ) ) ); + } + + public void testIndexedMissingDot() + throws Exception + { + List list = new ArrayList(); + list.add( new ValueHolder( "a-value" ) ); + Assert.assertNull( ReflectionValueExtractor.evaluate( "h.value[0]value", new ValueHolder( list ) ) ); + } + + public void testDotDot() + throws Exception + { + Assert.assertNull( ReflectionValueExtractor.evaluate( "h..value", new ValueHolder( "value" ) ) ); + } + + public void testBadIndexedSyntax() + throws Exception + { + List list = new ArrayList(); + list.add( "a-value" ); + Object value = new ValueHolder( list ); + + Assert.assertNull( ReflectionValueExtractor.evaluate( "h.value[", value ) ); + Assert.assertNull( ReflectionValueExtractor.evaluate( "h.value[]", value ) ); + Assert.assertNull( ReflectionValueExtractor.evaluate( "h.value[a]", value ) ); + Assert.assertNull( ReflectionValueExtractor.evaluate( "h.value[0", value ) ); + Assert.assertNull( ReflectionValueExtractor.evaluate( "h.value[0)", value ) ); + Assert.assertNull( ReflectionValueExtractor.evaluate( "h.value[-1]", value ) ); + } + + public void testBadMappedSyntax() + throws Exception + { + Map map = new HashMap(); + map.put( "a", "a-value" ); + Object value = new ValueHolder( map ); + + Assert.assertNull( ReflectionValueExtractor.evaluate( "h.value(", value ) ); + Assert.assertNull( ReflectionValueExtractor.evaluate( "h.value()", value ) ); + Assert.assertNull( ReflectionValueExtractor.evaluate( "h.value(a", value ) ); + Assert.assertNull( ReflectionValueExtractor.evaluate( "h.value(a]", value ) ); + } + + public void testIllegalIndexedType() + throws Exception + { + try + { + ReflectionValueExtractor.evaluate( "h.value[1]", new ValueHolder( "string" ) ); + } + catch ( Exception e ) + { + // TODO assert exception message + } + } + + public void testIllegalMappedType() + throws Exception + { + try + { + ReflectionValueExtractor.evaluate( "h.value(key)", new ValueHolder( "string" ) ); + } + catch ( Exception e ) + { + // TODO assert exception message + } + } + + public void testArtifactMap() + throws Exception + { + assertEquals( "g0", ((Artifact) ReflectionValueExtractor.evaluate( "project.artifactMap(g0:a0:c0)", project )).getGroupId() ); + assertEquals( "a1", ((Artifact) ReflectionValueExtractor.evaluate( "project.artifactMap(g1:a1:c1)", project )).getArtifactId() ); + assertEquals( "c2", ((Artifact) ReflectionValueExtractor.evaluate( "project.artifactMap(g2:a2:c2)", project )).getClassifier() ); + } + + public static class Artifact + { + private String groupId; + private String artifactId; + private String version; + private String extension; + private String classifier; + + public Artifact( String groupId, String artifactId, String version, String extension, String classifier ) + { + this.groupId = groupId; + this.artifactId = artifactId; + this.version = version; + this.extension = extension; + this.classifier = classifier; + } + + public String getGroupId() + { + return groupId; + } + public void setGroupId( String groupId ) + { + this.groupId = groupId; + } + public String getArtifactId() + { + return artifactId; + } + public void setArtifactId( String artifactId ) + { + this.artifactId = artifactId; + } + public String getVersion() + { + return version; + } + public void setVersion( String version ) + { + this.version = version; + } + public String getExtension() + { + return extension; + } + public void setExtension( String extension ) + { + this.extension = extension; + } + public String getClassifier() + { + return classifier; + } + public void setClassifier( String classifier ) + { + this.classifier = classifier; + } + } + public static class Project { private String modelVersion; @@ -164,6 +342,8 @@ public static class Project private String version; + private Map artifactMap = new HashMap(); + public void setModelVersion( String modelVersion ) { this.modelVersion = modelVersion; @@ -246,19 +426,30 @@ public String getVersion() public Dependency[] getDependenciesAsArray() { - return (Dependency[]) getDependencies().toArray( new Dependency[0]); + return (Dependency[]) getDependencies().toArray( new Dependency[0] ); } public Map getDependenciesAsMap() { Map ret = new HashMap(); - for ( Iterator it = getDependencies().iterator(); it.hasNext();) + for ( Iterator it = getDependencies().iterator(); it.hasNext(); ) { - Dependency dep = (Dependency)it.next(); + Dependency dep = (Dependency) it.next(); ret.put( dep.getArtifactId(), dep ); } return ret; } + + // ${project.artifactMap(g:a:v)} + public void addArtifact(Artifact a) + { + artifactMap.put( a.getGroupId() + ":" + a.getArtifactId() + ":" + a.getClassifier(), a ); + } + + public Map getArtifactMap() + { + return artifactMap; + } } public static class Build @@ -275,7 +466,7 @@ public String getArtifactId() return artifactId; } - public void setArtifactId(String id) + public void setArtifactId( String id ) { artifactId = id; } @@ -295,4 +486,19 @@ public String getConnection() return connection; } } + + public static class ValueHolder + { + private final Object value; + + public ValueHolder( Object value ) + { + this.value = value; + } + + public Object getValue() + { + return value; + } + } } \ No newline at end of file diff --git a/src/test/java/org/codehaus/plexus/util/xml/PrettyPrintXMLWriterTest.java b/src/test/java/org/codehaus/plexus/util/xml/PrettyPrintXMLWriterTest.java index b18f78ea..87416e3c 100644 --- a/src/test/java/org/codehaus/plexus/util/xml/PrettyPrintXMLWriterTest.java +++ b/src/test/java/org/codehaus/plexus/util/xml/PrettyPrintXMLWriterTest.java @@ -167,7 +167,7 @@ private String expectedResult( String lineSeparator ) private String expectedResult( String lineIndenter, String lineSeparator ) { - StringBuffer expected = new StringBuffer(); + StringBuilder expected = new StringBuilder(); expected.append( "" ).append( lineSeparator ); expected.append( StringUtils.repeat( lineIndenter, 1 ) ).append( "" ).append( lineSeparator ); diff --git a/src/test/java/org/codehaus/plexus/util/xml/XmlUtilTest.java b/src/test/java/org/codehaus/plexus/util/xml/XmlUtilTest.java index 6788837c..a09a7112 100644 --- a/src/test/java/org/codehaus/plexus/util/xml/XmlUtilTest.java +++ b/src/test/java/org/codehaus/plexus/util/xml/XmlUtilTest.java @@ -19,6 +19,7 @@ import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; +import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.Reader; @@ -41,7 +42,28 @@ public class XmlUtilTest extends TestCase { - private static final File basedir = new File( new File( "" ).getAbsolutePath() ); + private String basedir; + + public final String getBasedir() + { + if ( null == basedir ) + { + basedir = System.getProperty( "basedir", new File( "" ).getAbsolutePath() ); + } + return basedir; + } + + private File getTestOutputFile( String relPath ) + throws IOException + { + final File file = new File( getBasedir(), relPath ); + final File parentFile = file.getParentFile(); + if ( !parentFile.isDirectory() && !parentFile.mkdirs() ) + { + throw new IOException( "Could not create test directory " + parentFile ); + } + return file; + } /** {@inheritDoc} */ protected void setUp() @@ -60,7 +82,7 @@ protected void tearDown() public void testPrettyFormatInputStreamOutputStream() throws Exception { - File testDocument = new File( basedir, "src/test/resources/testDocument.xhtml" ); + File testDocument = new File( getBasedir(), "src/test/resources/testDocument.xhtml" ); assertTrue( testDocument.exists() ); InputStream is = null; @@ -68,7 +90,7 @@ public void testPrettyFormatInputStreamOutputStream() try { is = new FileInputStream( testDocument ); - os = new FileOutputStream( new File( basedir, "target/test/prettyFormatTestDocumentOutputStream.xml" ) ); + os = new FileOutputStream( getTestOutputFile( "target/test/prettyFormatTestDocumentOutputStream.xml" ) ); assertNotNull( is ); assertNotNull( os ); @@ -85,7 +107,7 @@ public void testPrettyFormatInputStreamOutputStream() public void testPrettyFormatReaderWriter() throws Exception { - File testDocument = new File( basedir, "src/test/resources/testDocument.xhtml" ); + File testDocument = new File( getBasedir(), "src/test/resources/testDocument.xhtml" ); assertTrue( testDocument.exists() ); Reader reader = null; @@ -93,7 +115,7 @@ public void testPrettyFormatReaderWriter() try { reader = ReaderFactory.newXmlReader( testDocument ); - writer = WriterFactory.newXmlWriter( new File( basedir, "target/test/prettyFormatTestDocumentWriter.xml" ) ); + writer = WriterFactory.newXmlWriter( getTestOutputFile( "target/test/prettyFormatTestDocumentWriter.xml" ) ); assertNotNull( reader ); assertNotNull( writer ); @@ -110,7 +132,7 @@ public void testPrettyFormatReaderWriter() public void testPrettyFormatString() throws Exception { - File testDocument = new File( basedir, "src/test/resources/testDocument.xhtml" ); + File testDocument = new File( getBasedir(), "src/test/resources/testDocument.xhtml" ); assertTrue( testDocument.exists() ); Reader reader = null; @@ -140,7 +162,7 @@ public void testPrettyFormatString() public void testPrettyFormatReaderWriter2() throws Exception { - File testDocument = new File( basedir, "src/test/resources/test.xdoc.xhtml" ); + File testDocument = new File( getBasedir(), "src/test/resources/test.xdoc.xhtml" ); assertTrue( testDocument.exists() ); Reader reader = null; @@ -148,7 +170,7 @@ public void testPrettyFormatReaderWriter2() try { reader = ReaderFactory.newXmlReader( testDocument ); - writer = WriterFactory.newXmlWriter( new File( basedir, "target/test/prettyFormatTestXdocWriter.xml" ) ); + writer = WriterFactory.newXmlWriter( getTestOutputFile( "target/test/prettyFormatTestXdocWriter.xml" ) ); assertNotNull( reader ); assertNotNull( writer ); @@ -160,5 +182,5 @@ public void testPrettyFormatReaderWriter2() IOUtil.close( reader ); IOUtil.close( writer ); } - } + } } diff --git a/src/test/java/org/codehaus/plexus/util/xml/Xpp3DomBuilderTest.java b/src/test/java/org/codehaus/plexus/util/xml/Xpp3DomBuilderTest.java index 2a40417f..988ddd9d 100644 --- a/src/test/java/org/codehaus/plexus/util/xml/Xpp3DomBuilderTest.java +++ b/src/test/java/org/codehaus/plexus/util/xml/Xpp3DomBuilderTest.java @@ -35,7 +35,7 @@ public class Xpp3DomBuilderTest extends TestCase { - private static final String LS = System.getProperty("line.separator"); + private static final String LS = System.getProperty( "line.separator" ); public void testBuildFromReader() throws Exception @@ -86,7 +86,7 @@ public void testBuildFromXpp3Dom() { String rawName = parser.getName(); - if ( rawName.equals( "root" ) ) + if ( "root".equals( rawName ) ) { dom = Xpp3DomBuilder.build( parser ); } @@ -95,15 +95,15 @@ else if ( eventType == XmlPullParser.END_TAG ) { String rawName = parser.getName(); - if ( rawName.equals( "configuration" ) ) + if ( "configuration".equals( rawName ) ) { configurationClosed = true; } - else if ( rawName.equals( "newRoot" ) ) + else if ( "newRoot".equals( rawName ) ) { newRootClosed = true; } - else if ( rawName.equals( "root" ) ) + else if ( "root".equals( rawName ) ) { rootClosed = true; } @@ -169,7 +169,7 @@ public void testEscapingInAttributes() private static String getAttributeEncodedString() { - StringBuffer domString = new StringBuffer(); + StringBuilder domString = new StringBuilder(); domString.append( "" ); domString.append( LS ); domString.append( " bar" ); @@ -181,7 +181,7 @@ private static String getAttributeEncodedString() private static String getEncodedString() { - StringBuffer domString = new StringBuffer(); + StringBuilder domString = new StringBuilder(); domString.append( "\n" ); domString.append( " \"text\"\n" ); domString.append( " \"text\"]]>\n" ); @@ -193,7 +193,7 @@ private static String getEncodedString() private static String getExpectedString() { - StringBuffer domString = new StringBuffer(); + StringBuilder domString = new StringBuilder(); domString.append( "" ); domString.append( LS ); domString.append( " "text"" ); @@ -213,7 +213,7 @@ private static String getExpectedString() private static String createDomString() { - StringBuffer buf = new StringBuffer(); + StringBuilder buf = new StringBuilder(); buf.append( "\n" ); buf.append( " element1\n \n" ); buf.append( " \n" ); @@ -221,6 +221,7 @@ private static String createDomString() buf.append( " \n" ); buf.append( " \n" ); buf.append( " \n" ); + buf.append( " do not trim \n" ); buf.append( "\n" ); return buf.toString(); @@ -244,6 +245,10 @@ private static Xpp3Dom createExpectedDom() expectedDom.addChild( el4 ); Xpp3Dom el5 = new Xpp3Dom( "el5" ); expectedDom.addChild( el5 ); + Xpp3Dom el6 = new Xpp3Dom( "el6" ); + el6.setAttribute( "xml:space", "preserve" ); + el6.setValue( " do not trim " ); + expectedDom.addChild( el6 ); return expectedDom; } } diff --git a/src/test/java/org/codehaus/plexus/util/xml/Xpp3DomTest.java b/src/test/java/org/codehaus/plexus/util/xml/Xpp3DomTest.java index f49cc85b..446f3a46 100644 --- a/src/test/java/org/codehaus/plexus/util/xml/Xpp3DomTest.java +++ b/src/test/java/org/codehaus/plexus/util/xml/Xpp3DomTest.java @@ -269,4 +269,10 @@ public void testShouldCopyRecessiveChildrenNotPresentInTarget() assertNotSame( result.getChild( "bar" ), recessiveConfig.getChild( "bar" ) ); } + public void testDupeChildren() throws IOException, XmlPullParserException { + String dupes = "xy"; + Xpp3Dom dom = Xpp3DomBuilder.build( new StringReader( dupes ) ); + assertNotNull( dom); + assertEquals("y", dom.getChild("foo").getValue()); + } } diff --git a/src/test/java/org/codehaus/plexus/util/xml/Xpp3DomWriterTest.java b/src/test/java/org/codehaus/plexus/util/xml/Xpp3DomWriterTest.java index b53f15c4..63207e21 100644 --- a/src/test/java/org/codehaus/plexus/util/xml/Xpp3DomWriterTest.java +++ b/src/test/java/org/codehaus/plexus/util/xml/Xpp3DomWriterTest.java @@ -48,7 +48,7 @@ public void testWriterNoEscape() private String createExpectedXML( boolean escape ) { - StringBuffer buf = new StringBuffer(); + StringBuilder buf = new StringBuilder(); buf.append( "" ); buf.append( LS ); buf.append( " element1" ); diff --git a/src/test/java/org/codehaus/plexus/util/xml/pull/MXParserTest.java b/src/test/java/org/codehaus/plexus/util/xml/pull/MXParserTest.java index 0b8f718d..7a013933 100644 --- a/src/test/java/org/codehaus/plexus/util/xml/pull/MXParserTest.java +++ b/src/test/java/org/codehaus/plexus/util/xml/pull/MXParserTest.java @@ -16,10 +16,11 @@ * limitations under the License. */ -import java.io.StringReader; - import junit.framework.TestCase; +import java.io.IOException; +import java.io.StringReader; + /** * @author Trygve Laugstøl * @version $Id$ @@ -87,6 +88,21 @@ public void testPredefinedEntities() assertEquals( XmlPullParser.END_TAG, parser.next() ); } + public void testEntityReplacementMap() + throws XmlPullParserException, IOException + { + EntityReplacementMap erm = new EntityReplacementMap( new String[][]{ { "abc", "CDE" }, { "EFG", "HIJ" } } ); + MXParser parser = new MXParser( erm ); + + String input = "&EFG;"; + parser.setInput( new StringReader( input ) ); + + assertEquals( XmlPullParser.START_TAG, parser.next() ); + assertEquals( XmlPullParser.TEXT, parser.next() ); + assertEquals( "HIJ", parser.getText() ); + assertEquals( XmlPullParser.END_TAG, parser.next() ); + } + public void testCustomEntities() throws Exception { diff --git a/src/test/resources/dir-layout-copy/dir1/dir2/.gitignore b/src/test/resources/dir-layout-copy/dir1/dir2/.gitignore new file mode 100644 index 00000000..695fc818 --- /dev/null +++ b/src/test/resources/dir-layout-copy/dir1/dir2/.gitignore @@ -0,0 +1 @@ +# just here to make the directory non-empty diff --git a/src/test/resources/dir-layout-copy/empty-dir/.gitignore b/src/test/resources/dir-layout-copy/empty-dir/.gitignore new file mode 100644 index 00000000..695fc818 --- /dev/null +++ b/src/test/resources/dir-layout-copy/empty-dir/.gitignore @@ -0,0 +1 @@ +# just here to make the directory non-empty diff --git a/src/test/resources/test.xdoc.xhtml b/src/test/resources/test.xdoc.xhtml index a342aedc..564bf676 100644 --- a/src/test/resources/test.xdoc.xhtml +++ b/src/test/resources/test.xdoc.xhtml @@ -1,109 +1,109 @@ - - - - Title - - - - - -

            Paragraph 1, line 1. Paragraph 1, line 2.

            -

            Paragraph 2, line 1. Paragraph 2, line 2.

            -
            -

            Section title

            -
            -

            Sub-section title

            -
            -

            Sub-sub-section title

            -
            -
            Sub-sub-sub-section
            -
            -
            Sub-sub-sub-sub-section
            -
              -
            • List item 1.
            • -
            • List item 2. - - - + + + + Title + + + + + +

              Paragraph 1, line 1. Paragraph 1, line 2.

              +

              Paragraph 2, line 1. Paragraph 2, line 2.

              +
              +

              Section title

              +
              +

              Sub-section title

              +
              +

              Sub-sub-section title

              +
              +
              Sub-sub-sub-section
              +
              +
              Sub-sub-sub-sub-section
              +
                +
              • List item 1.
              • +
              • List item 2. -

                Paragraph contained in list item 2.

                -
                  -
                • Sub-list item 1.
                • -
                • Sub-list item 2.
                • -
                -
              • -
              • List item 3. Force end of list:
              • -
              -
              -
              Verbatim text not contained in list item 3
              -
              -
                + + + +

                Paragraph contained in list item 2.

                +
                  +
                • Sub-list item 1.
                • +
                • Sub-list item 2.
                • +
                + +
              1. List item 3. Force end of list:
              2. +
            +
            +
            Verbatim text not contained in list item 3
            +
            +
            1. Numbered item 1. -
                -
              1. Numbered item A.
              2. -
              3. Numbered item B.
              4. -
              -
            2. -
            3. Numbered item 2.
            4. -
            -

            List numbering schemes: [[1]], [[a]], [[A]], [[i]], [[I]].

            -
            -
            - Defined term 1 -
            -
            of definition list.
            -
            - Defined term 2 -
            +
              +
            1. Numbered item A.
            2. +
            3. Numbered item B.
            4. +
            + +
          • Numbered item 2.
          • + +

            List numbering schemes: [[1]], [[a]], [[A]], [[i]], [[I]].

            +
            +
            + Defined term 1 +
            +
            of definition list.
            +
            + Defined term 2 +
            of definition list. -
            +
            Verbatim text
            -                            in a box        
            -
            -
            -
            -

            --- instead of +-- suppresses the box around verbatim text.

            - Figure caption - - - - - - - - - - - -
            Centered
            cell 1,1
            Left-aligned
            cell 1,2
            Right-aligned
            cell 1,3
            cell 2,1cell 2,2cell 2,3
            -

            - Table caption -

            -

            No grid, no caption:

            - - - - - - - - - -
            cellcell
            cellcell
            -

            Horizontal line:

            -
            -

            New page.

            -

            - Italic font. Bold font. Monospaced font.

            -

            + in a box +

            + + +

            --- instead of +-- suppresses the box around verbatim text.

            + Figure caption + + + + + + + + + + + +
            Centered
            cell 1,1
            Left-aligned
            cell 1,2
            Right-aligned
            cell 1,3
            cell 2,1cell 2,2cell 2,3
            +

            + Table caption +

            +

            No grid, no caption:

            + + + + + + + + + +
            cellcell
            cellcell
            +

            Horizontal line:

            +
            +

            New page.

            +

            + Italic font. Bold font. Monospaced font.

            +

            Anchor. Link to Anchor. Link to http://www.pixware.fr. Link to showing alternate text. Link to Pixware home page. -

            -

            Force line
            break.

            -

            Non breaking space.

            +

            +

            Force line
            break.

            +

            Non breaking space.

            Escaped special characters:
            ~
            =
            @@ -115,12 +115,12 @@ {
            }
            \ -

            -

            Copyright symbol: ©©©.

            -
            -
            -
            -
            - - +

            +

            Copyright symbol: ©©©.

            + + + + + + \ No newline at end of file