bodewig 01/08/08 09:13:08
Modified: src/main/org/apache/tools/ant Project.java
src/main/org/apache/tools/ant/taskdefs XSLTProcess.java
src/main/org/apache/tools/ant/util FileUtils.java
src/testcases/org/apache/tools/ant ProjectTest.java
src/testcases/org/apache/tools/ant/util FileUtilsTest.java
Log:
Move resolveFile methods to FileUtils, add a normalize method there.
Make sure Ant normalizes whatever it takes as its basedir - since
we've dropped canonicalPath
ant -f foo.xml
and
ant -f ./foo.xml
would have given different results (the . needed to be stripped).
Revision Changes Path
1.73 +6 -56 jakarta-ant/src/main/org/apache/tools/ant/Project.java
Index: Project.java
===================================================================
RCS file: /home/cvs/jakarta-ant/src/main/org/apache/tools/ant/Project.java,v
retrieving revision 1.72
retrieving revision 1.73
diff -u -r1.72 -r1.73
--- Project.java 2001/08/07 11:43:32 1.72
+++ Project.java 2001/08/08 16:13:08 1.73
@@ -339,11 +339,12 @@
}
public void setBaseDir(File baseDir) throws BuildException {
+ baseDir = fileUtils.normalize(baseDir.getAbsolutePath());
if (!baseDir.exists())
throw new BuildException("Basedir " + baseDir.getAbsolutePath()
+ " does not exist");
if (!baseDir.isDirectory())
throw new BuildException("Basedir " + baseDir.getAbsolutePath()
+ " is not a directory");
- this.baseDir = new File(baseDir.getAbsolutePath());
+ this.baseDir = baseDir;
setProperty( "basedir", this.baseDir.getPath());
String msg = "Project base dir set to: " + this.baseDir;
log(msg, MSG_VERBOSE);
@@ -594,66 +595,15 @@
*
* <p>If fileName is a relative file name, resolve it relative to
* rootDir.</p>
+ *
+ * @deprecated
*/
public File resolveFile(String fileName, File rootDir) {
- fileName = fileName.replace('/', File.separatorChar).replace('\\',
File.separatorChar);
-
- // deal with absolute files
- if (fileName.startsWith(File.separator)) {
- return new File(fileName);
- }
-
- // Eliminate consecutive slashes after the drive spec
- if (fileName.length() >= 2 &&
- Character.isLetter(fileName.charAt(0)) &&
- fileName.charAt(1) == ':') {
- char[] ca = fileName.replace('/', '\\').toCharArray();
- char c;
- StringBuffer sb = new StringBuffer();
-
- for (int i = 0; i < ca.length; i++) {
- if ((ca[i] != '\\') ||
- (ca[i] == '\\' &&
- i > 0 &&
- ca[i - 1] != '\\')) {
- if (i == 0 &&
- Character.isLetter(ca[i]) &&
- i < ca.length - 1 &&
- ca[i + 1] == ':') {
- c = Character.toUpperCase(ca[i]);
- } else {
- c = ca[i];
- }
-
- sb.append(c);
- }
- }
-
- return new File(sb.toString());
- }
-
- File file = new File(rootDir.getAbsolutePath());
- StringTokenizer tok = new StringTokenizer(fileName, File.separator,
false);
- while (tok.hasMoreTokens()) {
- String part = tok.nextToken();
- if (part.equals("..")) {
- String parentFile = file.getParent();
- if (parentFile == null) {
- throw new BuildException("The file or path you specified
(" + fileName + ") is invalid relative to " + rootDir.getAbsolutePath());
- }
- file = new File(parentFile);
- } else if (part.equals(".")) {
- // Do nothing here
- } else {
- file = new File(file, part);
- }
- }
-
- return new File(file.getAbsolutePath());
+ return fileUtils.resolveFile(rootDir, fileName);
}
public File resolveFile(String fileName) {
- return resolveFile(fileName, baseDir);
+ return fileUtils.resolveFile(baseDir, fileName);
}
/**
1.23 +7 -3
jakarta-ant/src/main/org/apache/tools/ant/taskdefs/XSLTProcess.java
Index: XSLTProcess.java
===================================================================
RCS file:
/home/cvs/jakarta-ant/src/main/org/apache/tools/ant/taskdefs/XSLTProcess.java,v
retrieving revision 1.22
retrieving revision 1.23
diff -u -r1.22 -r1.23
--- XSLTProcess.java 2001/06/22 07:24:17 1.22
+++ XSLTProcess.java 2001/08/08 16:13:08 1.23
@@ -63,6 +63,7 @@
import org.apache.tools.ant.*;
import org.apache.tools.ant.types.Path;
import org.apache.tools.ant.types.Reference;
+import org.apache.tools.ant.util.FileUtils;
/**
@@ -87,7 +88,7 @@
* @author <a href="mailto:[EMAIL PROTECTED]">Sam Ruby</a>
* @author <a href="mailto:[EMAIL PROTECTED]">Russell Gold</a>
* @author <a href="[EMAIL PROTECTED]">Stefan Bodewig</a>
- * @version $Revision: 1.22 $ $Date: 2001/06/22 07:24:17 $
+ * @version $Revision: 1.23 $ $Date: 2001/08/08 16:13:08 $
*/
public class XSLTProcess extends MatchingTask {
@@ -111,10 +112,13 @@
private boolean force = false;
+ private FileUtils fileUtils;
+
/**
* Creates a new XSLTProcess Task.
**/
public XSLTProcess() {
+ fileUtils = FileUtils.newFileUtils();
} //-- XSLTProcess
/**
@@ -137,9 +141,9 @@
liaison = getLiaison();
log("Using "+liaison.getClass().toString(), Project.MSG_VERBOSE);
- File stylesheet = project.resolveFile(xslFile, project.getBaseDir());
+ File stylesheet = project.resolveFile(xslFile);
if (!stylesheet.exists()) {
- stylesheet = project.resolveFile(xslFile, baseDir);
+ stylesheet = fileUtils.resolveFile(baseDir, xslFile);
/*
* shouldn't throw out deprecation warnings before we know,
* the wrong version has been used.
1.3 +172 -7
jakarta-ant/src/main/org/apache/tools/ant/util/FileUtils.java
Index: FileUtils.java
===================================================================
RCS file:
/home/cvs/jakarta-ant/src/main/org/apache/tools/ant/util/FileUtils.java,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -r1.2 -r1.3
--- FileUtils.java 2001/08/07 05:49:48 1.2
+++ FileUtils.java 2001/08/08 16:13:08 1.3
@@ -56,22 +56,22 @@
import java.io.*;
import java.lang.reflect.Method;
+import java.util.StringTokenizer;
+import java.util.Stack;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.types.FilterSet;
/**
- * Central representation of an Ant project. This class defines a
- * Ant project with all of it's targets and tasks. It also provides
- * the mechanism to kick off a build using a particular target name.
- * <p>
- * This class also encapsulates methods which allow Files to be refered
- * to using abstract path names which are translated to native system
- * file paths at runtime as well as defining various project properties.
+ * This class also encapsulates methods which allow Files to be
+ * refered to using abstract path names which are translated to native
+ * system file paths at runtime as well as copying files or setting
+ * there last modification time.
*
* @author [EMAIL PROTECTED]
* @author <a href="mailto:[EMAIL PROTECTED]">Conor MacNeill</a>
+ * @author <a href="mailto:[EMAIL PROTECTED]">Stefan Bodewig</a>
*/
public class FileUtils {
@@ -292,5 +292,170 @@
}
}
+ /**
+ * Interpret the filename as a file relative to the given file -
+ * unless the filename already represents an absolute filename.
+ *
+ * @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 /).
+ * @param filename a file name
+ *
+ * @return an absolute file that doesn't contain "./" or
+ * "../" sequences and uses the correct separator for
+ * the current platform.
+ */
+ public File resolveFile(File file, String filename) {
+ filename = filename.replace('/', File.separatorChar)
+ .replace('\\', File.separatorChar);
+
+ // deal with absolute files
+ if (filename.startsWith(File.separator) ||
+
+ (filename.length() >= 2 &&
+ Character.isLetter(filename.charAt(0)) &&
+ filename.charAt(1) == ':')
+
+ ) {
+ return normalize(filename);
+ }
+
+ if (filename.length() >= 2 &&
+ Character.isLetter(filename.charAt(0)) &&
+ filename.charAt(1) == ':') {
+ return normalize(filename);
+ }
+
+ File helpFile = new File(file.getAbsolutePath());
+ StringTokenizer tok = new StringTokenizer(filename, File.separator);
+ while (tok.hasMoreTokens()) {
+ String part = tok.nextToken();
+ if (part.equals("..")) {
+ String parentFile = helpFile.getParent();
+ if (parentFile == null) {
+ String msg = "The file or path you specified ("
+ + filename + ") is invalid relative to "
+ + file.getPath();
+ throw new BuildException(msg);
+ }
+ helpFile = new File(parentFile);
+ } else if (part.equals(".")) {
+ // Do nothing here
+ } else {
+ helpFile = new File(helpFile, part);
+ }
+ }
+
+ return new File(helpFile.getAbsolutePath());
+ }
+
+ /**
+ * "normalize" the given absolute path.
+ *
+ * <p>This includes:
+ * <ul>
+ * <li>Uppercase the drive letter if there is one.</li>
+ * <li>Remove redundant slashes after the drive spec.</li>
+ * <li>resolve all ./, .\, ../ and ..\ sequences.</li>
+ * <li>DOS style paths that start with a drive letter will have
+ * \ as the separator.</li>
+ * </ul>
+ */
+ public File normalize(String path) {
+ String orig = path;
+
+ path = path.replace('/', File.separatorChar)
+ .replace('\\', File.separatorChar);
+
+ // make sure we are dealing with an absolute path
+ if (!path.startsWith(File.separator) &&
+ ! (path.length() >= 2 &&
+ Character.isLetter(path.charAt(0)) &&
+ path.charAt(1) == ':')
+ ) {
+ String msg = path + " is not an absolute path";
+ throw new BuildException(msg);
+ }
+
+ boolean dosWithDrive = false;
+ String root = null;
+ // Eliminate consecutive slashes after the drive spec
+ if (path.length() >= 2 &&
+ Character.isLetter(path.charAt(0)) &&
+ path.charAt(1) == ':') {
+
+ dosWithDrive = true;
+
+ char[] ca = path.replace('/', '\\').toCharArray();
+ StringBuffer sb = new StringBuffer();
+ sb.append(Character.toUpperCase(ca[0])).append(':');
+
+ for (int i = 2; i < ca.length; i++) {
+ if ((ca[i] != '\\') ||
+ (ca[i] == '\\' && ca[i - 1] != '\\')
+ ) {
+ sb.append(ca[i]);
+ }
+ }
+
+ path = sb.toString().replace('\\', File.separatorChar);
+ if (path.length() == 2) {
+ root = path;
+ path = "";
+ } else {
+ root = path.substring(0, 3);
+ path = path.substring(3);
+ }
+
+ } else {
+ if (path.length() == 1) {
+ root = File.separator;
+ path = "";
+ } else if (path.charAt(1) == File.separatorChar) {
+ // UNC drive
+ root = File.separator+File.separator;
+ path = path.substring(2);
+ } else {
+ root = File.separator;
+ path = path.substring(1);
+ }
+ }
+
+ Stack s = new Stack();
+ s.push(root);
+ StringTokenizer tok = new StringTokenizer(path, File.separator);
+ while (tok.hasMoreTokens()) {
+ String thisToken = tok.nextToken();
+ if (".".equals(thisToken)) {
+ continue;
+ } else if ("..".equals(thisToken)) {
+ if (s.size() < 2) {
+ throw new BuildException("Cannot resolve path "+orig);
+ } else {
+ s.pop();
+ }
+ } else { // plain component
+ s.push(thisToken);
+ }
+ }
+
+ StringBuffer sb = new StringBuffer();
+ for (int i=0; i<s.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));
+ }
+
+
+ path = sb.toString();
+ if (dosWithDrive) {
+ path = path.replace('/', '\\');
+ }
+ return new File(path);
+ }
}
1.6 +3 -0
jakarta-ant/src/testcases/org/apache/tools/ant/ProjectTest.java
Index: ProjectTest.java
===================================================================
RCS file:
/home/cvs/jakarta-ant/src/testcases/org/apache/tools/ant/ProjectTest.java,v
retrieving revision 1.5
retrieving revision 1.6
diff -u -r1.5 -r1.6
--- ProjectTest.java 2001/08/08 12:02:15 1.5
+++ ProjectTest.java 2001/08/08 16:13:08 1.6
@@ -93,6 +93,9 @@
assert("Path", p.createDataType("path") instanceof Path);
}
+ /**
+ * This test has been a starting point for moving the code to FileUtils.
+ */
public void testResolveFile() {
/*
* Start with simple absolute file names.
1.2 +140 -1
jakarta-ant/src/testcases/org/apache/tools/ant/util/FileUtilsTest.java
Index: FileUtilsTest.java
===================================================================
RCS file:
/home/cvs/jakarta-ant/src/testcases/org/apache/tools/ant/util/FileUtilsTest.java,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -r1.1 -r1.2
--- FileUtilsTest.java 2001/08/08 08:30:49 1.1
+++ FileUtilsTest.java 2001/08/08 16:13:08 1.2
@@ -58,6 +58,8 @@
import junit.framework.TestCase;
+import org.apache.tools.ant.BuildException;
+
/**
* Tests for org.apache.tools.ant.util.FileUtils.
*
@@ -65,12 +67,19 @@
*/
public class FileUtilsTest extends TestCase {
+ private FileUtils fu;
private File removeThis;
+ private String root;
public FileUtilsTest(String name) {
super(name);
}
+ public void setUp() {
+ fu = FileUtils.newFileUtils();
+ root = new File(File.separator).getAbsolutePath();
+ }
+
public void tearDown() {
if (removeThis != null && removeThis.exists()) {
removeThis.delete();
@@ -78,7 +87,6 @@
}
public void testSetLastModified() throws IOException {
- FileUtils fu = FileUtils.newFileUtils();
removeThis = new File("dummy");
FileOutputStream fos = new FileOutputStream(removeThis);
fos.write(new byte[0]);
@@ -125,4 +133,135 @@
}
}
+ public void testResolveFile() {
+ /*
+ * Start with simple absolute file names.
+ */
+ assertEquals(File.separator,
+ fu.resolveFile(null, "/").getPath());
+ assertEquals(File.separator,
+ fu.resolveFile(null, "\\").getPath());
+
+ /*
+ * throw in drive letters
+ */
+ String driveSpec = "C:";
+ assertEquals(driveSpec + "\\",
+ fu.resolveFile(null, driveSpec + "/").getPath());
+ assertEquals(driveSpec + "\\",
+ fu.resolveFile(null, driveSpec + "\\").getPath());
+ String driveSpecLower = "c:";
+ assertEquals(driveSpec + "\\",
+ fu.resolveFile(null, driveSpecLower + "/").getPath());
+ assertEquals(driveSpec + "\\",
+ fu.resolveFile(null, driveSpecLower + "\\").getPath());
+ /*
+ * promised to eliminate consecutive slashes after drive letter.
+ */
+ assertEquals(driveSpec + "\\",
+ fu.resolveFile(null, driveSpec + "/////").getPath());
+ assertEquals(driveSpec + "\\",
+ fu.resolveFile(null, driveSpec +
"\\\\\\\\\\\\").getPath());
+
+ /*
+ * Now test some relative file name magic.
+ */
+ assertEquals(localize("/1/2/3/4"),
+ fu.resolveFile(new File(localize("/1/2/3")),
"4").getPath());
+ assertEquals(localize("/1/2/3/4"),
+ fu.resolveFile(new File(localize("/1/2/3")),
"./4").getPath());
+ assertEquals(localize("/1/2/3/4"),
+ fu.resolveFile(new File(localize("/1/2/3")),
".\\4").getPath());
+ assertEquals(localize("/1/2/3/4"),
+ fu.resolveFile(new File(localize("/1/2/3")),
"./.\\4").getPath());
+ assertEquals(localize("/1/2/3/4"),
+ fu.resolveFile(new File(localize("/1/2/3")),
"../3/4").getPath());
+ assertEquals(localize("/1/2/3/4"),
+ fu.resolveFile(new File(localize("/1/2/3")),
"..\\3\\4").getPath());
+ assertEquals(localize("/1/2/3/4"),
+ fu.resolveFile(new File(localize("/1/2/3")),
"../../5/.././2/./3/6/../4").getPath());
+ assertEquals(localize("/1/2/3/4"),
+ fu.resolveFile(new File(localize("/1/2/3")),
"..\\../5/..\\./2/./3/6\\../4").getPath());
+
+ try {
+ fu.resolveFile(new File(localize("/1")), "../../b");
+ fail("successfully crawled beyond the filesystem root");
+ } catch (BuildException e) {
+ // Expected Exception caught
+ }
+
+ }
+
+ public void testNormalize() {
+ /*
+ * Start with simple absolute file names.
+ */
+ assertEquals(File.separator,
+ fu.normalize("/").getPath());
+ assertEquals(File.separator,
+ fu.normalize("\\").getPath());
+
+ /*
+ * throw in drive letters
+ */
+ String driveSpec = "C:";
+ assertEquals(driveSpec + "\\",
+ fu.normalize(driveSpec + "/").getPath());
+ assertEquals(driveSpec + "\\",
+ fu.normalize(driveSpec + "\\").getPath());
+ String driveSpecLower = "c:";
+ assertEquals(driveSpec + "\\",
+ fu.normalize(driveSpecLower + "/").getPath());
+ assertEquals(driveSpec + "\\",
+ fu.normalize(driveSpecLower + "\\").getPath());
+ /*
+ * promised to eliminate consecutive slashes after drive letter.
+ */
+ assertEquals(driveSpec + "\\",
+ fu.normalize(driveSpec + "/////").getPath());
+ assertEquals(driveSpec + "\\",
+ fu.normalize(driveSpec + "\\\\\\\\\\\\").getPath());
+
+ /*
+ * Now test some relative file name magic.
+ */
+ assertEquals(localize("/1/2/3/4"),
+ fu.normalize(localize("/1/2/3/4")).getPath());
+ assertEquals(localize("/1/2/3/4"),
+ fu.normalize(localize("/1/2/3/./4")).getPath());
+ assertEquals(localize("/1/2/3/4"),
+ fu.normalize(localize("/1/2/3/.\\4")).getPath());
+ assertEquals(localize("/1/2/3/4"),
+ fu.normalize(localize("/1/2/3/./.\\4")).getPath());
+ assertEquals(localize("/1/2/3/4"),
+ fu.normalize(localize("/1/2/3/../3/4")).getPath());
+ assertEquals(localize("/1/2/3/4"),
+ fu.normalize(localize("/1/2/3/..\\3\\4")).getPath());
+ assertEquals(localize("/1/2/3/4"),
+
fu.normalize(localize("/1/2/3/../../5/.././2/./3/6/../4")).getPath());
+ assertEquals(localize("/1/2/3/4"),
+
fu.normalize(localize("/1/2/3/..\\../5/..\\./2/./3/6\\../4")).getPath());
+
+ try {
+ fu.normalize("foo");
+ fail("foo is not an absolute path");
+ } catch (BuildException e) {
+ // Expected exception caught
+ }
+
+ try {
+ fu.normalize(localize("/1/../../b"));
+ fail("successfully crawled beyond the filesystem root");
+ } catch (BuildException e) {
+ // Expected exception caught
+ }
+ }
+
+ /**
+ * adapt file separators to local conventions
+ */
+ private String localize(String path) {
+ path = root + path.substring(1);
+ return path.replace('\\', File.separatorChar).replace('/',
File.separatorChar);
+ }
}