diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/suppression/SuppressionHandler.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/suppression/SuppressionHandler.java
index a2126115f..8f4669bbf 100644
--- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/suppression/SuppressionHandler.java
+++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/suppression/SuppressionHandler.java
@@ -86,7 +86,7 @@ public class SuppressionHandler extends DefaultHandler {
/**
* The current node text being extracted from the element.
*/
- private StringBuffer currentText;
+ private StringBuilder currentText;
/**
* Handles the start element event.
@@ -100,7 +100,7 @@ public class SuppressionHandler extends DefaultHandler {
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
currentAttributes = attributes;
- currentText = new StringBuffer();
+ currentText = new StringBuilder();
if (SUPPRESS.equals(qName)) {
rule = new SuppressionRule();
final String base = currentAttributes.getValue("base");
diff --git a/dependency-check-core/src/main/java/org/owasp/dependencycheck/xml/pom/PomHandler.java b/dependency-check-core/src/main/java/org/owasp/dependencycheck/xml/pom/PomHandler.java
index 116032b20..36e3c630a 100644
--- a/dependency-check-core/src/main/java/org/owasp/dependencycheck/xml/pom/PomHandler.java
+++ b/dependency-check-core/src/main/java/org/owasp/dependencycheck/xml/pom/PomHandler.java
@@ -100,7 +100,7 @@ public class PomHandler extends DefaultHandler {
/**
* The current node text being extracted from the element.
*/
- private StringBuffer currentText;
+ private StringBuilder currentText;
/**
* Handles the start element event.
@@ -113,7 +113,7 @@ public class PomHandler extends DefaultHandler {
*/
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
- currentText = new StringBuffer();
+ currentText = new StringBuilder();
stack.push(qName);
if (LICENSE.equals(qName)) {
license = new License();
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..7015cdcc6
--- /dev/null
+++ b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/org/apache/tools/ant/Location.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;
+
+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() {
+ final StringBuilder buf = new StringBuilder();
+
+ if (fileName != null) {
+ buf.append(fileName);
+
+ if (lineNumber != 0) {
+ buf.append(':').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/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..e4f91baea
--- /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); + } + final StringBuilder buf = new StringBuilder(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; + StringBuilder 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 StringBuilder(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 StringBuilder(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/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..7f37eda43 --- /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,
+ * 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
+ * This includes:
+ *
+ * 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.
+ *
+ * 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 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.
+ *
+ * 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
+ * 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.
+ * 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
+ * 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.
+ * Unlike java.io.File#equals this method will try to compare the absolute paths and "normalize" the
+ * filenames before comparing them.
+// * This will remove
+ * Implementation note: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 Vectornull.
+ * @param separator the separator against which to tokenize.
+ *
+ * @return a Vector of path elements from the tokenized path
+ * @since Ant 1.6
+ */
+ public static Vectornull.
+// * @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.
+ * 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 "";
+ }
+ final StringBuilder path = new StringBuilder(toProcess.length() + EXPAND_SPACE);
+ final 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.
+ *
+ *
+ *
+ * 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);
+ }
+ }
+ final StringBuilder sb = new StringBuilder();
+ 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;
+
+ final StringBuilder sbPath = new StringBuilder();
+ // 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;
+ StringBuilder 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 StringBuilder(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 StringBuilder(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.
+ *
+ * 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.
+ *
+ * file: URI that represents the external form of the given pathname.
+ *
+ * file: URI.
+ *
+ * to (if it exists), ensure that to's parent directory exists and move
+// * from, which involves deleting from as well.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 :
+ *
+ *
+ *
+ *
+ * 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.
+ *
This function may throw an IOException if an I/O error occurs because its use of the
+ * canonical pathname may require filesystem queries.
+ * 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 StringBuilder buffer = new StringBuilder();
+
+ 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/utils/FileUtils.java b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/utils/FileUtils.java
index ba3f06c08..b066bb884 100644
--- a/dependency-check-utils/src/main/java/org/owasp/dependencycheck/utils/FileUtils.java
+++ b/dependency-check-utils/src/main/java/org/owasp/dependencycheck/utils/FileUtils.java
@@ -22,8 +22,6 @@ import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.IOException;
-import java.io.UnsupportedEncodingException;
-import java.net.URLDecoder;
import java.util.UUID;
/**