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
<mapper>
s.
+ Since Ant 1.7false
.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.
+
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.
The target file name is identical to the source file name. Both
to
and from
will be ignored.
<mapper type="identity"/> +<identitymapper />
<mapper type="glob" from="C*ies" to="Q*y"/> +<globmapper from="C*ies" to="Q*y"/>
<mapper type="regexp" from="^(.*)/([^/]+)/([^/]*)$$" to="\1/\2/\2-\3"/> +<regexpmapper from="^(.*)/([^/]+)/([^/]*)$$" to="\1/\2/\2-\3"/>
<mapper type="regexp" from="^(.*)\.(.*)$$" to="\2.\1"/> +<regexpmapper from="^(.*)\.(.*)$$" to="\2.\1"/>
${test.src.dir}/org/acme/AcmeTest.java |
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.
++<containermapper> + <identitymapper /> + <packagemapper from="*.java" to="*"/> +</containermapper> +
Source file name | +Target file names | +
foo/bar/A.java |
+ foo/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 name | +Target file names | +
foo/bar/A.java |
+ new/path/A.java1 |
+
new/path/A.java2 |
+ |
boo/far/B.java |
+ new/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 aString[]
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 {