diff --git a/WHATSNEW b/WHATSNEW index 5d3caad94..56043496a 100644 --- a/WHATSNEW +++ b/WHATSNEW @@ -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 element now accepts "defined" + nested FileNameMapper implementations directly, allowing a usage + comparable to those of , , and . + Changes from Ant 1.6.1 to current Ant 1.6 CVS version ============================================= diff --git a/docs/manual/CoreTypes/mapper.html b/docs/manual/CoreTypes/mapper.html index c6333fb95..b0e2b3f46 100644 --- a/docs/manual/CoreTypes/mapper.html +++ b/docs/manual/CoreTypes/mapper.html @@ -62,6 +62,12 @@ with the following attributes:

implementation. Depends on implementation. + + chained + Whether to chain nested <mapper>s. + Since Ant 1.7 + No, default is false. +

Note that Ant will not automatically convert / or \ characters in the to and from attributes to the correct @@ -71,14 +77,30 @@ this separator, use ${file.separator} instead.

The classpath can be specified via a nested <classpath>, as well - that is, a path-like structure.

+

Since Ant 1.7, nested File Mappers can +be supplied via either <mapper> elements or +<typedef>'d +implementations of org.apache.tools.ant.util.FileNameMapper. +If one or more nested File Mappers are specified using either convention, +only the chained attribute will be considered in the configuration +of the implicitly used container mapper. +

+

The built-in mapper types are:

All built-in mappers are case-sensitive.

+

As of Ant 1.7, 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 to and from.
+ The <mapper type|classname="..."> usage + form remains valid for reasons of backward compatibility.

identity

The target file name is identical to the source file name. Both to and from will be ignored.

Examples:
 <mapper type="identity"/>
+<identitymapper />
 
@@ -109,6 +131,7 @@ leading directory information stripped off. Both to and Examples:
 <mapper type="flatten"/>
+<flattenmapper />
 
@@ -138,6 +161,7 @@ leading directory information stripped off. Both to and
Examples:
 <mapper type="merge" to="archive.tar"/>
+<mergemapper to="archive.tar"/>
 
@@ -172,6 +196,7 @@ that don't match the from pattern will be ignored.

Examples:
 <mapper type="glob" from="*.java" to="*.java.bak"/>
+<globmapper from="*.java" to="*.java.bak"/>
 
@@ -197,6 +222,7 @@ that don't match the from pattern will be ignored.

 <mapper type="glob" from="C*ies" to="Q*y"/>
+<globmapper from="C*ies" to="Q*y"/>
 
@@ -267,6 +293,7 @@ jakarta-ORO and finally try jakarta-regexp.Examples:
 <mapper type="regexp" from="^(.*)\.java$$" to="\1.java.bak"/>
+<regexpmapper from="^(.*)\.java$$" to="\1.java.bak"/>
 
@@ -292,6 +319,7 @@ jakarta-ORO and finally try jakarta-regexp.
 <mapper type="regexp" from="^(.*)/([^/]+)/([^/]*)$$" to="\1/\2/\2-\3"/>
+<regexpmapper from="^(.*)/([^/]+)/([^/]*)$$" to="\1/\2/\2-\3"/>
 
@@ -317,6 +345,7 @@ jakarta-ORO and finally try jakarta-regexp.
 <mapper type="regexp" from="^(.*)\.(.*)$$" to="\2.\1"/>
+<regexpmapper from="^(.*)\.(.*)$$" to="\2.\1"/>
 
@@ -349,8 +378,8 @@ pattern placeholder. This mapper is particularly useful in combination with <uptodate> and <junit> output.

Example:
-<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"/>
 
@@ -375,8 +404,8 @@ with <uptodate> and <junit> output.

Example:
-<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">
 
@@ -388,7 +417,73 @@ with <uptodate> and <junit> output.

${test.src.dir}/org/acme/AcmeTest.java
- +

container (since ant 1.7)

+

This mapper implementation can contain multiple nested mappers, + and can process in one of two modes, controlled by the chained + attribute. In the default mode, file mapping is performed by passing + the source filename to each nested <mapper> in turn, + returning all results. When chained is set to true, + 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 to and from attributes are ignored.

+Examples: +
+<containermapper>
+  <identitymapper />
+  <packagemapper from="*.java" to="*"/>
+</containermapper>
+
+ + + + + + + + + + + + +
Source file nameTarget file names
foo/bar/A.javafoo/bar/A.java
foo.bar.A
+
+<mapper chained="true">
+  <flattenmapper />
+  <globmapper from="*" to="new/path/*"/>
+  <mapper>
+    <globmapper from="*" to="*1"/>
+    <globmapper from="*" to="*2"/>
+  </mapper>
+</mapper>
+
+ + + + + + + + + + + + + + + + + + + +
Source file nameTarget file names
foo/bar/A.javanew/path/A.java1
new/path/A.java2
boo/far/B.javanew/path/B.java1
new/path/B.java2
+

Finally, the container mapper is not built-in in the same sense + as the others. It is available via its own typedef, + <containermapper>, and the use of a common + <mapper> element with nested mappers will, as mentioned + previously, make implicit use of a container mapper. The container + mapper is not available, however (nor does it need to be), using + the common <mapper type="..."> syntax.


Copyright © 2000-2004 The Apache Software Foundation. All rights Reserved.

diff --git a/src/main/org/apache/tools/ant/types/Mapper.java b/src/main/org/apache/tools/ant/types/Mapper.java index b49499f93..f77746d54 100644 --- a/src/main/org/apache/tools/ant/types/Mapper.java +++ b/src/main/org/apache/tools/ant/types/Mapper.java @@ -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 { diff --git a/src/main/org/apache/tools/ant/types/defaults.properties b/src/main/org/apache/tools/ant/types/defaults.properties index 48bec7ed1..bceacab51 100644 --- a/src/main/org/apache/tools/ant/types/defaults.properties +++ b/src/main/org/apache/tools/ant/types/defaults.properties @@ -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 diff --git a/src/main/org/apache/tools/ant/util/ContainerMapper.java b/src/main/org/apache/tools/ant/util/ContainerMapper.java new file mode 100755 index 000000000..13a455942 --- /dev/null +++ b/src/main/org/apache/tools/ant/util/ContainerMapper.java @@ -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 String[] 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()]); + } +} + diff --git a/src/testcases/org/apache/tools/ant/types/MapperTest.java b/src/testcases/org/apache/tools/ant/types/MapperTest.java index 47276d0e2..1f46384fe 100644 --- a/src/testcases/org/apache/tools/ant/types/MapperTest.java +++ b/src/testcases/org/apache/tools/ant/types/MapperTest.java @@ -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 {