diff --git a/src/etc/testcases/core/include/with space/include.inc b/src/etc/testcases/core/include/with space/include.inc new file mode 100644 index 000000000..16342b383 --- /dev/null +++ b/src/etc/testcases/core/include/with space/include.inc @@ -0,0 +1,3 @@ + + + diff --git a/src/etc/testcases/core/include/with space/include.xml b/src/etc/testcases/core/include/with space/include.xml new file mode 100644 index 000000000..5dbeff6fd --- /dev/null +++ b/src/etc/testcases/core/include/with space/include.xml @@ -0,0 +1,9 @@ + + + +]> + + + &include; + diff --git a/src/etc/testcases/core/include/with space/relative.xml b/src/etc/testcases/core/include/with space/relative.xml new file mode 100644 index 000000000..f71ed407d --- /dev/null +++ b/src/etc/testcases/core/include/with space/relative.xml @@ -0,0 +1,9 @@ + + + +]> + + + &include; + diff --git a/src/etc/testcases/core/include/with space/simple.xml b/src/etc/testcases/core/include/with space/simple.xml new file mode 100644 index 000000000..755ae3713 --- /dev/null +++ b/src/etc/testcases/core/include/with space/simple.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/main/org/apache/tools/ant/helper/ProjectHelperImpl.java b/src/main/org/apache/tools/ant/helper/ProjectHelperImpl.java index 738a05851..935e536c7 100644 --- a/src/main/org/apache/tools/ant/helper/ProjectHelperImpl.java +++ b/src/main/org/apache/tools/ant/helper/ProjectHelperImpl.java @@ -71,6 +71,7 @@ import org.apache.tools.ant.Task; import org.apache.tools.ant.TaskAdapter; import org.apache.tools.ant.TaskContainer; import org.apache.tools.ant.UnknownElement; +import org.apache.tools.ant.util.FileUtils; import org.apache.tools.ant.util.JAXPUtils; import org.xml.sax.AttributeList; import org.xml.sax.DocumentHandler; @@ -115,6 +116,10 @@ public class ProjectHelperImpl extends ProjectHelper { * been placed outside of targets.

*/ private Target implicitTarget = new Target(); + /** + * helper for path -> URI and URI -> path conversions. + */ + private static FileUtils fu = FileUtils.newFileUtils(); public ProjectHelperImpl() { implicitTarget.setName(""); @@ -148,11 +153,7 @@ public class ProjectHelperImpl extends ProjectHelper { } - String uri = "file:" + buildFile.getAbsolutePath().replace('\\', '/'); - for (int index = uri.indexOf('#'); index != -1; index = uri.indexOf('#')) { - uri = uri.substring(0, index) + "%23" + uri.substring(index + 1); - } - + String uri = fu.toURI(buildFile.getAbsolutePath()); inputStream = new FileInputStream(buildFile); inputSource = new InputSource(inputStream); inputSource.setSystemId(uri); @@ -329,32 +330,15 @@ public class ProjectHelperImpl extends ProjectHelper { helperImpl.project.log("resolving systemId: " + systemId, Project.MSG_VERBOSE); if (systemId.startsWith("file:")) { - String path = systemId.substring(5); - int index = path.indexOf("file:"); - - // we only have to handle these for backward compatibility - // since they are in the FAQ. - while (index != -1) { - path = path.substring(0, index) + path.substring(index + 5); - index = path.indexOf("file:"); - } - - String entitySystemId = path; - index = path.indexOf("%23"); - // convert these to # - while (index != -1) { - path = path.substring(0, index) + "#" + path.substring(index + 3); - index = path.indexOf("%23"); - } + String path = fu.fromURI(systemId); File file = new File(path); if (!file.isAbsolute()) { - file = new File(helperImpl.buildFileParent, path); + file = fu.resolveFile(helperImpl.buildFileParent, path); } - try { InputSource inputSource = new InputSource(new FileInputStream(file)); - inputSource.setSystemId("file:" + entitySystemId); + inputSource.setSystemId(fu.toURI(file.getAbsolutePath())); return inputSource; } catch (FileNotFoundException fne) { helperImpl.project.log(file.getAbsolutePath() + " could not be found", diff --git a/src/main/org/apache/tools/ant/util/FileUtils.java b/src/main/org/apache/tools/ant/util/FileUtils.java index 7c8b00442..865bef767 100644 --- a/src/main/org/apache/tools/ant/util/FileUtils.java +++ b/src/main/org/apache/tools/ant/util/FileUtils.java @@ -70,7 +70,9 @@ import java.io.Reader; import java.lang.reflect.Method; import java.net.MalformedURLException; import java.net.URL; +import java.text.CharacterIterator; import java.text.DecimalFormat; +import java.text.StringCharacterIterator; import java.util.Random; import java.util.Stack; import java.util.StringTokenizer; @@ -103,6 +105,34 @@ public class FileUtils { private boolean onNetWare = Os.isFamily("netware"); + // for toURI + private static boolean[] isSpecial = new boolean[256]; + private static char[] escapedChar1 = new char[256]; + private static char[] escapedChar2 = new char[256]; + + + // stolen from FilePathToURI of the Xerces-J team + static { + for (int i = 0; i <= 0x20; i++) { + isSpecial[i] = true; + escapedChar1[i] = Character.forDigit(i >> 4, 16); + escapedChar2[i] = Character.forDigit(i & 0xf, 16); + } + isSpecial[0x7f] = true; + escapedChar1[0x7f] = '7'; + escapedChar2[0x7f] = 'F'; + char[] escChs = {'<', '>', '#', '%', '"', '{', '}', + '|', '\\', '^', '~', '[', ']', '`'}; + int len = escChs.length; + char ch; + for (int i = 0; i < len; i++) { + ch = escChs[i]; + isSpecial[ch] = true; + escapedChar1[ch] = Character.forDigit(ch >> 4, 16); + escapedChar2[ch] = Character.forDigit(ch & 0xf, 16); + } + } + /** * Factory method. */ @@ -124,14 +154,11 @@ public class FileUtils { * formed. */ public URL getFileURL(File file) throws MalformedURLException { - String uri = "file:" + file.getAbsolutePath().replace('\\', '/'); - for (int i = uri.indexOf('#'); i != -1; i = uri.indexOf('#')) { - uri = uri.substring(0, i) + "%23" + uri.substring(i + 1); - } + String path = file.getAbsolutePath(); if (file.isDirectory()) { - uri += "/"; + path += "/"; } - return new URL(uri); + return new URL(toURI(path)); } /** @@ -168,7 +195,7 @@ public class FileUtils { overwrite, false); } - /** + /** * Convienence 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 @@ -342,12 +369,12 @@ public class FileUtils { } else { in = new BufferedReader(new InputStreamReader( - new FileInputStream(sourceFile), - encoding)); + new FileInputStream(sourceFile), + encoding)); out = new BufferedWriter(new OutputStreamWriter( - new FileOutputStream(destFile), - encoding)); + new FileOutputStream(destFile), + encoding)); } if (filterChainsAvailable) { @@ -555,8 +582,8 @@ public class FileUtils { if (!onNetWare) { if (!path.startsWith(File.separator) && !(path.length() >= 2 && - Character.isLetter(path.charAt(0)) && - colon == 1)) { + Character.isLetter(path.charAt(0)) && + colon == 1)) { String msg = path + " is not an absolute path"; throw new BuildException(msg); } @@ -780,7 +807,7 @@ public class FileUtils { public static final String readFully(Reader rdr, int bufferSize) throws IOException { if (bufferSize <= 0) { throw new IllegalArgumentException("Buffer size must be greater " - + "than 0"); + + "than 0"); } final char[] buffer = new char[bufferSize]; int bufferLength = 0; @@ -791,7 +818,7 @@ public class FileUtils { if (bufferLength != -1) { if (textBuffer == null) { textBuffer = new StringBuffer( - new String(buffer, 0, bufferLength)); + new String(buffer, 0, bufferLength)); } else { textBuffer.append(new String(buffer, 0, bufferLength)); } @@ -875,5 +902,92 @@ public class FileUtils { return p; } } + + /** + * Constructs a file: URI that represents the + * external form of the given pathname. + * + *

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

+ * + *

This code doesn't handle non-ASCII characters properly.

+ * + * @since Ant 1.6 + */ + public String toURI(String path) { + StringBuffer sb = new StringBuffer("file:"); + + // catch exception if normalize thinks this is not an absolute path + try { + path = normalize(path).getAbsolutePath(); + sb.append("//"); + } catch (BuildException e) { + // relative path + } + + path = path.replace('\\', '/'); + CharacterIterator iter = new StringCharacterIterator(path); + for (char c = iter.first(); c != CharacterIterator.DONE; + c = iter.next()) { + if (isSpecial[c]) { + sb.append('%'); + sb.append(escapedChar1[c]); + sb.append(escapedChar2[c]); + } else { + sb.append(c); + } + } + return sb.toString(); + } + + /** + * Constructs a file path from a file: URI. + * + *

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

+ * + *

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

+ * + * @since Ant 1.6 + */ + public String fromURI(String uri) { + if (!uri.startsWith("file:")) { + throw new IllegalArgumentException("Can only handle file: URIs"); + } + if (uri.startsWith("file://")) { + uri = uri.substring(7); + } else { + uri = uri.substring(5); + } + + uri = uri.replace('/', File.separatorChar); + StringBuffer sb = new StringBuffer(); + 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, 16); + char c2 = iter.next(); + if (c2 != CharacterIterator.DONE) { + int i2 = Character.digit(c2, 16); + sb.append((char) ((i1 << 4) + i2)); + } + } + } else { + sb.append(c); + } + } + + String path = sb.toString(); + // catch exception if normalize thinks this is not an absolute path + try { + path = normalize(path).getAbsolutePath(); + } catch (BuildException e) { + // relative path + } + return path; + } + } diff --git a/src/testcases/org/apache/tools/ant/IncludeTest.java b/src/testcases/org/apache/tools/ant/IncludeTest.java index b422abb1e..490d0bea5 100644 --- a/src/testcases/org/apache/tools/ant/IncludeTest.java +++ b/src/testcases/org/apache/tools/ant/IncludeTest.java @@ -145,4 +145,19 @@ public class IncludeTest extends BuildFileTest { } } + public void testWithSpaceInclude() { + configureProject("src/etc/testcases/core/include/with space/include.xml"); + expectLog("test1", "from included entity in 'with space'"); + } + + public void testWithSpaceSimple() { + configureProject("src/etc/testcases/core/include/with space/simple.xml"); + expectLog("test1", "from simple buildfile in 'with space'"); + } + + public void testWithSpaceRelative() { + configureProject("src/etc/testcases/core/include/with space/relative.xml"); + expectLog("test1", "from included entity in 'with space'"); + } + } diff --git a/src/testcases/org/apache/tools/ant/util/FileUtilsTest.java b/src/testcases/org/apache/tools/ant/util/FileUtilsTest.java index df7425356..23b4b9422 100644 --- a/src/testcases/org/apache/tools/ant/util/FileUtilsTest.java +++ b/src/testcases/org/apache/tools/ant/util/FileUtilsTest.java @@ -408,6 +408,37 @@ public class FileUtilsTest extends TestCase { fu.removeLeadingPath(new File("/foo"), new File("/foobar"))); } + /** + * test toUri + */ + public void testToURI() { + if (Os.isFamily("windows")) { + assertEquals("file://C:/foo", fu.toURI("c:\\foo")); + } + assertEquals("file:///foo", fu.toURI("/foo")); + assertEquals("file:./foo", fu.toURI("./foo")); + assertEquals("file:///foo", fu.toURI("\\foo")); + assertEquals("file:./foo", fu.toURI(".\\foo")); + assertEquals("file:///foo%20bar", fu.toURI("/foo bar")); + assertEquals("file:///foo%20bar", fu.toURI("\\foo bar")); + assertEquals("file:///foo%23bar", fu.toURI("/foo#bar")); + assertEquals("file:///foo%23bar", fu.toURI("\\foo#bar")); + } + + /** + * test fromUri + */ + public void testFromURI() { + if (Os.isFamily("windows")) { + assertEquals("C:\\foo", fu.fromURI("file://c:/foo")); + } + assertEquals(localize("/foo"), fu.fromURI("file:///foo")); + assertEquals("." + File.separator + "foo", + fu.fromURI("file:./foo")); + assertEquals(localize("/foo bar"), fu.fromURI("file:///foo%20bar")); + assertEquals(localize("/foo#bar"), fu.fromURI("file:///foo%23bar")); + } + /** * adapt file separators to local conventions */