PR: 26364 git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@276223 13f79535-47bb-0310-9956-ffa450edef68master
@@ -41,6 +41,11 @@ Other changes: | |||
a granularity of two seconds). The default remains to round up. | |||
Bugzilla Report 17934. | |||
* Nested file mappers and a container mapper implementation have been | |||
introduced. Additionally, the <mapper> element now accepts "defined" | |||
nested FileNameMapper implementations directly, allowing a usage | |||
comparable to those of <condition>, <filter>, and <selector>. | |||
Changes from Ant 1.6.1 to current Ant 1.6 CVS version | |||
============================================= | |||
@@ -62,6 +62,12 @@ with the following attributes:</p> | |||
implementation.</td> | |||
<td align="center" valign="top">Depends on implementation.</td> | |||
</tr> | |||
<tr> | |||
<td valign="top">chained</td> | |||
<td valign="top">Whether to chain nested <CODE><mapper></CODE>s. | |||
<i>Since Ant 1.7</i></td> | |||
<td align="center" valign="top">No, default is <CODE>false</CODE>.</td> | |||
</tr> | |||
</table> | |||
<p>Note that Ant will not automatically convert / or \ characters in | |||
the <code>to</code> and <code>from</code> attributes to the correct | |||
@@ -71,14 +77,30 @@ this separator, use <code>${file.separator}</code> instead.</p> | |||
<p>The classpath can be specified via a nested | |||
<code><classpath></code>, as well - that is, | |||
a <a href="../using.html#path">path</a>-like structure.</p> | |||
<p><b>Since Ant 1.7,</b> nested File Mappers can | |||
be supplied via either <CODE><mapper></CODE> elements or | |||
<a href="../CoreTasks/typedef.html"><code><typedef></code></a>'d | |||
implementations of <CODE>org.apache.tools.ant.util.FileNameMapper</CODE>. | |||
If one or more nested File Mappers are specified using either convention, | |||
only the <i>chained</i> attribute will be considered in the configuration | |||
of the implicitly used <a href="#container-mapper">container mapper</a>. | |||
</p> | |||
<hr/> | |||
<h3>The built-in mapper types are:</h3> | |||
<p>All built-in mappers are case-sensitive.</p> | |||
<p><b>As of Ant 1.7,</b> each of the built-in mapper implementation | |||
types is directly accessible using a specific tagname. This makes it | |||
possible for filename mappers to support attributes in addition to | |||
the generally available <i>to</i> and <i>from</i>.<br/> | |||
The <code><mapper type|classname="..."></code> usage | |||
form remains valid for reasons of backward compatibility.</p> | |||
<h4><a name="identity-mapper">identity</a></h4> | |||
<p>The target file name is identical to the source file name. Both | |||
<code>to</code> and <code>from</code> will be ignored.</p> | |||
<b>Examples:</b> | |||
<blockquote><pre> | |||
<mapper type="identity"/> | |||
<identitymapper /> | |||
</pre></blockquote> | |||
<table border="1" cellpadding="2" cellspacing="0"> | |||
<tr> | |||
@@ -109,6 +131,7 @@ leading directory information stripped off. Both <code>to</code> and | |||
<b>Examples:</b> | |||
<blockquote><pre> | |||
<mapper type="flatten"/> | |||
<flattenmapper /> | |||
</pre></blockquote> | |||
<table border="1" cellpadding="2" cellspacing="0"> | |||
<tr> | |||
@@ -138,6 +161,7 @@ leading directory information stripped off. Both <code>to</code> and | |||
<h5>Examples:</h5> | |||
<blockquote><pre> | |||
<mapper type="merge" to="archive.tar"/> | |||
<mergemapper to="archive.tar"/> | |||
</pre></blockquote> | |||
<table border="1" cellpadding="2" cellspacing="0"> | |||
<tr> | |||
@@ -172,6 +196,7 @@ that don't match the <code>from</code> pattern will be ignored.</p> | |||
<b>Examples:</b> | |||
<blockquote><pre> | |||
<mapper type="glob" from="*.java" to="*.java.bak"/> | |||
<globmapper from="*.java" to="*.java.bak"/> | |||
</pre></blockquote> | |||
<table border="1" cellpadding="2" cellspacing="0"> | |||
<tr> | |||
@@ -197,6 +222,7 @@ that don't match the <code>from</code> pattern will be ignored.</p> | |||
</table> | |||
<blockquote><pre> | |||
<mapper type="glob" from="C*ies" to="Q*y"/> | |||
<globmapper from="C*ies" to="Q*y"/> | |||
</pre></blockquote> | |||
<table border="1" cellpadding="2" cellspacing="0"> | |||
<tr> | |||
@@ -267,6 +293,7 @@ jakarta-ORO and finally try jakarta-regexp.</li> | |||
<b>Examples:</b> | |||
<blockquote><pre> | |||
<mapper type="regexp" from="^(.*)\.java$$" to="\1.java.bak"/> | |||
<regexpmapper from="^(.*)\.java$$" to="\1.java.bak"/> | |||
</pre></blockquote> | |||
<table border="1" cellpadding="2" cellspacing="0"> | |||
<tr> | |||
@@ -292,6 +319,7 @@ jakarta-ORO and finally try jakarta-regexp.</li> | |||
</table> | |||
<blockquote><pre> | |||
<mapper type="regexp" from="^(.*)/([^/]+)/([^/]*)$$" to="\1/\2/\2-\3"/> | |||
<regexpmapper from="^(.*)/([^/]+)/([^/]*)$$" to="\1/\2/\2-\3"/> | |||
</pre></blockquote> | |||
<table border="1" cellpadding="2" cellspacing="0"> | |||
<tr> | |||
@@ -317,6 +345,7 @@ jakarta-ORO and finally try jakarta-regexp.</li> | |||
</table> | |||
<blockquote><pre> | |||
<mapper type="regexp" from="^(.*)\.(.*)$$" to="\2.\1"/> | |||
<regexpmapper from="^(.*)\.(.*)$$" to="\2.\1"/> | |||
</pre></blockquote> | |||
<table border="1" cellpadding="2" cellspacing="0"> | |||
<tr> | |||
@@ -349,8 +378,8 @@ pattern placeholder. This mapper is particularly useful in combination | |||
with <code><uptodate></code> and <code><junit></code> output.</p> | |||
<b>Example:</b> | |||
<blockquote><pre> | |||
<mapper type="package" | |||
from="*Test.java" to="TEST-*Test.xml"/> | |||
<mapper type="package" from="*Test.java" to="TEST-*Test.xml"/> | |||
<packagemapper from="*Test.java" to="TEST-*Test.xml"/> | |||
</pre></blockquote> | |||
<table border="1" cellpadding="2" cellspacing="0"> | |||
<tr> | |||
@@ -375,8 +404,8 @@ with <code><uptodate></code> and <code><junit></code> output.</p> | |||
</p> | |||
<b>Example:</b> | |||
<blockquote><pre> | |||
<mapper type="unpackage" | |||
from="TEST-*Test.xml" to="${test.src.dir}/*Test.java"> | |||
<mapper type="unpackage" from="TEST-*Test.xml" to="${test.src.dir}/*Test.java"> | |||
<unpackagemapper from="TEST-*Test.xml" to="${test.src.dir}/*Test.java"> | |||
</pre></blockquote> | |||
<table border="1" cellpadding="2" cellspacing="0"> | |||
<tr> | |||
@@ -388,7 +417,73 @@ with <code><uptodate></code> and <code><junit></code> output.</p> | |||
<td valign="top"><code>${test.src.dir}/org/acme/AcmeTest.java</code></td> | |||
</tr> | |||
</table> | |||
<h4><a name="container-mapper">container (since ant 1.7)</a></h4> | |||
<p>This mapper implementation can contain multiple nested mappers, | |||
and can process in one of two modes, controlled by the <i>chained</i> | |||
attribute. In the default mode, file mapping is performed by passing | |||
the source filename to each nested <code><mapper></code> in turn, | |||
returning all results. When <i>chained</i> is set to <CODE>true</CODE>, | |||
the source filename will be passed to the first nested mapper, its | |||
results will be passed to the second, and so on. The target filenames | |||
generated by the last nested mapper comprise the ultimate results of the | |||
mapping operation. The <i>to</i> and <i>from</i> attributes are ignored.</p> | |||
<b>Examples:</b> | |||
<blockquote><pre> | |||
<containermapper> | |||
<identitymapper /> | |||
<packagemapper from="*.java" to="*"/> | |||
</containermapper> | |||
</pre></blockquote> | |||
<table border="1" cellpadding="2" cellspacing="0"> | |||
<tr> | |||
<td valign="top"><b>Source file name</b></td> | |||
<td valign="top"><b>Target file names</b></td> | |||
</tr> | |||
<tr> | |||
<td valign="center" rowspan="2"><code>foo/bar/A.java</code></td> | |||
<td valign="top"><code>foo/bar/A.java</code></td> | |||
</tr> | |||
<tr> | |||
<td valign="top"><code>foo.bar.A</code></td> | |||
</tr> | |||
</table> | |||
<blockquote><pre> | |||
<mapper chained="true"> | |||
<flattenmapper /> | |||
<globmapper from="*" to="new/path/*"/> | |||
<mapper> | |||
<globmapper from="*" to="*1"/> | |||
<globmapper from="*" to="*2"/> | |||
</mapper> | |||
</mapper> | |||
</pre></blockquote> | |||
<table border="1" cellpadding="2" cellspacing="0"> | |||
<tr> | |||
<td valign="top"><b>Source file name</b></td> | |||
<td valign="top"><b>Target file names</b></td> | |||
</tr> | |||
<tr> | |||
<td valign="center" rowspan="2"><code>foo/bar/A.java</code></td> | |||
<td valign="top"><code>new/path/A.java1</code></td> | |||
</tr> | |||
<tr> | |||
<td valign="top"><code>new/path/A.java2</code></td> | |||
</tr> | |||
<tr> | |||
<td valign="center" rowspan="2"><code>boo/far/B.java</code></td> | |||
<td valign="top"><code>new/path/B.java1</code></td> | |||
</tr> | |||
<tr> | |||
<td valign="top"><code>new/path/B.java2</code></td> | |||
</tr> | |||
</table> | |||
<p>Finally, the container mapper is <b>not</b> built-in in the same sense | |||
as the others. It is available via its own typedef, | |||
<code><containermapper></code>, and the use of a common | |||
<code><mapper></code> element with nested mappers will, as mentioned | |||
previously, make implicit use of a container mapper. The container | |||
mapper is <b>not</b> available, however (nor does it need to be), using | |||
the common <code><mapper type="..."></code> syntax.</p> | |||
<hr> | |||
<p align="center">Copyright © 2000-2004 The Apache Software Foundation. All rights | |||
Reserved.</p> | |||
@@ -23,6 +23,7 @@ import org.apache.tools.ant.AntClassLoader; | |||
import org.apache.tools.ant.BuildException; | |||
import org.apache.tools.ant.Project; | |||
import org.apache.tools.ant.util.FileNameMapper; | |||
import org.apache.tools.ant.util.ContainerMapper; | |||
/** | |||
* Element to define a FileNameMapper. | |||
@@ -30,7 +31,9 @@ import org.apache.tools.ant.util.FileNameMapper; | |||
*/ | |||
public class Mapper extends DataType implements Cloneable { | |||
protected MapperType type = null; | |||
protected MapperType type = null; | |||
private ContainerMapper container = null; | |||
private Boolean chained = null; | |||
public Mapper(Project p) { | |||
setProject(p); | |||
@@ -48,6 +51,34 @@ public class Mapper extends DataType implements Cloneable { | |||
protected String classname = null; | |||
/** | |||
* Add a nested filename mapper | |||
* @param fileNameMapper the mapper to add | |||
*/ | |||
public void add(FileNameMapper fileNameMapper) { | |||
if (container == null) { | |||
container = new ContainerMapper(); | |||
} | |||
container.add(fileNameMapper); | |||
} | |||
/** | |||
* Add a Mapper | |||
* @param mapper the mapper to add | |||
*/ | |||
public void addConfiguredMapper(Mapper mapper) { | |||
add(mapper.getImplementation()); | |||
} | |||
/** | |||
* Set the chained attribute of the nested mapper container | |||
* @param chained the setting for the chained attribute of the | |||
* nested mapper container. | |||
*/ | |||
public void setChained(Boolean chained) { | |||
this.chained = chained; | |||
} | |||
/** | |||
* Set the class name of the FileNameMapper to use. | |||
*/ | |||
@@ -143,12 +174,28 @@ public class Mapper extends DataType implements Cloneable { | |||
return getRef().getImplementation(); | |||
} | |||
if (type == null && classname == null) { | |||
throw new BuildException("one of the attributes type or classname is required"); | |||
if (type == null && classname == null && container == null) { | |||
throw new BuildException( | |||
"nested mapper or " | |||
+ "one of the attributes type or classname is required"); | |||
} | |||
if (container != null) { | |||
if (type != null || classname != null || | |||
to != null || from != null) { | |||
throw new BuildException( | |||
"for nested mappers, type, classname, to and from" + | |||
" attributes are not allowed"); | |||
} | |||
if (chained != null) { | |||
container.setChained(chained.booleanValue()); | |||
} | |||
return container; | |||
} | |||
if (type != null && classname != null) { | |||
throw new BuildException("must not specify both type and classname attribute"); | |||
throw new BuildException( | |||
"must not specify both type and classname attribute"); | |||
} | |||
try { | |||
@@ -7,6 +7,16 @@ filterchain=org.apache.tools.ant.types.FilterChain | |||
filterreader=org.apache.tools.ant.types.AntFilterReader | |||
filterset=org.apache.tools.ant.types.FilterSet | |||
mapper=org.apache.tools.ant.types.Mapper | |||
# different filename mappers | |||
identitymapper=org.apache.tools.ant.util.IdentityMapper | |||
flattenmapper=org.apache.tools.ant.util.FlatFileNameMapper | |||
globmapper=org.apache.tools.ant.util.GlobPatternMapper | |||
mergemapper=org.apache.tools.ant.util.MergingMapper | |||
regexpmapper=org.apache.tools.ant.util.RegexpPatternMapper | |||
packagemapper=org.apache.tools.ant.util.PackageNameMapper | |||
unpackagemapper=org.apache.tools.ant.util.UnPackageNameMapper | |||
containermapper=org.apache.tools.ant.util.ContainerMapper | |||
path=org.apache.tools.ant.types.Path | |||
patternset=org.apache.tools.ant.types.PatternSet | |||
regexp=org.apache.tools.ant.types.RegularExpression | |||
@@ -0,0 +1,129 @@ | |||
/* | |||
* Copyright 2004 The Apache Software Foundation. | |||
* | |||
* Licensed under the Apache License, Version 2.0 (the "License"); | |||
* you may not use this file except in compliance with the License. | |||
* You may obtain a copy of the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, | |||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
* See the License for the specific language governing permissions and | |||
* limitations under the License. | |||
* | |||
*/ | |||
package org.apache.tools.ant.util; | |||
import java.util.ArrayList; | |||
import java.util.Iterator; | |||
import java.util.List; | |||
import org.apache.tools.ant.types.Mapper; | |||
import org.apache.tools.ant.util.FileNameMapper; | |||
/** | |||
* A filenamemapper that contains other filename mappers. | |||
* The mappers proceeded in a chain or separately. | |||
* @see FileNameMapper | |||
*/ | |||
public class ContainerMapper implements FileNameMapper { | |||
private boolean chained = false; | |||
private List mappers = new ArrayList(); | |||
/** | |||
* Add a file name mapper. | |||
* | |||
* @param fileNameMapper a file name mapper. | |||
*/ | |||
public void add(FileNameMapper fileNameMapper) { | |||
mappers.add(fileNameMapper); | |||
} | |||
/** | |||
* Add a Mapper | |||
* @param mapper the mapper to add | |||
*/ | |||
public void addConfiguredMapper(Mapper mapper) { | |||
mappers.add(mapper.getImplementation()); | |||
} | |||
/** | |||
* Set the chained attribute. | |||
* | |||
* @param chained if true the mappers are processed in | |||
* a chained fashion. The outputs of | |||
* a mapper are the inputs for the next mapper. | |||
* if false the mappers are processed indepentanly, the | |||
* outputs are combined. | |||
*/ | |||
public void setChained(boolean chained) { | |||
this.chained = chained; | |||
} | |||
/** | |||
* This method is ignored, present to fullfill the FileNameMapper | |||
* interface. | |||
* @param ignore this parameter is ignored. | |||
*/ | |||
public void setFrom(String ignore) { | |||
} | |||
/** | |||
* This method is ignored, present to fullfill the FileNameMapper | |||
* interface. | |||
* @param ignore this parameter is ignored. | |||
*/ | |||
public void setTo(String ignore) { | |||
} | |||
/** | |||
* Map a filename using the list of mappers. | |||
* | |||
* @param sourceFileName The filename to map. | |||
* @return a <code>String[]</code> value or null if there | |||
* are no mappings. | |||
*/ | |||
public String[] mapFileName(String sourceFileName) { | |||
List ret = new ArrayList(); | |||
if (chained) { | |||
List inputs = new ArrayList(); | |||
ret.add(sourceFileName); | |||
for (int i = 0; i < mappers.size(); ++i) { | |||
inputs = ret; | |||
ret = new ArrayList(); | |||
FileNameMapper mapper = (FileNameMapper) mappers.get(i); | |||
for (Iterator it = inputs.iterator(); it.hasNext();) { | |||
String[] mapped = mapper.mapFileName( | |||
(String) it.next()); | |||
if (mapped != null) { | |||
for (int m = 0; m < mapped.length; ++m) { | |||
ret.add(mapped[m]); | |||
} | |||
} | |||
} | |||
if (ret.size() == 0) { | |||
return null; | |||
} | |||
} | |||
} else { | |||
for (int i = 0; i < mappers.size(); ++i) { | |||
FileNameMapper mapper = (FileNameMapper) mappers.get(i); | |||
String[] mapped = mapper.mapFileName(sourceFileName); | |||
if (mapped != null) { | |||
for (int m = 0; m < mapped.length; ++m) { | |||
ret.add(mapped[m]); | |||
} | |||
} | |||
} | |||
if (ret.size() == 0) { | |||
return null; | |||
} | |||
} | |||
return (String[]) ret.toArray(new String[ret.size()]); | |||
} | |||
} | |||
@@ -26,6 +26,8 @@ import junit.framework.TestCase; | |||
import junit.framework.AssertionFailedError; | |||
import java.io.File; | |||
import java.util.List; | |||
import java.util.Arrays; | |||
/** | |||
* JUnit 3 testcases for org.apache.tools.ant.types.Mapper. | |||
@@ -138,6 +140,79 @@ public class MapperTest extends TestCase { | |||
assertEquals("a.class", result[0]); | |||
} | |||
public void testContainer() { | |||
Mapper mapper1 = new Mapper(project); | |||
Mapper.MapperType mt = new Mapper.MapperType(); | |||
mt.setValue("glob"); | |||
mapper1.setType(mt); | |||
mapper1.setFrom("from*"); | |||
mapper1.setTo("to*"); | |||
//mix element types | |||
FileNameMapper mapper2 = new FlatFileNameMapper(); | |||
FileNameMapper mapper3 = new MergingMapper(); | |||
mapper3.setTo("mergefile"); | |||
Mapper container = new Mapper(project); | |||
container.addConfiguredMapper(mapper1); | |||
container.add(mapper2); | |||
container.add(mapper3); | |||
FileNameMapper fileNameMapper = container.getImplementation(); | |||
String[] targets = fileNameMapper.mapFileName("fromfilename"); | |||
assertNotNull("no filenames mapped", targets); | |||
assertEquals("wrong number of filenames mapped", 3, targets.length); | |||
List list = Arrays.asList(targets); | |||
assertTrue("cannot find expected target \"tofilename\"", | |||
list.contains("tofilename")); | |||
assertTrue("cannot find expected target \"fromfilename\"", | |||
list.contains("fromfilename")); | |||
assertTrue("cannot find expected target \"mergefile\"", | |||
list.contains("mergefile")); | |||
} | |||
public void testChainedContainer() { | |||
// a --> b --> c --- def | |||
// \-- ghi | |||
FileNameMapper mapperAB = new GlobPatternMapper(); | |||
mapperAB.setFrom("a"); | |||
mapperAB.setTo("b"); | |||
FileNameMapper mapperBC = new GlobPatternMapper(); | |||
mapperBC.setFrom("b"); | |||
mapperBC.setTo("c"); | |||
Mapper mapperCX = new Mapper(project); | |||
FileNameMapper mapperDEF = new GlobPatternMapper(); | |||
mapperDEF.setFrom("c"); | |||
mapperDEF.setTo("def"); | |||
FileNameMapper mapperGHI = new GlobPatternMapper(); | |||
mapperGHI.setFrom("c"); | |||
mapperGHI.setTo("ghi"); | |||
mapperCX.add(mapperDEF); | |||
mapperCX.add(mapperGHI); | |||
ContainerMapper chained = new ContainerMapper(); | |||
chained.setChained(true); | |||
chained.add(mapperAB); | |||
chained.add(mapperBC); | |||
chained.addConfiguredMapper(mapperCX); | |||
String[] targets = chained.mapFileName("a"); | |||
assertNotNull("no filenames mapped", targets); | |||
assertEquals("wrong number of filenames mapped", 2, targets.length); | |||
List list = Arrays.asList(targets); | |||
assertTrue("cannot find expected target \"def\"", | |||
list.contains("def")); | |||
assertTrue("cannot find expected target \"ghi\"", | |||
list.contains("ghi")); | |||
} | |||
public void testCopyTaskWithTwoFilesets() { | |||
TaskdefForCopyTest t = new TaskdefForCopyTest("test1"); | |||
try { | |||