subclasses ChainedMapper and CompositeMapper, respectively. git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@276384 13f79535-47bb-0310-9956-ffa450edef68master
| @@ -32,7 +32,7 @@ with the following attributes:</p> | |||||
| <tr> | <tr> | ||||
| <td valign="top">type</td> | <td valign="top">type</td> | ||||
| <td valign="top">specifies one of the built-in implementations.</td> | <td valign="top">specifies one of the built-in implementations.</td> | ||||
| <td rowspan="2" align="center" valign="middle">Exactly one of both</td> | |||||
| <td rowspan="2" align="center" valign="middle">Exactly one of these</td> | |||||
| </tr> | </tr> | ||||
| <tr> | <tr> | ||||
| <td valign="top">classname</td> | <td valign="top">classname</td> | ||||
| @@ -62,12 +62,6 @@ with the following attributes:</p> | |||||
| implementation.</td> | implementation.</td> | ||||
| <td align="center" valign="top">Depends on implementation.</td> | <td align="center" valign="top">Depends on implementation.</td> | ||||
| </tr> | </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> | </table> | ||||
| <p>Note that Ant will not automatically convert / or \ characters in | <p>Note that Ant will not automatically convert / or \ characters in | ||||
| the <code>to</code> and <code>from</code> attributes to the correct | the <code>to</code> and <code>from</code> attributes to the correct | ||||
| @@ -77,18 +71,17 @@ this separator, use <code>${file.separator}</code> instead.</p> | |||||
| <p>The classpath can be specified via a nested | <p>The classpath can be specified via a nested | ||||
| <code><classpath></code>, as well - that is, | <code><classpath></code>, as well - that is, | ||||
| a <a href="../using.html#path">path</a>-like structure.</p> | a <a href="../using.html#path">path</a>-like structure.</p> | ||||
| <p><b>Since Ant 1.7,</b> nested File Mappers can | |||||
| <p><b>Since Ant 1.6.2,</b> nested File Mappers can | |||||
| be supplied via either <CODE><mapper></CODE> elements or | be supplied via either <CODE><mapper></CODE> elements or | ||||
| <a href="../CoreTasks/typedef.html"><code><typedef></code></a>'d | <a href="../CoreTasks/typedef.html"><code><typedef></code></a>'d | ||||
| implementations of <CODE>org.apache.tools.ant.util.FileNameMapper</CODE>. | 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>. | |||||
| If nested File Mappers are specified by either means, the mapper will be | |||||
| implicitly configured as a <a href="#composite-mapper">composite mapper</a>. | |||||
| </p> | </p> | ||||
| <hr/> | <hr/> | ||||
| <h3>The built-in mapper types are:</h3> | <h3>The built-in mapper types are:</h3> | ||||
| <p>All built-in mappers are case-sensitive.</p> | <p>All built-in mappers are case-sensitive.</p> | ||||
| <p><b>As of Ant 1.7,</b> each of the built-in mapper implementation | |||||
| <p><b>As of Ant 1.6.2,</b> each of the built-in mapper implementation | |||||
| types is directly accessible using a specific tagname. This makes it | types is directly accessible using a specific tagname. This makes it | ||||
| possible for filename mappers to support attributes in addition to | possible for filename mappers to support attributes in addition to | ||||
| the generally available <i>to</i> and <i>from</i>.<br/> | the generally available <i>to</i> and <i>from</i>.<br/> | ||||
| @@ -395,7 +388,7 @@ with <code><uptodate></code> and <code><junit></code> output.</p> | |||||
| <td valign="top">ignored</td> | <td valign="top">ignored</td> | ||||
| </tr> | </tr> | ||||
| </table> | </table> | ||||
| <h4><a name="unpackage-mapper">unpackage (since ant 1.6)</a></h4> | |||||
| <h4><a name="unpackage-mapper">unpackage (since ant 1.6.0)</a></h4> | |||||
| <p>This mapper is the inverse of the <a href="#package-mapper">package</a> mapper. | <p>This mapper is the inverse of the <a href="#package-mapper">package</a> mapper. | ||||
| It replaces the dots in a package name with directory separators. This | It replaces the dots in a package name with directory separators. This | ||||
| is useful for matching XML formatter results against their JUnit test | is useful for matching XML formatter results against their JUnit test | ||||
| @@ -417,22 +410,17 @@ with <code><uptodate></code> and <code><junit></code> output.</p> | |||||
| <td valign="top"><code>${test.src.dir}/org/acme/AcmeTest.java</code></td> | <td valign="top"><code>${test.src.dir}/org/acme/AcmeTest.java</code></td> | ||||
| </tr> | </tr> | ||||
| </table> | </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> | |||||
| <h4><a name="composite-mapper">composite (since ant 1.6.2)</a></h4> | |||||
| <p>This mapper implementation can contain multiple nested mappers. | |||||
| File mapping is performed by passing the source filename to each nested | |||||
| <code><mapper></code> in turn, returning all results. | |||||
| The <i>to</i> and <i>from</i> attributes are ignored.</p> | |||||
| <b>Examples:</b> | <b>Examples:</b> | ||||
| <blockquote><pre> | <blockquote><pre> | ||||
| <containermapper> | |||||
| <compositemapper> | |||||
| <identitymapper /> | <identitymapper /> | ||||
| <packagemapper from="*.java" to="*"/> | <packagemapper from="*.java" to="*"/> | ||||
| </containermapper> | |||||
| </compositemapper> | |||||
| </pre></blockquote> | </pre></blockquote> | ||||
| <table border="1" cellpadding="2" cellspacing="0"> | <table border="1" cellpadding="2" cellspacing="0"> | ||||
| <tr> | <tr> | ||||
| @@ -447,15 +435,25 @@ with <code><uptodate></code> and <code><junit></code> output.</p> | |||||
| <td valign="top"><code>foo.bar.A</code></td> | <td valign="top"><code>foo.bar.A</code></td> | ||||
| </tr> | </tr> | ||||
| </table> | </table> | ||||
| <p>The composite mapper has no corresponding | |||||
| <code><mapper <b>type</b>></code> attribute. | |||||
| </p> | |||||
| <h4><a name="chained-mapper">chained (since ant 1.6.2)</a></h4> | |||||
| <p>This mapper implementation can contain multiple nested mappers. | |||||
| File mapping is performed by passing the source filename to the first | |||||
| nested mapper, its results 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> | <blockquote><pre> | ||||
| <mapper chained="true"> | |||||
| <chainedmapper> | |||||
| <flattenmapper /> | <flattenmapper /> | ||||
| <globmapper from="*" to="new/path/*"/> | <globmapper from="*" to="new/path/*"/> | ||||
| <mapper> | <mapper> | ||||
| <globmapper from="*" to="*1"/> | <globmapper from="*" to="*1"/> | ||||
| <globmapper from="*" to="*2"/> | <globmapper from="*" to="*2"/> | ||||
| </mapper> | </mapper> | ||||
| </mapper> | |||||
| </chainedmapper> | |||||
| </pre></blockquote> | </pre></blockquote> | ||||
| <table border="1" cellpadding="2" cellspacing="0"> | <table border="1" cellpadding="2" cellspacing="0"> | ||||
| <tr> | <tr> | ||||
| @@ -477,13 +475,9 @@ with <code><uptodate></code> and <code><junit></code> output.</p> | |||||
| <td valign="top"><code>new/path/B.java2</code></td> | <td valign="top"><code>new/path/B.java2</code></td> | ||||
| </tr> | </tr> | ||||
| </table> | </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> | |||||
| <p>The chained mapper has no corresponding | |||||
| <code><mapper <b>type</b>></code> attribute. | |||||
| </p> | |||||
| <hr> | <hr> | ||||
| <p align="center">Copyright © 2000-2004 The Apache Software Foundation. All rights | <p align="center">Copyright © 2000-2004 The Apache Software Foundation. All rights | ||||
| Reserved.</p> | Reserved.</p> | ||||
| @@ -23,6 +23,7 @@ import org.apache.tools.ant.AntClassLoader; | |||||
| import org.apache.tools.ant.BuildException; | import org.apache.tools.ant.BuildException; | ||||
| import org.apache.tools.ant.Project; | import org.apache.tools.ant.Project; | ||||
| import org.apache.tools.ant.util.FileNameMapper; | import org.apache.tools.ant.util.FileNameMapper; | ||||
| import org.apache.tools.ant.util.CompositeMapper; | |||||
| import org.apache.tools.ant.util.ContainerMapper; | import org.apache.tools.ant.util.ContainerMapper; | ||||
| /** | /** | ||||
| @@ -31,16 +32,25 @@ import org.apache.tools.ant.util.ContainerMapper; | |||||
| */ | */ | ||||
| public class Mapper extends DataType implements Cloneable { | public class Mapper extends DataType implements Cloneable { | ||||
| protected MapperType type = null; | |||||
| private ContainerMapper container = null; | |||||
| private Boolean chained = null; | |||||
| protected MapperType type = null; | |||||
| protected String classname = null; | |||||
| protected Path classpath = null; | |||||
| protected String from = null; | |||||
| protected String to = null; | |||||
| private ContainerMapper container = null; | |||||
| /** | |||||
| * Construct a new <CODE>Mapper</CODE> element. | |||||
| * @param p the owning Ant <CODE>Project</CODE>. | |||||
| */ | |||||
| public Mapper(Project p) { | public Mapper(Project p) { | ||||
| setProject(p); | setProject(p); | ||||
| } | } | ||||
| /** | /** | ||||
| * Set the type of FileNameMapper to use. | |||||
| * Set the type of <code>FileNameMapper</code> to use. | |||||
| * @param type the <CODE>MapperType</CODE> enumerated attribute. | |||||
| */ | */ | ||||
| public void setType(MapperType type) { | public void setType(MapperType type) { | ||||
| if (isReference()) { | if (isReference()) { | ||||
| @@ -49,15 +59,26 @@ public class Mapper extends DataType implements Cloneable { | |||||
| this.type = type; | this.type = type; | ||||
| } | } | ||||
| protected String classname = null; | |||||
| /** | /** | ||||
| * Add a nested filename mapper | |||||
| * @param fileNameMapper the mapper to add | |||||
| * Add a nested <CODE>FileNameMapper</CODE>. | |||||
| * @param fileNameMapper the <CODE>FileNameMapper</CODE> to add. | |||||
| */ | */ | ||||
| public void add(FileNameMapper fileNameMapper) { | public void add(FileNameMapper fileNameMapper) { | ||||
| if (isReference()) { | |||||
| throw noChildrenAllowed(); | |||||
| } | |||||
| if (container == null) { | if (container == null) { | ||||
| container = new ContainerMapper(); | |||||
| if (type == null && classname == null) { | |||||
| container = new CompositeMapper(); | |||||
| } else { | |||||
| FileNameMapper m = getImplementation(); | |||||
| if (m instanceof ContainerMapper) { | |||||
| container = (ContainerMapper)m; | |||||
| } else { | |||||
| throw new BuildException(String.valueOf(m) | |||||
| + " mapper implementation does not support nested mappers!"); | |||||
| } | |||||
| } | |||||
| } | } | ||||
| container.add(fileNameMapper); | container.add(fileNameMapper); | ||||
| } | } | ||||
| @@ -69,16 +90,7 @@ public class Mapper extends DataType implements Cloneable { | |||||
| public void addConfiguredMapper(Mapper mapper) { | public void addConfiguredMapper(Mapper mapper) { | ||||
| add(mapper.getImplementation()); | 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. | * Set the class name of the FileNameMapper to use. | ||||
| */ | */ | ||||
| @@ -89,8 +101,6 @@ public class Mapper extends DataType implements Cloneable { | |||||
| this.classname = classname; | this.classname = classname; | ||||
| } | } | ||||
| protected Path classpath = null; | |||||
| /** | /** | ||||
| * Set the classpath to load the FileNameMapper through (attribute). | * Set the classpath to load the FileNameMapper through (attribute). | ||||
| */ | */ | ||||
| @@ -129,8 +139,6 @@ public class Mapper extends DataType implements Cloneable { | |||||
| createClasspath().setRefid(r); | createClasspath().setRefid(r); | ||||
| } | } | ||||
| protected String from = null; | |||||
| /** | /** | ||||
| * Set the argument to FileNameMapper.setFrom | * Set the argument to FileNameMapper.setFrom | ||||
| */ | */ | ||||
| @@ -141,8 +149,6 @@ public class Mapper extends DataType implements Cloneable { | |||||
| this.from = from; | this.from = from; | ||||
| } | } | ||||
| protected String to = null; | |||||
| /** | /** | ||||
| * Set the argument to FileNameMapper.setTo | * Set the argument to FileNameMapper.setTo | ||||
| */ | */ | ||||
| @@ -181,15 +187,6 @@ public class Mapper extends DataType implements Cloneable { | |||||
| } | } | ||||
| if (container != null) { | 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; | return container; | ||||
| } | } | ||||
| @@ -199,37 +196,41 @@ public class Mapper extends DataType implements Cloneable { | |||||
| } | } | ||||
| try { | try { | ||||
| if (type != null) { | |||||
| classname = type.getImplementation(); | |||||
| } | |||||
| Class c = null; | |||||
| if (classpath == null) { | |||||
| c = Class.forName(classname); | |||||
| } else { | |||||
| AntClassLoader al = getProject().createClassLoader(classpath); | |||||
| c = Class.forName(classname, true, al); | |||||
| } | |||||
| FileNameMapper m = (FileNameMapper) c.newInstance(); | |||||
| FileNameMapper m | |||||
| = (FileNameMapper)(getImplementationClass().newInstance()); | |||||
| final Project project = getProject(); | final Project project = getProject(); | ||||
| if (project != null) { | if (project != null) { | ||||
| project.setProjectReference(m); | project.setProjectReference(m); | ||||
| } | } | ||||
| m.setFrom(from); | m.setFrom(from); | ||||
| m.setTo(to); | m.setTo(to); | ||||
| return m; | return m; | ||||
| } catch (BuildException be) { | } catch (BuildException be) { | ||||
| throw be; | throw be; | ||||
| } catch (Throwable t) { | } catch (Throwable t) { | ||||
| throw new BuildException(t); | throw new BuildException(t); | ||||
| } finally { | |||||
| if (type != null) { | |||||
| classname = null; | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| /** | |||||
| * Gets the Class object associated with the mapper implementation. | |||||
| * @return <CODE>Class</CODE>. | |||||
| */ | |||||
| protected Class getImplementationClass() throws ClassNotFoundException { | |||||
| String classname = this.classname; | |||||
| if (type != null) { | |||||
| classname = type.getImplementation(); | |||||
| } | |||||
| ClassLoader loader = (classpath == null) | |||||
| ? getClass().getClassLoader() | |||||
| : getProject().createClassLoader(classpath); | |||||
| return Class.forName(classname, true, loader); | |||||
| } | |||||
| /** | /** | ||||
| * Performs the check for circular references and returns the | * Performs the check for circular references and returns the | ||||
| * referenced Mapper. | * referenced Mapper. | ||||
| @@ -16,7 +16,8 @@ mergemapper=org.apache.tools.ant.util.MergingMapper | |||||
| regexpmapper=org.apache.tools.ant.util.RegexpPatternMapper | regexpmapper=org.apache.tools.ant.util.RegexpPatternMapper | ||||
| packagemapper=org.apache.tools.ant.util.PackageNameMapper | packagemapper=org.apache.tools.ant.util.PackageNameMapper | ||||
| unpackagemapper=org.apache.tools.ant.util.UnPackageNameMapper | unpackagemapper=org.apache.tools.ant.util.UnPackageNameMapper | ||||
| containermapper=org.apache.tools.ant.util.ContainerMapper | |||||
| compositemapper=org.apache.tools.ant.util.CompositeMapper | |||||
| chainedmapper=org.apache.tools.ant.util.ChainedMapper | |||||
| path=org.apache.tools.ant.types.Path | path=org.apache.tools.ant.types.Path | ||||
| patternset=org.apache.tools.ant.types.PatternSet | patternset=org.apache.tools.ant.types.PatternSet | ||||
| @@ -0,0 +1,60 @@ | |||||
| /* | |||||
| * 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.List; | |||||
| import java.util.Arrays; | |||||
| import java.util.Iterator; | |||||
| import java.util.ArrayList; | |||||
| import org.apache.tools.ant.types.Mapper; | |||||
| /** | |||||
| * A <CODE>ContainerMapper</CODE> that chains the results of the first | |||||
| * nested <CODE>FileNameMapper</CODE>s into sourcefiles for the second, | |||||
| * the second to the third, and so on, returning the resulting mapped | |||||
| * filenames from the last nested <CODE>FileNameMapper</CODE>. | |||||
| */ | |||||
| public class ChainedMapper extends ContainerMapper { | |||||
| //inherit doc | |||||
| public String[] mapFileName(String sourceFileName) { | |||||
| List inputs = new ArrayList(); | |||||
| List results = new ArrayList(); | |||||
| results.add(sourceFileName); | |||||
| FileNameMapper mapper = null; | |||||
| for (Iterator mIter = getMappers().iterator(); mIter.hasNext();) { | |||||
| mapper = (FileNameMapper)(mIter.next()); | |||||
| if (mapper != null) { | |||||
| inputs.clear(); | |||||
| inputs.addAll(results); | |||||
| results.clear(); | |||||
| for (Iterator it = inputs.iterator(); it.hasNext();) { | |||||
| String[] mapped = mapper.mapFileName((String)(it.next())); | |||||
| if (mapped != null) { | |||||
| results.addAll(Arrays.asList(mapped)); | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| return (results.size() == 0) ? null | |||||
| : (String[]) results.toArray(new String[results.size()]); | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,48 @@ | |||||
| /* | |||||
| * 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.Arrays; | |||||
| import java.util.HashSet; | |||||
| import java.util.Iterator; | |||||
| /** | |||||
| * A <CODE>ContainerMapper</CODE> that unites the results of its constituent | |||||
| * <CODE>FileNameMapper</CODE>s into a single set of result filenames. | |||||
| */ | |||||
| public class CompositeMapper extends ContainerMapper { | |||||
| //inherit doc | |||||
| public String[] mapFileName(String sourceFileName) { | |||||
| HashSet results = new HashSet(); | |||||
| FileNameMapper mapper = null; | |||||
| for (Iterator mIter = getMappers().iterator(); mIter.hasNext();) { | |||||
| mapper = (FileNameMapper)(mIter.next()); | |||||
| if (mapper != null) { | |||||
| String[] mapped = mapper.mapFileName(sourceFileName); | |||||
| if (mapped != null) { | |||||
| results.addAll(Arrays.asList(mapped)); | |||||
| } | |||||
| } | |||||
| } | |||||
| return (results.size() == 0) ? null | |||||
| : (String[]) results.toArray(new String[results.size()]); | |||||
| } | |||||
| } | |||||
| @@ -17,112 +17,84 @@ | |||||
| package org.apache.tools.ant.util; | package org.apache.tools.ant.util; | ||||
| import java.util.ArrayList; | |||||
| import java.util.Iterator; | |||||
| import java.util.List; | import java.util.List; | ||||
| import java.util.Iterator; | |||||
| import java.util.ArrayList; | |||||
| import java.util.Collections; | |||||
| import org.apache.tools.ant.types.Mapper; | import org.apache.tools.ant.types.Mapper; | ||||
| /** | /** | ||||
| * A filenamemapper that contains other filename mappers. | |||||
| * The mappers proceeded in a chain or separately. | |||||
| * A <code>FileNameMapper</code> that contains | |||||
| * other <CODE>FileNameMapper</CODE>s. | |||||
| * @see FileNameMapper | * @see FileNameMapper | ||||
| */ | */ | ||||
| public abstract class ContainerMapper implements FileNameMapper { | |||||
| public class ContainerMapper implements FileNameMapper { | |||||
| private boolean chained = false; | |||||
| private List mappers = new ArrayList(); | private List mappers = new ArrayList(); | ||||
| /** | /** | ||||
| * Add a file name mapper. | |||||
| * | |||||
| * @param fileNameMapper a file name mapper. | |||||
| * Add a <code>Mapper</code>. | |||||
| * @param mapper the <code>Mapper</code> to add. | |||||
| */ | */ | ||||
| public void add(FileNameMapper fileNameMapper) { | |||||
| mappers.add(fileNameMapper); | |||||
| public void addConfiguredMapper(Mapper mapper) { | |||||
| add(mapper.getImplementation()); | |||||
| } | } | ||||
| /** | /** | ||||
| * Add a Mapper | |||||
| * @param mapper the mapper to add | |||||
| * Add a <code>FileNameMapper</code>. | |||||
| * @param fileNameMapper a <CODE>FileNameMapper</CODE>. | |||||
| * @throws <CODE>IllegalArgumentException</CODE> if attempting to add this | |||||
| * <CODE>ContainerMapper</CODE> to itself, or if the specified | |||||
| * <CODE>FileNameMapper</CODE> is itself a <CODE>ContainerMapper</CODE> | |||||
| * that contains this <CODE>ContainerMapper</CODE>. | |||||
| */ | */ | ||||
| public void addConfiguredMapper(Mapper mapper) { | |||||
| mappers.add(mapper.getImplementation()); | |||||
| public synchronized void add(FileNameMapper fileNameMapper) { | |||||
| if (this == fileNameMapper | |||||
| || (fileNameMapper instanceof ContainerMapper | |||||
| && ((ContainerMapper)fileNameMapper).contains(this))) { | |||||
| throw new IllegalArgumentException( | |||||
| "Circular mapper containment condition detected"); | |||||
| } else { | |||||
| mappers.add(fileNameMapper); | |||||
| } | |||||
| } | } | ||||
| /** | /** | ||||
| * 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. | |||||
| * Return <CODE>true</CODE> if this <CODE>ContainerMapper</CODE> or any of | |||||
| * its sub-elements contains the specified <CODE>FileNameMapper</CODE>. | |||||
| * @param fileNameMapper the <CODE>FileNameMapper</CODE> to search for. | |||||
| * @return <CODE>boolean</CODE>. | |||||
| */ | */ | ||||
| public void setChained(boolean chained) { | |||||
| this.chained = chained; | |||||
| protected synchronized boolean contains(FileNameMapper fileNameMapper) { | |||||
| boolean foundit = false; | |||||
| for (Iterator iter = mappers.iterator(); iter.hasNext() && !foundit;) { | |||||
| FileNameMapper next = (FileNameMapper)(iter.next()); | |||||
| foundit|= (next == fileNameMapper | |||||
| || (next instanceof ContainerMapper | |||||
| && ((ContainerMapper)next).contains(fileNameMapper))); | |||||
| } | |||||
| return foundit; | |||||
| } | } | ||||
| /** | /** | ||||
| * This method is ignored, present to fullfill the FileNameMapper | |||||
| * interface. | |||||
| * @param ignore this parameter is ignored. | |||||
| * Get the <CODE>List</CODE> of <CODE>FileNameMapper</CODE>s. | |||||
| * @return <CODE>List</CODE>. | |||||
| */ | */ | ||||
| public void setFrom(String ignore) { | |||||
| public synchronized List getMappers() { | |||||
| return Collections.unmodifiableList(mappers); | |||||
| } | } | ||||
| /** | /** | ||||
| * This method is ignored, present to fullfill the FileNameMapper | |||||
| * interface. | |||||
| * @param ignore this parameter is ignored. | |||||
| * Empty implementation. | |||||
| */ | */ | ||||
| public void setTo(String ignore) { | |||||
| public void setFrom(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. | |||||
| * Empty implementation. | |||||
| */ | */ | ||||
| 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()]); | |||||
| public void setTo(String ignore) { | |||||
| } | } | ||||
| } | } | ||||
| @@ -140,7 +140,7 @@ public class MapperTest extends TestCase { | |||||
| assertEquals("a.class", result[0]); | assertEquals("a.class", result[0]); | ||||
| } | } | ||||
| public void testContainer() { | |||||
| public void testNested() { | |||||
| Mapper mapper1 = new Mapper(project); | Mapper mapper1 = new Mapper(project); | ||||
| Mapper.MapperType mt = new Mapper.MapperType(); | Mapper.MapperType mt = new Mapper.MapperType(); | ||||
| mt.setValue("glob"); | mt.setValue("glob"); | ||||
| @@ -171,7 +171,7 @@ public class MapperTest extends TestCase { | |||||
| list.contains("mergefile")); | list.contains("mergefile")); | ||||
| } | } | ||||
| public void testChainedContainer() { | |||||
| public void testChained() { | |||||
| // a --> b --> c --- def | // a --> b --> c --- def | ||||
| // \-- ghi | // \-- ghi | ||||
| @@ -184,6 +184,7 @@ public class MapperTest extends TestCase { | |||||
| mapperBC.setFrom("b"); | mapperBC.setFrom("b"); | ||||
| mapperBC.setTo("c"); | mapperBC.setTo("c"); | ||||
| //implicit composite | |||||
| Mapper mapperCX = new Mapper(project); | Mapper mapperCX = new Mapper(project); | ||||
| FileNameMapper mapperDEF = new GlobPatternMapper(); | FileNameMapper mapperDEF = new GlobPatternMapper(); | ||||
| @@ -197,20 +198,19 @@ public class MapperTest extends TestCase { | |||||
| mapperCX.add(mapperDEF); | mapperCX.add(mapperDEF); | ||||
| mapperCX.add(mapperGHI); | mapperCX.add(mapperGHI); | ||||
| ContainerMapper chained = new ContainerMapper(); | |||||
| chained.setChained(true); | |||||
| Mapper chained = new Mapper(project); | |||||
| chained.setClassname(ChainedMapper.class.getName()); | |||||
| chained.add(mapperAB); | chained.add(mapperAB); | ||||
| chained.add(mapperBC); | chained.add(mapperBC); | ||||
| chained.addConfiguredMapper(mapperCX); | chained.addConfiguredMapper(mapperCX); | ||||
| String[] targets = chained.mapFileName("a"); | |||||
| FileNameMapper fileNameMapper = chained.getImplementation(); | |||||
| String[] targets = fileNameMapper.mapFileName("a"); | |||||
| assertNotNull("no filenames mapped", targets); | assertNotNull("no filenames mapped", targets); | ||||
| assertEquals("wrong number of filenames mapped", 2, targets.length); | assertEquals("wrong number of filenames mapped", 2, targets.length); | ||||
| List list = Arrays.asList(targets); | List list = Arrays.asList(targets); | ||||
| assertTrue("cannot find expected target \"def\"", | |||||
| list.contains("def")); | |||||
| assertTrue("cannot find expected target \"ghi\"", | |||||
| list.contains("ghi")); | |||||
| assertTrue("cannot find expected target \"def\"", list.contains("def")); | |||||
| assertTrue("cannot find expected target \"ghi\"", list.contains("ghi")); | |||||
| } | } | ||||
| public void testCopyTaskWithTwoFilesets() { | public void testCopyTaskWithTwoFilesets() { | ||||