/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You 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.taskdefs; import java.io.File; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.Task; import org.apache.tools.ant.launch.Locator; import org.apache.tools.ant.types.Path; import org.apache.tools.ant.util.FileUtils; /** * Converts a Path into a property suitable as a Manifest classpath. * * @since Ant 1.7 * * @ant.task category="property" */ public class ManifestClassPath extends Task { /** The property name to hold the classpath value. */ private String name; /** The directory the classpath will be relative from. */ private File dir; /** The maximum parent directory level to traverse. */ private int maxParentLevels = 2; /** The classpath to convert. */ private Path path; /** * Sets a property, which must not already exist, with a space * separated list of files and directories relative to the jar * file's parent directory. */ @Override public void execute() { if (name == null) { throw new BuildException("Missing 'property' attribute!"); } if (dir == null) { throw new BuildException("Missing 'jarfile' attribute!"); } if (getProject().getProperty(name) != null) { throw new BuildException("Property '%s' already set!", name); } if (path == null) { throw new BuildException("Missing nested <classpath>!"); } StringBuilder tooLongSb = new StringBuilder(); for (int i = 0; i < maxParentLevels + 1; i++) { tooLongSb.append("../"); } final String tooLongPrefix = tooLongSb.toString(); // Normalize the reference directory (containing the jar) final FileUtils fileUtils = FileUtils.getFileUtils(); dir = fileUtils.normalize(dir.getAbsolutePath()); String[] elements = path.list(); StringBuilder buffer = new StringBuilder(); for (String element : elements) { // Normalize the current file File pathEntry = new File(element); String fullPath = pathEntry.getAbsolutePath(); pathEntry = fileUtils.normalize(fullPath); String relPath = null; String canonicalPath = null; try { if (dir.equals(pathEntry)) { relPath = "."; } else { relPath = FileUtils.getRelativePath(dir, pathEntry); } canonicalPath = pathEntry.getCanonicalPath(); // getRelativePath always uses '/' as separator, adapt if (File.separatorChar != '/') { canonicalPath = canonicalPath.replace(File.separatorChar, '/'); } } catch (Exception e) { throw new BuildException("error trying to get the relative path" + " from " + dir + " to " + fullPath, e); } // No match, so bail out! if (relPath.equals(canonicalPath) || relPath.startsWith(tooLongPrefix)) { throw new BuildException( "No suitable relative path from %s to %s", dir, fullPath); } if (pathEntry.isDirectory() && !relPath.endsWith("/")) { relPath = relPath + '/'; } relPath = Locator.encodeURI(relPath); // Manifest's ClassPath: attribute always uses forward // slashes '/', and is space-separated. Ant will properly // format it on 72 columns with proper line continuation buffer.append(relPath); buffer.append(' '); } // Finally assign the property with the manifest classpath getProject().setNewProperty(name, buffer.toString().trim()); } /** * Sets the property name to hold the classpath value. * * @param name the property name */ public void setProperty(String name) { this.name = name; } /** * The JAR file to contain the classpath attribute in its manifest. * * @param jarfile the JAR file. Need not exist yet, but its parent * directory must exist on the other hand. */ public void setJarFile(File jarfile) { File parent = jarfile.getParentFile(); if (!parent.isDirectory()) { throw new BuildException("Jar's directory not found: %s", parent); } this.dir = parent; } /** * Sets the maximum parent directory levels allowed when computing * a relative path. * * @param levels the max level. Defaults to 2. */ public void setMaxParentLevels(int levels) { if (levels < 0) { throw new BuildException( "maxParentLevels must not be a negative number"); } this.maxParentLevels = levels; } /** * Adds the classpath to convert. * * @param path the classpath to convert. */ public void addClassPath(Path path) { this.path = path; } }