@@ -60,6 +60,16 @@ Other changes: | |||
strings to be present in the line. | |||
Bugzilla Report 62313 | |||
* <resourcelist> has a new basedir attribute that can be used to | |||
resolve relative names and provides a root for the FileResources | |||
generated. | |||
Bugzilla Report 62379 | |||
* The <includesfile> and <excludesfile> nested elements of | |||
<patternset> and <fileset> now support an encoding attribute that | |||
can be used to specify the file's encoding. | |||
Bugzilla Report 62379 | |||
Changes from Ant 1.10.2 TO Ant 1.10.3 | |||
===================================== | |||
@@ -122,6 +122,11 @@ attributes you can use to test the existence of a property.</p> | |||
is <strong>not</strong> set</a>.</td> | |||
<td>No</td> | |||
</tr> | |||
<tr> | |||
<td>encoding</td> | |||
<td>The encoding of the file. <em>Since Ant 1.10.4</em></td> | |||
<td>No, default is platform default</td> | |||
</tr> | |||
</table> | |||
<h4><code>patternset</code></h4> | |||
<p>Patternsets may be nested within one another, adding the nested patterns to the parent | |||
@@ -1249,6 +1249,15 @@ of <a href="filelist.html"><code><filelist></code></a>.</p> | |||
<td>The encoding of the nested resources</td> | |||
<td>No; default is default JVM character encoding</td> | |||
</tr> | |||
<tr> | |||
<td>basedir</td> | |||
<td>Base directory that is used to resolve relative file names | |||
against. Is also used to provide a base directory to the | |||
FileResources created by this resource collection. <em>Since Ant | |||
1.10.4</em> | |||
</td> | |||
<td>No</td> | |||
</tr> | |||
</table> | |||
<h5>Parameters specified as nested elements</h5> | |||
@@ -0,0 +1,40 @@ | |||
<?xml version="1.0"?> | |||
<!-- | |||
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. | |||
--> | |||
<project name="test"> | |||
<target name="no-encoding"> | |||
<echo file="${java.io.tmpdir}/foo">fileset.xml</echo> | |||
<pathconvert targetos="unix" property="property"> | |||
<fileset dir="${basedir}"> | |||
<includesfile name="${java.io.tmpdir}/foo"/> | |||
</fileset> | |||
<map from="${basedir}" to="/abc"/> | |||
</pathconvert> | |||
<echo>${property}</echo> | |||
</target> | |||
<target name="encoding"> | |||
<echo file="${java.io.tmpdir}/foo" encoding="UTF-16LE">fileset.xml</echo> | |||
<pathconvert targetos="unix" property="property"> | |||
<fileset dir="${basedir}"> | |||
<includesfile name="${java.io.tmpdir}/foo" encoding="UTF-16LE"/> | |||
</fileset> | |||
<map from="${basedir}" to="/abc"/> | |||
</pathconvert> | |||
<echo>${property}</echo> | |||
</target> | |||
</project> |
@@ -254,7 +254,7 @@ public class Delete extends MatchingTask { | |||
/** | |||
* add a name entry on the include files list | |||
* @return a NameEntry object to be configured | |||
* @return a PatternFileNameEntry object to be configured | |||
*/ | |||
@Override | |||
public PatternSet.NameEntry createIncludesFile() { | |||
@@ -274,7 +274,7 @@ public class Delete extends MatchingTask { | |||
/** | |||
* add a name entry on the include files list | |||
* @return a NameEntry object to be configured | |||
* @return a PatternFileNameEntry object to be configured | |||
*/ | |||
@Override | |||
public PatternSet.NameEntry createExcludesFile() { | |||
@@ -78,7 +78,7 @@ public abstract class MatchingTask extends Task implements SelectorContainer { | |||
/** | |||
* add a name entry on the include files list | |||
* @return an NameEntry object to be configured | |||
* @return an PatternFileNameEntry object to be configured | |||
*/ | |||
public PatternSet.NameEntry createIncludesFile() { | |||
return fileset.createIncludesFile(); | |||
@@ -94,7 +94,7 @@ public abstract class MatchingTask extends Task implements SelectorContainer { | |||
/** | |||
* add a name entry on the include files list | |||
* @return an NameEntry object to be configured | |||
* @return an PatternFileNameEntry object to be configured | |||
*/ | |||
public PatternSet.NameEntry createExcludesFile() { | |||
return fileset.createExcludesFile(); | |||
@@ -197,7 +197,7 @@ public abstract class AbstractFileSet extends DataType | |||
/** | |||
* Add a name entry to the include files list. | |||
* @return <code>PatternSet.NameEntry</code>. | |||
* @return <code>PatternSet.PatternFileNameEntry</code>. | |||
*/ | |||
public synchronized PatternSet.NameEntry createIncludesFile() { | |||
if (isReference()) { | |||
@@ -221,7 +221,7 @@ public abstract class AbstractFileSet extends DataType | |||
/** | |||
* Add a name entry to the excludes files list. | |||
* @return <code>PatternSet.NameEntry</code>. | |||
* @return <code>PatternSet.PatternFileNameEntry</code>. | |||
*/ | |||
public synchronized PatternSet.NameEntry createExcludesFile() { | |||
if (isReference()) { | |||
@@ -19,8 +19,11 @@ package org.apache.tools.ant.types; | |||
import java.io.BufferedReader; | |||
import java.io.File; | |||
import java.io.FileInputStream; | |||
import java.io.FileReader; | |||
import java.io.IOException; | |||
import java.io.InputStreamReader; | |||
import java.io.Reader; | |||
import java.util.ArrayList; | |||
import java.util.List; | |||
import java.util.Objects; | |||
@@ -41,8 +44,8 @@ import org.apache.tools.ant.PropertyHelper; | |||
public class PatternSet extends DataType implements Cloneable { | |||
private List<NameEntry> includeList = new ArrayList<>(); | |||
private List<NameEntry> excludeList = new ArrayList<>(); | |||
private List<NameEntry> includesFileList = new ArrayList<>(); | |||
private List<NameEntry> excludesFileList = new ArrayList<>(); | |||
private List<PatternFileNameEntry> includesFileList = new ArrayList<>(); | |||
private List<PatternFileNameEntry> excludesFileList = new ArrayList<>(); | |||
/** | |||
* inner class to hold a name on list. "If" and "Unless" attributes | |||
@@ -178,6 +181,45 @@ public class PatternSet extends DataType implements Cloneable { | |||
} | |||
} | |||
/** | |||
* Adds encoding support to {@link NameEntry}. | |||
* @since Ant 1.10.4 | |||
*/ | |||
public class PatternFileNameEntry extends NameEntry { | |||
private String encoding; | |||
/** | |||
* Encoding to use when reading the file, defaults to the platform's default | |||
* encoding. | |||
* | |||
* <p> | |||
* For a list of possible values see | |||
* <a href="https://docs.oracle.com/javase/1.5.0/docs/guide/intl/encoding.doc.html"> | |||
* https://docs.oracle.com/javase/1.5.0/docs/guide/intl/encoding.doc.html</a>. | |||
* </p> | |||
* | |||
* @param encoding String | |||
*/ | |||
public final void setEncoding(String encoding) { | |||
this.encoding = encoding; | |||
} | |||
/** | |||
* Encoding to use when reading the file, defaults to the platform's default | |||
* encoding. | |||
*/ | |||
public final String getEncoding() { | |||
return encoding; | |||
} | |||
@Override | |||
public String toString() { | |||
String baseString = super.toString(); | |||
return encoding == null ? baseString | |||
: new StringBuilder(baseString).append(";encoding->").append(encoding).toString(); | |||
} | |||
} | |||
private static final class InvertedPatternSet extends PatternSet { | |||
private InvertedPatternSet(PatternSet p) { | |||
setProject(p.getProject()); | |||
@@ -260,7 +302,7 @@ public class PatternSet extends DataType implements Cloneable { | |||
if (isReference()) { | |||
throw noChildrenAllowed(); | |||
} | |||
return addPatternToList(includesFileList); | |||
return addPatternFileToList(includesFileList); | |||
} | |||
/** | |||
@@ -282,7 +324,7 @@ public class PatternSet extends DataType implements Cloneable { | |||
if (isReference()) { | |||
throw noChildrenAllowed(); | |||
} | |||
return addPatternToList(excludesFileList); | |||
return addPatternFileToList(excludesFileList); | |||
} | |||
/** | |||
@@ -330,6 +372,15 @@ public class PatternSet extends DataType implements Cloneable { | |||
return result; | |||
} | |||
/** | |||
* add a pattern file name entry to the given list | |||
*/ | |||
private PatternFileNameEntry addPatternFileToList(List<PatternFileNameEntry> list) { | |||
PatternFileNameEntry result = new PatternFileNameEntry(); | |||
list.add(result); | |||
return result; | |||
} | |||
/** | |||
* Sets the name of the file containing the includes patterns. | |||
* | |||
@@ -360,11 +411,12 @@ public class PatternSet extends DataType implements Cloneable { | |||
* Reads path matching patterns from a file and adds them to the | |||
* includes or excludes list (as appropriate). | |||
*/ | |||
private void readPatterns(File patternfile, List<NameEntry> patternlist, Project p) | |||
private void readPatterns(File patternfile, String encoding, List<NameEntry> patternlist, Project p) | |||
throws BuildException { | |||
try (BufferedReader patternReader = | |||
new BufferedReader(new FileReader(patternfile))) { | |||
try (Reader r = encoding == null ? new FileReader(patternfile) | |||
: new InputStreamReader(new FileInputStream(patternfile), encoding); | |||
BufferedReader patternReader = new BufferedReader(r)) { | |||
// Create one NameEntry in the appropriate pattern list for each | |||
// line in the file. | |||
@@ -470,7 +522,7 @@ public class PatternSet extends DataType implements Cloneable { | |||
*/ | |||
private void readFiles(Project p) { | |||
if (!includesFileList.isEmpty()) { | |||
for (NameEntry ne : includesFileList) { | |||
for (PatternFileNameEntry ne : includesFileList) { | |||
String fileName = ne.evalName(p); | |||
if (fileName != null) { | |||
File inclFile = p.resolveFile(fileName); | |||
@@ -478,13 +530,13 @@ public class PatternSet extends DataType implements Cloneable { | |||
throw new BuildException("Includesfile " + inclFile.getAbsolutePath() | |||
+ " not found."); | |||
} | |||
readPatterns(inclFile, includeList, p); | |||
readPatterns(inclFile, ne.getEncoding(), includeList, p); | |||
} | |||
} | |||
includesFileList.clear(); | |||
} | |||
if (!excludesFileList.isEmpty()) { | |||
for (NameEntry ne : excludesFileList) { | |||
for (PatternFileNameEntry ne : excludesFileList) { | |||
String fileName = ne.evalName(p); | |||
if (fileName != null) { | |||
File exclFile = p.resolveFile(fileName); | |||
@@ -492,7 +544,7 @@ public class PatternSet extends DataType implements Cloneable { | |||
throw new BuildException("Excludesfile " + exclFile.getAbsolutePath() | |||
+ " not found."); | |||
} | |||
readPatterns(exclFile, excludeList, p); | |||
readPatterns(exclFile, ne.getEncoding(), excludeList, p); | |||
} | |||
} | |||
excludesFileList.clear(); | |||
@@ -123,7 +123,7 @@ public class Files extends AbstractSelectorContainer | |||
/** | |||
* Add a name entry to the include files list. | |||
* @return <code>PatternSet.NameEntry</code>. | |||
* @return <code>PatternSet.PatternFileNameEntry</code>. | |||
*/ | |||
public synchronized PatternSet.NameEntry createIncludesFile() { | |||
if (isReference()) { | |||
@@ -147,7 +147,7 @@ public class Files extends AbstractSelectorContainer | |||
/** | |||
* Add a name entry to the excludes files list. | |||
* @return <code>PatternSet.NameEntry</code>. | |||
* @return <code>PatternSet.PatternFileNameEntry</code>. | |||
*/ | |||
public synchronized PatternSet.NameEntry createExcludesFile() { | |||
if (isReference()) { | |||
@@ -19,6 +19,7 @@ package org.apache.tools.ant.types.resources; | |||
import java.io.BufferedInputStream; | |||
import java.io.BufferedReader; | |||
import java.io.File; | |||
import java.io.IOException; | |||
import java.io.InputStreamReader; | |||
import java.io.Reader; | |||
@@ -49,6 +50,7 @@ public class ResourceList extends DataType implements ResourceCollection { | |||
private final Union cachedResources = new Union(); | |||
private volatile boolean cached = false; | |||
private String encoding = null; | |||
private File baseDir; | |||
public ResourceList() { | |||
cachedResources.setCache(true); | |||
@@ -99,6 +101,21 @@ public class ResourceList extends DataType implements ResourceCollection { | |||
this.encoding = encoding; | |||
} | |||
/** | |||
* Basedir to use for file resources read from nested resources - | |||
* this allows the resources contained inside this collection to | |||
* be considered relative to a certain base directory. | |||
* | |||
* @param basedir the basedir | |||
* @since Ant 1.10.4 | |||
*/ | |||
public final void setBasedir(File baseDir) { | |||
if (isReference()) { | |||
throw tooManyAttributes(); | |||
} | |||
this.baseDir = baseDir; | |||
} | |||
/** | |||
* Makes this instance in effect a reference to another ResourceList | |||
* instance. | |||
@@ -242,6 +259,11 @@ public class ResourceList extends DataType implements ResourceCollection { | |||
// resource | |||
} | |||
} | |||
if (baseDir != null) { | |||
FileResource fr = new FileResource(baseDir, expandedLine); | |||
fr.setProject(getProject()); | |||
return fr; | |||
} | |||
return new FileResource(getProject(), expandedLine); | |||
} | |||
@@ -476,4 +476,22 @@ public class NullByteStreamResource extends Resource { | |||
</copy> | |||
<au:assertFileExists file="${output}/filea"/> | |||
</target> | |||
<target name="testCopyOfResourceListDoesntFlatten" | |||
description="https://issues.apache.org/bugzilla/show_bug.cgi?id=62379"> | |||
<mkdir dir="${input}/dir"/> | |||
<touch file="${input}/a"/> | |||
<touch file="${input}/b"/> | |||
<touch file="${input}/c"/> | |||
<echo file="${input}/dir/c">Testfile</echo> | |||
<mkdir dir="${output}"/> | |||
<copy todir="${output}"> | |||
<resourcelist basedir="${input}"> | |||
<string value="${input}/dir/c"/> | |||
</resourcelist> | |||
</copy> | |||
<au:assertFileDoesntExist file="${output}/c"/> | |||
<au:assertFileExists file="${output}/dir/c"/> | |||
<au:assertFilesMatch expected="${input}/dir/c" actual="${output}/dir/c"/> | |||
</target> | |||
</project> |
@@ -18,17 +18,41 @@ | |||
package org.apache.tools.ant.types; | |||
import org.apache.tools.ant.BuildFileRule; | |||
import org.junit.Before; | |||
import org.junit.Rule; | |||
import org.junit.Test; | |||
import static org.junit.Assert.assertEquals; | |||
/** | |||
* JUnit 4 testcases for org.apache.tools.ant.types.FileSet. | |||
* | |||
* <p>This doesn't actually test much, mainly reference handling.</p> | |||
*/ | |||
public class FileSetTest extends AbstractFileSetTest { | |||
@Rule | |||
public BuildFileRule buildRule = new BuildFileRule(); | |||
@Before | |||
public void buildFileRuleSetUp() { | |||
buildRule.configureProject("src/etc/testcases/types/fileset.xml"); | |||
} | |||
protected AbstractFileSet getInstance() { | |||
return new FileSet(); | |||
} | |||
@Test | |||
public void testNoEncoding() { | |||
buildRule.executeTarget("no-encoding"); | |||
assertEquals("/abc/fileset.xml", buildRule.getLog()); | |||
} | |||
@Test | |||
public void testEncoding() { | |||
buildRule.executeTarget("encoding"); | |||
assertEquals("/abc/fileset.xml", buildRule.getLog()); | |||
} | |||
} |
@@ -20,13 +20,20 @@ package org.apache.tools.ant.types; | |||
import org.apache.tools.ant.BuildException; | |||
import org.apache.tools.ant.Project; | |||
import org.apache.tools.ant.util.FileUtils; | |||
import org.junit.Before; | |||
import org.junit.Rule; | |||
import org.junit.Test; | |||
import org.junit.rules.ExpectedException; | |||
import java.io.File; | |||
import java.io.FileOutputStream; | |||
import java.io.IOException; | |||
import java.io.OutputStream; | |||
import java.io.OutputStreamWriter; | |||
import java.io.Writer; | |||
import static org.junit.Assert.assertArrayEquals; | |||
import static org.junit.Assert.assertEquals; | |||
/** | |||
@@ -211,4 +218,26 @@ public class PatternSetTest { | |||
assertEquals("Includes", "**/*.java", includes[0]); | |||
assertEquals("Excludes", "**/*.class", excludes[0]); | |||
} | |||
@Test | |||
public void testEncodingOfIncludesFile() throws IOException { | |||
File testFile = File.createTempFile("ant-", ".pattern"); | |||
testFile.deleteOnExit(); | |||
OutputStream o = null; | |||
Writer w = null; | |||
try { | |||
o = new FileOutputStream(testFile); | |||
w = new OutputStreamWriter(o, "UTF-16LE"); | |||
w.write("\u00e4\n"); | |||
} finally { | |||
FileUtils.close(w); | |||
FileUtils.close(o); | |||
} | |||
PatternSet p = new PatternSet(); | |||
PatternSet.PatternFileNameEntry ne = | |||
(PatternSet.PatternFileNameEntry) p.createIncludesFile(); | |||
ne.setName(testFile.getAbsolutePath()); | |||
ne.setEncoding("UTF-16LE"); | |||
assertArrayEquals(new String[] { "\u00e4" }, p.getIncludePatterns(project)); | |||
} | |||
} |