001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: *
017: */
018: package org.apache.tools.ant.taskdefs;
019:
020: import java.io.File;
021: import java.io.UnsupportedEncodingException;
022:
023: import org.apache.tools.ant.Task;
024: import org.apache.tools.ant.types.Path;
025: import org.apache.tools.ant.BuildException;
026: import org.apache.tools.ant.launch.Locator;
027: import org.apache.tools.ant.util.FileUtils;
028:
029: /**
030: * Converts a Path into a property suitable as a Manifest classpath.
031: *
032: * @since Ant 1.7
033: *
034: * @ant.task category="property"
035: */
036: public class ManifestClassPath extends Task {
037:
038: /** The property name to hold the classpath value. */
039: private String name;
040:
041: /** The directory the classpath will be relative from. */
042: private File dir;
043:
044: /** The maximum parent directory level to traverse. */
045: private int maxParentLevels = 2;
046:
047: /** The classpath to convert. */
048: private Path path;
049:
050: /**
051: * Sets a property, which must not already exist, with a space
052: * separated list of files and directories relative to the jar
053: * file's parent directory.
054: */
055: public void execute() {
056: if (name == null) {
057: throw new BuildException("Missing 'property' attribute!");
058: }
059: if (dir == null) {
060: throw new BuildException("Missing 'jarfile' attribute!");
061: }
062: if (getProject().getProperty(name) != null) {
063: throw new BuildException("Property '" + name
064: + "' already set!");
065: }
066: if (path == null) {
067: throw new BuildException("Missing nested <classpath>!");
068: }
069:
070: // Normalize the reference directory (containing the jar)
071: final FileUtils fileUtils = FileUtils.getFileUtils();
072: dir = fileUtils.normalize(dir.getAbsolutePath());
073:
074: // Create as many directory prefixes as parent levels to traverse,
075: // in addition to the reference directory itself
076: File currDir = dir;
077: String[] dirs = new String[maxParentLevels + 1];
078: for (int i = 0; i < maxParentLevels + 1; ++i) {
079: dirs[i] = currDir.getAbsolutePath() + File.separatorChar;
080: currDir = currDir.getParentFile();
081: if (currDir == null) {
082: maxParentLevels = i + 1;
083: break;
084: }
085: }
086:
087: String[] elements = path.list();
088: StringBuffer buffer = new StringBuffer();
089: StringBuffer element = new StringBuffer();
090: for (int i = 0; i < elements.length; ++i) {
091: // Normalize the current file
092: File pathEntry = new File(elements[i]);
093: pathEntry = fileUtils
094: .normalize(pathEntry.getAbsolutePath());
095: String fullPath = pathEntry.getAbsolutePath();
096:
097: // Find the longest prefix shared by the current file
098: // and the reference directory.
099: String relPath = null;
100: for (int j = 0; j <= maxParentLevels; ++j) {
101: String dir = dirs[j];
102: if (!fullPath.startsWith(dir)) {
103: continue;
104: }
105:
106: // We have a match! Add as many ../ as parent
107: // directory traversed to get the relative path
108: element.setLength(0);
109: for (int k = 0; k < j; ++k) {
110: element.append("..");
111: element.append(File.separatorChar);
112: }
113: element.append(fullPath.substring(dir.length()));
114: relPath = element.toString();
115: break;
116: }
117:
118: // No match, so bail out!
119: if (relPath == null) {
120: throw new BuildException(
121: "No suitable relative path from " + dir
122: + " to " + fullPath);
123: }
124:
125: // Manifest's ClassPath: attribute always uses forward
126: // slashes '/', and is space-separated. Ant will properly
127: // format it on 72 columns with proper line continuation
128: if (File.separatorChar != '/') {
129: relPath = relPath.replace(File.separatorChar, '/');
130: }
131: if (pathEntry.isDirectory()) {
132: relPath = relPath + '/';
133: }
134: try {
135: relPath = Locator.encodeURI(relPath);
136: } catch (UnsupportedEncodingException exc) {
137: throw new BuildException(exc);
138: }
139: buffer.append(relPath);
140: buffer.append(' ');
141: }
142:
143: // Finally assign the property with the manifest classpath
144: getProject().setNewProperty(name, buffer.toString().trim());
145: }
146:
147: /**
148: * Sets the property name to hold the classpath value.
149: *
150: * @param name the property name
151: */
152: public void setProperty(String name) {
153: this .name = name;
154: }
155:
156: /**
157: * The JAR file to contain the classpath attribute in its manifest.
158: *
159: * @param jarfile the JAR file. Need not exist yet, but its parent
160: * directory must exist on the other hand.
161: */
162: public void setJarFile(File jarfile) {
163: File parent = jarfile.getParentFile();
164: if (!parent.isDirectory()) {
165: throw new BuildException("Jar's directory not found: "
166: + parent);
167: }
168: this .dir = parent;
169: }
170:
171: /**
172: * Sets the maximum parent directory levels allowed when computing
173: * a relative path.
174: *
175: * @param levels the max level. Defaults to 2.
176: */
177: public void setMaxParentLevels(int levels) {
178: this .maxParentLevels = levels;
179: }
180:
181: /**
182: * Adds the classpath to convert.
183: *
184: * @param path the classpath to convert.
185: */
186: public void addClassPath(Path path) {
187: this.path = path;
188: }
189:
190: }
|