diff --git a/manual/Tasks/jar.html b/manual/Tasks/jar.html index 581ae4bb3..9db18f309 100644 --- a/manual/Tasks/jar.html +++ b/manual/Tasks/jar.html @@ -349,6 +349,14 @@ names used for the archives depend on your manifest:

inside the META-INF directory unless the indexmetainf attribute has been set to true.

+

indexjarsmapper

+ +

Since Ant 1.10.9

+ +

The nested indexjarsmapper element can be used to perform custom filename +transformations for the archives specified by indexjars if the +default filename transformation doesn't suffice. +

service

Since Ant 1.7.0

diff --git a/src/etc/testcases/taskdefs/jar.xml b/src/etc/testcases/taskdefs/jar.xml index c191ed289..0aaeeefed 100644 --- a/src/etc/testcases/taskdefs/jar.xml +++ b/src/etc/testcases/taskdefs/jar.xml @@ -248,7 +248,22 @@ - + + + + + + + + + + + + + + + + diff --git a/src/main/org/apache/tools/ant/taskdefs/Jar.java b/src/main/org/apache/tools/ant/taskdefs/Jar.java index 42f19fbeb..0c7bff952 100644 --- a/src/main/org/apache/tools/ant/taskdefs/Jar.java +++ b/src/main/org/apache/tools/ant/taskdefs/Jar.java @@ -50,11 +50,13 @@ import org.apache.tools.ant.taskdefs.Manifest.Section; import org.apache.tools.ant.types.ArchiveFileSet; import org.apache.tools.ant.types.EnumeratedAttribute; import org.apache.tools.ant.types.FileSet; +import org.apache.tools.ant.types.Mapper; import org.apache.tools.ant.types.Path; import org.apache.tools.ant.types.Resource; import org.apache.tools.ant.types.ResourceCollection; import org.apache.tools.ant.types.ZipFileSet; import org.apache.tools.ant.types.spi.Service; +import org.apache.tools.ant.util.FileNameMapper; import org.apache.tools.ant.util.FileUtils; import org.apache.tools.ant.util.StreamUtils; import org.apache.tools.zip.JarMarker; @@ -151,6 +153,14 @@ public class Jar extends Zip { */ private Path indexJars; + /** + * A mapper used to convert the jars to entries in the index. + * + * @since Ant 1.10.9 + */ + private FileNameMapper indexJarsMapper = null; + + // CheckStyle:LineLength OFF - Link is too long. /** * Strict mode for checking rules of the JAR-Specification. @@ -405,6 +415,30 @@ public class Jar extends Zip { indexJars.append(p); } + /** + * Add a mapper used to convert the jars to entries in the index. + * + * @param mapper a mapper + * @since Ant 1.10.9 + */ + public void addConfiguredIndexJarsMapper(Mapper mapper) { + if (indexJarsMapper != null) { + throw new BuildException("Cannot define more than one indexjar-mapper", + getLocation()); + } + indexJarsMapper = mapper.getImplementation(); + } + + /** + * Returns the mapper used to convert the jars to entries in the index. May be null. + * + * @since Ant 1.10.9 + */ + public FileNameMapper getIndexJarsMapper() { + return indexJarsMapper; + } + + /** * A nested SPI service element. * @param service the nested element. @@ -597,27 +631,18 @@ public class Jar extends Zip { writer.println(); if (indexJars != null) { - Manifest mf = createManifest(); - Manifest.Attribute classpath = - mf.getMainSection().getAttribute(Manifest.ATTRIBUTE_CLASSPATH); - String[] cpEntries = null; - if (classpath != null && classpath.getValue() != null) { - StringTokenizer tok = new StringTokenizer(classpath.getValue(), - " "); - cpEntries = new String[tok.countTokens()]; - int c = 0; - while (tok.hasMoreTokens()) { - cpEntries[c++] = tok.nextToken(); - } + FileNameMapper mapper = indexJarsMapper; + if (mapper == null) { + mapper = createDefaultIndexJarsMapper(); } for (String indexJarEntry : indexJars.list()) { - String name = findJarName(indexJarEntry, cpEntries); - if (name != null) { + String[] names = mapper.mapFileName(indexJarEntry); + if (names != null && names.length > 0) { ArrayList dirs = new ArrayList<>(); ArrayList files = new ArrayList<>(); grabFilesAndDirs(indexJarEntry, dirs, files); if (dirs.size() + files.size() > 0) { - writer.println(name); + writer.println(names[0]); writeIndexLikeList(dirs, files, writer); writer.println(); } @@ -636,6 +661,31 @@ public class Jar extends Zip { } } + /** + * Creates a mapper for the index based on the classpath attribute in the manifest. + * See {@link #findJarName(String, String[])} for more details. + * + * @return a mapper + * @since Ant 1.10.9 + */ + private FileNameMapper createDefaultIndexJarsMapper() { + Manifest mf = createManifest(); + Manifest.Attribute classpath = + mf.getMainSection().getAttribute(Manifest.ATTRIBUTE_CLASSPATH); + String[] cpEntries = null; + if (classpath != null && classpath.getValue() != null) { + StringTokenizer tok = new StringTokenizer(classpath.getValue(), + " "); + cpEntries = new String[tok.countTokens()]; + int c = 0; + while (tok.hasMoreTokens()) { + cpEntries[c++] = tok.nextToken(); + } + } + + return new IndexJarsFilenameMapper(cpEntries); + } + /** * Overridden from Zip class to deal with manifests and index lists. * @param is the stream to read data for the entry from. The @@ -1162,4 +1212,39 @@ public class Jar extends Zip { return "ignore".equals(getValue()) ? Project.MSG_VERBOSE : Project.MSG_WARN; } } + + /** + * A mapper for the index based on the classpath attribute in the manifest. + * See {@link #findJarName(String, String[])} for more details. + * + * @since Ant 1.10.9 + */ + private static class IndexJarsFilenameMapper implements FileNameMapper { + + private String[] classpath; + + IndexJarsFilenameMapper(String[] classpath) { + this.classpath = classpath; + } + + /** + * Empty implementation. + */ + @Override + public void setFrom(String from) { + } + + /** + * Empty implementation. + */ + @Override + public void setTo(String to) { + } + + @Override + public String[] mapFileName(String sourceFileName) { + String result = findJarName(sourceFileName, classpath); + return result == null ? null : new String[] {result}; + } + } } diff --git a/src/tests/junit/org/apache/tools/ant/taskdefs/JarTest.java b/src/tests/junit/org/apache/tools/ant/taskdefs/JarTest.java index d4e9cd781..21cdd6847 100644 --- a/src/tests/junit/org/apache/tools/ant/taskdefs/JarTest.java +++ b/src/tests/junit/org/apache/tools/ant/taskdefs/JarTest.java @@ -253,6 +253,7 @@ public class JarTest { ZipEntry ze = archive.getEntry("META-INF/INDEX.LIST"); InputStream is = archive.getInputStream(ze); BufferedReader r = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8)); + boolean foundArchive = false; boolean foundSub = false; boolean foundSubFoo = false; boolean foundFoo = false; @@ -260,6 +261,9 @@ public class JarTest { String line = r.readLine(); while (line != null) { switch (line) { + case "tmp.jar": + foundArchive = true; + break; case "foo": foundFoo = true; break; @@ -273,6 +277,7 @@ public class JarTest { line = r.readLine(); } + assertTrue(foundArchive); assertTrue(foundSub); assertFalse(foundSubFoo); assertTrue(foundFoo); @@ -287,8 +292,126 @@ public class JarTest { } @Test - public void testIndexJarsPlusJarMarker() { + public void testIndexJarsPlusJarMarker() throws IOException { buildRule.executeTarget("testIndexJarsPlusJarMarker"); + try (ZipFile archive = new ZipFile(new File(getOutputDir(), tempJar + "2"))) { + ZipEntry ze = archive.getEntry("META-INF/INDEX.LIST"); + InputStream is = archive.getInputStream(ze); + BufferedReader r = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8)); + // tmp.jar + boolean foundTmp = false; + boolean foundA = false; + boolean foundAB = false; + boolean foundABC = false; + + // tmp2.jar + boolean foundTmp2 = false; + boolean foundD = false; + boolean foundDE = false; + boolean foundDEF = false; + + String line = r.readLine(); + while (line != null) { + switch (line) { + case "tmp.jar": + foundTmp = true; + break; + case "a": + foundA = true; + break; + case "a/b": + foundAB = true; + break; + case "a/b/c": + foundABC = true; + break; + case "tmp.jar2": + foundTmp2 = true; + break; + case "d": + foundD = true; + break; + case "d/e": + foundDE = true; + break; + case "d/e/f": + foundDEF = true; + break; + } + line = r.readLine(); + } + + assertTrue(foundTmp); + assertTrue(foundA); + assertTrue(foundAB); + assertTrue(foundABC); + assertTrue(foundTmp2); + assertTrue(foundD); + assertTrue(foundDE); + assertTrue(foundDEF); + } + } + + @Test + public void testIndexJarsPlusJarMarkerWithMapping() throws IOException { + buildRule.executeTarget("testIndexJarsPlusJarMarkerWithMapping"); + try (ZipFile archive = new ZipFile(new File(getOutputDir(), tempJar + "2"))) { + ZipEntry ze = archive.getEntry("META-INF/INDEX.LIST"); + InputStream is = archive.getInputStream(ze); + BufferedReader r = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8)); + // tmp.jar + boolean foundTmp = false; + boolean foundA = false; + boolean foundAB = false; + boolean foundABC = false; + + // tmp2.jar + boolean foundTmp2 = false; + boolean foundD = false; + boolean foundDE = false; + boolean foundDEF = false; + + String line = r.readLine(); + while (line != null) { + System.out.println("line = " + line); + switch (line) { + case "foo/tmp.jar": + foundTmp = true; + break; + case "a": + foundA = true; + break; + case "a/b": + foundAB = true; + break; + case "a/b/c": + foundABC = true; + break; + case "tmp.jar2": + foundTmp2 = true; + break; + case "d": + foundD = true; + break; + case "d/e": + foundDE = true; + break; + case "d/e/f": + foundDEF = true; + break; + } + line = r.readLine(); + } + + assertTrue(foundTmp); + assertTrue(foundA); + assertTrue(foundAB); + assertTrue(foundABC); + assertTrue(foundTmp2); + assertTrue(foundD); + assertTrue(foundDE); + assertTrue(foundDEF); + } } @Test