Browse Source

Make ContainerMapper abstract; move chained/composite behaviors to

subclasses ChainedMapper and CompositeMapper, respectively.


git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@276384 13f79535-47bb-0310-9956-ffa450edef68
master
Matthew Jason Benson 21 years ago
parent
commit
2ead66e52c
7 changed files with 246 additions and 170 deletions
  1. +28
    -34
      docs/manual/CoreTypes/mapper.html
  2. +52
    -51
      src/main/org/apache/tools/ant/types/Mapper.java
  3. +2
    -1
      src/main/org/apache/tools/ant/types/defaults.properties
  4. +60
    -0
      src/main/org/apache/tools/ant/util/ChainedMapper.java
  5. +48
    -0
      src/main/org/apache/tools/ant/util/CompositeMapper.java
  6. +47
    -75
      src/main/org/apache/tools/ant/util/ContainerMapper.java
  7. +9
    -9
      src/testcases/org/apache/tools/ant/types/MapperTest.java

+ 28
- 34
docs/manual/CoreTypes/mapper.html View File

@@ -32,7 +32,7 @@ with the following attributes:</p>
<tr>
<td valign="top">type</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>
<td valign="top">classname</td>
@@ -62,12 +62,6 @@ 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>&lt;mapper&gt;</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
@@ -77,18 +71,17 @@ this separator, use <code>${file.separator}</code> instead.</p>
<p>The classpath can be specified via a nested
<code>&lt;classpath&gt;</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
<p><b>Since Ant 1.6.2,</b> nested File Mappers can
be supplied via either <CODE>&lt;mapper&gt;</CODE> elements or
<a href="../CoreTasks/typedef.html"><code>&lt;typedef&gt;</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>.
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>
<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
<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
possible for filename mappers to support attributes in addition to
the generally available <i>to</i> and <i>from</i>.<br/>
@@ -395,7 +388,7 @@ with <code>&lt;uptodate&gt;</code> and <code>&lt;junit&gt;</code> output.</p>
<td valign="top">ignored</td>
</tr>
</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.
It replaces the dots in a package name with directory separators. This
is useful for matching XML formatter results against their JUnit test
@@ -417,22 +410,17 @@ with <code>&lt;uptodate&gt;</code> and <code>&lt;junit&gt;</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>&lt;mapper&gt;</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>&lt;mapper&gt;</code> in turn, returning all results.
The <i>to</i> and <i>from</i> attributes are ignored.</p>
<b>Examples:</b>
<blockquote><pre>
&lt;containermapper&gt;
&lt;compositemapper&gt;
&lt;identitymapper /&gt;
&lt;packagemapper from="*.java" to="*"/&gt;
&lt;/containermapper&gt;
&lt;/compositemapper&gt;
</pre></blockquote>
<table border="1" cellpadding="2" cellspacing="0">
<tr>
@@ -447,15 +435,25 @@ with <code>&lt;uptodate&gt;</code> and <code>&lt;junit&gt;</code> output.</p>
<td valign="top"><code>foo.bar.A</code></td>
</tr>
</table>
<p>The composite mapper has no corresponding
<code>&lt;mapper <b>type</b>&gt;</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>
&lt;mapper chained=&quot;true&quot;&gt;
&lt;chainedmapper&gt;
&lt;flattenmapper /&gt;
&lt;globmapper from="*" to="new/path/*"/&gt;
&lt;mapper&gt;
&lt;globmapper from="*" to="*1"/&gt;
&lt;globmapper from="*" to="*2"/&gt;
&lt;/mapper&gt;
&lt;/mapper&gt;
&lt;/chainedmapper&gt;
</pre></blockquote>
<table border="1" cellpadding="2" cellspacing="0">
<tr>
@@ -477,13 +475,9 @@ with <code>&lt;uptodate&gt;</code> and <code>&lt;junit&gt;</code> output.</p>
<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>&lt;containermapper&gt;</code>, and the use of a common
<code>&lt;mapper&gt;</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>&lt;mapper type=&quot;...&quot;&gt;</code> syntax.</p>
<p>The chained mapper has no corresponding
<code>&lt;mapper <b>type</b>&gt;</code> attribute.
</p>
<hr>
<p align="center">Copyright &copy; 2000-2004 The Apache Software Foundation. All rights
Reserved.</p>


+ 52
- 51
src/main/org/apache/tools/ant/types/Mapper.java View File

@@ -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.CompositeMapper;
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 {

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) {
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) {
if (isReference()) {
@@ -49,15 +59,26 @@ public class Mapper extends DataType implements Cloneable {
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) {
if (isReference()) {
throw noChildrenAllowed();
}
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);
}
@@ -69,16 +90,7 @@ public class Mapper extends DataType implements Cloneable {
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.
*/
@@ -89,8 +101,6 @@ public class Mapper extends DataType implements Cloneable {
this.classname = classname;
}

protected Path classpath = null;

/**
* Set the classpath to load the FileNameMapper through (attribute).
*/
@@ -129,8 +139,6 @@ public class Mapper extends DataType implements Cloneable {
createClasspath().setRefid(r);
}

protected String from = null;

/**
* Set the argument to FileNameMapper.setFrom
*/
@@ -141,8 +149,6 @@ public class Mapper extends DataType implements Cloneable {
this.from = from;
}

protected String to = null;

/**
* Set the argument to FileNameMapper.setTo
*/
@@ -181,15 +187,6 @@ public class Mapper extends DataType implements Cloneable {
}

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;
}

@@ -199,37 +196,41 @@ public class Mapper extends DataType implements Cloneable {
}

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();
if (project != null) {
project.setProjectReference(m);
}
m.setFrom(from);
m.setTo(to);

return m;
} catch (BuildException be) {
throw be;
} catch (Throwable 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
* referenced Mapper.


+ 2
- 1
src/main/org/apache/tools/ant/types/defaults.properties View File

@@ -16,7 +16,8 @@ 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
compositemapper=org.apache.tools.ant.util.CompositeMapper
chainedmapper=org.apache.tools.ant.util.ChainedMapper

path=org.apache.tools.ant.types.Path
patternset=org.apache.tools.ant.types.PatternSet


+ 60
- 0
src/main/org/apache/tools/ant/util/ChainedMapper.java View File

@@ -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()]);
}
}


+ 48
- 0
src/main/org/apache/tools/ant/util/CompositeMapper.java View File

@@ -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()]);
}

}


+ 47
- 75
src/main/org/apache/tools/ant/util/ContainerMapper.java View File

@@ -17,112 +17,84 @@

package org.apache.tools.ant.util;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Iterator;
import java.util.ArrayList;
import java.util.Collections;
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
*/
public abstract class ContainerMapper implements 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.
* 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) {
}

}


+ 9
- 9
src/testcases/org/apache/tools/ant/types/MapperTest.java View File

@@ -140,7 +140,7 @@ public class MapperTest extends TestCase {
assertEquals("a.class", result[0]);
}

public void testContainer() {
public void testNested() {
Mapper mapper1 = new Mapper(project);
Mapper.MapperType mt = new Mapper.MapperType();
mt.setValue("glob");
@@ -171,7 +171,7 @@ public class MapperTest extends TestCase {
list.contains("mergefile"));
}

public void testChainedContainer() {
public void testChained() {

// a --> b --> c --- def
// \-- ghi
@@ -184,6 +184,7 @@ public class MapperTest extends TestCase {
mapperBC.setFrom("b");
mapperBC.setTo("c");

//implicit composite
Mapper mapperCX = new Mapper(project);

FileNameMapper mapperDEF = new GlobPatternMapper();
@@ -197,20 +198,19 @@ public class MapperTest extends TestCase {
mapperCX.add(mapperDEF);
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(mapperBC);
chained.addConfiguredMapper(mapperCX);

String[] targets = chained.mapFileName("a");
FileNameMapper fileNameMapper = chained.getImplementation();
String[] targets = fileNameMapper.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"));
assertTrue("cannot find expected target \"def\"", list.contains("def"));
assertTrue("cannot find expected target \"ghi\"", list.contains("ghi"));
}

public void testCopyTaskWithTwoFilesets() {


Loading…
Cancel
Save