From 175feaea23aff1a45bbb36b64a60256221c75cf1 Mon Sep 17 00:00:00 2001 From: Jeremy Long Date: Tue, 4 Nov 2014 05:27:46 -0500 Subject: [PATCH] added a modified copy of DirectoryScanner from Apache Ant to resolve issue #153 Former-commit-id: e37eb42cc574e4255533a0e7fbe78f5ed0c83146 --- .../org/apache/tools/ant/BuildException.java | 153 ++ .../apache/tools/ant/DirectoryScanner.java | 1816 +++++++++++++++++ .../org/apache/tools/ant/FileScanner.java | 158 ++ .../org/apache/tools/ant/Location.java | 178 ++ .../org/apache/tools/ant/PathTokenizer.java | 165 ++ .../apache/tools/ant/ProjectComponent.java | 161 ++ .../org/apache/tools/ant/launch/Locator.java | 530 +++++ .../apache/tools/ant/launch/package-info.java | 14 + .../org/apache/tools/ant/package-info.java | 14 + .../ant/taskdefs/condition/Condition.java | 35 + .../tools/ant/taskdefs/condition/Os.java | 321 +++ .../ant/taskdefs/condition/package-info.java | 14 + .../org/apache/tools/ant/types/DataType.java | 353 ++++ .../org/apache/tools/ant/types/Reference.java | 126 ++ .../org/apache/tools/ant/types/Resource.java | 462 +++++ .../tools/ant/types/ResourceCollection.java | 50 + .../tools/ant/types/ResourceFactory.java | 38 + .../apache/tools/ant/types/package-info.java | 14 + .../tools/ant/types/resources/Appendable.java | 35 + .../ant/types/resources/FileProvider.java | 36 + .../ant/types/resources/FileResource.java | 414 ++++ .../tools/ant/types/resources/Touchable.java | 32 + .../ant/types/resources/package-info.java | 14 + .../ant/types/selectors/FileSelector.java | 48 + .../ant/types/selectors/SelectorScanner.java | 49 + .../ant/types/selectors/SelectorUtils.java | 695 +++++++ .../ant/types/selectors/TokenizedPath.java | 219 ++ .../ant/types/selectors/TokenizedPattern.java | 177 ++ .../ant/types/selectors/package-info.java | 14 + .../tools/ant/util/CollectionUtils.java | 265 +++ .../org/apache/tools/ant/util/FileUtils.java | 1667 +++++++++++++++ .../tools/ant/util/SymbolicLinkUtils.java | 291 +++ .../org/apache/tools/ant/util/VectorSet.java | 242 +++ .../apache/tools/ant/util/package-info.java | 14 + .../tools/ant/DirectoryScannerTest.java | 732 +++++++ 35 files changed, 9546 insertions(+) create mode 100644 dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/BuildException.java create mode 100644 dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/DirectoryScanner.java create mode 100644 dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/FileScanner.java create mode 100644 dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/Location.java create mode 100644 dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/PathTokenizer.java create mode 100644 dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/ProjectComponent.java create mode 100644 dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/launch/Locator.java create mode 100644 dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/launch/package-info.java create mode 100644 dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/package-info.java create mode 100644 dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/taskdefs/condition/Condition.java create mode 100644 dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/taskdefs/condition/Os.java create mode 100644 dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/taskdefs/condition/package-info.java create mode 100644 dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/DataType.java create mode 100644 dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/Reference.java create mode 100644 dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/Resource.java create mode 100644 dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/ResourceCollection.java create mode 100644 dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/ResourceFactory.java create mode 100644 dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/package-info.java create mode 100644 dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/resources/Appendable.java create mode 100644 dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/resources/FileProvider.java create mode 100644 dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/resources/FileResource.java create mode 100644 dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/resources/Touchable.java create mode 100644 dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/resources/package-info.java create mode 100644 dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/selectors/FileSelector.java create mode 100644 dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/selectors/SelectorScanner.java create mode 100644 dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/selectors/SelectorUtils.java create mode 100644 dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/selectors/TokenizedPath.java create mode 100644 dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/selectors/TokenizedPattern.java create mode 100644 dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/selectors/package-info.java create mode 100644 dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/util/CollectionUtils.java create mode 100644 dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/util/FileUtils.java create mode 100644 dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/util/SymbolicLinkUtils.java create mode 100644 dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/util/VectorSet.java create mode 100644 dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/util/package-info.java create mode 100644 dependency-check-utils/src/test/java/org/owasp/dependencycheck/org/apache/tools/ant/DirectoryScannerTest.java diff --git a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/BuildException.java b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/BuildException.java new file mode 100644 index 000000000..59446d3ce --- /dev/null +++ b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/BuildException.java @@ -0,0 +1,153 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.owasp.dependencycheck.org.apache.tools.ant; + +/** + * Signals an error condition during a build + */ +public class BuildException extends RuntimeException { + + private static final long serialVersionUID = -5419014565354664240L; + + /** Location in the build file where the exception occurred */ + private Location location = Location.UNKNOWN_LOCATION; + + /** + * Constructs a build exception with no descriptive information. + */ + public BuildException() { + super(); + } + + /** + * Constructs an exception with the given descriptive message. + * + * @param message A description of or information about the exception. + * Should not be null. + */ + public BuildException(String message) { + super(message); + } + + /** + * Constructs an exception with the given message and exception as + * a root cause. + * + * @param message A description of or information about the exception. + * Should not be null unless a cause is specified. + * @param cause The exception that might have caused this one. + * May be null. + */ + public BuildException(String message, Throwable cause) { + super(message, cause); + } + + /** + * Constructs an exception with the given message and exception as + * a root cause and a location in a file. + * + * @param msg A description of or information about the exception. + * Should not be null unless a cause is specified. + * @param cause The exception that might have caused this one. + * May be null. + * @param location The location in the project file where the error + * occurred. Must not be null. + */ + public BuildException(String msg, Throwable cause, Location location) { + this(msg, cause); + this.location = location; + } + + /** + * Constructs an exception with the given exception as a root cause. + * + * @param cause The exception that might have caused this one. + * Should not be null. + */ + public BuildException(Throwable cause) { + super(cause); + } + + /** + * Constructs an exception with the given descriptive message and a + * location in a file. + * + * @param message A description of or information about the exception. + * Should not be null. + * @param location The location in the project file where the error + * occurred. Must not be null. + */ + public BuildException(String message, Location location) { + super(message); + this.location = location; + } + + /** + * Constructs an exception with the given exception as + * a root cause and a location in a file. + * + * @param cause The exception that might have caused this one. + * Should not be null. + * @param location The location in the project file where the error + * occurred. Must not be null. + */ + public BuildException(Throwable cause, Location location) { + this(cause); + this.location = location; + } + + /** + * Returns the nested exception, if any. + * + * @return the nested exception, or null if no + * exception is associated with this one + * @deprecated Use {@link #getCause} instead. + */ + public Throwable getException() { + return getCause(); + } + + /** + * Returns the location of the error and the error message. + * + * @return the location of the error and the error message + */ + public String toString() { + return location.toString() + getMessage(); + } + + /** + * Sets the file location where the error occurred. + * + * @param location The file location where the error occurred. + * Must not be null. + */ + public void setLocation(Location location) { + this.location = location; + } + + /** + * Returns the file location where the error occurred. + * + * @return the file location where the error occurred. + */ + public Location getLocation() { + return location; + } + +} diff --git a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/DirectoryScanner.java b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/DirectoryScanner.java new file mode 100644 index 000000000..e65d994a0 --- /dev/null +++ b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/DirectoryScanner.java @@ -0,0 +1,1816 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.owasp.dependencycheck.org.apache.tools.ant; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.Map; +import java.util.Set; +import java.util.Vector; +import org.owasp.dependencycheck.org.apache.tools.ant.taskdefs.condition.Os; +import org.owasp.dependencycheck.org.apache.tools.ant.types.Resource; +import org.owasp.dependencycheck.org.apache.tools.ant.types.ResourceFactory; +import org.owasp.dependencycheck.org.apache.tools.ant.types.resources.FileResource; +import org.owasp.dependencycheck.org.apache.tools.ant.types.selectors.FileSelector; +import org.owasp.dependencycheck.org.apache.tools.ant.types.selectors.SelectorScanner; +import org.owasp.dependencycheck.org.apache.tools.ant.types.selectors.SelectorUtils; +import org.owasp.dependencycheck.org.apache.tools.ant.types.selectors.TokenizedPath; +import org.owasp.dependencycheck.org.apache.tools.ant.types.selectors.TokenizedPattern; +import org.owasp.dependencycheck.org.apache.tools.ant.util.CollectionUtils; +import org.owasp.dependencycheck.org.apache.tools.ant.util.FileUtils; +import org.owasp.dependencycheck.org.apache.tools.ant.util.SymbolicLinkUtils; +import org.owasp.dependencycheck.org.apache.tools.ant.util.VectorSet; + +/** + * 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 and + * exclude patterns. Only files/directories which match at least one 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 File.separator ('/' under UNIX, '\' under + * Windows). 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 to match must also start with a + * File.separator. When a pattern does not start with a File.separator, the 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"};
+ *   String[] excludes = {"modules\\*\\**"};
+ *   ds.setIncludes(includes);
+ *   ds.setExcludes(excludes);
+ *   ds.setBasedir(new File("test"));
+ *   ds.setCaseSensitive(true);
+ *   ds.scan();
+ *
+ *   System.out.println("FILES:");
+ *   String[] files = ds.getIncludedFiles();
+ *   for (int i = 0; i < files.length; i++) {
+ *     System.out.println(files[i]);
+ *   }
+ * 
This will scan a directory called test for .class files, but excludes all files in all proper subdirectories + * of a directory called "modules". + * + */ +public class DirectoryScanner + implements FileScanner, SelectorScanner, ResourceFactory { + + /** + * Is OpenVMS the operating system we're running on? + */ + private static final boolean ON_VMS = Os.isFamily("openvms"); + + /** + * Patterns which should be excluded by default. + * + *

+ * Note that you can now add patterns to the list of default excludes. Added patterns will not become part of this + * array that has only been kept around for backwards compatibility reasons.

+ * + * @deprecated since 1.6.x. Use the {@link #getDefaultExcludes getDefaultExcludes} method instead. + */ + protected static final String[] DEFAULTEXCLUDES = { + // Miscellaneous typical temporary files + SelectorUtils.DEEP_TREE_MATCH + "/*~", + SelectorUtils.DEEP_TREE_MATCH + "/#*#", + SelectorUtils.DEEP_TREE_MATCH + "/.#*", + SelectorUtils.DEEP_TREE_MATCH + "/%*%", + SelectorUtils.DEEP_TREE_MATCH + "/._*", + // CVS + SelectorUtils.DEEP_TREE_MATCH + "/CVS", + SelectorUtils.DEEP_TREE_MATCH + "/CVS/" + SelectorUtils.DEEP_TREE_MATCH, + SelectorUtils.DEEP_TREE_MATCH + "/.cvsignore", + // SCCS + SelectorUtils.DEEP_TREE_MATCH + "/SCCS", + SelectorUtils.DEEP_TREE_MATCH + "/SCCS/" + SelectorUtils.DEEP_TREE_MATCH, + // Visual SourceSafe + SelectorUtils.DEEP_TREE_MATCH + "/vssver.scc", + // Subversion + SelectorUtils.DEEP_TREE_MATCH + "/.svn", + SelectorUtils.DEEP_TREE_MATCH + "/.svn/" + SelectorUtils.DEEP_TREE_MATCH, + // Git + SelectorUtils.DEEP_TREE_MATCH + "/.git", + SelectorUtils.DEEP_TREE_MATCH + "/.git/" + SelectorUtils.DEEP_TREE_MATCH, + SelectorUtils.DEEP_TREE_MATCH + "/.gitattributes", + SelectorUtils.DEEP_TREE_MATCH + "/.gitignore", + SelectorUtils.DEEP_TREE_MATCH + "/.gitmodules", + // Mercurial + SelectorUtils.DEEP_TREE_MATCH + "/.hg", + SelectorUtils.DEEP_TREE_MATCH + "/.hg/" + SelectorUtils.DEEP_TREE_MATCH, + SelectorUtils.DEEP_TREE_MATCH + "/.hgignore", + SelectorUtils.DEEP_TREE_MATCH + "/.hgsub", + SelectorUtils.DEEP_TREE_MATCH + "/.hgsubstate", + SelectorUtils.DEEP_TREE_MATCH + "/.hgtags", + // Bazaar + SelectorUtils.DEEP_TREE_MATCH + "/.bzr", + SelectorUtils.DEEP_TREE_MATCH + "/.bzr/" + SelectorUtils.DEEP_TREE_MATCH, + SelectorUtils.DEEP_TREE_MATCH + "/.bzrignore", + // Mac + SelectorUtils.DEEP_TREE_MATCH + "/.DS_Store" + }; + + /** + * default value for {@link #maxLevelsOfSymlinks maxLevelsOfSymlinks} + * + * @since Ant 1.8.0 + */ + public static final int MAX_LEVELS_OF_SYMLINKS = 5; + /** + * The end of the exception message if something that should be there doesn't exist. + */ + public static final String DOES_NOT_EXIST_POSTFIX = " does not exist."; + + /** + * Helper. + */ + private static final FileUtils FILE_UTILS = FileUtils.getFileUtils(); + + /** + * Helper. + */ + private static final SymbolicLinkUtils SYMLINK_UTILS + = SymbolicLinkUtils.getSymbolicLinkUtils(); + + /** + * Patterns which should be excluded by default. + * + * @see #addDefaultExcludes() + */ + private static final Set defaultExcludes = new HashSet(); + + static { + resetDefaultExcludes(); + } + + // CheckStyle:VisibilityModifier OFF - bc + /** + * The base directory to be scanned. + */ + protected File basedir; + + /** + * The patterns for the files to be included. + */ + protected String[] includes; + + /** + * The patterns for the files to be excluded. + */ + protected String[] excludes; + + /** + * Selectors that will filter which files are in our candidate list. + */ + protected FileSelector[] selectors = null; + + /** + * The files which matched at least one include and no excludes and were selected. + */ + protected Vector filesIncluded; + + /** + * 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; + + /** + * The directories which matched at least one include and no excludes and were selected. + */ + protected Vector dirsIncluded; + + /** + * 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; + + /** + * The files which matched at least one include and no excludes and which a selector discarded. + */ + protected Vector filesDeselected; + + /** + * The directories which matched at least one include and no excludes but which a selector discarded. + */ + protected Vector dirsDeselected; + + /** + * Whether or not our results were built by a slow scan. + */ + protected boolean haveSlowResults = false; + + /** + * Whether or not the file system should be treated as a case sensitive one. + */ + protected boolean isCaseSensitive = true; + + /** + * Whether a missing base directory is an error. + * + * @since Ant 1.7.1 + */ + protected boolean errorOnMissingDir = true; + + /** + * Whether or not symbolic links should be followed. + * + * @since Ant 1.5 + */ + private boolean followSymlinks = true; + + /** + * Whether or not everything tested so far has been included. + */ + protected boolean everythingIncluded = true; + + // CheckStyle:VisibilityModifier ON + /** + * List of all scanned directories. + * + * @since Ant 1.6 + */ + private Set scannedDirs = new HashSet(); + + /** + * Map of all include patterns that are full file names and don't contain any wildcards. + * + *

+ * Maps pattern string to TokenizedPath.

+ * + *

+ * If this instance is not case sensitive, the file names get turned to upper case.

+ * + *

+ * Gets lazily initialized on the first invocation of isIncluded or isExcluded and cleared at the end of the scan + * method (cleared in clearCaches, actually).

+ * + * @since Ant 1.8.0 + */ + private Map includeNonPatterns = new HashMap(); + + /** + * Map of all exclude patterns that are full file names and don't contain any wildcards. + * + *

+ * Maps pattern string to TokenizedPath.

+ * + *

+ * If this instance is not case sensitive, the file names get turned to upper case.

+ * + *

+ * Gets lazily initialized on the first invocation of isIncluded or isExcluded and cleared at the end of the scan + * method (cleared in clearCaches, actually).

+ * + * @since Ant 1.8.0 + */ + private Map excludeNonPatterns = new HashMap(); + + /** + * Array of all include patterns that contain wildcards. + * + *

+ * Gets lazily initialized on the first invocation of isIncluded or isExcluded and cleared at the end of the scan + * method (cleared in clearCaches, actually).

+ */ + private TokenizedPattern[] includePatterns; + + /** + * Array of all exclude patterns that contain wildcards. + * + *

+ * Gets lazily initialized on the first invocation of isIncluded or isExcluded and cleared at the end of the scan + * method (cleared in clearCaches, actually).

+ */ + private TokenizedPattern[] excludePatterns; + + /** + * Have the non-pattern sets and pattern arrays for in- and excludes been initialized? + * + * @since Ant 1.6.3 + */ + private boolean areNonPatternSetsReady = false; + + /** + * Scanning flag. + * + * @since Ant 1.6.3 + */ + private boolean scanning = false; + + /** + * Scanning lock. + * + * @since Ant 1.6.3 + */ + private Object scanLock = new Object(); + + /** + * Slow scanning flag. + * + * @since Ant 1.6.3 + */ + private boolean slowScanning = false; + + /** + * Slow scanning lock. + * + * @since Ant 1.6.3 + */ + private Object slowScanLock = new Object(); + + /** + * Exception thrown during scan. + * + * @since Ant 1.6.3 + */ + private IllegalStateException illegal = null; + + /** + * The maximum number of times a symbolic link may be followed during a scan. + * + * @since Ant 1.8.0 + */ + private int maxLevelsOfSymlinks = MAX_LEVELS_OF_SYMLINKS; + + /** + * Absolute paths of all symlinks that haven't been followed but would have been if followsymlinks had been true or + * maxLevelsOfSymlinks had been higher. + * + * @since Ant 1.8.0 + */ + private Set notFollowedSymlinks = new HashSet(); + + /** + * Sole constructor. + */ + public DirectoryScanner() { + } + + /** + * Test 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. + * + * @return whether or not a given path matches the start of a given pattern up to the first "**". + */ + protected static boolean matchPatternStart(String pattern, String str) { + return SelectorUtils.matchPatternStart(pattern, str); + } + + /** + * Test 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 isCaseSensitive Whether or not matching should be performed case sensitively. + * + * @return whether or not a given path matches the start of a given pattern up to the first "**". + */ + protected static boolean matchPatternStart(String pattern, String str, + boolean isCaseSensitive) { + return SelectorUtils.matchPatternStart(pattern, str, isCaseSensitive); + } + + /** + * Test 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. + * + * @return true if the pattern matches against the string, or false otherwise. + */ + protected static boolean matchPath(String pattern, String str) { + return SelectorUtils.matchPath(pattern, str); + } + + /** + * Test 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 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) { + return SelectorUtils.matchPath(pattern, str, isCaseSensitive); + } + + /** + * Test 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. + * + * @return true if the string matches against the pattern, or false otherwise. + */ + public static boolean match(String pattern, String str) { + return SelectorUtils.match(pattern, str); + } + + /** + * Test 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 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) { + return SelectorUtils.match(pattern, str, isCaseSensitive); + } + + /** + * Get the list of patterns that should be excluded by default. + * + * @return An array of String based on the current contents of the defaultExcludes + * Set. + * + * @since Ant 1.6 + */ + public static String[] getDefaultExcludes() { + synchronized (defaultExcludes) { + return (String[]) defaultExcludes.toArray(new String[defaultExcludes + .size()]); + } + } + + /** + * Add a pattern to the default excludes unless it is already a default exclude. + * + * @param s A string to add as an exclude pattern. + * @return true if the string was added; false if it already existed. + * + * @since Ant 1.6 + */ + public static boolean addDefaultExclude(String s) { + synchronized (defaultExcludes) { + return defaultExcludes.add(s); + } + } + + /** + * Remove a string if it is a default exclude. + * + * @param s The string to attempt to remove. + * @return true if s was a default exclude (and thus was removed); false if + * s was not in the default excludes list to begin with. + * + * @since Ant 1.6 + */ + public static boolean removeDefaultExclude(String s) { + synchronized (defaultExcludes) { + return defaultExcludes.remove(s); + } + } + + /** + * Go back to the hardwired default exclude patterns. + * + * @since Ant 1.6 + */ + public static void resetDefaultExcludes() { + synchronized (defaultExcludes) { + defaultExcludes.clear(); + for (int i = 0; i < DEFAULTEXCLUDES.length; i++) { + defaultExcludes.add(DEFAULTEXCLUDES[i]); + } + } + } + + /** + * Set the base directory to be scanned. This is the directory which is scanned recursively. All '/' and '\' + * characters are replaced by File.separatorChar, so the separator used need not match + * File.separatorChar. + * + * @param basedir The base directory to scan. + */ + public void setBasedir(String basedir) { + setBasedir(basedir == null ? (File) null + : new File(basedir.replace('/', File.separatorChar).replace( + '\\', File.separatorChar))); + } + + /** + * Set the base directory to be scanned. This is the directory which is scanned recursively. + * + * @param basedir The base directory for scanning. + */ + public synchronized void setBasedir(File basedir) { + this.basedir = basedir; + } + + /** + * Return the base directory to be scanned. This is the directory which is scanned recursively. + * + * @return the base directory to be scanned. + */ + public synchronized File getBasedir() { + return basedir; + } + + /** + * Find out whether include exclude patterns are matched in a case sensitive way. + * + * @return whether or not the scanning is case sensitive. + * @since Ant 1.6 + */ + public synchronized boolean isCaseSensitive() { + return isCaseSensitive; + } + + /** + * Set whether or not include and exclude patterns are matched in a case sensitive way. + * + * @param isCaseSensitive whether or not the file system should be regarded as a case sensitive one. + */ + public synchronized void setCaseSensitive(boolean isCaseSensitive) { + this.isCaseSensitive = isCaseSensitive; + } + + /** + * Sets whether or not a missing base directory is an error + * + * @param errorOnMissingDir whether or not a missing base directory is an error + * @since Ant 1.7.1 + */ + public void setErrorOnMissingDir(boolean errorOnMissingDir) { + this.errorOnMissingDir = errorOnMissingDir; + } + + /** + * Get whether or not a DirectoryScanner follows symbolic links. + * + * @return flag indicating whether symbolic links should be followed. + * + * @since Ant 1.6 + */ + public synchronized boolean isFollowSymlinks() { + return followSymlinks; + } + + /** + * Set whether or not symbolic links should be followed. + * + * @param followSymlinks whether or not symbolic links should be followed. + */ + public synchronized void setFollowSymlinks(boolean followSymlinks) { + this.followSymlinks = followSymlinks; + } + + /** + * The maximum number of times a symbolic link may be followed during a scan. + * + * @since Ant 1.8.0 + */ + public void setMaxLevelsOfSymlinks(int max) { + maxLevelsOfSymlinks = max; + } + + /** + * Set 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. + */ + public synchronized void setIncludes(String[] includes) { + if (includes == null) { + this.includes = null; + } else { + this.includes = new String[includes.length]; + for (int i = 0; i < includes.length; i++) { + this.includes[i] = normalizePattern(includes[i]); + } + } + } + + public synchronized void setIncludes(String include) { + if (include == null) { + this.includes = null; + } else { + this.includes = new String[1]; + this.includes[0] = normalizePattern(include); + } + } + + /** + * Set 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. May be null, indicating that no files should be + * excluded. If a non-null list is given, all elements must be non-null. + */ + public synchronized void setExcludes(String[] excludes) { + if (excludes == null) { + this.excludes = null; + } else { + this.excludes = new String[excludes.length]; + for (int i = 0; i < excludes.length; i++) { + this.excludes[i] = normalizePattern(excludes[i]); + } + } + } + + /** + * Add to 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. May be null, in which case the exclude patterns don't + * get changed at all. + * + * @since Ant 1.6.3 + */ + public synchronized void addExcludes(String[] excludes) { + if (excludes != null && excludes.length > 0) { + if (this.excludes != null && this.excludes.length > 0) { + String[] tmp = new String[excludes.length + + this.excludes.length]; + System.arraycopy(this.excludes, 0, tmp, 0, + this.excludes.length); + for (int i = 0; i < excludes.length; i++) { + tmp[this.excludes.length + i] + = normalizePattern(excludes[i]); + } + this.excludes = tmp; + } else { + setExcludes(excludes); + } + } + } + + /** + * 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. + * + * @since Ant 1.6.3 + */ + private static String normalizePattern(String p) { + String pattern = p.replace('/', File.separatorChar) + .replace('\\', File.separatorChar); + if (pattern.endsWith(File.separator)) { + pattern += SelectorUtils.DEEP_TREE_MATCH; + } + return pattern; + } + + /** + * Set the selectors that will select the filelist. + * + * @param selectors specifies the selectors to be invoked on a scan. + */ + public synchronized void setSelectors(FileSelector[] selectors) { + this.selectors = selectors; + } + + /** + * Return whether or not the scanner has included all the files or directories it has come across so far. + * + * @return true if all files and directories which have been found so far have been included. + */ + public synchronized boolean isEverythingIncluded() { + return everythingIncluded; + } + + /** + * Scan for files which match at least one include pattern and don't match any exclude patterns. If there are + * selectors then the files must pass muster there, as well. Scans under basedir, if set; otherwise the include + * patterns without leading wildcards specify the absolute paths of the files that may be included. + * + * @exception IllegalStateException if the base directory was set incorrectly (i.e. if it doesn't exist or isn't a + * directory). + */ + public void scan() throws IllegalStateException { + synchronized (scanLock) { + if (scanning) { + while (scanning) { + try { + scanLock.wait(); + } catch (InterruptedException e) { + continue; + } + } + if (illegal != null) { + throw illegal; + } + return; + } + scanning = true; + } + File savedBase = basedir; + try { + synchronized (this) { + illegal = null; + clearResults(); + + // set in/excludes to reasonable defaults if needed: + boolean nullIncludes = (includes == null); + includes = nullIncludes + ? new String[]{SelectorUtils.DEEP_TREE_MATCH} : includes; + boolean nullExcludes = (excludes == null); + excludes = nullExcludes ? new String[0] : excludes; + + if (basedir != null && !followSymlinks + && SYMLINK_UTILS.isSymbolicLink(basedir)) { + notFollowedSymlinks.add(basedir.getAbsolutePath()); + basedir = null; + } + + if (basedir == null) { + // if no basedir and no includes, nothing to do: + if (nullIncludes) { + return; + } + } else { + if (!basedir.exists()) { + if (errorOnMissingDir) { + illegal = new IllegalStateException("basedir " + + basedir + + DOES_NOT_EXIST_POSTFIX); + } else { + // Nothing to do - basedir does not exist + return; + } + } else if (!basedir.isDirectory()) { + illegal = new IllegalStateException("basedir " + + basedir + + " is not a" + + " directory."); + } + if (illegal != null) { + throw illegal; + } + } + if (isIncluded(TokenizedPath.EMPTY_PATH)) { + if (!isExcluded(TokenizedPath.EMPTY_PATH)) { + if (isSelected("", basedir)) { + dirsIncluded.addElement(""); + } else { + dirsDeselected.addElement(""); + } + } else { + dirsExcluded.addElement(""); + } + } else { + dirsNotIncluded.addElement(""); + } + checkIncludePatterns(); + clearCaches(); + includes = nullIncludes ? null : includes; + excludes = nullExcludes ? null : excludes; + } + } catch (IOException ex) { + throw new BuildException(ex); + } finally { + basedir = savedBase; + synchronized (scanLock) { + scanning = false; + scanLock.notifyAll(); + } + } + } + + /** + * This routine is actually checking all the include patterns in order to avoid scanning everything under base dir. + * + * @since Ant 1.6 + */ + private void checkIncludePatterns() { + ensureNonPatternSetsReady(); + Map newroots = new HashMap(); + + // put in the newroots map the include patterns without + // wildcard tokens + for (int i = 0; i < includePatterns.length; i++) { + String pattern = includePatterns[i].toString(); + if (!shouldSkipPattern(pattern)) { + newroots.put(includePatterns[i].rtrimWildcardTokens(), + pattern); + } + } + for (Map.Entry entry : includeNonPatterns.entrySet()) { + String pattern = entry.getKey(); + if (!shouldSkipPattern(pattern)) { + newroots.put(entry.getValue(), pattern); + } + } + + if (newroots.containsKey(TokenizedPath.EMPTY_PATH) + && basedir != null) { + // we are going to scan everything anyway + scandir(basedir, "", true); + } else { + File canonBase = null; + if (basedir != null) { + try { + canonBase = basedir.getCanonicalFile(); + } catch (IOException ex) { + throw new BuildException(ex); + } + } + // only scan directories that can include matched files or + // directories + for (Map.Entry entry : newroots.entrySet()) { + TokenizedPath currentPath = entry.getKey(); + String currentelement = currentPath.toString(); + if (basedir == null + && !FileUtils.isAbsolutePath(currentelement)) { + continue; + } + File myfile = new File(basedir, currentelement); + + if (myfile.exists()) { + // may be on a case insensitive file system. We want + // the results to show what's really on the disk, so + // we need to double check. + try { + String path = (basedir == null) + ? myfile.getCanonicalPath() + : FILE_UTILS.removeLeadingPath(canonBase, + myfile.getCanonicalFile()); + if (!path.equals(currentelement) || ON_VMS) { + myfile = currentPath.findFile(basedir, true); + if (myfile != null && basedir != null) { + currentelement = FILE_UTILS.removeLeadingPath( + basedir, myfile); + if (!currentPath.toString() + .equals(currentelement)) { + currentPath + = new TokenizedPath(currentelement); + } + } + } + } catch (IOException ex) { + throw new BuildException(ex); + } + } + + if ((myfile == null || !myfile.exists()) && !isCaseSensitive()) { + File f = currentPath.findFile(basedir, false); + if (f != null && f.exists()) { + // adapt currentelement to the case we've + // actually found + currentelement = (basedir == null) + ? f.getAbsolutePath() + : FILE_UTILS.removeLeadingPath(basedir, f); + myfile = f; + currentPath = new TokenizedPath(currentelement); + } + } + + if (myfile != null && myfile.exists()) { + if (!followSymlinks && currentPath.isSymlink(basedir)) { + accountForNotFollowedSymlink(currentPath, myfile); + continue; + } + if (myfile.isDirectory()) { + if (isIncluded(currentPath) + && currentelement.length() > 0) { + accountForIncludedDir(currentPath, myfile, true); + } else { + scandir(myfile, currentPath, true); + } + } else if (myfile.isFile()) { + String originalpattern = (String) entry.getValue(); + boolean included = isCaseSensitive() + ? originalpattern.equals(currentelement) + : originalpattern.equalsIgnoreCase(currentelement); + if (included) { + accountForIncludedFile(currentPath, myfile); + } + } + } + } + } + } + + /** + * true if the pattern specifies a relative path without basedir or an absolute path not inside basedir. + * + * @since Ant 1.8.0 + */ + private boolean shouldSkipPattern(String pattern) { + if (FileUtils.isAbsolutePath(pattern)) { + //skip abs. paths not under basedir, if set: + if (basedir != null + && !SelectorUtils.matchPatternStart(pattern, + basedir.getAbsolutePath(), + isCaseSensitive())) { + return true; + } + } else if (basedir == null) { + //skip non-abs. paths if basedir == null: + return true; + } + return false; + } + + /** + * Clear the result caches for a scan. + */ + protected synchronized void clearResults() { + filesIncluded = new VectorSet(); + filesNotIncluded = new VectorSet(); + filesExcluded = new VectorSet(); + filesDeselected = new VectorSet(); + dirsIncluded = new VectorSet(); + dirsNotIncluded = new VectorSet(); + dirsExcluded = new VectorSet(); + dirsDeselected = new VectorSet(); + everythingIncluded = (basedir != null); + scannedDirs.clear(); + notFollowedSymlinks.clear(); + } + + /** + * Top level invocation for a slow scan. A slow scan builds up a full 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() { + synchronized (slowScanLock) { + if (haveSlowResults) { + return; + } + if (slowScanning) { + while (slowScanning) { + try { + slowScanLock.wait(); + } catch (InterruptedException e) { + // Empty + } + } + return; + } + slowScanning = true; + } + try { + synchronized (this) { + + // set in/excludes to reasonable defaults if needed: + boolean nullIncludes = (includes == null); + includes = nullIncludes + ? new String[]{SelectorUtils.DEEP_TREE_MATCH} : includes; + boolean nullExcludes = (excludes == null); + excludes = nullExcludes ? new String[0] : excludes; + + String[] excl = new String[dirsExcluded.size()]; + dirsExcluded.copyInto(excl); + + String[] notIncl = new String[dirsNotIncluded.size()]; + dirsNotIncluded.copyInto(notIncl); + + ensureNonPatternSetsReady(); + + processSlowScan(excl); + processSlowScan(notIncl); + clearCaches(); + includes = nullIncludes ? null : includes; + excludes = nullExcludes ? null : excludes; + } + } finally { + synchronized (slowScanLock) { + haveSlowResults = true; + slowScanning = false; + slowScanLock.notifyAll(); + } + } + } + + private void processSlowScan(String[] arr) { + for (int i = 0; i < arr.length; i++) { + TokenizedPath path = new TokenizedPath(arr[i]); + if (!couldHoldIncluded(path) || contentsExcluded(path)) { + scandir(new File(basedir, arr[i]), path, false); + } + } + } + + /** + * Scan the given directory for files and directories. Found files and directories are placed in their respective + * collections, based on the matching of includes, excludes, and the selectors. When a directory is found, it is + * scanned recursively. + * + * @param dir The directory to scan. Must not be null. + * @param vpath The path relative to the base directory (needed to 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. + * + * @see #filesIncluded + * @see #filesNotIncluded + * @see #filesExcluded + * @see #dirsIncluded + * @see #dirsNotIncluded + * @see #dirsExcluded + * @see #slowScan + */ + protected void scandir(File dir, String vpath, boolean fast) { + scandir(dir, new TokenizedPath(vpath), fast); + } + + /** + * Scan the given directory for files and directories. Found files and directories are placed in their respective + * collections, based on the matching of includes, excludes, and the selectors. When a directory is found, it is + * scanned recursively. + * + * @param dir The directory to scan. Must not be null. + * @param path The path relative to the base directory (needed to 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. + * + * @see #filesIncluded + * @see #filesNotIncluded + * @see #filesExcluded + * @see #dirsIncluded + * @see #dirsNotIncluded + * @see #dirsExcluded + * @see #slowScan + */ + private void scandir(File dir, TokenizedPath path, boolean fast) { + if (dir == null) { + throw new BuildException("dir must not be null."); + } + String[] newfiles = dir.list(); + if (newfiles == null) { + if (!dir.exists()) { + throw new BuildException(dir + DOES_NOT_EXIST_POSTFIX); + } else if (!dir.isDirectory()) { + throw new BuildException(dir + " is not a directory."); + } else { + throw new BuildException("IO error scanning directory '" + + dir.getAbsolutePath() + "'"); + } + } + scandir(dir, path, fast, newfiles, new LinkedList()); + } + + private void scandir(File dir, TokenizedPath path, boolean fast, + String[] newfiles, LinkedList directoryNamesFollowed) { + String vpath = path.toString(); + if (vpath.length() > 0 && !vpath.endsWith(File.separator)) { + vpath += File.separator; + } + + // avoid double scanning of directories, can only happen in fast mode + if (fast && hasBeenScanned(vpath)) { + return; + } + if (!followSymlinks) { + ArrayList noLinks = new ArrayList(); + for (int i = 0; i < newfiles.length; i++) { + try { + if (SYMLINK_UTILS.isSymbolicLink(dir, newfiles[i])) { + String name = vpath + newfiles[i]; + File file = new File(dir, newfiles[i]); + if (file.isDirectory()) { + dirsExcluded.addElement(name); + } else if (file.isFile()) { + filesExcluded.addElement(name); + } + accountForNotFollowedSymlink(name, file); + } else { + noLinks.add(newfiles[i]); + } + } catch (IOException ioe) { + String msg = "IOException caught while checking " + + "for links, couldn't get canonical path!"; + // will be caught and redirected to Ant's logging system + System.err.println(msg); + noLinks.add(newfiles[i]); + } + } + newfiles = (String[]) (noLinks.toArray(new String[noLinks.size()])); + } else { + directoryNamesFollowed.addFirst(dir.getName()); + } + + for (int i = 0; i < newfiles.length; i++) { + String name = vpath + newfiles[i]; + TokenizedPath newPath = new TokenizedPath(path, newfiles[i]); + File file = new File(dir, newfiles[i]); + String[] children = file.list(); + if (children == null || (children.length == 0 && file.isFile())) { + if (isIncluded(newPath)) { + accountForIncludedFile(newPath, file); + } else { + everythingIncluded = false; + filesNotIncluded.addElement(name); + } + } else if (file.isDirectory()) { // dir + + if (followSymlinks + && causesIllegalSymlinkLoop(newfiles[i], dir, + directoryNamesFollowed)) { + // will be caught and redirected to Ant's logging system + System.err.println("skipping symbolic link " + + file.getAbsolutePath() + + " -- too many levels of symbolic" + + " links."); + notFollowedSymlinks.add(file.getAbsolutePath()); + continue; + } + + if (isIncluded(newPath)) { + accountForIncludedDir(newPath, file, fast, children, + directoryNamesFollowed); + } else { + everythingIncluded = false; + dirsNotIncluded.addElement(name); + if (fast && couldHoldIncluded(newPath) + && !contentsExcluded(newPath)) { + scandir(file, newPath, fast, children, + directoryNamesFollowed); + } + } + if (!fast) { + scandir(file, newPath, fast, children, directoryNamesFollowed); + } + } + } + + if (followSymlinks) { + directoryNamesFollowed.removeFirst(); + } + } + + /** + * Process included file. + * + * @param name path of the file relative to the directory of the FileSet. + * @param file included File. + */ + private void accountForIncludedFile(TokenizedPath name, File file) { + processIncluded(name, file, filesIncluded, filesExcluded, + filesDeselected); + } + + /** + * Process included directory. + * + * @param name path of the directory relative to the directory of the FileSet. + * @param file directory as File. + * @param fast whether to perform fast scans. + */ + private void accountForIncludedDir(TokenizedPath name, File file, + boolean fast) { + processIncluded(name, file, dirsIncluded, dirsExcluded, dirsDeselected); + if (fast && couldHoldIncluded(name) && !contentsExcluded(name)) { + scandir(file, name, fast); + } + } + + private void accountForIncludedDir(TokenizedPath name, + File file, boolean fast, + String[] children, + LinkedList directoryNamesFollowed) { + processIncluded(name, file, dirsIncluded, dirsExcluded, dirsDeselected); + if (fast && couldHoldIncluded(name) && !contentsExcluded(name)) { + scandir(file, name, fast, children, directoryNamesFollowed); + } + } + + private void accountForNotFollowedSymlink(String name, File file) { + accountForNotFollowedSymlink(new TokenizedPath(name), file); + } + + private void accountForNotFollowedSymlink(TokenizedPath name, File file) { + if (!isExcluded(name) + && (isIncluded(name) + || (file.isDirectory() && couldHoldIncluded(name) + && !contentsExcluded(name)))) { + notFollowedSymlinks.add(file.getAbsolutePath()); + } + } + + private void processIncluded(TokenizedPath path, + File file, Vector inc, Vector exc, + Vector des) { + String name = path.toString(); + if (inc.contains(name) || exc.contains(name) || des.contains(name)) { + return; + } + + boolean included = false; + if (isExcluded(path)) { + exc.add(name); + } else if (isSelected(name, file)) { + included = true; + inc.add(name); + } else { + des.add(name); + } + everythingIncluded &= included; + } + + /** + * Test whether or not a name matches against at least one include pattern. + * + * @param name The name to match. Must not be null. + * @return true when the name matches against at least one include pattern, or false + * otherwise. + */ + protected boolean isIncluded(String name) { + return isIncluded(new TokenizedPath(name)); + } + + /** + * Test whether or not a name matches against at least one include pattern. + * + * @param name The name to match. Must not be null. + * @return true when the name matches against at least one include pattern, or false + * otherwise. + */ + private boolean isIncluded(TokenizedPath path) { + ensureNonPatternSetsReady(); + + if (isCaseSensitive() + ? includeNonPatterns.containsKey(path.toString()) + : includeNonPatterns.containsKey(path.toString().toUpperCase())) { + return true; + } + for (int i = 0; i < includePatterns.length; i++) { + if (includePatterns[i].matchPath(path, isCaseSensitive())) { + return true; + } + } + return false; + } + + /** + * Test whether or not a name matches the start of at least one include pattern. + * + * @param name The name to match. Must not be null. + * @return true when the name matches against the start of at least one include pattern, or + * false otherwise. + */ + protected boolean couldHoldIncluded(String name) { + return couldHoldIncluded(new TokenizedPath(name)); + } + + /** + * Test whether or not a name matches the start of at least one include pattern. + * + * @param tokenizedName The name to match. Must not be null. + * @return true when the name matches against the start of at least one include pattern, or + * false otherwise. + */ + private boolean couldHoldIncluded(TokenizedPath tokenizedName) { + for (int i = 0; i < includePatterns.length; i++) { + if (couldHoldIncluded(tokenizedName, includePatterns[i])) { + return true; + } + } + for (Iterator iter = includeNonPatterns.values().iterator(); + iter.hasNext();) { + if (couldHoldIncluded(tokenizedName, + iter.next().toPattern())) { + return true; + } + } + return false; + } + + /** + * Test whether or not a name matches the start of the given include pattern. + * + * @param tokenizedName The name to match. Must not be null. + * @return true when the name matches against the start of the include pattern, or false + * otherwise. + */ + private boolean couldHoldIncluded(TokenizedPath tokenizedName, + TokenizedPattern tokenizedInclude) { + return tokenizedInclude.matchStartOf(tokenizedName, isCaseSensitive()) + && isMorePowerfulThanExcludes(tokenizedName.toString()) + && isDeeper(tokenizedInclude, tokenizedName); + } + + /** + * Verify that a pattern specifies files deeper than the level of the specified file. + * + * @param pattern the pattern to check. + * @param name the name to check. + * @return whether the pattern is deeper than the name. + * @since Ant 1.6.3 + */ + private boolean isDeeper(TokenizedPattern pattern, TokenizedPath name) { + return pattern.containsPattern(SelectorUtils.DEEP_TREE_MATCH) + || pattern.depth() > name.depth(); + } + + /** + * Find out whether one particular include pattern is more powerful than all the excludes. Note: the power + * comparison is based on the length of the include pattern and of the exclude patterns without the wildcards. + * Ideally the comparison should be done based on the depth of the match; that is to say how many file separators + * have been matched before the first ** or the end of the pattern. + * + * IMPORTANT : this function should return false "with care". + * + * @param name the relative path to test. + * @return true if there is no exclude pattern more powerful than this include pattern. + * @since Ant 1.6 + */ + private boolean isMorePowerfulThanExcludes(String name) { + final String soughtexclude + = name + File.separatorChar + SelectorUtils.DEEP_TREE_MATCH; + for (int counter = 0; counter < excludePatterns.length; counter++) { + if (excludePatterns[counter].toString().equals(soughtexclude)) { + return false; + } + } + return true; + } + + /** + * Test whether all contents of the specified directory must be excluded. + * + * @param path the path to check. + * @return whether all the specified directory's contents are excluded. + */ + /* package */ boolean contentsExcluded(TokenizedPath path) { + for (int i = 0; i < excludePatterns.length; i++) { + if (excludePatterns[i].endsWith(SelectorUtils.DEEP_TREE_MATCH) + && excludePatterns[i].withoutLastToken() + .matchPath(path, isCaseSensitive())) { + return true; + } + } + return false; + } + + /** + * Test whether or not a name matches against at least one exclude pattern. + * + * @param name The name to match. Must not be null. + * @return true when the name matches against at least one exclude pattern, or false + * otherwise. + */ + protected boolean isExcluded(String name) { + return isExcluded(new TokenizedPath(name)); + } + + /** + * Test whether or not a name matches against at least one exclude pattern. + * + * @param name The name to match. Must not be null. + * @return true when the name matches against at least one exclude pattern, or false + * otherwise. + */ + private boolean isExcluded(TokenizedPath name) { + ensureNonPatternSetsReady(); + + if (isCaseSensitive() + ? excludeNonPatterns.containsKey(name.toString()) + : excludeNonPatterns.containsKey(name.toString().toUpperCase())) { + return true; + } + for (int i = 0; i < excludePatterns.length; i++) { + if (excludePatterns[i].matchPath(name, isCaseSensitive())) { + return true; + } + } + return false; + } + + /** + * Test whether a file should be selected. + * + * @param name the filename to check for selecting. + * @param file the java.io.File object for this filename. + * @return false when the selectors says that the file should not be selected, true + * otherwise. + */ + protected boolean isSelected(String name, File file) { + if (selectors != null) { + for (int i = 0; i < selectors.length; i++) { + if (!selectors[i].isSelected(basedir, name, file)) { + return false; + } + } + } + return true; + } + + /** + * Return the names of the files which matched at least one of the include patterns and none of the exclude + * patterns. The names are relative to the base directory. + * + * @return the names of the files which matched at least one of the include patterns and none of the exclude + * patterns. + */ + public String[] getIncludedFiles() { + String[] files; + synchronized (this) { + if (filesIncluded == null) { + throw new IllegalStateException("Must call scan() first"); + } + files = new String[filesIncluded.size()]; + filesIncluded.copyInto(files); + } + Arrays.sort(files); + return files; + } + + /** + * Return the count of included files. + * + * @return int. + * @since Ant 1.6.3 + */ + public synchronized int getIncludedFilesCount() { + if (filesIncluded == null) { + throw new IllegalStateException("Must call scan() first"); + } + return filesIncluded.size(); + } + + /** + * Return the names of the files which matched none of the include patterns. 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 matched none of the include patterns. + * + * @see #slowScan + */ + public synchronized String[] getNotIncludedFiles() { + slowScan(); + String[] files = new String[filesNotIncluded.size()]; + filesNotIncluded.copyInto(files); + return files; + } + + /** + * Return the names of the files which matched at least one of the include patterns and at least one of the exclude + * patterns. 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 matched at least one of the include patterns and at least one of the exclude + * patterns. + * + * @see #slowScan + */ + public synchronized String[] getExcludedFiles() { + slowScan(); + String[] files = new String[filesExcluded.size()]; + filesExcluded.copyInto(files); + return files; + } + + /** + *

+ * Return 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 synchronized String[] getDeselectedFiles() { + slowScan(); + String[] files = new String[filesDeselected.size()]; + filesDeselected.copyInto(files); + return files; + } + + /** + * Return the names of the directories which matched at least one of the include patterns and none of the exclude + * patterns. 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. + */ + public String[] getIncludedDirectories() { + String[] directories; + synchronized (this) { + if (dirsIncluded == null) { + throw new IllegalStateException("Must call scan() first"); + } + directories = new String[dirsIncluded.size()]; + dirsIncluded.copyInto(directories); + } + Arrays.sort(directories); + return directories; + } + + /** + * Return the count of included directories. + * + * @return int. + * @since Ant 1.6.3 + */ + public synchronized int getIncludedDirsCount() { + if (dirsIncluded == null) { + throw new IllegalStateException("Must call scan() first"); + } + return dirsIncluded.size(); + } + + /** + * Return the names of the directories which matched none of the include patterns. 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 matched none of the include patterns. + * + * @see #slowScan + */ + public synchronized String[] getNotIncludedDirectories() { + slowScan(); + String[] directories = new String[dirsNotIncluded.size()]; + dirsNotIncluded.copyInto(directories); + return directories; + } + + /** + * Return the names of the directories which matched at least one of the include patterns and at least one of the + * exclude patterns. 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 matched at least one of the include patterns and at least one of the + * exclude patterns. + * + * @see #slowScan + */ + public synchronized String[] getExcludedDirectories() { + slowScan(); + String[] directories = new String[dirsExcluded.size()]; + dirsExcluded.copyInto(directories); + return directories; + } + + /** + *

+ * Return 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 synchronized String[] getDeselectedDirectories() { + slowScan(); + String[] directories = new String[dirsDeselected.size()]; + dirsDeselected.copyInto(directories); + return directories; + } + + /** + * Absolute paths of all symbolic links that haven't been followed but would have been followed had followsymlinks + * been true or maxLevelsOfSymlinks been bigger. + * + * @return sorted array of not followed symlinks + * @since Ant 1.8.0 + * @see #notFollowedSymlinks + */ + public synchronized String[] getNotFollowedSymlinks() { + String[] links; + synchronized (this) { + links = (String[]) notFollowedSymlinks + .toArray(new String[notFollowedSymlinks.size()]); + } + Arrays.sort(links); + return links; + } + + /** + * Add default exclusions to the current exclusions set. + */ + public synchronized void addDefaultExcludes() { + int excludesLength = excludes == null ? 0 : excludes.length; + String[] newExcludes; + String[] defaultExcludesTemp = getDefaultExcludes(); + newExcludes = new String[excludesLength + defaultExcludesTemp.length]; + if (excludesLength > 0) { + System.arraycopy(excludes, 0, newExcludes, 0, excludesLength); + } + for (int i = 0; i < defaultExcludesTemp.length; i++) { + newExcludes[i + excludesLength] + = defaultExcludesTemp[i].replace('/', File.separatorChar) + .replace('\\', File.separatorChar); + } + excludes = newExcludes; + } + + /** + * Get the named resource. + * + * @param name path name of the file relative to the dir attribute. + * + * @return the resource with the given name. + * @since Ant 1.5.2 + */ + public synchronized Resource getResource(String name) { + return new FileResource(basedir, name); + } + + /** + * Has the directory with the given path relative to the base directory already been scanned? + * + *

+ * Registers the given directory as scanned as a side effect.

+ * + * @since Ant 1.6 + */ + private boolean hasBeenScanned(String vpath) { + return !scannedDirs.add(vpath); + } + + /** + * This method is of interest for testing purposes. The returned Set is live and should not be modified. + * + * @return the Set of relative directory names that have been scanned. + */ + /* package-private */ Set getScannedDirs() { + return scannedDirs; + } + + /** + * Clear internal caches. + * + * @since Ant 1.6 + */ + private synchronized void clearCaches() { + includeNonPatterns.clear(); + excludeNonPatterns.clear(); + includePatterns = null; + excludePatterns = null; + areNonPatternSetsReady = false; + } + + /** + * Ensure that the in|exclude "patterns" have been properly divided up. + * + * @since Ant 1.6.3 + */ + /* package */ synchronized void ensureNonPatternSetsReady() { + if (!areNonPatternSetsReady) { + includePatterns = fillNonPatternSet(includeNonPatterns, includes); + excludePatterns = fillNonPatternSet(excludeNonPatterns, excludes); + areNonPatternSetsReady = true; + } + } + + /** + * Add all patterns that are not real patterns (do not contain wildcards) to the set and returns the real patterns. + * + * @param map Map to populate. + * @param patterns String[] of patterns. + * @since Ant 1.8.0 + */ + private TokenizedPattern[] fillNonPatternSet(Map map, String[] patterns) { + ArrayList al = new ArrayList(patterns.length); + for (int i = 0; i < patterns.length; i++) { + if (!SelectorUtils.hasWildcards(patterns[i])) { + String s = isCaseSensitive() + ? patterns[i] : patterns[i].toUpperCase(); + map.put(s, new TokenizedPath(s)); + } else { + al.add(new TokenizedPattern(patterns[i])); + } + } + return (TokenizedPattern[]) al.toArray(new TokenizedPattern[al.size()]); + } + + /** + * Would following the given directory cause a loop of symbolic links deeper than allowed? + * + *

+ * Can only happen if the given directory has been seen at least more often than allowed during the current scan and + * it is a symbolic link and enough other occurrences of the same name higher up are symbolic links that point to + * the same place.

+ * + * @since Ant 1.8.0 + */ + private boolean causesIllegalSymlinkLoop(String dirName, File parent, + LinkedList directoryNamesFollowed) { + try { + if (directoryNamesFollowed.size() >= maxLevelsOfSymlinks + && CollectionUtils.frequency(directoryNamesFollowed, dirName) + >= maxLevelsOfSymlinks + && SYMLINK_UTILS.isSymbolicLink(parent, dirName)) { + + ArrayList files = new ArrayList(); + File f = FILE_UTILS.resolveFile(parent, dirName); + String target = f.getCanonicalPath(); + files.add(target); + + String relPath = ""; + for (String dir : directoryNamesFollowed) { + relPath += "../"; + if (dirName.equals(dir)) { + f = FILE_UTILS.resolveFile(parent, relPath + dir); + files.add(f.getCanonicalPath()); + if (files.size() > maxLevelsOfSymlinks + && CollectionUtils.frequency(files, target) + > maxLevelsOfSymlinks) { + return true; + } + } + } + + } + return false; + } catch (IOException ex) { + throw new BuildException("Caught error while checking for" + + " symbolic links", ex); + } + } + +} diff --git a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/FileScanner.java b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/FileScanner.java new file mode 100644 index 000000000..2fdd654f5 --- /dev/null +++ b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/FileScanner.java @@ -0,0 +1,158 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.owasp.dependencycheck.org.apache.tools.ant; + +import java.io.File; + +/** + * An interface used to describe the actions required of any type of + * directory scanner. + * + */ +public interface FileScanner { + /** + * Adds default exclusions to the current exclusions set. + */ + void addDefaultExcludes(); + + /** + * Returns the base directory to be scanned. + * This is the directory which is scanned recursively. + * + * @return the base directory to be scanned + */ + File getBasedir(); + + /** + * Returns the names of the directories which matched at least one of the + * include patterns and at least one of the exclude patterns. + * The names are relative to the base directory. + * + * @return the names of the directories which matched at least one of the + * include patterns and at least one of the exclude patterns. + */ + String[] getExcludedDirectories(); + + /** + * Returns the names of the files which matched at least one of the + * include patterns and at least one of the exclude patterns. + * The names are relative to the base directory. + * + * @return the names of the files which matched at least one of the + * include patterns and at least one of the exclude patterns. + * + */ + String[] getExcludedFiles(); + + /** + * Returns the names of the directories which matched at least one of the + * include patterns and none of the exclude patterns. + * 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. + */ + String[] getIncludedDirectories(); + + /** + * Returns the names of the files which matched at least one of the + * include patterns and none of the exclude patterns. + * The names are relative to the base directory. + * + * @return the names of the files which matched at least one of the + * include patterns and none of the exclude patterns. + */ + String[] getIncludedFiles(); + + /** + * Returns the names of the directories which matched none of the include + * patterns. The names are relative to the base directory. + * + * @return the names of the directories which matched none of the include + * patterns. + */ + String[] getNotIncludedDirectories(); + + /** + * Returns the names of the files which matched none of the include + * patterns. The names are relative to the base directory. + * + * @return the names of the files which matched none of the include + * patterns. + */ + String[] getNotIncludedFiles(); + + /** + * Scans the base directory for files which match at least one include + * pattern and don't match any exclude patterns. + * + * @exception IllegalStateException if the base directory was set + * incorrectly (i.e. if it is null, doesn't exist, + * or isn't a directory). + */ + void scan() throws IllegalStateException; + + /** + * Sets the base directory to be scanned. This is the directory which is + * scanned recursively. All '/' and '\' characters should be replaced by + * File.separatorChar, so the separator used need not match + * File.separatorChar. + * + * @param basedir The base directory to scan. + * Must not be null. + */ + void setBasedir(String basedir); + + /** + * Sets the base directory to be scanned. This is the directory which is + * scanned recursively. + * + * @param basedir The base directory for scanning. + * Should not be null. + */ + void setBasedir(File basedir); + + /** + * Sets the list of exclude patterns to use. + * + * @param excludes A list of exclude patterns. + * May be null, indicating that no files + * should be excluded. If a non-null list is + * given, all elements must be non-null. + */ + void setExcludes(String[] excludes); + + /** + * Sets the list of include patterns to use. + * + * @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. + */ + void setIncludes(String[] includes); + + /** + * Sets whether or not the file system should be regarded as case sensitive. + * + * @param isCaseSensitive whether or not the file system should be + * regarded as a case sensitive one + */ + void setCaseSensitive(boolean isCaseSensitive); +} diff --git a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/Location.java b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/Location.java new file mode 100644 index 000000000..44137ab5b --- /dev/null +++ b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/Location.java @@ -0,0 +1,178 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.owasp.dependencycheck.org.apache.tools.ant; + +import java.io.Serializable; +import org.owasp.dependencycheck.org.apache.tools.ant.util.FileUtils; +import org.xml.sax.Locator; + +/** + * Stores the location of a piece of text within a file (file name, + * line number and column number). Note that the column number is + * currently ignored. + * + */ +public class Location implements Serializable { + private static final long serialVersionUID = 1L; + + /** Name of the file. */ + private final String fileName; + /** Line number within the file. */ + private final int lineNumber; + /** Column number within the file. */ + private final int columnNumber; + + /** Location to use when one is needed but no information is available */ + public static final Location UNKNOWN_LOCATION = new Location(); + + private static final FileUtils FILE_UTILS = FileUtils.getFileUtils(); + + /** + * Creates an "unknown" location. + */ + private Location() { + this(null, 0, 0); + } + + /** + * Creates a location consisting of a file name but no line number or + * column number. + * + * @param fileName The name of the file. May be null, + * in which case the location is equivalent to + * {@link #UNKNOWN_LOCATION UNKNOWN_LOCATION}. + */ + public Location(String fileName) { + this(fileName, 0, 0); + } + + /** + * Creates a location from the SAX locator using the system ID as + * the filename. + * + * @param loc Must not be null. + * + * @since Ant 1.6 + */ + public Location(Locator loc) { + this(loc.getSystemId(), loc.getLineNumber(), loc.getColumnNumber()); + } + + /** + * Creates a location consisting of a file name, line number and + * column number. + * + * @param fileName The name of the file. May be null, + * in which case the location is equivalent to + * {@link #UNKNOWN_LOCATION UNKNOWN_LOCATION}. + * + * @param lineNumber Line number within the file. Use 0 for unknown + * positions within a file. + * @param columnNumber Column number within the line. + */ + public Location(String fileName, int lineNumber, int columnNumber) { + if (fileName != null && fileName.startsWith("file:")) { + this.fileName = FILE_UTILS.fromURI(fileName); + } else { + this.fileName = fileName; + } + this.lineNumber = lineNumber; + this.columnNumber = columnNumber; + } + + /** + * @return the filename portion of the location + * @since Ant 1.6 + */ + public String getFileName() { + return fileName; + } + + /** + * @return the line number + * @since Ant 1.6 + */ + public int getLineNumber() { + return lineNumber; + } + + /** + * @return the column number + * @since Ant 1.7 + */ + public int getColumnNumber() { + return columnNumber; + } + + /** + * Returns the file name, line number, a colon and a trailing space. + * An error message can be appended easily. For unknown locations, an + * empty string is returned. + * + * @return a String of the form "fileName:lineNumber: " + * if both file name and line number are known, + * "fileName: " if only the file name is known, + * and the empty string for unknown locations. + */ + public String toString() { + StringBuffer buf = new StringBuffer(); + + if (fileName != null) { + buf.append(fileName); + + if (lineNumber != 0) { + buf.append(":"); + buf.append(lineNumber); + } + + buf.append(": "); + } + + return buf.toString(); + } + + /** + * Equality operation. + * @param other the object to compare to. + * @return true if the other object contains the same information + * as this object. + * @since Ant 1.6.3 + */ + public boolean equals(Object other) { + if (this == other) { + return true; + } + if (other == null) { + return false; + } + if (!(other.getClass() == getClass())) { + return false; + } + return toString().equals(other.toString()); + } + + /** + * Hash operation. + * @return a hash code value for this location. + * @since Ant 1.6.3 + */ + public int hashCode() { + return toString().hashCode(); + } +} diff --git a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/PathTokenizer.java b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/PathTokenizer.java new file mode 100644 index 000000000..5d1a9a47b --- /dev/null +++ b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/PathTokenizer.java @@ -0,0 +1,165 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.owasp.dependencycheck.org.apache.tools.ant; + +import java.io.File; +import java.util.NoSuchElementException; +import java.util.StringTokenizer; +import org.owasp.dependencycheck.org.apache.tools.ant.taskdefs.condition.Os; + +/** + * A Path tokenizer takes a path and returns the components that make up + * that path. + * + * The path can use path separators of either ':' or ';' and file separators + * of either '/' or '\'. + * + */ +public class PathTokenizer { + /** + * A tokenizer to break the string up based on the ':' or ';' separators. + */ + private StringTokenizer tokenizer; + + /** + * A String which stores any path components which have been read ahead + * due to DOS filesystem compensation. + */ + private String lookahead = null; + + /** + * A boolean that determines if we are running on Novell NetWare, which + * exhibits slightly different path name characteristics (multi-character + * volume / drive names) + */ + private boolean onNetWare = Os.isFamily("netware"); + + /** + * Flag to indicate whether or not we are running on a platform with a + * DOS style filesystem + */ + private boolean dosStyleFilesystem; + + /** + * Constructs a path tokenizer for the specified path. + * + * @param path The path to tokenize. Must not be null. + */ + public PathTokenizer(String path) { + if (onNetWare) { + // For NetWare, use the boolean=true mode, so we can use delimiter + // information to make a better decision later. + tokenizer = new StringTokenizer(path, ":;", true); + } else { + // on Windows and Unix, we can ignore delimiters and still have + // enough information to tokenize correctly. + tokenizer = new StringTokenizer(path, ":;", false); + } + dosStyleFilesystem = File.pathSeparatorChar == ';'; + } + + /** + * Tests if there are more path elements available from this tokenizer's + * path. If this method returns true, then a subsequent call + * to nextToken will successfully return a token. + * + * @return true if and only if there is at least one token + * in the string after the current position; false otherwise. + */ + public boolean hasMoreTokens() { + if (lookahead != null) { + return true; + } + + return tokenizer.hasMoreTokens(); + } + + /** + * Returns the next path element from this tokenizer. + * + * @return the next path element from this tokenizer. + * + * @exception NoSuchElementException if there are no more elements in this + * tokenizer's path. + */ + public String nextToken() throws NoSuchElementException { + String token = null; + if (lookahead != null) { + token = lookahead; + lookahead = null; + } else { + token = tokenizer.nextToken().trim(); + } + + if (!onNetWare) { + if (token.length() == 1 && Character.isLetter(token.charAt(0)) + && dosStyleFilesystem + && tokenizer.hasMoreTokens()) { + // we are on a dos style system so this path could be a drive + // spec. We look at the next token + String nextToken = tokenizer.nextToken().trim(); + if (nextToken.startsWith("\\") || nextToken.startsWith("/")) { + // we know we are on a DOS style platform and the next path + // starts with a slash or backslash, so we know this is a + // drive spec + token += ":" + nextToken; + } else { + // store the token just read for next time + lookahead = nextToken; + } + } + } else { + // we are on NetWare, tokenizing is handled a little differently, + // due to the fact that NetWare has multiple-character volume names. + if (token.equals(File.pathSeparator) || token.equals(":")) { + // ignore ";" and get the next token + token = tokenizer.nextToken().trim(); + } + + if (tokenizer.hasMoreTokens()) { + // this path could be a drive spec, so look at the next token + String nextToken = tokenizer.nextToken().trim(); + + // make sure we aren't going to get the path separator next + if (!nextToken.equals(File.pathSeparator)) { + if (nextToken.equals(":")) { + if (!token.startsWith("/") && !token.startsWith("\\") + && !token.startsWith(".") + && !token.startsWith("..")) { + // it indeed is a drive spec, get the next bit + String oneMore = tokenizer.nextToken().trim(); + if (!oneMore.equals(File.pathSeparator)) { + token += ":" + oneMore; + } else { + token += ":"; + lookahead = oneMore; + } + } + // implicit else: ignore the ':' since we have either a + // UNIX or a relative path + } else { + // store the token just read for next time + lookahead = nextToken; + } + } + } + } + return token; + } +} + diff --git a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/ProjectComponent.java b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/ProjectComponent.java new file mode 100644 index 000000000..7b57e4956 --- /dev/null +++ b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/ProjectComponent.java @@ -0,0 +1,161 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.owasp.dependencycheck.org.apache.tools.ant; + +/** + * Base class for components of a project, including tasks and data types. Provides common facilities. + * + */ +public abstract class ProjectComponent implements Cloneable { + +// // CheckStyle:VisibilityModifier OFF - bc +// /** +// * Project object of this component. +// * @deprecated since 1.6.x. +// * You should not be directly accessing this variable directly. +// * You should access project object via the getProject() +// * or setProject() accessor/mutators. +// */ +// protected Project project; + /** + * Location within the build file of this task definition. + * + * @deprecated since 1.6.x. You should not be accessing this variable directly. Please use the + * {@link #getLocation()} method. + */ + protected Location location = Location.UNKNOWN_LOCATION; + + /** + * Description of this component, if any. + * + * @deprecated since 1.6.x. You should not be accessing this variable directly. + */ + protected String description; + // CheckStyle:VisibilityModifier ON + + /** + * Sole constructor. + */ + public ProjectComponent() { + } + +// /** +// * Sets the project object of this component. This method is used by +// * Project when a component is added to it so that the component has +// * access to the functions of the project. It should not be used +// * for any other purpose. +// * +// * @param project Project in whose scope this component belongs. +// * Must not be null. +// */ +// public void setProject(Project project) { +// this.project = project; +// } +// +// /** +// * Returns the project to which this component belongs. +// * +// * @return the components's project. +// */ +// public Project getProject() { +// return project; +// } + /** + * Returns the file/location where this task was defined. + * + * @return the file/location where this task was defined. Should not return null. + * Location.UNKNOWN_LOCATION is used for unknown locations. + * + * @see Location#UNKNOWN_LOCATION + */ + public Location getLocation() { + return location; + } + + /** + * Sets the file/location where this task was defined. + * + * @param location The file/location where this task was defined. Should not be null--use + * Location.UNKNOWN_LOCATION if the location isn't known. + * + * @see Location#UNKNOWN_LOCATION + */ + public void setLocation(Location location) { + this.location = location; + } + + /** + * Sets a description of the current action. This may be used for logging purposes. + * + * @param desc Description of the current action. May be null, indicating that no description is + * available. + * + */ + public void setDescription(String desc) { + description = desc; + } + + /** + * Returns the description of the current action. + * + * @return the description of the current action, or null if no description is available. + */ + public String getDescription() { + return description; + } + + /** + * Logs a message with the default (INFO) priority. + * + * @param msg The message to be logged. Should not be null. + */ + public void log(String msg) { +// log(msg, Project.MSG_INFO); + } + + /** + * Logs a message with the given priority. + * + * @param msg The message to be logged. Should not be null. + * @param msgLevel the message priority at which this message is to be logged. + */ + public void log(String msg, int msgLevel) { +// if (getProject() != null) { +// getProject().log(msg, msgLevel); +// } else { +// // 'reasonable' default, if the component is used without +// // a Project ( for example as a standalone Bean ). +// // Most ant components can be used this way. +// if (msgLevel <= Project.MSG_INFO) { +// System.err.println(msg); +// } +// } + } + + /** + * @since Ant 1.7 + * @return a shallow copy of this projectcomponent. + * @throws CloneNotSupportedException does not happen, but is declared to allow subclasses to do so. + */ + public Object clone() throws CloneNotSupportedException { + ProjectComponent pc = (ProjectComponent) super.clone(); + pc.setLocation(getLocation()); + //pc.setProject(getProject()); + return pc; + } +} diff --git a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/launch/Locator.java b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/launch/Locator.java new file mode 100644 index 000000000..dca690640 --- /dev/null +++ b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/launch/Locator.java @@ -0,0 +1,530 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.owasp.dependencycheck.org.apache.tools.ant.launch; + +import java.net.MalformedURLException; +import java.net.URL; +import java.io.File; +import java.io.FilenameFilter; +import java.io.ByteArrayOutputStream; +import java.io.UnsupportedEncodingException; +import java.text.CharacterIterator; +import java.text.StringCharacterIterator; +import java.util.Locale; + +import org.owasp.dependencycheck.org.apache.tools.ant.util.FileUtils; + +// CheckStyle:LineLengthCheck OFF - urls are long! +/** + * The Locator is a utility class which is used to find certain items + * in the environment. + * + * It is used at boot time in the launcher, and cannot make use of any of Ant's other classes. + * + * This is a surprisingly brittle piece of code, and has had lots of bugs filed against it. + * {@link running ant off a network share can cause Ant to fail} + * {@link use File.toURI().toURL().toExternalForm()} + * {@link Locator implementation not encoding URI strings properly: spaces in paths} + * It also breaks Eclipse 3.3 Betas + * {@link Exception if installation path has spaces} + * + * Be very careful when making changes to this class, as a break will upset a lot of people. + * @since Ant 1.6 + */ +// CheckStyle:LineLengthCheck ON - urls are long! +public final class Locator { + + private static final int NIBBLE = 4; + private static final int NIBBLE_MASK = 0xF; + + private static final int ASCII_SIZE = 128; + + private static final int BYTE_SIZE = 256; + + private static final int WORD = 16; + + private static final int SPACE = 0x20; + private static final int DEL = 0x7F; + + /** + * encoding used to represent URIs + */ + public static final String URI_ENCODING = "UTF-8"; + // stolen from org.apache.xerces.impl.XMLEntityManager#getUserDir() + // of the Xerces-J team + // which ASCII characters need to be escaped + private static boolean[] gNeedEscaping = new boolean[ASCII_SIZE]; + // the first hex character if a character needs to be escaped + private static char[] gAfterEscaping1 = new char[ASCII_SIZE]; + // the second hex character if a character needs to be escaped + private static char[] gAfterEscaping2 = new char[ASCII_SIZE]; + private static char[] gHexChs = {'0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; + /** Error string used when an invalid uri is seen */ + public static final String ERROR_NOT_FILE_URI + = "Can only handle valid file: URIs, not "; + + // initialize the above 3 arrays + static { + for (int i = 0; i < SPACE; i++) { + gNeedEscaping[i] = true; + gAfterEscaping1[i] = gHexChs[i >> NIBBLE]; + gAfterEscaping2[i] = gHexChs[i & NIBBLE_MASK]; + } + gNeedEscaping[DEL] = true; + gAfterEscaping1[DEL] = '7'; + gAfterEscaping2[DEL] = 'F'; + char[] escChs = {' ', '<', '>', '#', '%', '"', '{', '}', + '|', '\\', '^', '~', '[', ']', '`'}; + int len = escChs.length; + char ch; + for (int i = 0; i < len; i++) { + ch = escChs[i]; + gNeedEscaping[ch] = true; + gAfterEscaping1[ch] = gHexChs[ch >> NIBBLE]; + gAfterEscaping2[ch] = gHexChs[ch & NIBBLE_MASK]; + } + } + /** + * Not instantiable + */ + private Locator() { + } + + /** + * Find the directory or jar file the class has been loaded from. + * + * @param c the class whose location is required. + * @return the file or jar with the class or null if we cannot + * determine the location. + * + * @since Ant 1.6 + */ + public static File getClassSource(Class c) { + String classResource = c.getName().replace('.', '/') + ".class"; + return getResourceSource(c.getClassLoader(), classResource); + } + + /** + * Find the directory or jar a given resource has been loaded from. + * + * @param c the classloader to be consulted for the source. + * @param resource the resource whose location is required. + * + * @return the file with the resource source or null if + * we cannot determine the location. + * + * @since Ant 1.6 + */ + public static File getResourceSource(ClassLoader c, String resource) { + if (c == null) { + c = Locator.class.getClassLoader(); + } + URL url = null; + if (c == null) { + url = ClassLoader.getSystemResource(resource); + } else { + url = c.getResource(resource); + } + if (url != null) { + String u = url.toString(); + try { + if (u.startsWith("jar:file:")) { + return new File(fromJarURI(u)); + } else if (u.startsWith("file:")) { + int tail = u.indexOf(resource); + String dirName = u.substring(0, tail); + return new File(fromURI(dirName)); + } + } catch (IllegalArgumentException e) { + //unable to determine the URI for reasons unknown. + return null; + } + } + return null; + } + + + + /** + * Constructs a file path from a file: URI. + * + *

Will be an absolute path if the given URI is absolute.

+ * + *

Prior to Java 1.4, + * swallows '%' that are not followed by two characters.

+ * + * See dt-sysid + * which makes some mention of how + * characters not supported by URI Reference syntax should be escaped. + * + * @param uri the URI designating a file in the local filesystem. + * @return the local file system path for the file. + * @throws IllegalArgumentException if the URI is malformed or not a legal file: URL + * @since Ant 1.6 + */ + public static String fromURI(String uri) { + return fromURIJava13(uri); + // #buzilla8031: first try Java 1.4. + // TODO should use java.net.URI now that we can rely on 1.4... + // but check for UNC-related regressions, e.g. #42275 + // (and remember that \\server\share\file -> file:////server/share/file + // rather than -> file://server/share/file as it should; + // fixed only in JDK 7's java.nio.file.Path.toUri) + // return fromUriJava14(uri); + } + + /** + * Java1.4+ code to extract the path from the URI. + * @param uri + * @return null if a conversion was not possible + */ + /* currently unused: + private static String fromUriJava14(String uri) { + // Also check for properly formed URIs. Ant formerly recommended using + // nonsense URIs such as "file:./foo.xml" in XML includes. You shouldn't + // do that (just "foo.xml" is correct) but for compatibility we special-case + // things when the path is not absolute, and fall back to the old parsing behavior. + if (uri.startsWith("file:/")) { + try { + File f = new File(URI.create(encodeURI(uri))); + //bug #42227 forgot to decode before returning + return decodeUri(f.getAbsolutePath()); + } catch (IllegalArgumentException e) { + // Bad URI, pass this on. + // no, this is downgraded to a warning after various + // JRE bugs surfaced. Hand off + // to our built in code on a failure + //throw new IllegalArgumentException( + // "Bad URI " + uri + ":" + e.getMessage(), e); + e.printStackTrace(); + } catch (Exception e) { + // Unexpected exception? Should not happen. + e.printStackTrace(); + } + } + return null; + } + */ + + /** + * @param uri uri to expand + * @return the decoded URI + * @since Ant1.7.1 + */ + private static String fromURIJava13(String uri) { + // Fallback method for Java 1.3 or earlier. + + URL url = null; + try { + url = new URL(uri); + } catch (MalformedURLException emYouEarlEx) { + // Ignore malformed exception + } + if (url == null || !("file".equals(url.getProtocol()))) { + throw new IllegalArgumentException(ERROR_NOT_FILE_URI + uri); + } + StringBuffer buf = new StringBuffer(url.getHost()); + if (buf.length() > 0) { + buf.insert(0, File.separatorChar).insert(0, File.separatorChar); + } + String file = url.getFile(); + int queryPos = file.indexOf('?'); + buf.append((queryPos < 0) ? file : file.substring(0, queryPos)); + + uri = buf.toString().replace('/', File.separatorChar); + + if (File.pathSeparatorChar == ';' && uri.startsWith("\\") && uri.length() > 2 + && Character.isLetter(uri.charAt(1)) && uri.lastIndexOf(':') > -1) { + uri = uri.substring(1); + } + String path = null; + try { + path = decodeUri(uri); + //consider adding the current directory. This is not done when + //the path is a UNC name + String cwd = System.getProperty("user.dir"); + int posi = cwd.indexOf(':'); + boolean pathStartsWithFileSeparator = path.startsWith(File.separator); + boolean pathStartsWithUNC = path.startsWith("" + File.separator + File.separator); + if ((posi > 0) && pathStartsWithFileSeparator && !pathStartsWithUNC) { + path = cwd.substring(0, posi + 1) + path; + } + } catch (UnsupportedEncodingException exc) { + // not sure whether this is clean, but this method is + // declared not to throw exceptions. + throw new IllegalStateException( + "Could not convert URI " + uri + " to path: " + + exc.getMessage()); + } + return path; + } + + /** + * Crack a JAR URI. + * This method is public for testing; we may delete it without any warning -it is not part of Ant's stable API. + * @param uri uri to expand; contains jar: somewhere in it + * @return the decoded URI + * @since Ant1.7.1 + */ + public static String fromJarURI(String uri) { + int pling = uri.indexOf("!/"); + String jarName = uri.substring("jar:".length(), pling); + return fromURI(jarName); + } + + /** + * Decodes an Uri with % characters. + * The URI is escaped + * @param uri String with the uri possibly containing % characters. + * @return The decoded Uri + * @throws UnsupportedEncodingException if UTF-8 is not available + * @since Ant 1.7 + */ + public static String decodeUri(String uri) throws UnsupportedEncodingException { + if (uri.indexOf('%') == -1) { + return uri; + } + ByteArrayOutputStream sb = new ByteArrayOutputStream(uri.length()); + CharacterIterator iter = new StringCharacterIterator(uri); + for (char c = iter.first(); c != CharacterIterator.DONE; + c = iter.next()) { + if (c == '%') { + char c1 = iter.next(); + if (c1 != CharacterIterator.DONE) { + int i1 = Character.digit(c1, WORD); + char c2 = iter.next(); + if (c2 != CharacterIterator.DONE) { + int i2 = Character.digit(c2, WORD); + sb.write((char) ((i1 << NIBBLE) + i2)); + } + } + } else if (c >= 0x0000 && c < 0x0080) { + sb.write(c); + } else { // #50543 + byte[] bytes = String.valueOf(c).getBytes(URI_ENCODING); + sb.write(bytes, 0, bytes.length); + } + } + return sb.toString(URI_ENCODING); + } + + /** + * Encodes an Uri with % characters. + * The URI is escaped + * @param path String to encode. + * @return The encoded string, according to URI norms + * @throws UnsupportedEncodingException if UTF-8 is not available + * @since Ant 1.7 + */ + public static String encodeURI(String path) throws UnsupportedEncodingException { + int i = 0; + int len = path.length(); + int ch = 0; + StringBuffer sb = null; + for (; i < len; i++) { + ch = path.charAt(i); + // if it's not an ASCII character, break here, and use UTF-8 encoding + if (ch >= ASCII_SIZE) { + break; + } + if (gNeedEscaping[ch]) { + if (sb == null) { + sb = new StringBuffer(path.substring(0, i)); + } + sb.append('%'); + sb.append(gAfterEscaping1[ch]); + sb.append(gAfterEscaping2[ch]); + // record the fact that it's escaped + } else if (sb != null) { + sb.append((char) ch); + } + } + + // we saw some non-ascii character + if (i < len) { + if (sb == null) { + sb = new StringBuffer(path.substring(0, i)); + } + // get UTF-8 bytes for the remaining sub-string + byte[] bytes = null; + byte b; + bytes = path.substring(i).getBytes(URI_ENCODING); + len = bytes.length; + + // for each byte + for (i = 0; i < len; i++) { + b = bytes[i]; + // for non-ascii character: make it positive, then escape + if (b < 0) { + ch = b + BYTE_SIZE; + sb.append('%'); + sb.append(gHexChs[ch >> NIBBLE]); + sb.append(gHexChs[ch & NIBBLE_MASK]); + } else if (gNeedEscaping[b]) { + sb.append('%'); + sb.append(gAfterEscaping1[b]); + sb.append(gAfterEscaping2[b]); + } else { + sb.append((char) b); + } + } + } + return sb == null ? path : sb.toString(); + } + + /** + * Convert a File to a URL. + * File.toURL() does not encode characters like #. + * File.toURI() has been introduced in java 1.4, so + * Ant cannot use it (except by reflection) + * FileUtils.toURI() cannot be used by Locator.java + * Implemented this way. + * File.toURL() adds file: and changes '\' to '/' for dos OSes + * encodeURI converts characters like ' ' and '#' to %DD + * @param file the file to convert + * @return URL the converted File + * @throws MalformedURLException on error + * @deprecated since 1.9, use {@link FileUtils#getFileURL(File)} + */ + @Deprecated + public static URL fileToURL(File file) + throws MalformedURLException { + return new URL(file.toURI().toASCIIString()); + } + + /** + * Get the File necessary to load the Sun compiler tools. If the classes + * are available to this class, then no additional URL is required and + * null is returned. This may be because the classes are explicitly in the + * class path or provided by the JVM directly. + * + * @return the tools jar as a File if required, null otherwise. + */ + public static File getToolsJar() { + // firstly check if the tools jar is already in the classpath + boolean toolsJarAvailable = false; + try { + // just check whether this throws an exception + Class.forName("com.sun.tools.javac.Main"); + toolsJarAvailable = true; + } catch (Exception e) { + try { + Class.forName("sun.tools.javac.Main"); + toolsJarAvailable = true; + } catch (Exception e2) { + // ignore + } + } + if (toolsJarAvailable) { + return null; + } + // couldn't find compiler - try to find tools.jar + // based on java.home setting + String libToolsJar + = File.separator + "lib" + File.separator + "tools.jar"; + String javaHome = System.getProperty("java.home"); + File toolsJar = new File(javaHome + libToolsJar); + if (toolsJar.exists()) { + // Found in java.home as given + return toolsJar; + } + if (javaHome.toLowerCase(Locale.ENGLISH).endsWith(File.separator + "jre")) { + javaHome = javaHome.substring( + 0, javaHome.length() - "/jre".length()); + toolsJar = new File(javaHome + libToolsJar); + } + if (!toolsJar.exists()) { + System.out.println("Unable to locate tools.jar. " + + "Expected to find it in " + toolsJar.getPath()); + return null; + } + return toolsJar; + } + + /** + * Get an array of URLs representing all of the jar files in the + * given location. If the location is a file, it is returned as the only + * element of the array. If the location is a directory, it is scanned for + * jar files. + * + * @param location the location to scan for Jars. + * + * @return an array of URLs for all jars in the given location. + * + * @exception MalformedURLException if the URLs for the jars cannot be + * formed. + */ + public static URL[] getLocationURLs(File location) + throws MalformedURLException { + return getLocationURLs(location, new String[]{".jar"}); + } + + /** + * Get an array of URLs representing all of the files of a given set of + * extensions in the given location. If the location is a file, it is + * returned as the only element of the array. If the location is a + * directory, it is scanned for matching files. + * + * @param location the location to scan for files. + * @param extensions an array of extension that are to match in the + * directory search. + * + * @return an array of URLs of matching files. + * @exception MalformedURLException if the URLs for the files cannot be + * formed. + */ + public static URL[] getLocationURLs(File location, + final String[] extensions) + throws MalformedURLException { + URL[] urls = new URL[0]; + + if (!location.exists()) { + return urls; + } + if (!location.isDirectory()) { + urls = new URL[1]; + String path = location.getPath(); + String littlePath = path.toLowerCase(Locale.ENGLISH); + for (int i = 0; i < extensions.length; ++i) { + if (littlePath.endsWith(extensions[i])) { + urls[0] = fileToURL(location); + break; + } + } + return urls; + } + File[] matches = location.listFiles( + new FilenameFilter() { + public boolean accept(File dir, String name) { + String littleName = name.toLowerCase(Locale.ENGLISH); + for (int i = 0; i < extensions.length; ++i) { + if (littleName.endsWith(extensions[i])) { + return true; + } + } + return false; + } + }); + urls = new URL[matches.length]; + for (int i = 0; i < matches.length; ++i) { + urls[i] = fileToURL(matches[i]); + } + return urls; + } +} diff --git a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/launch/package-info.java b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/launch/package-info.java new file mode 100644 index 000000000..115f3b29e --- /dev/null +++ b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/launch/package-info.java @@ -0,0 +1,14 @@ +/** + * + * + * org.owasp.dependencycheck.org.apache.tools.ant.launch + * + * + * This is a copy of classes within Apache Ant. The DirectoryScanner + * is needed by dependency-check. However, we did not want to make + * Ant a dependency. As such, a few files were copied and slightly + * modified to remove any references to the Ant Project class. + * + * + */ +package org.owasp.dependencycheck.org.apache.tools.ant.launch; diff --git a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/package-info.java b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/package-info.java new file mode 100644 index 000000000..cb3c12962 --- /dev/null +++ b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/package-info.java @@ -0,0 +1,14 @@ +/** + * + * + * org.owasp.dependencycheck.org.apache.tools.ant + * + * + * This is a copy of classes within Apache Ant. The DirectoryScanner + * is needed by dependency-check. However, we did not want to make + * Ant a dependency. As such, a few files were copied and slightly + * modified to remove any references to the Ant Project class. + * + * + */ +package org.owasp.dependencycheck.org.apache.tools.ant; diff --git a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/taskdefs/condition/Condition.java b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/taskdefs/condition/Condition.java new file mode 100644 index 000000000..d6e0351d3 --- /dev/null +++ b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/taskdefs/condition/Condition.java @@ -0,0 +1,35 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.owasp.dependencycheck.org.apache.tools.ant.taskdefs.condition; + +import org.owasp.dependencycheck.org.apache.tools.ant.BuildException; + +/** + * Interface for conditions to use inside the <condition> task. + * + */ +public interface Condition { + /** + * Is this condition true? + * @return true if the condition is true + * @exception BuildException if an error occurs + */ + boolean eval() throws BuildException; +} + diff --git a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/taskdefs/condition/Os.java b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/taskdefs/condition/Os.java new file mode 100644 index 000000000..4ea20554e --- /dev/null +++ b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/taskdefs/condition/Os.java @@ -0,0 +1,321 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.owasp.dependencycheck.org.apache.tools.ant.taskdefs.condition; + +import java.util.Locale; + +import org.owasp.dependencycheck.org.apache.tools.ant.BuildException; + +/** + * Condition that tests the OS type. + * + * @since Ant 1.4 + */ +public class Os implements Condition { + private static final String OS_NAME = + System.getProperty("os.name").toLowerCase(Locale.ENGLISH); + private static final String OS_ARCH = + System.getProperty("os.arch").toLowerCase(Locale.ENGLISH); + private static final String OS_VERSION = + System.getProperty("os.version").toLowerCase(Locale.ENGLISH); + private static final String PATH_SEP = + System.getProperty("path.separator"); + + /** + * OS family to look for + */ + private String family; + /** + * Name of OS + */ + private String name; + /** + * version of OS + */ + private String version; + /** + * OS architecture + */ + private String arch; + /** + * OS family that can be tested for. {@value} + */ + public static final String FAMILY_WINDOWS = "windows"; + /** + * OS family that can be tested for. {@value} + */ + public static final String FAMILY_9X = "win9x"; + /** + * OS family that can be tested for. {@value} + */ + public static final String FAMILY_NT = "winnt"; + /** + * OS family that can be tested for. {@value} + */ + public static final String FAMILY_OS2 = "os/2"; + /** + * OS family that can be tested for. {@value} + */ + public static final String FAMILY_NETWARE = "netware"; + /** + * OS family that can be tested for. {@value} + */ + public static final String FAMILY_DOS = "dos"; + /** + * OS family that can be tested for. {@value} + */ + public static final String FAMILY_MAC = "mac"; + /** + * OS family that can be tested for. {@value} + */ + public static final String FAMILY_TANDEM = "tandem"; + /** + * OS family that can be tested for. {@value} + */ + public static final String FAMILY_UNIX = "unix"; + /** + * OS family that can be tested for. {@value} + */ + public static final String FAMILY_VMS = "openvms"; + /** + * OS family that can be tested for. {@value} + */ + public static final String FAMILY_ZOS = "z/os"; + /** OS family that can be tested for. {@value} */ + public static final String FAMILY_OS400 = "os/400"; + + /** + * OpenJDK is reported to call MacOS X "Darwin" + * @see https://issues.apache.org/bugzilla/show_bug.cgi?id=44889 + * @see https://issues.apache.org/jira/browse/HADOOP-3318 + */ + private static final String DARWIN = "darwin"; + + /** + * Default constructor + * + */ + public Os() { + //default + } + + /** + * Constructor that sets the family attribute + * @param family a String value + */ + public Os(String family) { + setFamily(family); + } + + /** + * Sets the desired OS family type + * + * @param f The OS family type desired
+ * Possible values:
+ *
    + *
  • dos
  • + *
  • mac
  • + *
  • netware
  • + *
  • os/2
  • + *
  • tandem
  • + *
  • unix
  • + *
  • windows
  • + *
  • win9x
  • + *
  • z/os
  • + *
  • os/400
  • + *
+ */ + public void setFamily(String f) { + family = f.toLowerCase(Locale.ENGLISH); + } + + /** + * Sets the desired OS name + * + * @param name The OS name + */ + public void setName(String name) { + this.name = name.toLowerCase(Locale.ENGLISH); + } + + /** + * Sets the desired OS architecture + * + * @param arch The OS architecture + */ + public void setArch(String arch) { + this.arch = arch.toLowerCase(Locale.ENGLISH); + } + + /** + * Sets the desired OS version + * + * @param version The OS version + */ + public void setVersion(String version) { + this.version = version.toLowerCase(Locale.ENGLISH); + } + + /** + * Determines if the OS on which Ant is executing matches the type of + * that set in setFamily. + * @return true if the os matches. + * @throws BuildException if there is an error. + * @see Os#setFamily(String) + */ + public boolean eval() throws BuildException { + return isOs(family, name, arch, version); + } + + /** + * Determines if the OS on which Ant is executing matches the + * given OS family. + * @param family the family to check for + * @return true if the OS matches + * @since 1.5 + */ + public static boolean isFamily(String family) { + return isOs(family, null, null, null); + } + + /** + * Determines if the OS on which Ant is executing matches the + * given OS name. + * + * @param name the OS name to check for + * @return true if the OS matches + * @since 1.7 + */ + public static boolean isName(String name) { + return isOs(null, name, null, null); + } + + /** + * Determines if the OS on which Ant is executing matches the + * given OS architecture. + * + * @param arch the OS architecture to check for + * @return true if the OS matches + * @since 1.7 + */ + public static boolean isArch(String arch) { + return isOs(null, null, arch, null); + } + + /** + * Determines if the OS on which Ant is executing matches the + * given OS version. + * + * @param version the OS version to check for + * @return true if the OS matches + * @since 1.7 + */ + public static boolean isVersion(String version) { + return isOs(null, null, null, version); + } + + /** + * Determines if the OS on which Ant is executing matches the + * given OS family, name, architecture and version + * + * @param family The OS family + * @param name The OS name + * @param arch The OS architecture + * @param version The OS version + * @return true if the OS matches + * @since 1.7 + */ + public static boolean isOs(String family, String name, String arch, + String version) { + boolean retValue = false; + + if (family != null || name != null || arch != null + || version != null) { + + boolean isFamily = true; + boolean isName = true; + boolean isArch = true; + boolean isVersion = true; + + if (family != null) { + + //windows probing logic relies on the word 'windows' in + //the OS + boolean isWindows = OS_NAME.indexOf(FAMILY_WINDOWS) > -1; + boolean is9x = false; + boolean isNT = false; + if (isWindows) { + //there are only four 9x platforms that we look for + is9x = (OS_NAME.indexOf("95") >= 0 + || OS_NAME.indexOf("98") >= 0 + || OS_NAME.indexOf("me") >= 0 + //wince isn't really 9x, but crippled enough to + //be a muchness. Ant doesnt run on CE, anyway. + || OS_NAME.indexOf("ce") >= 0); + isNT = !is9x; + } + if (family.equals(FAMILY_WINDOWS)) { + isFamily = isWindows; + } else if (family.equals(FAMILY_9X)) { + isFamily = isWindows && is9x; + } else if (family.equals(FAMILY_NT)) { + isFamily = isWindows && isNT; + } else if (family.equals(FAMILY_OS2)) { + isFamily = OS_NAME.indexOf(FAMILY_OS2) > -1; + } else if (family.equals(FAMILY_NETWARE)) { + isFamily = OS_NAME.indexOf(FAMILY_NETWARE) > -1; + } else if (family.equals(FAMILY_DOS)) { + isFamily = PATH_SEP.equals(";") && !isFamily(FAMILY_NETWARE); + } else if (family.equals(FAMILY_MAC)) { + isFamily = OS_NAME.indexOf(FAMILY_MAC) > -1 + || OS_NAME.indexOf(DARWIN) > -1; + } else if (family.equals(FAMILY_TANDEM)) { + isFamily = OS_NAME.indexOf("nonstop_kernel") > -1; + } else if (family.equals(FAMILY_UNIX)) { + isFamily = PATH_SEP.equals(":") + && !isFamily(FAMILY_VMS) + && (!isFamily(FAMILY_MAC) || OS_NAME.endsWith("x") + || OS_NAME.indexOf(DARWIN) > -1); + } else if (family.equals(FAMILY_ZOS)) { + isFamily = OS_NAME.indexOf(FAMILY_ZOS) > -1 + || OS_NAME.indexOf("os/390") > -1; + } else if (family.equals(FAMILY_OS400)) { + isFamily = OS_NAME.indexOf(FAMILY_OS400) > -1; + } else if (family.equals(FAMILY_VMS)) { + isFamily = OS_NAME.indexOf(FAMILY_VMS) > -1; + } else { + throw new BuildException( + "Don\'t know how to detect os family \"" + + family + "\""); + } + } + if (name != null) { + isName = name.equals(OS_NAME); + } + if (arch != null) { + isArch = arch.equals(OS_ARCH); + } + if (version != null) { + isVersion = version.equals(OS_VERSION); + } + retValue = isFamily && isName && isArch && isVersion; + } + return retValue; + } +} diff --git a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/taskdefs/condition/package-info.java b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/taskdefs/condition/package-info.java new file mode 100644 index 000000000..58f73450b --- /dev/null +++ b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/taskdefs/condition/package-info.java @@ -0,0 +1,14 @@ +/** + * + * + * org.owasp.dependencycheck.org.apache.tools.ant.taskdefs.condition + * + * + * This is a copy of classes within Apache Ant. The DirectoryScanner + * is needed by dependency-check. However, we did not want to make + * Ant a dependency. As such, a few files were copied and slightly + * modified to remove any references to the Ant Project class. + * + * + */ +package org.owasp.dependencycheck.org.apache.tools.ant.taskdefs.condition; diff --git a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/DataType.java b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/DataType.java new file mode 100644 index 000000000..d97367c90 --- /dev/null +++ b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/DataType.java @@ -0,0 +1,353 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.owasp.dependencycheck.org.apache.tools.ant.types; + +import org.owasp.dependencycheck.org.apache.tools.ant.BuildException; +import org.owasp.dependencycheck.org.apache.tools.ant.ProjectComponent; + +/** + * Base class for those classes that can appear inside the build file as stand alone data types. + * + *

+ * This class handles the common description attribute and provides a default implementation for reference handling and + * checking for circular references that is appropriate for types that can not be nested inside elements of the same + * type (i.e. <patternset> but not <path>).

+ * + */ +public abstract class DataType extends ProjectComponent implements Cloneable { + // CheckStyle:VisibilityModifier OFF + + /** + * Value to the refid attribute. + * + * @deprecated since 1.7. The user should not be directly referencing variable. Please use {@link #getRefid} + * instead. + */ + protected Reference ref; + + /** + * Are we sure we don't hold circular references? + * + *

+ * Subclasses are responsible for setting this value to false if we'd need to investigate this condition (usually + * because a child element has been added that is a subclass of DataType).

+ * + * @deprecated since 1.7. The user should not be directly referencing variable. Please use {@link #setChecked} or + * {@link #isChecked} instead. + */ + protected boolean checked = true; + // CheckStyle:VisibilityModifier ON + + /** + * Has the refid attribute of this element been set? + * + * @return true if the refid attribute has been set + */ + public boolean isReference() { + return ref != null; + } + + /** + * Set the value of the refid attribute. + * + *

+ * Subclasses may need to check whether any other attributes have been set as well or child elements have been + * created and thus override this method. if they do the must call super.setRefid.

+ * + * @param ref the reference to use + */ + public void setRefid(final Reference ref) { + this.ref = ref; + checked = false; + } + +// /** +// * Gets as descriptive as possible a name used for this datatype instance. +// * +// * @return String name. +// */ +// protected String getDataTypeName() { +// return ComponentHelper.getElementName(getProject(), this, true); +// } +// /** +// * Convenience method. +// * @since Ant 1.7 +// */ +// protected void dieOnCircularReference() { +// dieOnCircularReference(getProject()); +// } +// +// /** +// * Convenience method. +// * @param p the Ant Project instance against which to resolve references. +// * @since Ant 1.7 +// */ +// protected void dieOnCircularReference(Project p) { +// if (checked || !isReference()) { +// return; +// } +// dieOnCircularReference(new IdentityStack(this), p); +// } +// +// /** +// * Check to see whether any DataType we hold references to is +// * included in the Stack (which holds all DataType instances that +// * directly or indirectly reference this instance, including this +// * instance itself). +// * +// *

If one is included, throw a BuildException created by {@link +// * #circularReference circularReference}.

+// * +// *

This implementation is appropriate only for a DataType that +// * cannot hold other DataTypes as children.

+// * +// *

The general contract of this method is that it shouldn't do +// * anything if {@link #checked checked} is true and +// * set it to true on exit.

+// * @param stack the stack of references to check. +// * @param project the project to use to dereference the references. +// * @throws BuildException on error. +// */ +// protected void dieOnCircularReference(final Stack stack, +// final Project project) +// throws BuildException { +// +// if (checked || !isReference()) { +// return; +// } +// Object o = ref.getReferencedObject(project); +// +// if (o instanceof DataType) { +// IdentityStack id = IdentityStack.getInstance(stack); +// +// if (id.contains(o)) { +// throw circularReference(); +// } else { +// id.push(o); +// ((DataType) o).dieOnCircularReference(id, project); +// id.pop(); +// } +// } +// checked = true; +// } +// /** +// * Allow DataTypes outside org.apache.tools.ant.types to indirectly call dieOnCircularReference on nested DataTypes. +// * +// * @param dt the DataType to check. +// * @param stk the stack of references to check. +// * @param p the project to use to dereference the references. +// * @throws BuildException on error. +// * @since Ant 1.7 +// */ +// public static void invokeCircularReferenceCheck(DataType dt, Stack stk, +// Project p) { +// dt.dieOnCircularReference(stk, p); +// } +// +// /** +// * Allow DataTypes outside org.apache.tools.ant.types to indirectly call dieOnCircularReference on nested DataTypes. +// * +// *

+// * Pushes dt on the stack, runs dieOnCircularReference and pops it again.

+// * +// * @param dt the DataType to check. +// * @param stk the stack of references to check. +// * @param p the project to use to dereference the references. +// * @throws BuildException on error. +// * @since Ant 1.8.0 +// */ +// public static void pushAndInvokeCircularReferenceCheck(DataType dt, +// Stack stk, +// Project p) { +// stk.push(dt); +// dt.dieOnCircularReference(stk, p); +// stk.pop(); +// } +// /** +// * Performs the check for circular references and returns the referenced object. +// * +// * @return the dereferenced object. +// * @throws BuildException if the reference is invalid (circular ref, wrong class, etc). +// * @since Ant 1.7 +// */ +// protected Object getCheckedRef() { +// return getCheckedRef(getProject()); +// } +// +// /** +// * Performs the check for circular references and returns the referenced object. +// * +// * @param p the Ant Project instance against which to resolve references. +// * @return the dereferenced object. +// * @throws BuildException if the reference is invalid (circular ref, wrong class, etc). +// * @since Ant 1.7 +// */ +// protected Object getCheckedRef(Project p) { +// return getCheckedRef(getClass(), getDataTypeName(), p); +// } +// +// /** +// * Performs the check for circular references and returns the referenced object. +// * +// * @param requiredClass the class that this reference should be a subclass of. +// * @param dataTypeName the name of the datatype that the reference should be (error message use only). +// * @return the dereferenced object. +// * @throws BuildException if the reference is invalid (circular ref, wrong class, etc). +// */ +// protected T getCheckedRef(final Class requiredClass, +// final String dataTypeName) { +// return getCheckedRef(requiredClass, dataTypeName, getProject()); +// } +// +// /** +// * Performs the check for circular references and returns the referenced object. This version allows the fallback +// * Project instance to be specified. +// * +// * @param requiredClass the class that this reference should be a subclass of. +// * @param dataTypeName the name of the datatype that the reference should be (error message use only). +// * @param project the fallback Project instance for dereferencing. +// * @return the dereferenced object. +// * @throws BuildException if the reference is invalid (circular ref, wrong class, etc), or if project +// * is null. +// * @since Ant 1.7 +// */ +// protected T getCheckedRef(final Class requiredClass, +// final String dataTypeName, final Project project) { +// if (project == null) { +// throw new BuildException("No Project specified"); +// } +// dieOnCircularReference(project); +// Object o = ref.getReferencedObject(project); +// if (!(requiredClass.isAssignableFrom(o.getClass()))) { +// log("Class " + o.getClass() + " is not a subclass of " + requiredClass, +// Project.MSG_VERBOSE); +// String msg = ref.getRefId() + " doesn\'t denote a " + dataTypeName; +// throw new BuildException(msg); +// } +// @SuppressWarnings("unchecked") +// final T result = (T) o; +// return result; +// } + /** + * Creates an exception that indicates that refid has to be the only attribute if it is set. + * + * @return the exception to throw + */ + protected BuildException tooManyAttributes() { + return new BuildException("You must not specify more than one " + + "attribute when using refid"); + } + + /** + * Creates an exception that indicates that this XML element must not have child elements if the refid attribute is + * set. + * + * @return the exception to throw + */ + protected BuildException noChildrenAllowed() { + return new BuildException("You must not specify nested elements " + + "when using refid"); + } + + /** + * Creates an exception that indicates the user has generated a loop of data types referencing each other. + * + * @return the exception to throw + */ + protected BuildException circularReference() { + return new BuildException("This data type contains a circular " + + "reference."); + } + + /** + * The flag that is used to indicate that circular references have been checked. + * + * @return true if circular references have been checked + */ + protected boolean isChecked() { + return checked; + } + + /** + * Set the flag that is used to indicate that circular references have been checked. + * + * @param checked if true, if circular references have been checked + */ + protected void setChecked(final boolean checked) { + this.checked = checked; + } + + /** + * get the reference set on this object + * + * @return the reference or null + */ + public Reference getRefid() { + return ref; + } + + /** + * check that it is ok to set attributes, i.e that no reference is defined + * + * @since Ant 1.6 + * @throws BuildException if not allowed + */ + protected void checkAttributesAllowed() { + if (isReference()) { + throw tooManyAttributes(); + } + } + + /** + * check that it is ok to add children, i.e that no reference is defined + * + * @since Ant 1.6 + * @throws BuildException if not allowed + */ + protected void checkChildrenAllowed() { + if (isReference()) { + throw noChildrenAllowed(); + } + } + + /** + * Basic DataType toString(). + * + * @return this DataType formatted as a String. + */ + public String toString() { + String d = getDescription(); + //return d == null ? getDataTypeName() : getDataTypeName() + " " + d; + return d == null ? "DataType" : d; + } + + /** + * @since Ant 1.7 + * @return a shallow copy of this DataType. + * @throws CloneNotSupportedException if there is a problem. + */ + public Object clone() throws CloneNotSupportedException { + DataType dt = (DataType) super.clone(); + dt.setDescription(getDescription()); + if (getRefid() != null) { + dt.setRefid(getRefid()); + } + dt.setChecked(isChecked()); + return dt; + } +} diff --git a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/Reference.java b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/Reference.java new file mode 100644 index 000000000..1150a38f5 --- /dev/null +++ b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/Reference.java @@ -0,0 +1,126 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.owasp.dependencycheck.org.apache.tools.ant.types; + +/** + * Class to hold a reference to another object in the project. + * + */ +public class Reference { + + private String refid; + //private Project project; + + /** + * Create a reference. + * + * @deprecated since 1.7. Please use {@link Reference#Reference(Project,String)} instead. + */ + public Reference() { + } + + /** + * Create a reference to a named ID. + * + * @param id the name of this reference + * @deprecated since 1.7. Please use {@link Reference#Reference(Project,String)} instead. + */ + public Reference(String id) { + setRefId(id); + } +// +// /** +// * Create a reference to a named ID in a particular project. +// * @param p the project this reference is associated with +// * @param id the name of this reference +// * @since Ant 1.6.3 +// */ +// public Reference(Project p, String id) { +// setRefId(id); +// setProject(p); +// } + + /** + * Set the reference id. Should not normally be necessary; use {@link Reference#Reference(Project, String)}. + * + * @param id the reference id to use + */ + public void setRefId(String id) { + refid = id; + } + + /** + * Get the reference id of this reference. + * + * @return the reference id + */ + public String getRefId() { + return refid; + } + +// /** +// * Set the associated project. Should not normally be necessary; +// * use {@link Reference#Reference(Project,String)}. +// * @param p the project to use +// * @since Ant 1.6.3 +// */ +// public void setProject(Project p) { +// this.project = p; +// } +// +// /** +// * Get the associated project, if any; may be null. +// * @return the associated project +// * @since Ant 1.6.3 +// */ +// public Project getProject() { +// return project; +// } +// /** +// * Resolve the reference, using the associated project if +// * it set, otherwise use the passed in project. +// * @param fallback the fallback project to use if the project attribute of +// * reference is not set. +// * @return the dereferenced object. +// * @throws BuildException if the reference cannot be dereferenced. +// */ +// public Object getReferencedObject(Project fallback) throws BuildException { +// if (refid == null) { +// throw new BuildException("No reference specified"); +// } +// +// Object o = project == null ? fallback.getReference(refid) : project.getReference(refid); +// if (o == null) { +// throw new BuildException("Reference " + refid + " not found."); +// } +// return o; +// } +// /** +// * Resolve the reference, looking in the associated project. +// * @see Project#getReference +// * @return the dereferenced object. +// * @throws BuildException if the project is null or the reference cannot be dereferenced +// * @since Ant 1.6.3 +// */ +// public Object getReferencedObject() throws BuildException { +// if (project == null) { +// throw new BuildException("No project set on reference to " + refid); +// } +// return getReferencedObject(project); +// } +} diff --git a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/Resource.java b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/Resource.java new file mode 100644 index 000000000..f8379619a --- /dev/null +++ b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/Resource.java @@ -0,0 +1,462 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.owasp.dependencycheck.org.apache.tools.ant.types; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.math.BigInteger; +import java.util.Iterator; +import java.util.NoSuchElementException; +import org.owasp.dependencycheck.org.apache.tools.ant.types.resources.FileProvider; + +/** + * Describes a "File-like" resource (File, ZipEntry, etc.). + * + * This class is meant to be used by classes needing to record path and date/time information about a file, a zip entry + * or some similar resource (URL, archive in a version control repository, ...). + * + * @since Ant 1.5.2 + * @see org.apache.tools.ant.types.resources.Touchable + */ +public class Resource extends DataType implements Comparable, ResourceCollection { + + /** + * Constant unknown size + */ + public static final long UNKNOWN_SIZE = -1; + + /** + * Constant unknown datetime for getLastModified + */ + public static final long UNKNOWN_DATETIME = 0L; + + /** + * Magic number + */ + protected static final int MAGIC = getMagicNumber("Resource".getBytes()); + + private static final int NULL_NAME = getMagicNumber("null name".getBytes()); + + /** + * Create a "magic number" for use in hashCode calculations. + * + * @param seed byte[] to seed with. + * @return a magic number as int. + */ + protected static int getMagicNumber(byte[] seed) { + return new BigInteger(seed).intValue(); + } + + private String name = null; + private Boolean exists = null; + private Long lastmodified = null; + private Boolean directory = null; + private Long size = null; + + /** + * Default constructor. + */ + public Resource() { + } + + /** + * Only sets the name. + * + *

+ * This is a dummy, used for not existing resources.

+ * + * @param name relative path of the resource. Expects "/" to be used as the directory separator. + */ + public Resource(String name) { + this(name, false, 0, false); + } + + /** + * Sets the name, lastmodified flag, and exists flag. + * + * @param name relative path of the resource. Expects "/" to be used as the directory separator. + * @param exists if true, this resource exists. + * @param lastmodified the last modification time of this resource. + */ + public Resource(String name, boolean exists, long lastmodified) { + this(name, exists, lastmodified, false); + } + + /** + * Sets the name, lastmodified flag, exists flag, and directory flag. + * + * @param name relative path of the resource. Expects "/" to be used as the directory separator. + * @param exists if true the resource exists + * @param lastmodified the last modification time of the resource + * @param directory if true, this resource is a directory + */ + public Resource(String name, boolean exists, long lastmodified, boolean directory) { + this(name, exists, lastmodified, directory, UNKNOWN_SIZE); + } + + /** + * Sets the name, lastmodified flag, exists flag, directory flag, and size. + * + * @param name relative path of the resource. Expects "/" to be used as the directory separator. + * @param exists if true the resource exists + * @param lastmodified the last modification time of the resource + * @param directory if true, this resource is a directory + * @param size the size of this resource. + */ + public Resource(String name, boolean exists, long lastmodified, boolean directory, long size) { + this.name = name; + setName(name); + setExists(exists); + setLastModified(lastmodified); + setDirectory(directory); + setSize(size); + } + + /** + * Name attribute will contain the path of a file relative to the root directory of its fileset or the recorded path + * of a zip entry. + * + *

+ * example for a file with fullpath /var/opt/adm/resource.txt in a file set with root dir /var/opt it will be + * adm/resource.txt.

+ * + *

+ * "/" will be used as the directory separator.

+ * + * @return the name of this resource. + */ + public String getName() { + //return isReference() ? ((Resource) getCheckedRef()).getName() : name; + return name; + } + + /** + * Set the name of this Resource. + * + * @param name relative path of the resource. Expects "/" to be used as the directory separator. + */ + public void setName(String name) { + checkAttributesAllowed(); + this.name = name; + } + + /** + * The exists attribute tells whether a resource exists. + * + * @return true if this resource exists. + */ + public boolean isExists() { +// if (isReference()) { +// return ((Resource) getCheckedRef()).isExists(); +// } + //default true: + return exists == null || exists.booleanValue(); + } + + /** + * Set the exists attribute. + * + * @param exists if true, this resource exists. + */ + public void setExists(boolean exists) { + checkAttributesAllowed(); + this.exists = exists ? Boolean.TRUE : Boolean.FALSE; + } + + /** + * Tells the modification time in milliseconds since 01.01.1970 (the "epoch"). + * + * @return the modification time, if that is meaningful (e.g. for a file resource which exists); 0 if the resource + * does not exist, to mirror the behavior of {@link java.io.File#lastModified}; or 0 if the notion of modification + * time is meaningless for this class of resource (e.g. an inline string) + */ + public long getLastModified() { +// if (isReference()) { +// return ((Resource) getCheckedRef()).getLastModified(); +// } + if (!isExists() || lastmodified == null) { + return UNKNOWN_DATETIME; + } + long result = lastmodified.longValue(); + return result < UNKNOWN_DATETIME ? UNKNOWN_DATETIME : result; + } + + /** + * Set the last modification attribute. + * + * @param lastmodified the modification time in milliseconds since 01.01.1970. + */ + public void setLastModified(long lastmodified) { + checkAttributesAllowed(); + this.lastmodified = new Long(lastmodified); + } + + /** + * Tells if the resource is a directory. + * + * @return boolean flag indicating if the resource is a directory. + */ + public boolean isDirectory() { +// if (isReference()) { +// return ((Resource) getCheckedRef()).isDirectory(); +// } + //default false: + return directory != null && directory.booleanValue(); + } + + /** + * Set the directory attribute. + * + * @param directory if true, this resource is a directory. + */ + public void setDirectory(boolean directory) { + checkAttributesAllowed(); + this.directory = directory ? Boolean.TRUE : Boolean.FALSE; + } + + /** + * Set the size of this Resource. + * + * @param size the size, as a long. + * @since Ant 1.6.3 + */ + public void setSize(long size) { + checkAttributesAllowed(); + this.size = new Long(size > UNKNOWN_SIZE ? size : UNKNOWN_SIZE); + } + + /** + * Get the size of this Resource. + * + * @return the size, as a long, 0 if the Resource does not exist (for compatibility with java.io.File), or + * UNKNOWN_SIZE if not known. + * @since Ant 1.6.3 + */ + public long getSize() { +// if (isReference()) { +// return ((Resource) getCheckedRef()).getSize(); +// } + return isExists() + ? (size != null ? size.longValue() : UNKNOWN_SIZE) + : 0L; + } + + /** + * Clone this Resource. + * + * @return copy of this. + */ + public Object clone() { + try { + return super.clone(); + } catch (CloneNotSupportedException e) { + throw new UnsupportedOperationException( + "CloneNotSupportedException for a Resource caught. " + + "Derived classes must support cloning."); + } + } + + /** + * Delegates to a comparison of names. + * + * @param other the object to compare to. + * @return a negative integer, zero, or a positive integer as this Resource is less than, equal to, or greater than + * the specified Resource. + * @since Ant 1.6 + */ + public int compareTo(Resource other) { +// if (isReference()) { +// return ((Resource) getCheckedRef()).compareTo(other); +// } + return toString().compareTo(other.toString()); + } + + /** + * Implement basic Resource equality. + * + * @param other the object to check against. + * @return true if the specified Object is equal to this Resource. + * @since Ant 1.7 + */ + public boolean equals(Object other) { +// if (isReference()) { +// return getCheckedRef().equals(other); +// } + return other != null && other.getClass().equals(getClass()) + && compareTo((Resource) other) == 0; + } + + /** + * Get the hash code for this Resource. + * + * @return hash code as int. + * @since Ant 1.7 + */ + public int hashCode() { +// if (isReference()) { +// return getCheckedRef().hashCode(); +// } + String name = getName(); + return MAGIC * (name == null ? NULL_NAME : name.hashCode()); + } + + /** + * Get an InputStream for the Resource. + * + * @return an InputStream containing this Resource's content. + * @throws IOException if unable to provide the content of this Resource as a stream. + * @throws UnsupportedOperationException if InputStreams are not supported for this Resource type. + * @since Ant 1.7 + */ + public InputStream getInputStream() throws IOException { +// if (isReference()) { +// return ((Resource) getCheckedRef()).getInputStream(); +// } + throw new UnsupportedOperationException(); + } + + /** + * Get an OutputStream for the Resource. + * + * @return an OutputStream to which content can be written. + * @throws IOException if unable to provide the content of this Resource as a stream. + * @throws UnsupportedOperationException if OutputStreams are not supported for this Resource type. + * @since Ant 1.7 + */ + public OutputStream getOutputStream() throws IOException { +// if (isReference()) { +// return ((Resource) getCheckedRef()).getOutputStream(); +// } + throw new UnsupportedOperationException(); + } + + /** + * Fulfill the ResourceCollection contract. + * + * @return an Iterator of Resources. + * @since Ant 1.7 + */ + public Iterator iterator() { + //return isReference() ? ((Resource) getCheckedRef()).iterator() + // : new Iterator() { + return new Iterator() { + private boolean done = false; + + public boolean hasNext() { + return !done; + } + + public Resource next() { + if (done) { + throw new NoSuchElementException(); + } + done = true; + return Resource.this; + } + + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + + /** + * Fulfill the ResourceCollection contract. + * + * @return the size of this ResourceCollection. + * @since Ant 1.7 + */ + public int size() { + //return isReference() ? ((Resource) getCheckedRef()).size() : 1; + return 1; + } + + /** + * Fulfill the ResourceCollection contract. + * + * @return whether this Resource is a FileProvider. + * @since Ant 1.7 + */ + public boolean isFilesystemOnly() { +// return (isReference() && ((Resource) getCheckedRef()).isFilesystemOnly()) +// || this.as(FileProvider.class) != null; + return this.as(FileProvider.class) != null; + } + + /** + * Get the string representation of this Resource. + * + * @return this Resource formatted as a String. + * @since Ant 1.7 + */ + public String toString() { +// if (isReference()) { +// return getCheckedRef().toString(); +// } + String n = getName(); + return n == null ? "(anonymous)" : n; + } + + /** + * Get a long String representation of this Resource. This typically should be the value of toString() + * prefixed by a type description. + * + * @return this Resource formatted as a long String. + * @since Ant 1.7 + */ + public final String toLongString() { +// return isReference() ? ((Resource) getCheckedRef()).toLongString() +// : getDataTypeName() + " \"" + toString() + '"'; + return toString(); + } + + /** + * Overrides the base version. + * + * @param r the Reference to set. + */ + public void setRefid(Reference r) { + if (name != null + || exists != null + || lastmodified != null + || directory != null + || size != null) { + throw tooManyAttributes(); + } + super.setRefid(r); + } + + /** + * Returns a view of this resource that implements the interface given as the argument or null if there is no such + * view. + * + *

+ * This allows extension interfaces to be added to resources without growing the number of permutations of + * interfaces decorators/adapters need to implement.

+ * + *

+ * This implementation of the method will return the current instance itself if it can be assigned to the given + * class.

+ * + * @since Ant 1.8.0 + */ + public T as(Class clazz) { + return clazz.isAssignableFrom(getClass()) ? clazz.cast(this) : null; + } +} diff --git a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/ResourceCollection.java b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/ResourceCollection.java new file mode 100644 index 000000000..3aed793fe --- /dev/null +++ b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/ResourceCollection.java @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.owasp.dependencycheck.org.apache.tools.ant.types; + +import java.util.Iterator; +import org.owasp.dependencycheck.org.apache.tools.ant.types.resources.FileProvider; + +/** + * Interface describing a collection of Resources. + * @since Ant 1.7 + */ +public interface ResourceCollection extends Iterable { + + /** + * Gets the contents of this collection. + * @return all resources in the collection + */ + Iterator iterator(); + + /** + * Learn the number of contained Resources. + * @return number of elements as int. + */ + int size(); + + /** + * Indicate whether this ResourceCollection is composed entirely of + * Resources accessible via local filesystem conventions. If true, + * all resources returned from this collection should + * respond with a {@link FileProvider} when asked via {@link Resource#as}. + * @return whether this is a filesystem-only resource collection. + */ + boolean isFilesystemOnly(); + +} diff --git a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/ResourceFactory.java b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/ResourceFactory.java new file mode 100644 index 000000000..552e2ee73 --- /dev/null +++ b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/ResourceFactory.java @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.owasp.dependencycheck.org.apache.tools.ant.types; + +/** + * this interface should be implemented by classes (Scanners) needing + * to deliver information about resources. + * + * @since Ant 1.5.2 + */ +public interface ResourceFactory { + + /** + * Query a resource (file, zipentry, ...) by name + * + * @param name relative path of the resource about which + * information is sought. Expects "/" to be used as the + * directory separator. + * @return instance of Resource; the exists attribute of Resource + * will tell whether the sought resource exists + */ + Resource getResource(String name); +} diff --git a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/package-info.java b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/package-info.java new file mode 100644 index 000000000..6834fbfc2 --- /dev/null +++ b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/package-info.java @@ -0,0 +1,14 @@ +/** + * + * + * org.owasp.dependencycheck.org.apache.tools.ant.types + * + * + * This is a copy of classes within Apache Ant. The DirectoryScanner + * is needed by dependency-check. However, we did not want to make + * Ant a dependency. As such, a few files were copied and slightly + * modified to remove any references to the Ant Project class. + * + * + */ +package org.owasp.dependencycheck.org.apache.tools.ant.types; diff --git a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/resources/Appendable.java b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/resources/Appendable.java new file mode 100644 index 000000000..076912049 --- /dev/null +++ b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/resources/Appendable.java @@ -0,0 +1,35 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.owasp.dependencycheck.org.apache.tools.ant.types.resources; + +import java.io.IOException; +import java.io.OutputStream; + +/** + * Interface to be implemented by "appendable" resources. + * @since Ant 1.8 + */ +public interface Appendable { + + /** + * Get an appending OutputStream. + * @return OutputStream + * @throws IOException if anything goes wrong + */ + OutputStream getAppendOutputStream() throws IOException; +} diff --git a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/resources/FileProvider.java b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/resources/FileProvider.java new file mode 100644 index 000000000..fdfac2598 --- /dev/null +++ b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/resources/FileProvider.java @@ -0,0 +1,36 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.owasp.dependencycheck.org.apache.tools.ant.types.resources; + +import java.io.File; + +/** + * This is an interface that resources that can provide a file should implement. + * This is a refactoring of {@link FileResource}, to allow other resources + * to act as sources of files (and to make components that only support + * file-based resources from only support FileResource resources. + * @since Ant 1.8 + */ +public interface FileProvider { + /** + * Get the file represented by this Resource. + * @return the file. + */ + File getFile(); +} diff --git a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/resources/FileResource.java b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/resources/FileResource.java new file mode 100644 index 000000000..430da35c4 --- /dev/null +++ b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/resources/FileResource.java @@ -0,0 +1,414 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.owasp.dependencycheck.org.apache.tools.ant.types.resources; + +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 org.owasp.dependencycheck.org.apache.tools.ant.BuildException; +import org.owasp.dependencycheck.org.apache.tools.ant.types.Reference; +import org.owasp.dependencycheck.org.apache.tools.ant.types.Resource; +import org.owasp.dependencycheck.org.apache.tools.ant.types.ResourceFactory; +import org.owasp.dependencycheck.org.apache.tools.ant.util.FileUtils; + +/** + * A Resource representation of a File. + * + * @since Ant 1.7 + */ +public class FileResource extends Resource implements Touchable, FileProvider, + ResourceFactory, Appendable { + + private static final FileUtils FILE_UTILS = FileUtils.getFileUtils(); + private static final int NULL_FILE + = Resource.getMagicNumber("null file".getBytes()); + + private File file; + private File baseDir; + + /** + * Default constructor. + */ + public FileResource() { + } + + /** + * Construct a new FileResource using the specified basedir and relative name. + * + * @param b the basedir as File. + * @param name the relative filename. + */ + public FileResource(File b, String name) { + this.baseDir = b; + this.file = FILE_UTILS.resolveFile(b, name); + } + + /** + * Construct a new FileResource from a File. + * + * @param f the File represented. + */ + public FileResource(File f) { + setFile(f); + } + +// /** +// * Create a new FileResource. +// * @param p Project +// * @param f File represented +// * @since Ant 1.8 +// */ +// public FileResource(Project p, File f) { +// this(f); +// setProject(p); +// } +// +// /** +// * Constructor for Ant attribute introspection. +// * @param p the Project against which to resolve s. +// * @param s the absolute or Project-relative filename as a String. +// * @see org.apache.tools.ant.IntrospectionHelper +// */ +// public FileResource(Project p, String s) { +// this(p, p.resolveFile(s)); +// } + /** + * Set the File for this FileResource. + * + * @param f the File to be represented. + */ + public void setFile(File f) { + checkAttributesAllowed(); + file = f; + if (f != null && (getBaseDir() == null || !FILE_UTILS.isLeadingPath(getBaseDir(), f))) { + setBaseDir(f.getParentFile()); + } + } + + /** + * Get the file represented by this FileResource. + * + * @return the File. + */ + public File getFile() { +// if (isReference()) { +// return ((FileResource) getCheckedRef()).getFile(); +// } +// dieOnCircularReference(); + synchronized (this) { + if (file == null) { + //try to resolve file set via basedir/name property setters: + File d = getBaseDir(); + String n = super.getName(); + if (n != null) { + setFile(FILE_UTILS.resolveFile(d, n)); + } + } + } + return file; + } + + /** + * Set the basedir for this FileResource. + * + * @param b the basedir as File. + */ + public void setBaseDir(File b) { + checkAttributesAllowed(); + baseDir = b; + } + + /** + * Return the basedir to which the name is relative. + * + * @return the basedir as File. + */ + public File getBaseDir() { +// if (isReference()) { +// return ((FileResource) getCheckedRef()).getBaseDir(); +// } +// dieOnCircularReference(); + return baseDir; + } + + /** + * Overrides the super version. + * + * @param r the Reference to set. + */ + public void setRefid(Reference r) { + if (file != null || baseDir != null) { + throw tooManyAttributes(); + } + super.setRefid(r); + } + + /** + * Get the name of this FileResource. If the basedir is set, the name will be relative to that. Otherwise the + * basename only will be returned. + * + * @return the name of this resource. + */ + public String getName() { +// if (isReference()) { +// return ((Resource) getCheckedRef()).getName(); +// } + File b = getBaseDir(); + return b == null ? getNotNullFile().getName() + : FILE_UTILS.removeLeadingPath(b, getNotNullFile()); + } + + /** + * Learn whether this file exists. + * + * @return true if this resource exists. + */ + public boolean isExists() { +// return isReference() ? ((Resource) getCheckedRef()).isExists() +// : getNotNullFile().exists(); + return getNotNullFile().exists(); + } + + /** + * Get the modification time in milliseconds since 01.01.1970 . + * + * @return 0 if the resource does not exist. + */ + public long getLastModified() { +// return isReference() +// ? ((Resource) getCheckedRef()).getLastModified() +// : getNotNullFile().lastModified(); + return getNotNullFile().lastModified(); + } + + /** + * Learn whether the resource is a directory. + * + * @return boolean flag indicating if the resource is a directory. + */ + public boolean isDirectory() { +// return isReference() ? ((Resource) getCheckedRef()).isDirectory() +// : getNotNullFile().isDirectory(); + return getNotNullFile().isDirectory(); + } + + /** + * Get the size of this Resource. + * + * @return the size, as a long, 0 if the Resource does not exist. + */ + public long getSize() { +// return isReference() ? ((Resource) getCheckedRef()).getSize() +// : getNotNullFile().length(); + return getNotNullFile().length(); + } + + /** + * Return an InputStream for reading the contents of this Resource. + * + * @return an InputStream object. + * @throws IOException if an error occurs. + */ + public InputStream getInputStream() throws IOException { +// return isReference() +// ? ((Resource) getCheckedRef()).getInputStream() +// : new FileInputStream(getNotNullFile()); + return new FileInputStream(getNotNullFile()); + } + + /** + * Get an OutputStream for the Resource. + * + * @return an OutputStream to which content can be written. + * @throws IOException if unable to provide the content of this Resource as a stream. + * @throws UnsupportedOperationException if OutputStreams are not supported for this Resource type. + */ + public OutputStream getOutputStream() throws IOException { +// if (isReference()) { +// return ((FileResource) getCheckedRef()).getOutputStream(); +// } + return getOutputStream(false); + } + + /** + * {@inheritDoc} + */ + public OutputStream getAppendOutputStream() throws IOException { +// if (isReference()) { +// return ((FileResource) getCheckedRef()).getAppendOutputStream(); +// } + return getOutputStream(true); + } + + private OutputStream getOutputStream(boolean append) throws IOException { + File f = getNotNullFile(); + if (f.exists()) { + if (f.isFile() && !append) { + f.delete(); + } + } else { + File p = f.getParentFile(); + if (p != null && !(p.exists())) { + p.mkdirs(); + } + } + return append ? new FileOutputStream(f.getAbsolutePath(), true) : new FileOutputStream(f); + } + + /** + * Compare this FileResource to another Resource. + * + * @param another the other Resource against which to compare. + * @return a negative integer, zero, or a positive integer as this FileResource is less than, equal to, or greater + * than the specified Resource. + */ + public int compareTo(Resource another) { +// if (isReference()) { +// return ((Resource) getCheckedRef()).compareTo(another); +// } + if (this.equals(another)) { + return 0; + } + FileProvider otherFP = another.as(FileProvider.class); + if (otherFP != null) { + File f = getFile(); + if (f == null) { + return -1; + } + File of = otherFP.getFile(); + if (of == null) { + return 1; + } + return f.compareTo(of); + } + return super.compareTo(another); + } + + /** + * Compare another Object to this FileResource for equality. + * + * @param another the other Object to compare. + * @return true if another is a FileResource representing the same file. + */ + public boolean equals(Object another) { + if (this == another) { + return true; + } +// if (isReference()) { +// return getCheckedRef().equals(another); +// } + if (another == null || !(another.getClass().equals(getClass()))) { + return false; + } + FileResource otherfr = (FileResource) another; + return getFile() == null + ? otherfr.getFile() == null + : getFile().equals(otherfr.getFile()); + } + + /** + * Get the hash code for this Resource. + * + * @return hash code as int. + */ + public int hashCode() { +// if (isReference()) { +// return getCheckedRef().hashCode(); +// } + return MAGIC * (getFile() == null ? NULL_FILE : getFile().hashCode()); + } + + /** + * Get the string representation of this Resource. + * + * @return this FileResource formatted as a String. + */ + public String toString() { +// if (isReference()) { +// return getCheckedRef().toString(); +// } + if (file == null) { + return "(unbound file resource)"; + } + String absolutePath = file.getAbsolutePath(); + return FILE_UTILS.normalize(absolutePath).getAbsolutePath(); + } + + /** + * Fulfill the ResourceCollection contract. + * + * @return whether this Resource is a FileResource. + */ + public boolean isFilesystemOnly() { +// if (isReference()) { +// return ((FileResource) getCheckedRef()).isFilesystemOnly(); +// } +// dieOnCircularReference(); + return true; + } + + /** + * Implement the Touchable interface. + * + * @param modTime new last modification time. + */ + public void touch(long modTime) { +// if (isReference()) { +// ((FileResource) getCheckedRef()).touch(modTime); +// return; +// } + if (!getNotNullFile().setLastModified(modTime)) { + //log("Failed to change file modification time", Project.MSG_WARN); + } + } + + /** + * Get the file represented by this FileResource, ensuring it is not null. + * + * @return the not-null File. + * @throws BuildException if file is null. + */ + protected File getNotNullFile() { + if (getFile() == null) { + throw new BuildException("file attribute is null!"); + } +// dieOnCircularReference(); + return getFile(); + } + + /** + * Create a new resource that matches a relative or absolute path. If the current instance has a compatible baseDir + * attribute, it is copied. + * + * @param path relative/absolute path to a resource + * @return a new resource of type FileResource + * @throws BuildException if desired + * @since Ant1.8 + */ + public Resource getResource(String path) { + File newfile = FILE_UTILS.resolveFile(getFile(), path); + FileResource fileResource = new FileResource(newfile); + if (FILE_UTILS.isLeadingPath(getBaseDir(), newfile)) { + fileResource.setBaseDir(getBaseDir()); + } + return fileResource; + } +} diff --git a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/resources/Touchable.java b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/resources/Touchable.java new file mode 100644 index 000000000..fdb81f9bf --- /dev/null +++ b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/resources/Touchable.java @@ -0,0 +1,32 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.owasp.dependencycheck.org.apache.tools.ant.types.resources; + +/** + * Interface to be implemented by "touchable" resources; + * that is, those whose modification time can be altered. + * @since Ant 1.7 + */ +public interface Touchable { + /** + * Method called to "touch" the resource. + * @param modTime the time to set the modified "field" of the resource, + * measured in milliseconds since the epoch. + */ + void touch(long modTime); +} diff --git a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/resources/package-info.java b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/resources/package-info.java new file mode 100644 index 000000000..2e1e26783 --- /dev/null +++ b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/resources/package-info.java @@ -0,0 +1,14 @@ +/** + * + * + * org.owasp.dependencycheck.org.apache.tools.ant.types.resources + * + * + * This is a copy of classes within Apache Ant. The DirectoryScanner + * is needed by dependency-check. However, we did not want to make + * Ant a dependency. As such, a few files were copied and slightly + * modified to remove any references to the Ant Project class. + * + * + */ +package org.owasp.dependencycheck.org.apache.tools.ant.types.resources; diff --git a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/selectors/FileSelector.java b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/selectors/FileSelector.java new file mode 100644 index 000000000..787e5e928 --- /dev/null +++ b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/selectors/FileSelector.java @@ -0,0 +1,48 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.owasp.dependencycheck.org.apache.tools.ant.types.selectors; + +import java.io.File; + +import org.owasp.dependencycheck.org.apache.tools.ant.BuildException; + +/** + * This is the interface to be used by all selectors. + * + * @since 1.5 + */ +public interface FileSelector { + + /** + * Method that each selector will implement to create their + * selection behaviour. If there is a problem with the setup + * of a selector, it can throw a BuildException to indicate + * the problem. + * + * @param basedir A java.io.File object for the base directory + * @param filename The name of the file to check + * @param file A File object for this filename + * @return whether the file should be selected or not + * @exception BuildException if the selector was not configured correctly + */ + boolean isSelected(File basedir, String filename, File file) + throws BuildException; + +} + diff --git a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/selectors/SelectorScanner.java b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/selectors/SelectorScanner.java new file mode 100644 index 000000000..6597bfd16 --- /dev/null +++ b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/selectors/SelectorScanner.java @@ -0,0 +1,49 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.owasp.dependencycheck.org.apache.tools.ant.types.selectors; + +/** + * An interface used to describe the actions required by any type of + * directory scanner that supports Selectors. + * + * @since 1.5 + */ +public interface SelectorScanner { + /** + * Sets the selectors the scanner should use. + * + * @param selectors the list of selectors + */ + void setSelectors(FileSelector[] selectors); + + /** + * Directories which were selected out of a scan. + * + * @return list of directories not selected + */ + String[] getDeselectedDirectories(); + + /** + * Files which were selected out of a scan. + * + * @return list of files not selected + */ + String[] getDeselectedFiles(); + + +} diff --git a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/selectors/SelectorUtils.java b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/selectors/SelectorUtils.java new file mode 100644 index 000000000..6cbcd1fbf --- /dev/null +++ b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/selectors/SelectorUtils.java @@ -0,0 +1,695 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.owasp.dependencycheck.org.apache.tools.ant.types.selectors; + +import java.io.File; +import java.util.StringTokenizer; +import java.util.Vector; + +import org.owasp.dependencycheck.org.apache.tools.ant.types.Resource; +import org.owasp.dependencycheck.org.apache.tools.ant.util.FileUtils; + +/** + *

This is a utility class used by selectors and DirectoryScanner. The + * functionality more properly belongs just to selectors, but unfortunately + * DirectoryScanner exposed these as protected methods. Thus we have to + * support any subclasses of DirectoryScanner that may access these methods. + *

+ *

This is a Singleton.

+ * + * @since 1.5 + */ +public final class SelectorUtils { + + /** + * The pattern that matches an arbitrary number of directories. + * @since Ant 1.8.0 + */ + public static final String DEEP_TREE_MATCH = "**"; + + private static final SelectorUtils instance = new SelectorUtils(); + private static final FileUtils FILE_UTILS = FileUtils.getFileUtils(); + + /** + * Private Constructor + */ + private SelectorUtils() { + } + + /** + * Retrieves the instance of the Singleton. + * @return singleton instance + */ + public static SelectorUtils getInstance() { + return instance; + } + + /** + * 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. + * + * @return whether or not a given path matches the start of a given + * pattern up to the first "**". + */ + public static boolean matchPatternStart(String pattern, String str) { + return matchPatternStart(pattern, str, true); + } + + /** + * 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 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 "**". + */ + public static boolean matchPatternStart(String pattern, String str, + 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(File.separator) + != pattern.startsWith(File.separator)) { + return false; + } + + String[] patDirs = tokenizePathAsArray(pattern); + String[] strDirs = tokenizePathAsArray(str); + return matchPatternStart(patDirs, strDirs, 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. + * + * @param patDirs The tokenized pattern to match against. Must not be + * null. + * @param strDirs The tokenized path to match. 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 "**". + */ + static boolean matchPatternStart(String[] patDirs, String[] 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) { + String patDir = patDirs[patIdxStart]; + if (patDir.equals(DEEP_TREE_MATCH)) { + break; + } + if (!match(patDir, strDirs[strIdxStart], isCaseSensitive)) { + return false; + } + patIdxStart++; + strIdxStart++; + } + + // CheckStyle:SimplifyBooleanReturnCheck OFF + // Check turned off as the code needs the comments for the various + // code paths. + 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; + } + } + + /** + * Tests whether or not a given path matches a given pattern. + * + * If you need to call this method multiple times with the same + * pattern you should rather use TokenizedPath + * + * @see TokenizedPath + * + * @param pattern The pattern to match against. Must not be + * 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. + */ + public static boolean matchPath(String pattern, String str) { + String[] patDirs = tokenizePathAsArray(pattern); + return matchPath(patDirs, tokenizePathAsArray(str), true); + } + + /** + * Tests whether or not a given path matches a given pattern. + * + * If you need to call this method multiple times with the same + * pattern you should rather use TokenizedPattern + * + * @see TokenizedPattern + * + * @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) { + String[] patDirs = tokenizePathAsArray(pattern); + return matchPath(patDirs, tokenizePathAsArray(str), isCaseSensitive); + } + + /** + * Core implementation of matchPath. It is isolated so that it + * can be called from TokenizedPattern. + */ + static boolean matchPath(String[] tokenizedPattern, String[] strDirs, + boolean isCaseSensitive) { + int patIdxStart = 0; + int patIdxEnd = tokenizedPattern.length - 1; + int strIdxStart = 0; + int strIdxEnd = strDirs.length - 1; + + // up to first '**' + while (patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd) { + String patDir = tokenizedPattern[patIdxStart]; + if (patDir.equals(DEEP_TREE_MATCH)) { + 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 (!tokenizedPattern[i].equals(DEEP_TREE_MATCH)) { + 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) { + String patDir = tokenizedPattern[patIdxEnd]; + if (patDir.equals(DEEP_TREE_MATCH)) { + break; + } + if (!match(patDir, strDirs[strIdxEnd], isCaseSensitive)) { + return false; + } + patIdxEnd--; + strIdxEnd--; + } + if (strIdxStart > strIdxEnd) { + // String is exhausted + for (int i = patIdxStart; i <= patIdxEnd; i++) { + if (!tokenizedPattern[i].equals(DEEP_TREE_MATCH)) { + return false; + } + } + return true; + } + + while (patIdxStart != patIdxEnd && strIdxStart <= strIdxEnd) { + int patIdxTmp = -1; + for (int i = patIdxStart + 1; i <= patIdxEnd; i++) { + if (tokenizedPattern[i].equals(DEEP_TREE_MATCH)) { + 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 = tokenizedPattern[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 (!tokenizedPattern[i].equals(DEEP_TREE_MATCH)) { + return false; + } + } + + return true; + } + + /** + * 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. + * + * @return true if the string matches against the pattern, + * or false otherwise. + */ + public static boolean match(String pattern, String str) { + return match(pattern, str, true); + } + + /** + * 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 caseSensitive 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 caseSensitive) { + char[] patArr = pattern.toCharArray(); + char[] strArr = str.toCharArray(); + int patIdxStart = 0; + int patIdxEnd = patArr.length - 1; + int strIdxStart = 0; + int strIdxEnd = strArr.length - 1; + char ch; + + boolean containsStar = false; + for (int i = 0; i < patArr.length; i++) { + if (patArr[i] == '*') { + containsStar = true; + break; + } + } + + if (!containsStar) { + // No '*'s, so we make a shortcut + if (patIdxEnd != strIdxEnd) { + return false; // Pattern and string do not have the same size + } + for (int i = 0; i <= patIdxEnd; i++) { + ch = patArr[i]; + if (ch != '?') { + if (different(caseSensitive, ch, strArr[i])) { + return false; // Character mismatch + } + } + } + return true; // String matches against pattern + } + + if (patIdxEnd == 0) { + return true; // Pattern contains only '*', which matches anything + } + + // Process characters before first star + while (true) { + ch = patArr[patIdxStart]; + if (ch == '*' || strIdxStart > strIdxEnd) { + break; + } + if (ch != '?') { + if (different(caseSensitive, ch, strArr[strIdxStart])) { + return false; // Character mismatch + } + } + patIdxStart++; + strIdxStart++; + } + if (strIdxStart > strIdxEnd) { + // All characters in the string are used. Check if only '*'s are + // left in the pattern. If so, we succeeded. Otherwise failure. + return allStars(patArr, patIdxStart, patIdxEnd); + } + + // Process characters after last star + while (true) { + ch = patArr[patIdxEnd]; + if (ch == '*' || strIdxStart > strIdxEnd) { + break; + } + if (ch != '?') { + if (different(caseSensitive, ch, strArr[strIdxEnd])) { + return false; // Character mismatch + } + } + patIdxEnd--; + strIdxEnd--; + } + if (strIdxStart > strIdxEnd) { + // All characters in the string are used. Check if only '*'s are + // left in the pattern. If so, we succeeded. Otherwise failure. + return allStars(patArr, patIdxStart, patIdxEnd); + } + + // process pattern between stars. padIdxStart and patIdxEnd point + // always to a '*'. + while (patIdxStart != patIdxEnd && strIdxStart <= strIdxEnd) { + int patIdxTmp = -1; + for (int i = patIdxStart + 1; i <= patIdxEnd; i++) { + if (patArr[i] == '*') { + patIdxTmp = i; + break; + } + } + if (patIdxTmp == patIdxStart + 1) { + // Two stars next to each other, skip the first 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++) { + ch = patArr[patIdxStart + j + 1]; + if (ch != '?') { + if (different(caseSensitive, ch, + strArr[strIdxStart + i + j])) { + continue strLoop; + } + } + } + + foundIdx = strIdxStart + i; + break; + } + + if (foundIdx == -1) { + return false; + } + + patIdxStart = patIdxTmp; + strIdxStart = foundIdx + patLength; + } + + // All characters in the string are used. Check if only '*'s are left + // in the pattern. If so, we succeeded. Otherwise failure. + return allStars(patArr, patIdxStart, patIdxEnd); + } + + private static boolean allStars(char[] chars, int start, int end) { + for (int i = start; i <= end; ++i) { + if (chars[i] != '*') { + return false; + } + } + return true; + } + + private static boolean different( + boolean caseSensitive, char ch, char other) { + return caseSensitive + ? ch != other + : Character.toUpperCase(ch) != Character.toUpperCase(other); + } + + /** + * 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); + } + + /** + * Breaks a path up into a Vector of path elements, tokenizing on + * + * @param path Path to tokenize. Must not be null. + * @param separator the separator against which to tokenize. + * + * @return a Vector of path elements from the tokenized path + * @since Ant 1.6 + */ + public static Vector tokenizePath(String path, String separator) { + Vector ret = new Vector(); + if (FileUtils.isAbsolutePath(path)) { + String[] s = FILE_UTILS.dissect(path); + ret.add(s[0]); + path = s[1]; + } + StringTokenizer st = new StringTokenizer(path, separator); + while (st.hasMoreTokens()) { + ret.addElement(st.nextToken()); + } + return ret; + } + + /** + * Same as {@link #tokenizePath tokenizePath} but hopefully faster. + */ + /*package*/ static String[] tokenizePathAsArray(String path) { + String root = null; + if (FileUtils.isAbsolutePath(path)) { + String[] s = FILE_UTILS.dissect(path); + root = s[0]; + path = s[1]; + } + char sep = File.separatorChar; + int start = 0; + int len = path.length(); + int count = 0; + for (int pos = 0; pos < len; pos++) { + if (path.charAt(pos) == sep) { + if (pos != start) { + count++; + } + start = pos + 1; + } + } + if (len != start) { + count++; + } + String[] l = new String[count + ((root == null) ? 0 : 1)]; + + if (root != null) { + l[0] = root; + count = 1; + } else { + count = 0; + } + start = 0; + for (int pos = 0; pos < len; pos++) { + if (path.charAt(pos) == sep) { + if (pos != start) { + String tok = path.substring(start, pos); + l[count++] = tok; + } + start = pos + 1; + } + } + if (len != start) { + String tok = path.substring(start); + l[count/*++*/] = tok; + } + return l; + } + + /** + * Returns dependency information on these two files. If src has been + * modified later than target, it returns true. If target doesn't exist, + * it likewise returns true. Otherwise, target is newer than src and + * is not out of date, thus the method returns false. It also returns + * 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 granularity the amount in seconds of slack we will give in + * determining out of dateness + * @return whether the target is out of date + */ + public static boolean isOutOfDate(File src, File target, int granularity) { + if (!src.exists()) { + return false; + } + if (!target.exists()) { + return true; + } + if ((src.lastModified() - granularity) > target.lastModified()) { + return true; + } + return false; + } + + /** + * Returns dependency information on these two resources. If src has been + * modified later than target, it returns true. If target doesn't exist, + * it likewise returns true. Otherwise, target is newer than src and + * is not out of date, thus the method returns false. It also returns + * false if the src file doesn't even exist, since how could the + * target then be out of date. + * + * @param src the original resource + * @param target the resource being compared against + * @param granularity the int amount in seconds of slack we will give in + * determining out of dateness + * @return whether the target is out of date + */ + public static boolean isOutOfDate(Resource src, Resource target, + int granularity) { + return isOutOfDate(src, target, (long) granularity); + } + + /** + * Returns dependency information on these two resources. If src has been + * modified later than target, it returns true. If target doesn't exist, + * it likewise returns true. Otherwise, target is newer than src and + * is not out of date, thus the method returns false. It also returns + * false if the src file doesn't even exist, since how could the + * target then be out of date. + * + * @param src the original resource + * @param target the resource being compared against + * @param granularity the long amount in seconds of slack we will give in + * determining out of dateness + * @return whether the target is out of date + */ + public static boolean isOutOfDate(Resource src, Resource target, long granularity) { + long sourceLastModified = src.getLastModified(); + long targetLastModified = target.getLastModified(); + return src.isExists() + && (sourceLastModified == Resource.UNKNOWN_DATETIME + || targetLastModified == Resource.UNKNOWN_DATETIME + || (sourceLastModified - granularity) > targetLastModified); + } + + /** + * "Flattens" a string by removing all whitespace (space, tab, linefeed, + * carriage return, and formfeed). This uses StringTokenizer and the + * default set of tokens as documented in the single argument constructor. + * + * @param input a String to remove all whitespace. + * @return a String that has had all whitespace removed. + */ + public static String removeWhitespace(String input) { + StringBuffer result = new StringBuffer(); + if (input != null) { + StringTokenizer st = new StringTokenizer(input); + while (st.hasMoreTokens()) { + result.append(st.nextToken()); + } + } + return result.toString(); + } + + /** + * Tests if a string contains stars or question marks + * @param input a String which one wants to test for containing wildcard + * @return true if the string contains at least a star or a question mark + */ + public static boolean hasWildcards(String input) { + return (input.indexOf('*') != -1 || input.indexOf('?') != -1); + } + + /** + * removes from a pattern all tokens to the right containing wildcards + * @param input the input string + * @return the leftmost part of the pattern without wildcards + */ + public static String rtrimWildcardTokens(String input) { + return new TokenizedPattern(input).rtrimWildcardTokens().toString(); + } +} + diff --git a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/selectors/TokenizedPath.java b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/selectors/TokenizedPath.java new file mode 100644 index 000000000..d3c19b037 --- /dev/null +++ b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/selectors/TokenizedPath.java @@ -0,0 +1,219 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.owasp.dependencycheck.org.apache.tools.ant.types.selectors; + +import java.io.File; + +import org.owasp.dependencycheck.org.apache.tools.ant.BuildException; +import org.owasp.dependencycheck.org.apache.tools.ant.util.FileUtils; +import org.owasp.dependencycheck.org.apache.tools.ant.util.SymbolicLinkUtils; + +/** + * Container for a path that has been split into its components. + * @since 1.8.0 + */ +public class TokenizedPath { + + /** + * Instance that holds no tokens at all. + */ + public static final TokenizedPath EMPTY_PATH = + new TokenizedPath("", new String[0]); + + /** Helper. */ + private static final FileUtils FILE_UTILS = FileUtils.getFileUtils(); + /** Helper. */ + private static final SymbolicLinkUtils SYMLINK_UTILS = + SymbolicLinkUtils.getSymbolicLinkUtils(); + /** iterations for case-sensitive scanning. */ + private static final boolean[] CS_SCAN_ONLY = new boolean[] {true}; + /** iterations for non-case-sensitive scanning. */ + private static final boolean[] CS_THEN_NON_CS = new boolean[] {true, false}; + + private final String path; + private final String[] tokenizedPath; + + /** + * Initialize the TokenizedPath by parsing it. + * @param path The path to tokenize. Must not be + * null. + */ + public TokenizedPath(String path) { + this(path, SelectorUtils.tokenizePathAsArray(path)); + } + + /** + * Creates a new path as a child of another path. + * + * @param parent the parent path + * @param child the child, must not contain the file separator + */ + public TokenizedPath(TokenizedPath parent, String child) { + if (parent.path.length() > 0 + && parent.path.charAt(parent.path.length() - 1) + != File.separatorChar) { + path = parent.path + File.separatorChar + child; + } else { + path = parent.path + child; + } + tokenizedPath = new String[parent.tokenizedPath.length + 1]; + System.arraycopy(parent.tokenizedPath, 0, tokenizedPath, 0, + parent.tokenizedPath.length); + tokenizedPath[parent.tokenizedPath.length] = child; + } + + /* package */ TokenizedPath(String path, String[] tokens) { + this.path = path; + this.tokenizedPath = tokens; + } + + /** + * @return The original path String + */ + public String toString() { + return path; + } + + /** + * The depth (or length) of a path. + */ + public int depth() { + return tokenizedPath.length; + } + + /* package */ String[] getTokens() { + return tokenizedPath; + } + + /** + * From base traverse the filesystem in order to find + * a file that matches the given name. + * + * @param base base File (dir). + * @param cs whether to scan case-sensitively. + * @return File object that points to the file in question or null. + */ + public File findFile(File base, final boolean cs) { + String[] tokens = tokenizedPath; + if (FileUtils.isAbsolutePath(path)) { + if (base == null) { + String[] s = FILE_UTILS.dissect(path); + base = new File(s[0]); + tokens = SelectorUtils.tokenizePathAsArray(s[1]); + } else { + File f = FILE_UTILS.normalize(path); + String s = FILE_UTILS.removeLeadingPath(base, f); + if (s.equals(f.getAbsolutePath())) { + //removing base from path yields no change; path + //not child of base + return null; + } + tokens = SelectorUtils.tokenizePathAsArray(s); + } + } + return findFile(base, tokens, cs); + } + + /** + * Do we have to traverse a symlink when trying to reach path from + * basedir? + * @param base base File (dir). + */ + public boolean isSymlink(File base) { + for (int i = 0; i < tokenizedPath.length; i++) { + try { + if ((base != null + && SYMLINK_UTILS.isSymbolicLink(base, tokenizedPath[i])) + || + (base == null + && SYMLINK_UTILS.isSymbolicLink(tokenizedPath[i])) + ) { + return true; + } + base = new File(base, tokenizedPath[i]); + } catch (java.io.IOException ioe) { + String msg = "IOException caught while checking " + + "for links, couldn't get canonical path!"; + // will be caught and redirected to Ant's logging system + System.err.println(msg); + } + } + return false; + } + + /** + * true if the original paths are equal. + */ + public boolean equals(Object o) { + return o instanceof TokenizedPath + && path.equals(((TokenizedPath) o).path); + } + + public int hashCode() { + return path.hashCode(); + } + + /** + * From base traverse the filesystem in order to find + * a file that matches the given stack of names. + * + * @param base base File (dir) - must not be null. + * @param pathElements array of path elements (dirs...file). + * @param cs whether to scan case-sensitively. + * @return File object that points to the file in question or null. + */ + private static File findFile(File base, final String[] pathElements, + final boolean cs) { + for (int current = 0; current < pathElements.length; current++) { + if (!base.isDirectory()) { + return null; + } + String[] files = base.list(); + if (files == null) { + throw new BuildException("IO error scanning directory " + + base.getAbsolutePath()); + } + boolean found = false; + boolean[] matchCase = cs ? CS_SCAN_ONLY : CS_THEN_NON_CS; + for (int i = 0; !found && i < matchCase.length; i++) { + for (int j = 0; !found && j < files.length; j++) { + if (matchCase[i] + ? files[j].equals(pathElements[current]) + : files[j].equalsIgnoreCase(pathElements[current])) { + base = new File(base, files[j]); + found = true; + } + } + } + if (!found) { + return null; + } + } + return pathElements.length == 0 && !base.isDirectory() ? null : base; + } + + /** + * Creates a TokenizedPattern from the same tokens that make up + * this path. + */ + public TokenizedPattern toPattern() { + return new TokenizedPattern(path, tokenizedPath); + } + +} diff --git a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/selectors/TokenizedPattern.java b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/selectors/TokenizedPattern.java new file mode 100644 index 000000000..bfb666db6 --- /dev/null +++ b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/selectors/TokenizedPattern.java @@ -0,0 +1,177 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.owasp.dependencycheck.org.apache.tools.ant.types.selectors; + +import java.io.File; + +/** + * Provides reusable path pattern matching. PathPattern is preferable + * to equivalent SelectorUtils methods if you need to execute multiple + * matching with the same pattern because here the pattern itself will + * be parsed only once. + * @see SelectorUtils#matchPath(String, String) + * @see SelectorUtils#matchPath(String, String, boolean) + * @since 1.8.0 + */ +public class TokenizedPattern { + + /** + * Instance that holds no tokens at all. + */ + public static final TokenizedPattern EMPTY_PATTERN = + new TokenizedPattern("", new String[0]); + + private final String pattern; + private final String[] tokenizedPattern; + + /** + * Initialize the PathPattern by parsing it. + * @param pattern The pattern to match against. Must not be + * null. + */ + public TokenizedPattern(String pattern) { + this(pattern, SelectorUtils.tokenizePathAsArray(pattern)); + } + + TokenizedPattern(String pattern, String[] tokens) { + this.pattern = pattern; + this.tokenizedPattern = tokens; + } + + /** + * Tests whether or not a given path matches a given pattern. + * + * @param path 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 boolean matchPath(TokenizedPath path, boolean isCaseSensitive) { + return SelectorUtils.matchPath(tokenizedPattern, path.getTokens(), + isCaseSensitive); + } + + /** + * Tests whether or not this pattern matches the start of + * a path. + */ + public boolean matchStartOf(TokenizedPath path, + boolean caseSensitive) { + return SelectorUtils.matchPatternStart(tokenizedPattern, + path.getTokens(), caseSensitive); + } + + /** + * @return The pattern String + */ + public String toString() { + return pattern; + } + + public String getPattern() { + return pattern; + } + + /** + * true if the original patterns are equal. + */ + public boolean equals(Object o) { + return o instanceof TokenizedPattern + && pattern.equals(((TokenizedPattern) o).pattern); + } + + public int hashCode() { + return pattern.hashCode(); + } + + /** + * The depth (or length) of a pattern. + */ + public int depth() { + return tokenizedPattern.length; + } + + /** + * Does the tokenized pattern contain the given string? + */ + public boolean containsPattern(String pat) { + for (int i = 0; i < tokenizedPattern.length; i++) { + if (tokenizedPattern[i].equals(pat)) { + return true; + } + } + return false; + } + + /** + * Returns a new TokenizedPath where all tokens of this pattern to + * the right containing wildcards have been removed + * @return the leftmost part of the pattern without wildcards + */ + public TokenizedPath rtrimWildcardTokens() { + StringBuilder sb = new StringBuilder(); + int newLen = 0; + for (; newLen < tokenizedPattern.length; newLen++) { + if (SelectorUtils.hasWildcards(tokenizedPattern[newLen])) { + break; + } + if (newLen > 0 + && sb.charAt(sb.length() - 1) != File.separatorChar) { + sb.append(File.separator); + } + sb.append(tokenizedPattern[newLen]); + } + if (newLen == 0) { + return TokenizedPath.EMPTY_PATH; + } + String[] newPats = new String[newLen]; + System.arraycopy(tokenizedPattern, 0, newPats, 0, newLen); + return new TokenizedPath(sb.toString(), newPats); + } + + /** + * true if the last token equals the given string. + */ + public boolean endsWith(String s) { + return tokenizedPattern.length > 0 + && tokenizedPattern[tokenizedPattern.length - 1].equals(s); + } + + /** + * Returns a new pattern without the last token of this pattern. + */ + public TokenizedPattern withoutLastToken() { + if (tokenizedPattern.length == 0) { + throw new IllegalStateException("cant strip a token from nothing"); + } else if (tokenizedPattern.length == 1) { + return EMPTY_PATTERN; + } else { + String toStrip = tokenizedPattern[tokenizedPattern.length - 1]; + int index = pattern.lastIndexOf(toStrip); + String[] tokens = new String[tokenizedPattern.length - 1]; + System.arraycopy(tokenizedPattern, 0, tokens, 0, + tokenizedPattern.length - 1); + return new TokenizedPattern(pattern.substring(0, index), tokens); + } + } + +} diff --git a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/selectors/package-info.java b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/selectors/package-info.java new file mode 100644 index 000000000..b5a6ae6d6 --- /dev/null +++ b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/types/selectors/package-info.java @@ -0,0 +1,14 @@ +/** + * + * + * org.owasp.dependencycheck.org.apache.tools.ant.types.selectors + * + * + * This is a copy of classes within Apache Ant. The DirectoryScanner + * is needed by dependency-check. However, we did not want to make + * Ant a dependency. As such, a few files were copied and slightly + * modified to remove any references to the Ant Project class. + * + * + */ +package org.owasp.dependencycheck.org.apache.tools.ant.types.selectors; diff --git a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/util/CollectionUtils.java b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/util/CollectionUtils.java new file mode 100644 index 000000000..fe334e346 --- /dev/null +++ b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/util/CollectionUtils.java @@ -0,0 +1,265 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.owasp.dependencycheck.org.apache.tools.ant.util; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Dictionary; +import java.util.Enumeration; +import java.util.Iterator; +import java.util.List; +import java.util.NoSuchElementException; +import java.util.Vector; + +// CheckStyle:HideUtilityClassConstructorCheck OFF - bc + +/** + * A set of helper methods related to collection manipulation. + * + * @since Ant 1.5 + */ +public class CollectionUtils { + + /** + * Collections.emptyList() is Java5+. + */ + @SuppressWarnings("rawtypes") + @Deprecated + public static final List EMPTY_LIST = Collections.EMPTY_LIST; + + /** + * Please use Vector.equals() or List.equals(). + * @param v1 the first vector. + * @param v2 the second vector. + * @return true if the vectors are equal. + * @since Ant 1.5 + * @deprecated since 1.6.x. + */ + public static boolean equals(Vector v1, Vector v2) { + if (v1 == v2) { + return true; + } + + if (v1 == null || v2 == null) { + return false; + } + + return v1.equals(v2); + } + + /** + * Dictionary does not have an equals. + * Please use Map.equals(). + * + *

Follows the equals contract of Java 2's Map.

+ * @param d1 the first directory. + * @param d2 the second directory. + * @return true if the directories are equal. + * @since Ant 1.5 + * @deprecated since 1.6.x. + */ + public static boolean equals(Dictionary d1, Dictionary d2) { + if (d1 == d2) { + return true; + } + + if (d1 == null || d2 == null) { + return false; + } + + if (d1.size() != d2.size()) { + return false; + } + + Enumeration e1 = d1.keys(); + while (e1.hasMoreElements()) { + Object key = e1.nextElement(); + Object value1 = d1.get(key); + Object value2 = d2.get(key); + if (value2 == null || !value1.equals(value2)) { + return false; + } + } + + // don't need the opposite check as the Dictionaries have the + // same size, so we've also covered all keys of d2 already. + + return true; + } + + /** + * Creates a comma separated list of all values held in the given + * collection. + * + * @since Ant 1.8.0 + */ + public static String flattenToString(Collection c) { + final StringBuilder sb = new StringBuilder(); + for (Object o : c) { + if (sb.length() != 0) { + sb.append(","); + } + sb.append(o); + } + return sb.toString(); + } + + /** + * Dictionary does not know the putAll method. Please use Map.putAll(). + * @param m1 the to directory. + * @param m2 the from directory. + * @since Ant 1.6 + * @deprecated since 1.6.x. + */ + public static void putAll(Dictionary m1, Dictionary m2) { + for (Enumeration it = m2.keys(); it.hasMoreElements();) { + K key = it.nextElement(); + m1.put(key, m2.get(key)); + } + } + + /** + * An empty enumeration. + * @since Ant 1.6 + */ + public static final class EmptyEnumeration implements Enumeration { + /** Constructor for the EmptyEnumeration */ + public EmptyEnumeration() { + } + + /** + * @return false always. + */ + public boolean hasMoreElements() { + return false; + } + + /** + * @return nothing. + * @throws NoSuchElementException always. + */ + public E nextElement() throws NoSuchElementException { + throw new NoSuchElementException(); + } + } + + /** + * Append one enumeration to another. + * Elements are evaluated lazily. + * @param e1 the first enumeration. + * @param e2 the subsequent enumeration. + * @return an enumeration representing e1 followed by e2. + * @since Ant 1.6.3 + */ + public static Enumeration append(Enumeration e1, Enumeration e2) { + return new CompoundEnumeration(e1, e2); + } + + /** + * Adapt the specified Iterator to the Enumeration interface. + * @param iter the Iterator to adapt. + * @return an Enumeration. + */ + public static Enumeration asEnumeration(final Iterator iter) { + return new Enumeration() { + public boolean hasMoreElements() { + return iter.hasNext(); + } + public E nextElement() { + return iter.next(); + } + }; + } + + /** + * Adapt the specified Enumeration to the Iterator interface. + * @param e the Enumeration to adapt. + * @return an Iterator. + */ + public static Iterator asIterator(final Enumeration e) { + return new Iterator() { + public boolean hasNext() { + return e.hasMoreElements(); + } + public E next() { + return e.nextElement(); + } + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + + /** + * Returns a collection containing all elements of the iterator. + * + * @since Ant 1.8.0 + */ + public static Collection asCollection(final Iterator iter) { + List l = new ArrayList(); + while (iter.hasNext()) { + l.add(iter.next()); + } + return l; + } + + private static final class CompoundEnumeration implements Enumeration { + + private final Enumeration e1, e2; + + public CompoundEnumeration(Enumeration e1, Enumeration e2) { + this.e1 = e1; + this.e2 = e2; + } + + public boolean hasMoreElements() { + return e1.hasMoreElements() || e2.hasMoreElements(); + } + + public E nextElement() throws NoSuchElementException { + if (e1.hasMoreElements()) { + return e1.nextElement(); + } else { + return e2.nextElement(); + } + } + + } + + /** + * Counts how often the given Object occurs in the given + * collection using equals() for comparison. + * + * @since Ant 1.8.0 + */ + public static int frequency(Collection c, Object o) { + // same as Collections.frequency introduced with JDK 1.5 + int freq = 0; + if (c != null) { + for (Iterator i = c.iterator(); i.hasNext(); ) { + Object test = i.next(); + if (o == null ? test == null : o.equals(test)) { + freq++; + } + } + } + return freq; + } + +} diff --git a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/util/FileUtils.java b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/util/FileUtils.java new file mode 100644 index 000000000..a097c7991 --- /dev/null +++ b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/util/FileUtils.java @@ -0,0 +1,1667 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.owasp.dependencycheck.org.apache.tools.ant.util; + +import java.io.File; +import java.io.FilenameFilter; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.Reader; +import java.io.Writer; +import java.net.HttpURLConnection; +import java.net.JarURLConnection; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLConnection; +import java.nio.channels.Channel; +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.Stack; +import java.util.StringTokenizer; +import java.util.jar.JarFile; +import org.owasp.dependencycheck.org.apache.tools.ant.BuildException; +import org.owasp.dependencycheck.org.apache.tools.ant.PathTokenizer; +import org.owasp.dependencycheck.org.apache.tools.ant.launch.Locator; +import org.owasp.dependencycheck.org.apache.tools.ant.taskdefs.condition.Os; + +/** + * This class also encapsulates methods which allow Files to be referred to using abstract path names which are + * translated to native system file paths at runtime as well as copying files or setting their last modification time. + * + */ +public class FileUtils { + + private static final int DELETE_RETRY_SLEEP_MILLIS = 10; + private static final int EXPAND_SPACE = 50; + private static final FileUtils PRIMARY_INSTANCE = new FileUtils(); + + //get some non-crypto-grade randomness from various places. + private static Random rand = new Random(System.currentTimeMillis() + + Runtime.getRuntime().freeMemory()); + + private static final boolean ON_NETWARE = Os.isFamily("netware"); + private static final boolean ON_DOS = Os.isFamily("dos"); + private static final boolean ON_WIN9X = Os.isFamily("win9x"); + private static final boolean ON_WINDOWS = Os.isFamily("windows"); + + static final int BUF_SIZE = 8192; + + /** + * The granularity of timestamps under FAT. + */ + public static final long FAT_FILE_TIMESTAMP_GRANULARITY = 2000; + + /** + * The granularity of timestamps under Unix. + */ + public static final long UNIX_FILE_TIMESTAMP_GRANULARITY = 1000; + + /** + * The granularity of timestamps under the NT File System. NTFS has a granularity of 100 nanoseconds, which is less + * than 1 millisecond, so we round this up to 1 millisecond. + */ + public static final long NTFS_FILE_TIMESTAMP_GRANULARITY = 1; + + /** + * A one item cache for fromUri. fromUri is called for each element when parseing ant build files. It is a costly + * operation. This just caches the result of the last call. + */ + private Object cacheFromUriLock = new Object(); + private String cacheFromUriRequest = null; + private String cacheFromUriResponse = null; + + /** + * Factory method. + * + * @return a new instance of FileUtils. + * @deprecated since 1.7. Use getFileUtils instead, FileUtils do not have state. + */ + public static FileUtils newFileUtils() { + return new FileUtils(); + } + + /** + * Method to retrieve The FileUtils, which is shared by all users of this method. + * + * @return an instance of FileUtils. + * @since Ant 1.6.3 + */ + public static FileUtils getFileUtils() { + return PRIMARY_INSTANCE; + } + + /** + * Empty constructor. + */ + protected FileUtils() { + } + + /** + * Get the URL for a file taking into account # characters. + * + * @param file the file whose URL representation is required. + * @return The FileURL value. + * @throws MalformedURLException if the URL representation cannot be formed. + */ + public URL getFileURL(File file) throws MalformedURLException { + return new URL(file.toURI().toASCIIString()); + } + +// /** +// * Convenience method to copy a file from a source to a destination. +// * No filtering is performed. +// * +// * @param sourceFile Name of file to copy from. +// * Must not be null. +// * @param destFile Name of file to copy to. +// * Must not be null. +// * +// * @throws IOException if the copying fails. +// */ +// public void copyFile(String sourceFile, String destFile) throws IOException { +// copyFile(new File(sourceFile), new File(destFile), null, false, false); +// } +// +// /** +// * Convenience method to copy a file from a source to a destination +// * specifying if token filtering must be used. +// * +// * @param sourceFile Name of file to copy from. +// * Must not be null. +// * @param destFile Name of file to copy to. +// * Must not be null. +// * @param filters the collection of filters to apply to this copy. +// * +// * @throws IOException if the copying fails. +// */ +// public void copyFile(String sourceFile, String destFile, FilterSetCollection filters) +// throws IOException { +// copyFile(new File(sourceFile), new File(destFile), filters, false, false); +// } +// +// /** +// * Convenience method to copy a file from a source to a destination specifying if token +// * filtering must be used and if source files may overwrite newer destination files. +// * +// * @param sourceFile Name of file to copy from. Must not be null. +// * @param destFile Name of file to copy to. Must not be null. +// * @param filters the collection of filters to apply to this copy. +// * @param overwrite Whether or not the destination file should be overwritten if it already +// * exists. +// * +// * @throws IOException if the copying fails. +// */ +// public void copyFile(String sourceFile, String destFile, FilterSetCollection filters, +// boolean overwrite) throws IOException { +// copyFile(new File(sourceFile), new File(destFile), filters, overwrite, false); +// } +// +// /** +// * Convenience method to copy a file from a source to a destination +// * specifying if token +// * filtering must be used, if source files may overwrite newer destination +// * files and the last +// * modified time of destFile file should be made equal to +// * the last modified time +// * of sourceFile. +// * +// * @param sourceFile Name of file to copy from. Must not be null. +// * @param destFile Name of file to copy to. Must not be null. +// * @param filters the collection of filters to apply to this copy. +// * @param overwrite Whether or not the destination file should be +// * overwritten if it already exists. +// * @param preserveLastModified Whether or not the last modified time of +// * the resulting file +// * should be set to that of the source file. +// * +// * @throws IOException if the copying fails. +// */ +// public void copyFile(String sourceFile, String destFile, +// FilterSetCollection filters, +// boolean overwrite, boolean preserveLastModified) +// throws IOException { +// copyFile(new File(sourceFile), new File(destFile), filters, overwrite, +// preserveLastModified); +// } +// +// /** +// * Convenience method to copy a file from a source to a destination specifying if token +// * filtering must be used, if source files may overwrite newer destination files and the last +// * modified time of destFile file should be made equal to the last modified time +// * of sourceFile. +// * +// * @param sourceFile Name of file to copy from. Must not be null. +// * @param destFile Name of file to copy to. Must not be null. +// * @param filters the collection of filters to apply to this copy. +// * @param overwrite Whether or not the destination file should be overwritten if it already +// * exists. +// * @param preserveLastModified Whether or not the last modified time of the resulting file +// * should be set to that of the source file. +// * @param encoding the encoding used to read and write the files. +// * +// * @throws IOException if the copying fails. +// * +// * @since Ant 1.5 +// */ +// public void copyFile(String sourceFile, String destFile, +// FilterSetCollection filters, boolean overwrite, +// boolean preserveLastModified, String encoding) throws IOException { +// copyFile(new File(sourceFile), new File(destFile), filters, +// overwrite, preserveLastModified, encoding); +// } +// // CheckStyle:ParameterNumberCheck OFF - bc +// /** +// * Convenience method to copy a file from a source to a +// * destination specifying if token filtering must be used, if +// * filter chains must be used, if source files may overwrite +// * newer destination files and the last modified time of +// * destFile file should be made equal +// * to the last modified time of sourceFile. +// * +// * @param sourceFile Name of file to copy from. +// * Must not be null. +// * @param destFile Name of file to copy to. +// * Must not be null. +// * @param filters the collection of filters to apply to this copy. +// * @param filterChains filterChains to apply during the copy. +// * @param overwrite Whether or not the destination file should be +// * overwritten if it already exists. +// * @param preserveLastModified Whether or not the last modified time of +// * the resulting file should be set to that +// * of the source file. +// * @param encoding the encoding used to read and write the files. +// * @param project the project instance. +// * +// * @throws IOException if the copying fails. +// * +// * @since Ant 1.5 +// */ +// public void copyFile(String sourceFile, String destFile, +// FilterSetCollection filters, Vector filterChains, +// boolean overwrite, boolean preserveLastModified, +// String encoding, Project project) throws IOException { +// copyFile(new File(sourceFile), new File(destFile), filters, filterChains, overwrite, +// preserveLastModified, encoding, project); +// } +// +// /** +// * Convenience method to copy a file from a source to a destination specifying if token +// * filtering must be used, if filter chains must be used, if source files may overwrite newer +// * destination files and the last modified time of destFile file should be made +// * equal to the last modified time of sourceFile. +// * +// * @param sourceFile Name of file to copy from. Must not be null. +// * @param destFile Name of file to copy to. Must not be null. +// * @param filters the collection of filters to apply to this copy. +// * @param filterChains filterChains to apply during the copy. +// * @param overwrite Whether or not the destination file should be overwritten if it already +// * exists. +// * @param preserveLastModified Whether or not the last modified time of the resulting file +// * should be set to that of the source file. +// * @param inputEncoding the encoding used to read the files. +// * @param outputEncoding the encoding used to write the files. +// * @param project the project instance. +// * +// * @throws IOException if the copying fails. +// * +// * @since Ant 1.6 +// */ +// public void copyFile(String sourceFile, String destFile, +// FilterSetCollection filters, Vector filterChains, +// boolean overwrite, boolean preserveLastModified, +// String inputEncoding, String outputEncoding, +// Project project) throws IOException { +// copyFile(new File(sourceFile), new File(destFile), filters, filterChains, overwrite, +// preserveLastModified, inputEncoding, outputEncoding, project); +// } +// +// /** +// * Convenience method to copy a file from a source to a destination. No filtering is performed. +// * +// * @param sourceFile the file to copy from. Must not be null. +// * @param destFile the file to copy to. Must not be null. +// * +// * @throws IOException if the copying fails. +// */ +// public void copyFile(File sourceFile, File destFile) throws IOException { +// copyFile(sourceFile, destFile, null, false, false); +// } +// +// /** +// * Convenience method to copy a file from a source to a destination +// * specifying if token filtering must be used. +// * +// * @param sourceFile the file to copy from. +// * Must not be null. +// * @param destFile the file to copy to. +// * Must not be null. +// * @param filters the collection of filters to apply to this copy. +// * +// * @throws IOException if the copying fails. +// */ +// public void copyFile(File sourceFile, File destFile, FilterSetCollection filters) +// throws IOException { +// copyFile(sourceFile, destFile, filters, false, false); +// } +// +// /** +// * Convenience method to copy a file from a source to a +// * destination specifying if token filtering must be used and if +// * source files may overwrite newer destination files. +// * +// * @param sourceFile the file to copy from. +// * Must not be null. +// * @param destFile the file to copy to. +// * Must not be null. +// * @param filters the collection of filters to apply to this copy. +// * @param overwrite Whether or not the destination file should be +// * overwritten if it already exists. +// * +// * @throws IOException if the copying fails. +// */ +// public void copyFile(File sourceFile, File destFile, FilterSetCollection filters, +// boolean overwrite) throws IOException { +// copyFile(sourceFile, destFile, filters, overwrite, false); +// } +// +// /** +// * Convenience method to copy a file from a source to a +// * destination specifying if token filtering must be used, if +// * source files may overwrite newer destination files and the +// * last modified time of destFile file should be made equal +// * to the last modified time of sourceFile. +// * +// * @param sourceFile the file to copy from. +// * Must not be null. +// * @param destFile the file to copy to. +// * Must not be null. +// * @param filters the collection of filters to apply to this copy. +// * @param overwrite Whether or not the destination file should be +// * overwritten if it already exists. +// * @param preserveLastModified Whether or not the last modified time of +// * the resulting file should be set to that +// * of the source file. +// * +// * @throws IOException if the copying fails. +// */ +// public void copyFile(File sourceFile, File destFile, FilterSetCollection filters, +// boolean overwrite, boolean preserveLastModified) throws IOException { +// copyFile(sourceFile, destFile, filters, overwrite, preserveLastModified, null); +// } +// +// /** +// * Convenience method to copy a file from a source to a destination specifying if token +// * filtering must be used, if source files may overwrite newer destination files, the last +// * modified time of destFile file should be made equal to the last modified time +// * of sourceFile and which character encoding to assume. +// * +// * @param sourceFile the file to copy from. Must not be null. +// * @param destFile the file to copy to. Must not be null. +// * @param filters the collection of filters to apply to this copy. +// * @param overwrite Whether or not the destination file should be overwritten if it already +// * exists. +// * @param preserveLastModified Whether or not the last modified time of the resulting file +// * should be set to that of the source file. +// * @param encoding the encoding used to read and write the files. +// * +// * @throws IOException if the copying fails. +// * +// * @since Ant 1.5 +// */ +// public void copyFile(File sourceFile, File destFile, +// FilterSetCollection filters, boolean overwrite, +// boolean preserveLastModified, String encoding) throws IOException { +// copyFile(sourceFile, destFile, filters, null, overwrite, +// preserveLastModified, encoding, null); +// } +// /** +// * Convenience method to copy a file from a source to a +// * destination specifying if token filtering must be used, if +// * filter chains must be used, if source files may overwrite +// * newer destination files and the last modified time of +// * destFile file should be made equal +// * to the last modified time of sourceFile. +// * +// * @param sourceFile the file to copy from. +// * Must not be null. +// * @param destFile the file to copy to. +// * Must not be null. +// * @param filters the collection of filters to apply to this copy. +// * @param filterChains filterChains to apply during the copy. +// * @param overwrite Whether or not the destination file should be +// * overwritten if it already exists. +// * @param preserveLastModified Whether or not the last modified time of +// * the resulting file should be set to that +// * of the source file. +// * @param encoding the encoding used to read and write the files. +// * @param project the project instance. +// * +// * @throws IOException if the copying fails. +// * +// * @since Ant 1.5 +// */ +// public void copyFile(File sourceFile, File destFile, +// FilterSetCollection filters, Vector filterChains, +// boolean overwrite, boolean preserveLastModified, +// String encoding, Project project) throws IOException { +// copyFile(sourceFile, destFile, filters, filterChains, +// overwrite, preserveLastModified, encoding, encoding, project); +// } +// +// /** +// * Convenience method to copy a file from a source to a +// * destination specifying if token filtering must be used, if +// * filter chains must be used, if source files may overwrite +// * newer destination files and the last modified time of +// * destFile file should be made equal +// * to the last modified time of sourceFile. +// * +// * @param sourceFile the file to copy from. +// * Must not be null. +// * @param destFile the file to copy to. +// * Must not be null. +// * @param filters the collection of filters to apply to this copy. +// * @param filterChains filterChains to apply during the copy. +// * @param overwrite Whether or not the destination file should be +// * overwritten if it already exists. +// * @param preserveLastModified Whether or not the last modified time of +// * the resulting file should be set to that +// * of the source file. +// * @param inputEncoding the encoding used to read the files. +// * @param outputEncoding the encoding used to write the files. +// * @param project the project instance. +// * +// * +// * @throws IOException if the copying fails. +// * +// * @since Ant 1.6 +// */ +// public void copyFile(File sourceFile, File destFile, +// FilterSetCollection filters, Vector filterChains, +// boolean overwrite, boolean preserveLastModified, +// String inputEncoding, String outputEncoding, +// Project project) throws IOException { +// copyFile(sourceFile, destFile, filters, filterChains, overwrite, preserveLastModified, +// false, inputEncoding, outputEncoding, project); +// } +// +// /** +// * Convenience method to copy a file from a source to a +// * destination specifying if token filtering must be used, if +// * filter chains must be used, if source files may overwrite +// * newer destination files and the last modified time of +// * destFile file should be made equal +// * to the last modified time of sourceFile. +// * +// * @param sourceFile the file to copy from. +// * Must not be null. +// * @param destFile the file to copy to. +// * Must not be null. +// * @param filters the collection of filters to apply to this copy. +// * @param filterChains filterChains to apply during the copy. +// * @param overwrite Whether or not the destination file should be +// * overwritten if it already exists. +// * @param preserveLastModified Whether or not the last modified time of +// * the resulting file should be set to that +// * of the source file. +// * @param append whether to append to the destination file. +// * @param inputEncoding the encoding used to read the files. +// * @param outputEncoding the encoding used to write the files. +// * @param project the project instance. +// * +// * +// * @throws IOException if the copying fails. +// * +// * @since Ant 1.8 +// */ +// public void copyFile(File sourceFile, File destFile, +// FilterSetCollection filters, Vector filterChains, +// boolean overwrite, boolean preserveLastModified, +// boolean append, +// String inputEncoding, String outputEncoding, +// Project project) throws IOException { +// copyFile(sourceFile, destFile, filters, filterChains, overwrite, +// preserveLastModified, append, inputEncoding, outputEncoding, +// project, /* force: */ false); +// } +// +// /** +// * Convenience method to copy a file from a source to a +// * destination specifying if token filtering must be used, if +// * filter chains must be used, if source files may overwrite +// * newer destination files and the last modified time of +// * destFile file should be made equal +// * to the last modified time of sourceFile. +// * +// * @param sourceFile the file to copy from. +// * Must not be null. +// * @param destFile the file to copy to. +// * Must not be null. +// * @param filters the collection of filters to apply to this copy. +// * @param filterChains filterChains to apply during the copy. +// * @param overwrite Whether or not the destination file should be +// * overwritten if it already exists. +// * @param preserveLastModified Whether or not the last modified time of +// * the resulting file should be set to that +// * of the source file. +// * @param append whether to append to the destination file. +// * @param inputEncoding the encoding used to read the files. +// * @param outputEncoding the encoding used to write the files. +// * @param project the project instance. +// * @param force whether to overwrite read-only destination files. +// * +// * @throws IOException if the copying fails. +// * +// * @since Ant 1.8.2 +// */ +// public void copyFile(File sourceFile, File destFile, +// FilterSetCollection filters, Vector filterChains, +// boolean overwrite, boolean preserveLastModified, +// boolean append, +// String inputEncoding, String outputEncoding, +// Project project, boolean force) throws IOException { +// ResourceUtils.copyResource(new FileResource(sourceFile), +// new FileResource(destFile), +// filters, filterChains, overwrite, +// preserveLastModified, append, inputEncoding, +// outputEncoding, project, force); +// } +// +// // CheckStyle:ParameterNumberCheck ON +// +// /** +// * Calls File.setLastModified(long time). Originally written to +// * to dynamically bind to that call on Java1.2+. +// * +// * @param file the file whose modified time is to be set +// * @param time the time to which the last modified time is to be set. +// * if this is -1, the current time is used. +// */ +// public void setFileLastModified(File file, long time) { +// ResourceUtils.setLastModified(new FileResource(file), time); +// } + /** + * Interpret the filename as a file relative to the given file unless the filename already represents an absolute + * filename. Differs from new File(file, filename) in that the resulting File's path will always be a + * normalized, absolute pathname. Also, if it is determined that filename is context-relative, + * file will be discarded and the reference will be resolved using available context/state information + * about the filesystem. + * + * @param file the "reference" file for relative paths. This instance must be an absolute file and must not contain + * "./" or "../" sequences (same for \ instead of /). If it is null, this call is equivalent to + * new java.io.File(filename).getAbsoluteFile(). + * + * @param filename a file name. + * + * @return an absolute file. + * @throws java.lang.NullPointerException if filename is null. + */ + public File resolveFile(File file, String filename) { + if (!isAbsolutePath(filename)) { + char sep = File.separatorChar; + filename = filename.replace('/', sep).replace('\\', sep); + if (isContextRelativePath(filename)) { + file = null; + // on cygwin, our current directory can be a UNC; + // assume user.dir is absolute or all hell breaks loose... + String udir = System.getProperty("user.dir"); + if (filename.charAt(0) == sep && udir.charAt(0) == sep) { + filename = dissect(udir)[0] + filename.substring(1); + } + } + filename = new File(file, filename).getAbsolutePath(); + } + return normalize(filename); + } + + /** + * On DOS and NetWare, the evaluation of certain file specifications is context-dependent. These are filenames + * beginning with a single separator (relative to current root directory) and filenames with a drive specification + * and no intervening separator (relative to current directory of the specified root). + * + * @param filename the filename to evaluate. + * @return true if the filename is relative to system context. + * @throws java.lang.NullPointerException if filename is null. + * @since Ant 1.7 + */ + public static boolean isContextRelativePath(String filename) { + if (!(ON_DOS || ON_NETWARE) || filename.length() == 0) { + return false; + } + char sep = File.separatorChar; + filename = filename.replace('/', sep).replace('\\', sep); + char c = filename.charAt(0); + int len = filename.length(); + return (c == sep && (len == 1 || filename.charAt(1) != sep)) + || (Character.isLetter(c) && len > 1 + && filename.charAt(1) == ':' + && (len == 2 || filename.charAt(2) != sep)); + } + + /** + * Verifies that the specified filename represents an absolute path. Differs from new + * java.io.File("filename").isAbsolute() in that a path beginning with a double file separator--signifying a Windows + * UNC--must at minimum match "\\a\b" to be considered an absolute path. + * + * @param filename the filename to be checked. + * @return true if the filename represents an absolute path. + * @throws java.lang.NullPointerException if filename is null. + * @since Ant 1.6.3 + */ + public static boolean isAbsolutePath(String filename) { + int len = filename.length(); + if (len == 0) { + return false; + } + char sep = File.separatorChar; + filename = filename.replace('/', sep).replace('\\', sep); + char c = filename.charAt(0); + if (!(ON_DOS || ON_NETWARE)) { + return (c == sep); + } + if (c == sep) { + // CheckStyle:MagicNumber OFF + if (!(ON_DOS && len > 4 && filename.charAt(1) == sep)) { + return false; + } + // CheckStyle:MagicNumber ON + int nextsep = filename.indexOf(sep, 2); + return nextsep > 2 && nextsep + 1 < len; + } + int colon = filename.indexOf(':'); + return (Character.isLetter(c) && colon == 1 + && filename.length() > 2 && filename.charAt(2) == sep) + || (ON_NETWARE && colon > 0); + } + + /** + * Translate a path into its native (platform specific) format. + *

+ * This method uses PathTokenizer to separate the input path into its components. This handles DOS style paths in a + * relatively sensible way. The file separators are then converted to their platform specific versions. + * + * @param toProcess The path to be translated. May be null. + * + * @return the native version of the specified path or an empty string if the path is null or empty. + * + * @since ant 1.7 + * @see PathTokenizer + */ + public static String translatePath(String toProcess) { + if (toProcess == null || toProcess.length() == 0) { + return ""; + } + StringBuffer path = new StringBuffer(toProcess.length() + EXPAND_SPACE); + PathTokenizer tokenizer = new PathTokenizer(toProcess); + while (tokenizer.hasMoreTokens()) { + String pathComponent = tokenizer.nextToken(); + pathComponent = pathComponent.replace('/', File.separatorChar); + pathComponent = pathComponent.replace('\\', File.separatorChar); + if (path.length() != 0) { + path.append(File.pathSeparatorChar); + } + path.append(pathComponent); + } + return path.toString(); + } + + /** + * "Normalize" the given absolute path. + * + *

+ * This includes: + *

    + *
  • Uppercase the drive letter if there is one.
  • + *
  • Remove redundant slashes after the drive spec.
  • + *
  • Resolve all ./, .\, ../ and ..\ sequences.
  • + *
  • DOS style paths that start with a drive letter will have \ as the separator.
  • + *
+ * Unlike {@link File#getCanonicalPath()} this method specifically does not resolve symbolic links. + * + * @param path the path to be normalized. + * @return the normalized version of the path. + * + * @throws java.lang.NullPointerException if path is null. + */ + public File normalize(final String path) { + Stack s = new Stack(); + String[] dissect = dissect(path); + s.push(dissect[0]); + + StringTokenizer tok = new StringTokenizer(dissect[1], File.separator); + while (tok.hasMoreTokens()) { + String thisToken = tok.nextToken(); + if (".".equals(thisToken)) { + continue; + } + if ("..".equals(thisToken)) { + if (s.size() < 2) { + // Cannot resolve it, so skip it. + return new File(path); + } + s.pop(); + } else { // plain component + s.push(thisToken); + } + } + StringBuffer sb = new StringBuffer(); + final int size = s.size(); + for (int i = 0; i < size; i++) { + if (i > 1) { + // not before the filesystem root and not after it, since root + // already contains one + sb.append(File.separatorChar); + } + sb.append(s.elementAt(i)); + } + return new File(sb.toString()); + } + + /** + * Dissect the specified absolute path. + * + * @param path the path to dissect. + * @return String[] {root, remaining path}. + * @throws java.lang.NullPointerException if path is null. + * @since Ant 1.7 + */ + public String[] dissect(String path) { + char sep = File.separatorChar; + path = path.replace('/', sep).replace('\\', sep); + + // make sure we are dealing with an absolute path + if (!isAbsolutePath(path)) { + throw new BuildException(path + " is not an absolute path"); + } + String root = null; + int colon = path.indexOf(':'); + if (colon > 0 && (ON_DOS || ON_NETWARE)) { + + int next = colon + 1; + root = path.substring(0, next); + char[] ca = path.toCharArray(); + root += sep; + //remove the initial separator; the root has it. + next = (ca[next] == sep) ? next + 1 : next; + + StringBuffer sbPath = new StringBuffer(); + // Eliminate consecutive slashes after the drive spec: + for (int i = next; i < ca.length; i++) { + if (ca[i] != sep || ca[i - 1] != sep) { + sbPath.append(ca[i]); + } + } + path = sbPath.toString(); + } else if (path.length() > 1 && path.charAt(1) == sep) { + // UNC drive + int nextsep = path.indexOf(sep, 2); + nextsep = path.indexOf(sep, nextsep + 1); + root = (nextsep > 2) ? path.substring(0, nextsep + 1) : path; + path = path.substring(root.length()); + } else { + root = File.separator; + path = path.substring(1); + } + return new String[]{root, path}; + } + + /** + * Returns a VMS String representation of a File object. This is useful since the JVM by default + * internally converts VMS paths to Unix style. The returned String is always an absolute path. + * + * @param f The File to get the VMS path for. + * @return The absolute VMS path to f. + */ + public String toVMSPath(File f) { + // format: "DEVICE:[DIR.SUBDIR]FILE" + String osPath; + String path = normalize(f.getAbsolutePath()).getPath(); + String name = f.getName(); + boolean isAbsolute = path.charAt(0) == File.separatorChar; + // treat directories specified using .DIR syntax as files + // CheckStyle:MagicNumber OFF + boolean isDirectory = f.isDirectory() + && !name.regionMatches(true, name.length() - 4, ".DIR", 0, 4); + // CheckStyle:MagicNumber ON + String device = null; + StringBuffer directory = null; + String file = null; + + int index = 0; + + if (isAbsolute) { + index = path.indexOf(File.separatorChar, 1); + if (index == -1) { + return path.substring(1) + ":[000000]"; + } + device = path.substring(1, index++); + } + if (isDirectory) { + directory = new StringBuffer(path.substring(index).replace(File.separatorChar, '.')); + } else { + int dirEnd = path.lastIndexOf(File.separatorChar, path.length()); + if (dirEnd == -1 || dirEnd < index) { + file = path.substring(index); + } else { + directory = new StringBuffer(path.substring(index, dirEnd). + replace(File.separatorChar, '.')); + index = dirEnd + 1; + if (path.length() > index) { + file = path.substring(index); + } + } + } + if (!isAbsolute && directory != null) { + directory.insert(0, '.'); + } + osPath = ((device != null) ? device + ":" : "") + + ((directory != null) ? "[" + directory + "]" : "") + + ((file != null) ? file : ""); + return osPath; + } + + /** + * Create a File object for a temporary file in a given directory. Without actually creating the file. + * + *

+ * The file denoted by the returned abstract pathname did not exist before this method was invoked, any subsequent + * invocation of this method will yield a different file name. + *

+ *

+ * The filename is prefixNNNNNsuffix where NNNN is a random number. + *

+ * + * @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 specified. + * + * @deprecated since ant 1.7.1 use createTempFile(String, String, File, boolean, boolean) instead. + * @return a File reference to the new, nonexistent temporary file. + */ + public File createTempFile(String prefix, String suffix, File parentDir) { + return createTempFile(prefix, suffix, parentDir, false, false); + } + + private static final String NULL_PLACEHOLDER = "null"; + + /** + * Create a temporary file in a given directory. + * + *

+ * The file denoted by the returned abstract pathname did not exist before this method was invoked, any subsequent + * invocation of this method will yield a different file name.

+ * + * @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 specified. + * @param deleteOnExit whether to set the tempfile for deletion on normal VM exit. + * @param createFile true if the file must actually be created. If false chances exist that a file with the same + * name is created in the time between invoking this method and the moment the file is actually created. If possible + * set to true. + * + * @return a File reference to the new temporary file. + * @since Ant 1.7.1 + */ + public File createTempFile(String prefix, String suffix, File parentDir, + boolean deleteOnExit, boolean createFile) { + File result = null; + String parent = (parentDir == null) + ? System.getProperty("java.io.tmpdir") + : parentDir.getPath(); + if (prefix == null) { + prefix = NULL_PLACEHOLDER; + } + if (suffix == null) { + suffix = NULL_PLACEHOLDER; + } + + if (createFile) { + try { + result = File.createTempFile(prefix, suffix, new File(parent)); + } catch (IOException e) { + throw new BuildException("Could not create tempfile in " + + parent, e); + } + } else { + DecimalFormat fmt = new DecimalFormat("#####"); + synchronized (rand) { + do { + result = new File(parent, prefix + + fmt.format(rand.nextInt(Integer.MAX_VALUE)) + suffix); + } while (result.exists()); + } + } + + if (deleteOnExit) { + result.deleteOnExit(); + } + return result; + } + + /** + * Create a File object for a temporary file in a given directory. Without actually creating the file. + * + *

+ * The file denoted by the returned abstract pathname did not exist before this method was invoked, any subsequent + * invocation of this method will yield a different file name. + *

+ *

+ * The filename is prefixNNNNNsuffix where NNNN is a random number. + *

+ * + * @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 specified. + * @param deleteOnExit whether to set the tempfile for deletion on normal VM exit. + * + * @deprecated since ant 1.7.1 use createTempFile(String, String, File, boolean, boolean) instead. + * @return a File reference to the new, nonexistent temporary file. + */ + public File createTempFile(String prefix, String suffix, + File parentDir, boolean deleteOnExit) { + return createTempFile(prefix, suffix, parentDir, deleteOnExit, false); + } + +// /** +// * Compares the contents of two files. +// * +// * @param f1 the file whose content is to be compared. +// * @param f2 the other file whose content is to be compared. +// * +// * @return true if the content of the files is the same. +// * +// * @throws IOException if the files cannot be read. +// */ +// public boolean contentEquals(File f1, File f2) throws IOException { +// return contentEquals(f1, f2, false); +// } +// +// /** +// * Compares the contents of two files. +// * +// * @param f1 the file whose content is to be compared. +// * @param f2 the other file whose content is to be compared. +// * @param textfile true if the file is to be treated as a text file and +// * differences in kind of line break are to be ignored. +// * +// * @return true if the content of the files is the same. +// * +// * @throws IOException if the files cannot be read. +// * @since Ant 1.6.3 +// */ +// public boolean contentEquals(File f1, File f2, boolean textfile) throws IOException { +// return ResourceUtils.contentEquals(new FileResource(f1), new FileResource(f2), textfile); +// } + /** + * This was originally an emulation of {@link File#getParentFile} for JDK 1.1, but it is now implemented using that + * method (Ant 1.6.3 onwards). + * + * @param f the file whose parent is required. + * @return the given file's parent, or null if the file does not have a parent. + * @since 1.10 + * @deprecated since 1.7. Just use {@link File#getParentFile} directly. + */ + public File getParentFile(File f) { + return (f == null) ? null : f.getParentFile(); + } + + /** + * Read from reader till EOF. + * + * @param rdr the reader from which to read. + * @return the contents read out of the given reader. + * + * @throws IOException if the contents could not be read out from the reader. + */ + public static String readFully(Reader rdr) throws IOException { + return readFully(rdr, BUF_SIZE); + } + + /** + * Read from reader till EOF. + * + * @param rdr the reader from which to read. + * @param bufferSize the buffer size to use when reading. + * + * @return the contents read out of the given reader. + * + * @throws IOException if the contents could not be read out from the reader. + */ + public static String readFully(Reader rdr, int bufferSize) + throws IOException { + if (bufferSize <= 0) { + throw new IllegalArgumentException("Buffer size must be greater " + + "than 0"); + } + final char[] buffer = new char[bufferSize]; + int bufferLength = 0; + StringBuffer textBuffer = null; + while (bufferLength != -1) { + bufferLength = rdr.read(buffer); + if (bufferLength > 0) { + textBuffer = (textBuffer == null) ? new StringBuffer() : textBuffer; + textBuffer.append(new String(buffer, 0, bufferLength)); + } + } + return (textBuffer == null) ? null : textBuffer.toString(); + } + + /** + * Safe read fully - do not return a null for an empty reader. + * + * @param reader the input to read from. + * @return the string. + * @throws IOException if unable to read from reader. + * @since Ant 1.7.1 + */ + public static String safeReadFully(Reader reader) throws IOException { + String ret = readFully(reader); + return ret == null ? "" : ret; + } + + /** + * This was originally an emulation of File.createNewFile for JDK 1.1, but it is now implemented using that method + * (Ant 1.6.3 onwards). + * + *

+ * This method has historically not guaranteed that the operation was atomic. In its current + * implementation it is. + * + * @param f the file to be created. + * @return true if the file did not exist already. + * @throws IOException on error. + * @since Ant 1.5 + */ + public boolean createNewFile(File f) throws IOException { + return f.createNewFile(); + } + + /** + * Create a new file, optionally creating parent directories. + * + * @param f the file to be created. + * @param mkdirs boolean whether to create parent directories. + * @return true if the file did not exist already. + * @throws IOException on error. + * @since Ant 1.6.3 + */ + public boolean createNewFile(File f, boolean mkdirs) throws IOException { + File parent = f.getParentFile(); + if (mkdirs && !(parent.exists())) { + parent.mkdirs(); + } + return f.createNewFile(); + } + + /** + * 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. + * + * @return true if the file is a symbolic link. + * @throws IOException on error. + * @since Ant 1.5 + * @deprecated use SymbolicLinkUtils instead + */ + public boolean isSymbolicLink(File parent, String name) + throws IOException { + SymbolicLinkUtils u = SymbolicLinkUtils.getSymbolicLinkUtils(); + if (parent == null) { + return u.isSymbolicLink(name); + } + return u.isSymbolicLink(parent, name); + } + + /** + * Removes a leading path from a second path. + * + * @param leading The leading path, must not be null, must be absolute. + * @param path The path to remove from, must not be null, must be absolute. + * + * @return path's normalized absolute if it doesn't start with leading; path's path with leading's path removed + * otherwise. + * + * @since Ant 1.5 + */ + public String removeLeadingPath(File leading, File path) { + String l = normalize(leading.getAbsolutePath()).getAbsolutePath(); + String p = normalize(path.getAbsolutePath()).getAbsolutePath(); + if (l.equals(p)) { + return ""; + } + // ensure that l ends with a / + // so we never think /foo was a parent directory of /foobar + if (!l.endsWith(File.separator)) { + l += File.separator; + } + return (p.startsWith(l)) ? p.substring(l.length()) : p; + } + + /** + * Learn whether one path "leads" another. + * + * @param leading The leading path, must not be null, must be absolute. + * @param path The path to remove from, must not be null, must be absolute. + * @return true if path starts with leading; false otherwise. + * @since Ant 1.7 + */ + public boolean isLeadingPath(File leading, File path) { + String l = normalize(leading.getAbsolutePath()).getAbsolutePath(); + String p = normalize(path.getAbsolutePath()).getAbsolutePath(); + if (l.equals(p)) { + return true; + } + // ensure that l ends with a / + // so we never think /foo was a parent directory of /foobar + if (!l.endsWith(File.separator)) { + l += File.separator; + } + return p.startsWith(l); + } + + /** + * Constructs a file: URI that represents the external form of the given pathname. + * + *

+ * Will be an absolute URI if the given path is absolute.

+ * + *

+ * This code encodes non ASCII characters too.

+ * + *

+ * The coding of the output is the same as what File.toURI().toASCIIString() produces

+ * + * See dt-sysid + * which makes some mention of how characters not supported by URI Reference syntax should be escaped. + * + * @param path the path in the local file system. + * @return the URI version of the local path. + * @since Ant 1.6 + */ + public String toURI(String path) { + return new File(path).toURI().toASCIIString(); + } + + /** + * Constructs a file path from a file: URI. + * + *

+ * Will be an absolute path if the given URI is absolute.

+ * + *

+ * Swallows '%' that are not followed by two characters, doesn't deal with non-ASCII characters.

+ * + * @param uri the URI designating a file in the local filesystem. + * @return the local file system path for the file. + * @since Ant 1.6 + */ + public String fromURI(String uri) { + synchronized (cacheFromUriLock) { + if (uri.equals(cacheFromUriRequest)) { + return cacheFromUriResponse; + } + String path = Locator.fromURI(uri); + String ret = isAbsolutePath(path) ? normalize(path).getAbsolutePath() : path; + cacheFromUriRequest = uri; + cacheFromUriResponse = ret; + return ret; + } + } + + /** + * Compares two filenames. + * + *

+ * Unlike java.io.File#equals this method will try to compare the absolute paths and "normalize" the + * filenames before comparing them.

+ * + * @param f1 the file whose name is to be compared. + * @param f2 the other file whose name is to be compared. + * + * @return true if the file are for the same file. + * + * @since Ant 1.5.3 + */ + public boolean fileNameEquals(File f1, File f2) { + return normalize(f1.getAbsolutePath()).getAbsolutePath().equals( + normalize(f2.getAbsolutePath()).getAbsolutePath()); + } + + /** + * Are the two File instances pointing to the same object on the file system? + * + * @since Ant 1.8.2 + */ + public boolean areSame(File f1, File f2) throws IOException { + if (f1 == null && f2 == null) { + return true; + } + if (f1 == null || f2 == null) { + return false; + } + File f1Normalized = normalize(f1.getAbsolutePath()); + File f2Normalized = normalize(f2.getAbsolutePath()); + return f1Normalized.equals(f2Normalized) + || f1Normalized.getCanonicalFile().equals(f2Normalized + .getCanonicalFile()); + } +// +// /** +// * Renames a file, even if that involves crossing file system boundaries. +// * +// *

+// * This will remove to (if it exists), ensure that to's parent directory exists and move +// * from, which involves deleting from as well.

+// * +// * @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. +// * +// * @since Ant 1.6 +// */ +// public void rename(File from, File to) throws IOException { +// // identical logic lives in Move.renameFile(): +// from = normalize(from.getAbsolutePath()).getCanonicalFile(); +// to = normalize(to.getAbsolutePath()); +// if (!from.exists()) { +// System.err.println("Cannot rename nonexistent file " + from); +// return; +// } +// if (from.getAbsolutePath().equals(to.getAbsolutePath())) { +// System.err.println("Rename of " + from + " to " + to + " is a no-op."); +// return; +// } +// if (to.exists() && !(areSame(from, to) || tryHardToDelete(to))) { +// throw new IOException("Failed to delete " + to + " while trying to rename " + from); +// } +// File parent = to.getParentFile(); +// if (parent != null && !parent.isDirectory() +// && !(parent.mkdirs() || parent.isDirectory())) { +// throw new IOException("Failed to create directory " + parent +// + " while trying to rename " + from); +// } +// if (!from.renameTo(to)) { +// copyFile(from, to); +// if (!tryHardToDelete(from)) { +// throw new IOException("Failed to delete " + from + " while trying to rename it."); +// } +// } +// } + + /** + * Get the granularity of file timestamps. The choice is made based on OS, which is incorrect--it should really be + * by filesystem. We do not have an easy way to probe for file systems, however, so this heuristic gives us a decent + * default. + * + * @return the difference, in milliseconds, which two file timestamps must have in order for the two files to be + * considered to have different timestamps. + */ + public long getFileTimestampGranularity() { + if (ON_WIN9X) { + return FAT_FILE_TIMESTAMP_GRANULARITY; + } + if (ON_WINDOWS) { + return NTFS_FILE_TIMESTAMP_GRANULARITY; + } + if (ON_DOS) { + return FAT_FILE_TIMESTAMP_GRANULARITY; + } + return UNIX_FILE_TIMESTAMP_GRANULARITY; + } + + /** + * test whether a file or directory exists, with an error in the upper/lower case spelling of the name. Using this + * method is only interesting on case insensitive file systems (Windows).
+ * It will return true only if 3 conditions are met : + *
+ *
    + *
  • operating system is case insensitive
  • + *
  • file exists
  • + *
  • actual name from directory reading is different from the supplied argument
  • + *
+ *
+ * the purpose is to identify files or directories on case-insensitive filesystems whose case is not what is + * expected.
+ * Possibly to rename them afterwards to the desired upper/lowercase combination. + *
+ * + * @param localFile file to test + * @return true if the file exists and the case of the actual file is not the case of the parameter + * @since Ant 1.7.1 + */ + public boolean hasErrorInCase(File localFile) { + localFile = normalize(localFile.getAbsolutePath()); + if (!localFile.exists()) { + return false; + } + final String localFileName = localFile.getName(); + FilenameFilter ff = new FilenameFilter() { + public boolean accept(File dir, String name) { + return name.equalsIgnoreCase(localFileName) && (!name.equals(localFileName)); + } + }; + String[] names = localFile.getParentFile().list(ff); + return names != null && names.length == 1; + } + + /** + * Returns true if the source is older than the dest. If the dest file does not exist, then the test returns false; + * it is implicitly not up do date. + * + * @param source source file (should be the older). + * @param dest dest file (should be the newer). + * @param granularity an offset added to the source time. + * @return true if the source is older than the dest after accounting for granularity. + * @since Ant 1.6.3 + */ + public boolean isUpToDate(File source, File dest, long granularity) { + //do a check for the destination file existing + if (!dest.exists()) { + //if it does not, then the file is not up to date. + return false; + } + long sourceTime = source.lastModified(); + long destTime = dest.lastModified(); + return isUpToDate(sourceTime, destTime, granularity); + } + + /** + * Returns true if the source is older than the dest. + * + * @param source source file (should be the older). + * @param dest dest file (should be the newer). + * @return true if the source is older than the dest, taking the granularity into account. + * @since Ant 1.6.3 + */ + public boolean isUpToDate(File source, File dest) { + return isUpToDate(source, dest, getFileTimestampGranularity()); + } + + /** + * Compare two timestamps for being up to date using the specified granularity. + * + * @param sourceTime timestamp of source file. + * @param destTime timestamp of dest file. + * @param granularity os/filesys granularity. + * @return true if the dest file is considered up to date. + */ + public boolean isUpToDate(long sourceTime, long destTime, long granularity) { + return destTime != -1 && destTime >= sourceTime + granularity; + } + + /** + * Compare two timestamps for being up to date using the current granularity. + * + * @param sourceTime timestamp of source file. + * @param destTime timestamp of dest file. + * @return true if the dest file is considered up to date. + */ + public boolean isUpToDate(long sourceTime, long destTime) { + return isUpToDate(sourceTime, destTime, getFileTimestampGranularity()); + } + + /** + * Close a Writer without throwing any exception if something went wrong. Do not attempt to close it if the argument + * is null. + * + * @param device output writer, can be null. + */ + public static void close(Writer device) { + if (null != device) { + try { + device.close(); + } catch (IOException e) { + //ignore + } + } + } + + /** + * Close a Reader without throwing any exception if something went wrong. Do not attempt to close it if the argument + * is null. + * + * @param device Reader, can be null. + */ + public static void close(Reader device) { + if (null != device) { + try { + device.close(); + } catch (IOException e) { + //ignore + } + } + } + + /** + * Close a stream without throwing any exception if something went wrong. Do not attempt to close it if the argument + * is null. + * + * @param device stream, can be null. + */ + public static void close(OutputStream device) { + if (null != device) { + try { + device.close(); + } catch (IOException e) { + //ignore + } + } + } + + /** + * Close a stream without throwing any exception if something went wrong. Do not attempt to close it if the argument + * is null. + * + * @param device stream, can be null. + */ + public static void close(InputStream device) { + if (null != device) { + try { + device.close(); + } catch (IOException e) { + //ignore + } + } + } + + /** + * Close a Channel without throwing any exception if something went wrong. Do not attempt to close it if the + * argument is null. + * + * @param device channel, can be null. + * @since Ant 1.8.0 + */ + public static void close(Channel device) { + if (null != device) { + try { + device.close(); + } catch (IOException e) { + //ignore + } + } + } + + /** + * Closes an URLConnection if its concrete implementation provides a way to close it that Ant knows of. + * + * @param conn connection, can be null + * @since Ant 1.8.0 + */ + public static void close(URLConnection conn) { + if (conn != null) { + try { + if (conn instanceof JarURLConnection) { + JarURLConnection juc = (JarURLConnection) conn; + JarFile jf = juc.getJarFile(); + jf.close(); + jf = null; + } else if (conn instanceof HttpURLConnection) { + ((HttpURLConnection) conn).disconnect(); + } + } catch (IOException exc) { + //ignore + } + } + } + + /** + * Delete the file with {@link File#delete()} if the argument is not null. Do nothing on a null argument. + * + * @param file file to delete. + */ + public static void delete(File file) { + if (file != null) { + file.delete(); + } + } + + /** + * Accommodate Windows bug encountered in both Sun and IBM JDKs. Others possible. If the delete does not work, call + * System.gc(), wait a little and try again. + * + * @return whether deletion was successful + * @since Ant 1.8.0 + */ + public boolean tryHardToDelete(File f) { + return tryHardToDelete(f, ON_WINDOWS); + } + + /** + * If delete does not work, call System.gc() if asked to, wait a little and try again. + * + * @return whether deletion was successful + * @since Ant 1.8.3 + */ + public boolean tryHardToDelete(File f, boolean runGC) { + if (!f.delete()) { + if (runGC) { + System.gc(); + } + try { + Thread.sleep(DELETE_RETRY_SLEEP_MILLIS); + } catch (InterruptedException ex) { + // Ignore Exception + } + return f.delete(); + } + return true; + } + + /** + * Calculates the relative path between two files. + *

+ * Implementation note:
This function may throw an IOException if an I/O error occurs because its use of the + * canonical pathname may require filesystem queries. + *

+ * + * @param fromFile the File to calculate the path from + * @param toFile the File to calculate the path to + * @return the relative path between the files + * @throws Exception for undocumented reasons + * @see File#getCanonicalPath() + * + * @since Ant 1.7 + */ + public static String getRelativePath(File fromFile, File toFile) throws Exception { + String fromPath = fromFile.getCanonicalPath(); + String toPath = toFile.getCanonicalPath(); + + // build the path stack info to compare + String[] fromPathStack = getPathStack(fromPath); + String[] toPathStack = getPathStack(toPath); + + if (0 < toPathStack.length && 0 < fromPathStack.length) { + if (!fromPathStack[0].equals(toPathStack[0])) { + // not the same device (would be "" on Linux/Unix) + + return getPath(Arrays.asList(toPathStack)); + } + } else { + // no comparison possible + return getPath(Arrays.asList(toPathStack)); + } + + int minLength = Math.min(fromPathStack.length, toPathStack.length); + int same = 1; // Used outside the for loop + + // get index of parts which are equal + for (; + same < minLength && fromPathStack[same].equals(toPathStack[same]); + same++) { + // Do nothing + } + + List relativePathStack = new ArrayList(); + + // if "from" part is longer, fill it up with ".." + // to reach path which is equal to both paths + for (int i = same; i < fromPathStack.length; i++) { + relativePathStack.add(".."); + } + + // fill it up path with parts which were not equal + for (int i = same; i < toPathStack.length; i++) { + relativePathStack.add(toPathStack[i]); + } + + return getPath(relativePathStack); + } + + /** + * Gets all names of the path as an array of Strings. + * + * @param path to get names from + * @return Strings, never null + * + * @since Ant 1.7 + */ + public static String[] getPathStack(String path) { + String normalizedPath = path.replace(File.separatorChar, '/'); + + return normalizedPath.split("/"); + } + + /** + * Gets path from a List of Strings. + * + * @param pathStack List of Strings to be concatenated as a path. + * @return String, never null + * + * @since Ant 1.7 + */ + public static String getPath(List pathStack) { + // can safely use '/' because Windows understands '/' as separator + return getPath(pathStack, '/'); + } + + /** + * Gets path from a List of Strings. + * + * @param pathStack List of Strings to be concated as a path. + * @param separatorChar char to be used as separator between names in path + * @return String, never null + * + * @since Ant 1.7 + */ + public static String getPath(final List pathStack, final char separatorChar) { + final StringBuffer buffer = new StringBuffer(); + + final Iterator iter = pathStack.iterator(); + if (iter.hasNext()) { + buffer.append(iter.next()); + } + while (iter.hasNext()) { + buffer.append(separatorChar); + buffer.append(iter.next()); + } + return buffer.toString(); + } + + /** + * Get the default encoding. This is done by opening an InputStreamReader on a dummy InputStream and getting the + * encoding. Could use System.getProperty("file.encoding"), but cannot see where this is documented. + * + * @return the default file encoding. + */ + public String getDefaultEncoding() { + InputStreamReader is = new InputStreamReader( + new InputStream() { + public int read() { + return -1; + } + }); + try { + return is.getEncoding(); + } finally { + close(is); + } + } +} diff --git a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/util/SymbolicLinkUtils.java b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/util/SymbolicLinkUtils.java new file mode 100644 index 000000000..81aae5541 --- /dev/null +++ b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/util/SymbolicLinkUtils.java @@ -0,0 +1,291 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.owasp.dependencycheck.org.apache.tools.ant.util; + +import java.io.File; +import java.io.FilenameFilter; +import java.io.IOException; +//import org.apache.tools.ant.Task; +//import org.apache.tools.ant.taskdefs.Execute; + +/** + * Contains methods related to symbolic links - or what Ant thinks is a symbolic link based on the absent support for + * them in Java. + * + * @since Ant 1.8.0 + */ +public class SymbolicLinkUtils { + + private static final FileUtils FILE_UTILS = FileUtils.getFileUtils(); + + /** + * Shared instance. + */ + private static final SymbolicLinkUtils PRIMARY_INSTANCE + = new SymbolicLinkUtils(); + + /** + * Method to retrieve The SymbolicLinkUtils, which is shared by all users of this method. + * + * @return an instance of SymbolicLinkUtils. + */ + public static SymbolicLinkUtils getSymbolicLinkUtils() { + // keep the door open for Java X.Y specific subclass if symbolic + // links ever become supported in the classlib + return PRIMARY_INSTANCE; + } + + /** + * Empty constructor. + */ + protected SymbolicLinkUtils() { + } + + /** + * 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 file the file to test. Must not be null. + * + * @return true if the file is a symbolic link. + * @throws IOException on error. + */ + public boolean isSymbolicLink(File file) throws IOException { + return isSymbolicLink(file.getParentFile(), file.getName()); + } + + /** + * 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 name the name of the file to test. + * + * @return true if the file is a symbolic link. + * @throws IOException on error. + */ + public boolean isSymbolicLink(String name) throws IOException { + return isSymbolicLink(new File(name)); + } + + /** + * 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. + * + * @return true if the file is a symbolic link. + * @throws IOException on error. + */ + public boolean isSymbolicLink(File parent, String name) + throws IOException { + File toTest = parent != null + ? new File(parent.getCanonicalPath(), name) + : new File(name); + return !toTest.getAbsolutePath().equals(toTest.getCanonicalPath()); + } + + /** + * Checks whether a given file is a broken symbolic link. + * + *

+ * It doesn't really test for symbolic links but whether Java reports that the File doesn't exist but its parent's + * child list contains it--this may lead to false positives on some platforms.

+ * + *

+ * Note that #isSymbolicLink returns false if this method returns true since Java won't produce a canonical name + * different from the abolute one if the link is broken.

+ * + * @param name the name of the file to test. + * + * @return true if the file is a broken symbolic link. + * @throws IOException on error. + */ + public boolean isDanglingSymbolicLink(String name) throws IOException { + return isDanglingSymbolicLink(new File(name)); + } + + /** + * Checks whether a given file is a broken symbolic link. + * + *

+ * It doesn't really test for symbolic links but whether Java reports that the File doesn't exist but its parent's + * child list contains it--this may lead to false positives on some platforms.

+ * + *

+ * Note that #isSymbolicLink returns false if this method returns true since Java won't produce a canonical name + * different from the abolute one if the link is broken.

+ * + * @param file the file to test. + * + * @return true if the file is a broken symbolic link. + * @throws IOException on error. + */ + public boolean isDanglingSymbolicLink(File file) throws IOException { + return isDanglingSymbolicLink(file.getParentFile(), file.getName()); + } + + /** + * Checks whether a given file is a broken symbolic link. + * + *

+ * It doesn't really test for symbolic links but whether Java reports that the File doesn't exist but its parent's + * child list contains it--this may lead to false positives on some platforms.

+ * + *

+ * Note that #isSymbolicLink returns false if this method returns true since Java won't produce a canonical name + * different from the abolute one if the link is broken.

+ * + * @param parent the parent directory of the file to test + * @param name the name of the file to test. + * + * @return true if the file is a broken symbolic link. + * @throws IOException on error. + */ + public boolean isDanglingSymbolicLink(File parent, String name) + throws IOException { + File f = new File(parent, name); + if (!f.exists()) { + final String localName = f.getName(); + String[] c = parent.list(new FilenameFilter() { + public boolean accept(File d, String n) { + return localName.equals(n); + } + }); + return c != null && c.length > 0; + } + return false; + } +// +// /** +// * Delete a symlink (without deleting the associated resource). +// * +// *

This is a utility method that removes a unix symlink without +// * removing the resource that the symlink points to. If it is +// * accidentally invoked on a real file, the real file will not be +// * harmed, but silently ignored.

+// * +// *

Normally this method works by +// * getting the canonical path of the link, using the canonical path to +// * rename the resource (breaking the link) and then deleting the link. +// * The resource is then returned to its original name inside a finally +// * block to ensure that the resource is unharmed even in the event of +// * an exception.

+// * +// *

There may be cases where the algorithm described above doesn't work, +// * in that case the method tries to use the native "rm" command on +// * the symlink instead.

+// * +// * @param link A File object of the symlink to delete. +// * @param task An Ant Task required if "rm" needs to be invoked. +// * +// * @throws IOException If calls to File.rename, +// * File.delete or File.getCanonicalPath +// * fail. +// * @throws BuildException if the execution of "rm" failed. +// */ +// public void deleteSymbolicLink(File link, Task task) +// throws IOException { +// if (isDanglingSymbolicLink(link)) { +// if (!link.delete()) { +// throw new IOException("failed to remove dangling symbolic link " +// + link); +// } +// return; +// } +// +// if (!isSymbolicLink(link)) { +// // plain file, not a link +// return; +// } +// +// if (!link.exists()) { +// throw new FileNotFoundException("No such symbolic link: " + link); +// } +// +// // find the resource of the existing link: +// File target = link.getCanonicalFile(); +// +// // no reason to try the renaming algorithm if we aren't allowed to +// // write to the target's parent directory. Let's hope that +// // File.canWrite works on all platforms. +// +// if (task == null || target.getParentFile().canWrite()) { +// +// // rename the resource, thus breaking the link: +// File temp = FILE_UTILS.createTempFile("symlink", ".tmp", +// target.getParentFile(), false, +// false); +// +// if (FILE_UTILS.isLeadingPath(target, link)) { +// // link points to a parent directory, renaming the parent +// // will rename the file +// link = new File(temp, +// FILE_UTILS.removeLeadingPath(target, link)); +// } +// +// boolean renamedTarget = false; +// try { +// try { +// FILE_UTILS.rename(target, temp); +// renamedTarget = true; +// } catch (IOException e) { +// throw new IOException("Couldn't rename resource when " +// + "attempting to delete '" + link +// + "'. Reason: " + e.getMessage()); +// } +// // delete the (now) broken link: +// if (!link.delete()) { +// throw new IOException("Couldn't delete symlink: " +// + link +// + " (was it a real file? is this " +// + "not a UNIX system?)"); +// } +// } finally { +// if (renamedTarget) { +// // return the resource to its original name: +// try { +// FILE_UTILS.rename(temp, target); +// } catch (IOException e) { +// throw new IOException("Couldn't return resource " +// + temp +// + " to its original name: " +// + target.getAbsolutePath() +// + ". Reason: " + e.getMessage() +// + "\n THE RESOURCE'S NAME ON DISK" +// + " HAS BEEN CHANGED BY THIS" +// + " ERROR!\n"); +// } +// } +// } +// } else { +// Execute.runCommand(task, +// new String[] {"rm", link.getAbsolutePath()}); +// } +// } + +} diff --git a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/util/VectorSet.java b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/util/VectorSet.java new file mode 100644 index 000000000..36617fe00 --- /dev/null +++ b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/util/VectorSet.java @@ -0,0 +1,242 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.owasp.dependencycheck.org.apache.tools.ant.util; + +import java.util.Collection; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.Set; +import java.util.Vector; + +/** + * Subclass of Vector that won't store duplicate entries and shows + * HashSet's constant time performance characteristics for the + * contains method. + * + *

This is not a general purpose class but has been written because + * the protected members of {@link + * org.apache.tools.ant.DirectoryScanner DirectoryScanner} prohibited + * later revisions from using a more efficient collection.

+ * + *

Methods are synchronized to keep Vector's contract.

+ * + * @since Ant 1.8.0 + */ +public final class VectorSet extends Vector { + private static final long serialVersionUID = 1L; + + private final HashSet set = new HashSet(); + + public VectorSet() { super(); } + + public VectorSet(int initialCapacity) { super(initialCapacity); } + + public VectorSet(int initialCapacity, int capacityIncrement) { + super(initialCapacity, capacityIncrement); + } + + public VectorSet(Collection c) { + if (c != null) { + for (E e : c) { + add(e); + } + } + } + + public synchronized boolean add(E o) { + if (!set.contains(o)) { + doAdd(size(), o); + return true; + } + return false; + } + + /** + * This implementation may not add the element at the given index + * if it is already contained in the collection. + */ + public void add(int index, E o) { + doAdd(index, o); + } + + private synchronized void doAdd(int index, E o) { + // Vector.add seems to delegate to insertElementAt, but this + // is not documented so we may better implement it ourselves + if (set.add(o)) { + int count = size(); + ensureCapacity(count + 1); + if (index != count) { + System.arraycopy(elementData, index, elementData, index + 1, + count - index); + } + elementData[index] = o; + elementCount++; + } + } + + public synchronized void addElement(E o) { + doAdd(size(), o); + } + + public synchronized boolean addAll(Collection c) { + boolean changed = false; + for (E e : c) { + changed |= add(e); + } + return changed; + } + + /** + * This implementation may not add all elements at the given index + * if any of them are already contained in the collection. + */ + public synchronized boolean addAll(int index, Collection c) { + LinkedList toAdd = new LinkedList(); + for (E e : c) { + if (set.add(e)) { + toAdd.add(e); + } + } + if (toAdd.isEmpty()) { + return false; + } + int count = size(); + ensureCapacity(count + toAdd.size()); + if (index != count) { + System.arraycopy(elementData, index, elementData, index + toAdd.size(), + count - index); + } + for (Object o : toAdd) { + elementData[index++] = o; + } + elementCount += toAdd.size(); + return true; + } + + public synchronized void clear() { + super.clear(); + set.clear(); + } + + public Object clone() { + @SuppressWarnings("unchecked") + final VectorSet vs = (VectorSet) super.clone(); + vs.set.addAll(set); + return vs; + } + + public synchronized boolean contains(Object o) { + return set.contains(o); + } + + public synchronized boolean containsAll(Collection c) { + return set.containsAll(c); + } + + public void insertElementAt(E o, int index) { + doAdd(index, o); + } + + public synchronized E remove(int index) { + E o = get(index); + remove(o); + return o; + } + + public boolean remove(Object o) { + return doRemove(o); + } + + private synchronized boolean doRemove(Object o) { + // again, remove seems to delegate to removeElement, but we + // shouldn't trust it + if (set.remove(o)) { + int index = indexOf(o); + if (index < elementData.length - 1) { + System.arraycopy(elementData, index + 1, elementData, index, + elementData.length - index - 1); + } + elementCount--; + return true; + } + return false; + } + + public synchronized boolean removeAll(Collection c) { + boolean changed = false; + for (Object o : c) { + changed |= remove(o); + } + return changed; + } + + public synchronized void removeAllElements() { + set.clear(); + super.removeAllElements(); + } + + public boolean removeElement(Object o) { + return doRemove(o); + } + + public synchronized void removeElementAt(int index) { + remove(get(index)); + } + + public synchronized void removeRange(final int fromIndex, int toIndex) { + while (toIndex > fromIndex) { + remove(--toIndex); + } + } + + public synchronized boolean retainAll(Collection c) { + if (!(c instanceof Set)) { + c = new HashSet(c); + } + LinkedList l = new LinkedList(); + for (E o : this) { + if (!c.contains(o)) { + l.addLast(o); + } + } + if (!l.isEmpty()) { + removeAll(l); + return true; + } + return false; + } + + public synchronized E set(int index, E o) { + E orig = get(index); + if (set.add(o)) { + elementData[index] = o; + set.remove(orig); + } else { + int oldIndexOfO = indexOf(o); + remove(o); + remove(orig); + add(oldIndexOfO > index ? index : index - 1, o); + } + return orig; + } + + public void setElementAt(E o, int index) { + set(index, o); + } + +} diff --git a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/util/package-info.java b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/util/package-info.java new file mode 100644 index 000000000..963f6d332 --- /dev/null +++ b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/util/package-info.java @@ -0,0 +1,14 @@ +/** + * + * + * org.owasp.dependencycheck.org.apache.tools.ant.util + * + * + * This is a copy of classes within Apache Ant. The DirectoryScanner + * is needed by dependency-check. However, we did not want to make + * Ant a dependency. As such, a few files were copied and slightly + * modified to remove any references to the Ant Project class. + * + * + */ +package org.owasp.dependencycheck.org.apache.tools.ant.util; diff --git a/dependency-check-utils/src/test/java/org/owasp/dependencycheck/org/apache/tools/ant/DirectoryScannerTest.java b/dependency-check-utils/src/test/java/org/owasp/dependencycheck/org/apache/tools/ant/DirectoryScannerTest.java new file mode 100644 index 000000000..99d533e31 --- /dev/null +++ b/dependency-check-utils/src/test/java/org/owasp/dependencycheck/org/apache/tools/ant/DirectoryScannerTest.java @@ -0,0 +1,732 @@ +/* + * Copyright 2014 OWASP. + * + * 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. + */ +package org.owasp.dependencycheck.org.apache.tools.ant; + +import org.owasp.dependencycheck.org.apache.tools.ant.DirectoryScanner; +import java.io.File; +import org.junit.After; +import org.junit.AfterClass; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +/** + * + * @author Jeremy Long + */ +public class DirectoryScannerTest { + + public DirectoryScannerTest() { + } + + @BeforeClass + public static void setUpClass() { + } + + @AfterClass + public static void tearDownClass() { + } + + @Before + public void setUp() { + } + + @After + public void tearDown() { + } + + /** + * Testing the expected use of the directory scanner. + */ + @Test + public void testExpectedUse() { + DirectoryScanner scanner = new DirectoryScanner(); + scanner.setBasedir("./target"); + scanner.setIncludes("/test-classes/**"); + scanner.scan(); + + File base = new File("./target"); + for (String t : scanner.getIncludedFiles()) { + assertTrue(t.startsWith("test-classes")); + File test = new File(base, t); + assertTrue(test.exists()); + } + } + + /** + * Test of matchPatternStart method, of class DirectoryScanner. + */ + @Test + public void testMatchPatternStart_String_String() { + String pattern = "alpha/be?a/**"; + String str = "alpha/beta/gamma/"; + boolean expResult = true; + boolean result = DirectoryScanner.matchPatternStart(pattern, str); + assertEquals(expResult, result); + } + + /** + * Test of matchPatternStart method, of class DirectoryScanner. + */ + @Test + public void testMatchPatternStart_3args() { + String pattern = "Alpha/be?a/**"; + String str = "alpha/beta/gamma/"; + boolean isCaseSensitive = true; + boolean expResult = false; + boolean result = DirectoryScanner.matchPatternStart(pattern, str, isCaseSensitive); + assertEquals(expResult, result); + + isCaseSensitive = false; + expResult = true; + result = DirectoryScanner.matchPatternStart(pattern, str, isCaseSensitive); + assertEquals(expResult, result); + } + + /** + * Test of matchPath method, of class DirectoryScanner. + */ + @Test + public void testMatchPath_String_String() { + String pattern = "alpha/be?a/**"; + String str = "alpha/beta/gamma/"; + boolean expResult = true; + boolean result = DirectoryScanner.matchPath(pattern, str); + assertEquals(expResult, result); + } +// +// /** +// * Test of matchPath method, of class DirectoryScanner. +// */ +// @Test +// public void testMatchPath_3args() { +// System.out.println("matchPath"); +// String pattern = ""; +// String str = ""; +// boolean isCaseSensitive = false; +// boolean expResult = false; +// boolean result = DirectoryScanner.matchPath(pattern, str, isCaseSensitive); +// assertEquals(expResult, result); +// // TODO review the generated test code and remove the default call to fail. +// fail("The test case is a prototype."); +// } +// +// /** +// * Test of match method, of class DirectoryScanner. +// */ +// @Test +// public void testMatch_String_String() { +// System.out.println("match"); +// String pattern = ""; +// String str = ""; +// boolean expResult = false; +// boolean result = DirectoryScanner.match(pattern, str); +// assertEquals(expResult, result); +// // TODO review the generated test code and remove the default call to fail. +// fail("The test case is a prototype."); +// } +// +// /** +// * Test of match method, of class DirectoryScanner. +// */ +// @Test +// public void testMatch_3args() { +// System.out.println("match"); +// String pattern = ""; +// String str = ""; +// boolean isCaseSensitive = false; +// boolean expResult = false; +// boolean result = DirectoryScanner.match(pattern, str, isCaseSensitive); +// assertEquals(expResult, result); +// // TODO review the generated test code and remove the default call to fail. +// fail("The test case is a prototype."); +// } +// +// /** +// * Test of getDefaultExcludes method, of class DirectoryScanner. +// */ +// @Test +// public void testGetDefaultExcludes() { +// System.out.println("getDefaultExcludes"); +// String[] expResult = null; +// String[] result = DirectoryScanner.getDefaultExcludes(); +// assertArrayEquals(expResult, result); +// // TODO review the generated test code and remove the default call to fail. +// fail("The test case is a prototype."); +// } +// +// /** +// * Test of addDefaultExclude method, of class DirectoryScanner. +// */ +// @Test +// public void testAddDefaultExclude() { +// System.out.println("addDefaultExclude"); +// String s = ""; +// boolean expResult = false; +// boolean result = DirectoryScanner.addDefaultExclude(s); +// assertEquals(expResult, result); +// // TODO review the generated test code and remove the default call to fail. +// fail("The test case is a prototype."); +// } +// +// /** +// * Test of removeDefaultExclude method, of class DirectoryScanner. +// */ +// @Test +// public void testRemoveDefaultExclude() { +// System.out.println("removeDefaultExclude"); +// String s = ""; +// boolean expResult = false; +// boolean result = DirectoryScanner.removeDefaultExclude(s); +// assertEquals(expResult, result); +// // TODO review the generated test code and remove the default call to fail. +// fail("The test case is a prototype."); +// } +// +// /** +// * Test of resetDefaultExcludes method, of class DirectoryScanner. +// */ +// @Test +// public void testResetDefaultExcludes() { +// System.out.println("resetDefaultExcludes"); +// DirectoryScanner.resetDefaultExcludes(); +// // TODO review the generated test code and remove the default call to fail. +// fail("The test case is a prototype."); +// } +// +// /** +// * Test of setBasedir method, of class DirectoryScanner. +// */ +// @Test +// public void testSetBasedir_String() { +// System.out.println("setBasedir"); +// String basedir = ""; +// DirectoryScanner instance = new DirectoryScanner(); +// instance.setBasedir(basedir); +// // TODO review the generated test code and remove the default call to fail. +// fail("The test case is a prototype."); +// } +// +// /** +// * Test of setBasedir method, of class DirectoryScanner. +// */ +// @Test +// public void testSetBasedir_File() { +// System.out.println("setBasedir"); +// File basedir = null; +// DirectoryScanner instance = new DirectoryScanner(); +// instance.setBasedir(basedir); +// // TODO review the generated test code and remove the default call to fail. +// fail("The test case is a prototype."); +// } +// +// /** +// * Test of getBasedir method, of class DirectoryScanner. +// */ +// @Test +// public void testGetBasedir() { +// System.out.println("getBasedir"); +// DirectoryScanner instance = new DirectoryScanner(); +// File expResult = null; +// File result = instance.getBasedir(); +// assertEquals(expResult, result); +// // TODO review the generated test code and remove the default call to fail. +// fail("The test case is a prototype."); +// } +// +// /** +// * Test of isCaseSensitive method, of class DirectoryScanner. +// */ +// @Test +// public void testIsCaseSensitive() { +// System.out.println("isCaseSensitive"); +// DirectoryScanner instance = new DirectoryScanner(); +// boolean expResult = false; +// boolean result = instance.isCaseSensitive(); +// assertEquals(expResult, result); +// // TODO review the generated test code and remove the default call to fail. +// fail("The test case is a prototype."); +// } +// +// /** +// * Test of setCaseSensitive method, of class DirectoryScanner. +// */ +// @Test +// public void testSetCaseSensitive() { +// System.out.println("setCaseSensitive"); +// boolean isCaseSensitive = false; +// DirectoryScanner instance = new DirectoryScanner(); +// instance.setCaseSensitive(isCaseSensitive); +// // TODO review the generated test code and remove the default call to fail. +// fail("The test case is a prototype."); +// } +// +// /** +// * Test of setErrorOnMissingDir method, of class DirectoryScanner. +// */ +// @Test +// public void testSetErrorOnMissingDir() { +// System.out.println("setErrorOnMissingDir"); +// boolean errorOnMissingDir = false; +// DirectoryScanner instance = new DirectoryScanner(); +// instance.setErrorOnMissingDir(errorOnMissingDir); +// // TODO review the generated test code and remove the default call to fail. +// fail("The test case is a prototype."); +// } +// +// /** +// * Test of isFollowSymlinks method, of class DirectoryScanner. +// */ +// @Test +// public void testIsFollowSymlinks() { +// System.out.println("isFollowSymlinks"); +// DirectoryScanner instance = new DirectoryScanner(); +// boolean expResult = false; +// boolean result = instance.isFollowSymlinks(); +// assertEquals(expResult, result); +// // TODO review the generated test code and remove the default call to fail. +// fail("The test case is a prototype."); +// } +// +// /** +// * Test of setFollowSymlinks method, of class DirectoryScanner. +// */ +// @Test +// public void testSetFollowSymlinks() { +// System.out.println("setFollowSymlinks"); +// boolean followSymlinks = false; +// DirectoryScanner instance = new DirectoryScanner(); +// instance.setFollowSymlinks(followSymlinks); +// // TODO review the generated test code and remove the default call to fail. +// fail("The test case is a prototype."); +// } +// +// /** +// * Test of setMaxLevelsOfSymlinks method, of class DirectoryScanner. +// */ +// @Test +// public void testSetMaxLevelsOfSymlinks() { +// System.out.println("setMaxLevelsOfSymlinks"); +// int max = 0; +// DirectoryScanner instance = new DirectoryScanner(); +// instance.setMaxLevelsOfSymlinks(max); +// // TODO review the generated test code and remove the default call to fail. +// fail("The test case is a prototype."); +// } +// +// /** +// * Test of setIncludes method, of class DirectoryScanner. +// */ +// @Test +// public void testSetIncludes() { +// System.out.println("setIncludes"); +// String[] includes = null; +// DirectoryScanner instance = new DirectoryScanner(); +// instance.setIncludes(includes); +// // TODO review the generated test code and remove the default call to fail. +// fail("The test case is a prototype."); +// } +// +// /** +// * Test of setExcludes method, of class DirectoryScanner. +// */ +// @Test +// public void testSetExcludes() { +// System.out.println("setExcludes"); +// String[] excludes = null; +// DirectoryScanner instance = new DirectoryScanner(); +// instance.setExcludes(excludes); +// // TODO review the generated test code and remove the default call to fail. +// fail("The test case is a prototype."); +// } +// +// /** +// * Test of addExcludes method, of class DirectoryScanner. +// */ +// @Test +// public void testAddExcludes() { +// System.out.println("addExcludes"); +// String[] excludes = null; +// DirectoryScanner instance = new DirectoryScanner(); +// instance.addExcludes(excludes); +// // TODO review the generated test code and remove the default call to fail. +// fail("The test case is a prototype."); +// } +// +// /** +// * Test of setSelectors method, of class DirectoryScanner. +// */ +// @Test +// public void testSetSelectors() { +// System.out.println("setSelectors"); +// FileSelector[] selectors = null; +// DirectoryScanner instance = new DirectoryScanner(); +// instance.setSelectors(selectors); +// // TODO review the generated test code and remove the default call to fail. +// fail("The test case is a prototype."); +// } +// +// /** +// * Test of isEverythingIncluded method, of class DirectoryScanner. +// */ +// @Test +// public void testIsEverythingIncluded() { +// System.out.println("isEverythingIncluded"); +// DirectoryScanner instance = new DirectoryScanner(); +// boolean expResult = false; +// boolean result = instance.isEverythingIncluded(); +// assertEquals(expResult, result); +// // TODO review the generated test code and remove the default call to fail. +// fail("The test case is a prototype."); +// } +// +// /** +// * Test of scan method, of class DirectoryScanner. +// */ +// @Test +// public void testScan() { +// System.out.println("scan"); +// DirectoryScanner instance = new DirectoryScanner(); +// instance.scan(); +// // TODO review the generated test code and remove the default call to fail. +// fail("The test case is a prototype."); +// } +// +// /** +// * Test of clearResults method, of class DirectoryScanner. +// */ +// @Test +// public void testClearResults() { +// System.out.println("clearResults"); +// DirectoryScanner instance = new DirectoryScanner(); +// instance.clearResults(); +// // TODO review the generated test code and remove the default call to fail. +// fail("The test case is a prototype."); +// } +// +// /** +// * Test of slowScan method, of class DirectoryScanner. +// */ +// @Test +// public void testSlowScan() { +// System.out.println("slowScan"); +// DirectoryScanner instance = new DirectoryScanner(); +// instance.slowScan(); +// // TODO review the generated test code and remove the default call to fail. +// fail("The test case is a prototype."); +// } +// +// /** +// * Test of scandir method, of class DirectoryScanner. +// */ +// @Test +// public void testScandir() { +// System.out.println("scandir"); +// File dir = null; +// String vpath = ""; +// boolean fast = false; +// DirectoryScanner instance = new DirectoryScanner(); +// instance.scandir(dir, vpath, fast); +// // TODO review the generated test code and remove the default call to fail. +// fail("The test case is a prototype."); +// } +// +// /** +// * Test of isIncluded method, of class DirectoryScanner. +// */ +// @Test +// public void testIsIncluded() { +// System.out.println("isIncluded"); +// String name = ""; +// DirectoryScanner instance = new DirectoryScanner(); +// boolean expResult = false; +// boolean result = instance.isIncluded(name); +// assertEquals(expResult, result); +// // TODO review the generated test code and remove the default call to fail. +// fail("The test case is a prototype."); +// } +// +// /** +// * Test of couldHoldIncluded method, of class DirectoryScanner. +// */ +// @Test +// public void testCouldHoldIncluded() { +// System.out.println("couldHoldIncluded"); +// String name = ""; +// DirectoryScanner instance = new DirectoryScanner(); +// boolean expResult = false; +// boolean result = instance.couldHoldIncluded(name); +// assertEquals(expResult, result); +// // TODO review the generated test code and remove the default call to fail. +// fail("The test case is a prototype."); +// } +// +// /** +// * Test of contentsExcluded method, of class DirectoryScanner. +// */ +// @Test +// public void testContentsExcluded() { +// System.out.println("contentsExcluded"); +// TokenizedPath path = null; +// DirectoryScanner instance = new DirectoryScanner(); +// boolean expResult = false; +// boolean result = instance.contentsExcluded(path); +// assertEquals(expResult, result); +// // TODO review the generated test code and remove the default call to fail. +// fail("The test case is a prototype."); +// } +// +// /** +// * Test of isExcluded method, of class DirectoryScanner. +// */ +// @Test +// public void testIsExcluded() { +// System.out.println("isExcluded"); +// String name = ""; +// DirectoryScanner instance = new DirectoryScanner(); +// boolean expResult = false; +// boolean result = instance.isExcluded(name); +// assertEquals(expResult, result); +// // TODO review the generated test code and remove the default call to fail. +// fail("The test case is a prototype."); +// } +// +// /** +// * Test of isSelected method, of class DirectoryScanner. +// */ +// @Test +// public void testIsSelected() { +// System.out.println("isSelected"); +// String name = ""; +// File file = null; +// DirectoryScanner instance = new DirectoryScanner(); +// boolean expResult = false; +// boolean result = instance.isSelected(name, file); +// assertEquals(expResult, result); +// // TODO review the generated test code and remove the default call to fail. +// fail("The test case is a prototype."); +// } +// +// /** +// * Test of getIncludedFiles method, of class DirectoryScanner. +// */ +// @Test +// public void testGetIncludedFiles() { +// System.out.println("getIncludedFiles"); +// DirectoryScanner instance = new DirectoryScanner(); +// String[] expResult = null; +// String[] result = instance.getIncludedFiles(); +// assertArrayEquals(expResult, result); +// // TODO review the generated test code and remove the default call to fail. +// fail("The test case is a prototype."); +// } +// +// /** +// * Test of getIncludedFilesCount method, of class DirectoryScanner. +// */ +// @Test +// public void testGetIncludedFilesCount() { +// System.out.println("getIncludedFilesCount"); +// DirectoryScanner instance = new DirectoryScanner(); +// int expResult = 0; +// int result = instance.getIncludedFilesCount(); +// assertEquals(expResult, result); +// // TODO review the generated test code and remove the default call to fail. +// fail("The test case is a prototype."); +// } +// +// /** +// * Test of getNotIncludedFiles method, of class DirectoryScanner. +// */ +// @Test +// public void testGetNotIncludedFiles() { +// System.out.println("getNotIncludedFiles"); +// DirectoryScanner instance = new DirectoryScanner(); +// String[] expResult = null; +// String[] result = instance.getNotIncludedFiles(); +// assertArrayEquals(expResult, result); +// // TODO review the generated test code and remove the default call to fail. +// fail("The test case is a prototype."); +// } +// +// /** +// * Test of getExcludedFiles method, of class DirectoryScanner. +// */ +// @Test +// public void testGetExcludedFiles() { +// System.out.println("getExcludedFiles"); +// DirectoryScanner instance = new DirectoryScanner(); +// String[] expResult = null; +// String[] result = instance.getExcludedFiles(); +// assertArrayEquals(expResult, result); +// // TODO review the generated test code and remove the default call to fail. +// fail("The test case is a prototype."); +// } +// +// /** +// * Test of getDeselectedFiles method, of class DirectoryScanner. +// */ +// @Test +// public void testGetDeselectedFiles() { +// System.out.println("getDeselectedFiles"); +// DirectoryScanner instance = new DirectoryScanner(); +// String[] expResult = null; +// String[] result = instance.getDeselectedFiles(); +// assertArrayEquals(expResult, result); +// // TODO review the generated test code and remove the default call to fail. +// fail("The test case is a prototype."); +// } +// +// /** +// * Test of getIncludedDirectories method, of class DirectoryScanner. +// */ +// @Test +// public void testGetIncludedDirectories() { +// System.out.println("getIncludedDirectories"); +// DirectoryScanner instance = new DirectoryScanner(); +// String[] expResult = null; +// String[] result = instance.getIncludedDirectories(); +// assertArrayEquals(expResult, result); +// // TODO review the generated test code and remove the default call to fail. +// fail("The test case is a prototype."); +// } +// +// /** +// * Test of getIncludedDirsCount method, of class DirectoryScanner. +// */ +// @Test +// public void testGetIncludedDirsCount() { +// System.out.println("getIncludedDirsCount"); +// DirectoryScanner instance = new DirectoryScanner(); +// int expResult = 0; +// int result = instance.getIncludedDirsCount(); +// assertEquals(expResult, result); +// // TODO review the generated test code and remove the default call to fail. +// fail("The test case is a prototype."); +// } +// +// /** +// * Test of getNotIncludedDirectories method, of class DirectoryScanner. +// */ +// @Test +// public void testGetNotIncludedDirectories() { +// System.out.println("getNotIncludedDirectories"); +// DirectoryScanner instance = new DirectoryScanner(); +// String[] expResult = null; +// String[] result = instance.getNotIncludedDirectories(); +// assertArrayEquals(expResult, result); +// // TODO review the generated test code and remove the default call to fail. +// fail("The test case is a prototype."); +// } +// +// /** +// * Test of getExcludedDirectories method, of class DirectoryScanner. +// */ +// @Test +// public void testGetExcludedDirectories() { +// System.out.println("getExcludedDirectories"); +// DirectoryScanner instance = new DirectoryScanner(); +// String[] expResult = null; +// String[] result = instance.getExcludedDirectories(); +// assertArrayEquals(expResult, result); +// // TODO review the generated test code and remove the default call to fail. +// fail("The test case is a prototype."); +// } +// +// /** +// * Test of getDeselectedDirectories method, of class DirectoryScanner. +// */ +// @Test +// public void testGetDeselectedDirectories() { +// System.out.println("getDeselectedDirectories"); +// DirectoryScanner instance = new DirectoryScanner(); +// String[] expResult = null; +// String[] result = instance.getDeselectedDirectories(); +// assertArrayEquals(expResult, result); +// // TODO review the generated test code and remove the default call to fail. +// fail("The test case is a prototype."); +// } +// +// /** +// * Test of getNotFollowedSymlinks method, of class DirectoryScanner. +// */ +// @Test +// public void testGetNotFollowedSymlinks() { +// System.out.println("getNotFollowedSymlinks"); +// DirectoryScanner instance = new DirectoryScanner(); +// String[] expResult = null; +// String[] result = instance.getNotFollowedSymlinks(); +// assertArrayEquals(expResult, result); +// // TODO review the generated test code and remove the default call to fail. +// fail("The test case is a prototype."); +// } +// +// /** +// * Test of addDefaultExcludes method, of class DirectoryScanner. +// */ +// @Test +// public void testAddDefaultExcludes() { +// System.out.println("addDefaultExcludes"); +// DirectoryScanner instance = new DirectoryScanner(); +// instance.addDefaultExcludes(); +// // TODO review the generated test code and remove the default call to fail. +// fail("The test case is a prototype."); +// } +// +// /** +// * Test of getResource method, of class DirectoryScanner. +// */ +// @Test +// public void testGetResource() { +// System.out.println("getResource"); +// String name = ""; +// DirectoryScanner instance = new DirectoryScanner(); +// Resource expResult = null; +// Resource result = instance.getResource(name); +// assertEquals(expResult, result); +// // TODO review the generated test code and remove the default call to fail. +// fail("The test case is a prototype."); +// } +// +// /** +// * Test of getScannedDirs method, of class DirectoryScanner. +// */ +// @Test +// public void testGetScannedDirs() { +// System.out.println("getScannedDirs"); +// DirectoryScanner instance = new DirectoryScanner(); +// Set expResult = null; +// Set result = instance.getScannedDirs(); +// assertEquals(expResult, result); +// // TODO review the generated test code and remove the default call to fail. +// fail("The test case is a prototype."); +// } +// +// /** +// * Test of ensureNonPatternSetsReady method, of class DirectoryScanner. +// */ +// @Test +// public void testEnsureNonPatternSetsReady() { +// System.out.println("ensureNonPatternSetsReady"); +// DirectoryScanner instance = new DirectoryScanner(); +// instance.ensureNonPatternSetsReady(); +// // TODO review the generated test code and remove the default call to fail. +// fail("The test case is a prototype."); +// } + +}