001: /*
002: * Copyright 1994-2007 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: */
025:
026: package sun.tools.java;
027:
028: import java.util.Enumeration;
029: import java.util.Hashtable;
030: import java.io.File;
031: import java.io.IOException;
032: import java.util.zip.*;
033:
034: /**
035: * This class is used to represent a class path, which can contain both
036: * directories and zip files.
037: *
038: * WARNING: The contents of this source file are not part of any
039: * supported API. Code that depends on them does so at its own risk:
040: * they are subject to change or removal without notice.
041: */
042: public class ClassPath {
043: static final char dirSeparator = File.pathSeparatorChar;
044:
045: /**
046: * The original class path string
047: */
048: String pathstr;
049:
050: /**
051: * List of class path entries
052: */
053: private ClassPathEntry[] path;
054:
055: /**
056: * Build a class path from the specified path string
057: */
058: public ClassPath(String pathstr) {
059: init(pathstr);
060: }
061:
062: /**
063: * Build a class path from the specified array of class path
064: * element strings. This constructor, and the corresponding
065: * "init" method, were added as part of the fix for 6473331, which
066: * adds support for Class-Path manifest entries in JAR files to
067: * rmic. It is conceivable that the value of a Class-Path
068: * manifest entry will contain a path separator, which would cause
069: * incorrect behavior if the expanded path were passed to the
070: * previous constructor as a single path-separator-delimited
071: * string; use of this constructor avoids that problem.
072: */
073: public ClassPath(String[] patharray) {
074: init(patharray);
075: }
076:
077: /**
078: * Build a default class path from the path strings specified by
079: * the properties sun.boot.class.path and env.class.path, in that
080: * order.
081: */
082: public ClassPath() {
083: String syscp = System.getProperty("sun.boot.class.path");
084: String envcp = System.getProperty("env.class.path");
085: if (envcp == null)
086: envcp = ".";
087: String cp = syscp + File.pathSeparator + envcp;
088: init(cp);
089: }
090:
091: private void init(String pathstr) {
092: int i, j, n;
093: // Save original class path string
094: this .pathstr = pathstr;
095:
096: if (pathstr.length() == 0) {
097: this .path = new ClassPathEntry[0];
098: }
099:
100: // Count the number of path separators
101: i = n = 0;
102: while ((i = pathstr.indexOf(dirSeparator, i)) != -1) {
103: n++;
104: i++;
105: }
106: // Build the class path
107: ClassPathEntry[] path = new ClassPathEntry[n + 1];
108: int len = pathstr.length();
109: for (i = n = 0; i < len; i = j + 1) {
110: if ((j = pathstr.indexOf(dirSeparator, i)) == -1) {
111: j = len;
112: }
113: if (i == j) {
114: path[n] = new ClassPathEntry();
115: path[n++].dir = new File(".");
116: } else {
117: File file = new File(pathstr.substring(i, j));
118: if (file.isFile()) {
119: try {
120: ZipFile zip = new ZipFile(file);
121: path[n] = new ClassPathEntry();
122: path[n++].zip = zip;
123: } catch (ZipException e) {
124: } catch (IOException e) {
125: // Ignore exceptions, at least for now...
126: }
127: } else {
128: path[n] = new ClassPathEntry();
129: path[n++].dir = file;
130: }
131: }
132: }
133: // Trim class path to exact size
134: this .path = new ClassPathEntry[n];
135: System.arraycopy((Object) path, 0, (Object) this .path, 0, n);
136: }
137:
138: private void init(String[] patharray) {
139: // Save original class path string
140: if (patharray.length == 0) {
141: this .pathstr = "";
142: } else {
143: StringBuilder sb = new StringBuilder(patharray[0]);
144: for (int i = 1; i < patharray.length; i++) {
145: sb.append(File.separator);
146: sb.append(patharray[i]);
147: }
148: this .pathstr = sb.toString();
149: }
150:
151: // Build the class path
152: ClassPathEntry[] path = new ClassPathEntry[patharray.length];
153: int n = 0;
154: for (String name : patharray) {
155: File file = new File(name);
156: if (file.isFile()) {
157: try {
158: ZipFile zip = new ZipFile(file);
159: path[n] = new ClassPathEntry();
160: path[n++].zip = zip;
161: } catch (ZipException e) {
162: } catch (IOException e) {
163: // Ignore exceptions, at least for now...
164: }
165: } else {
166: path[n] = new ClassPathEntry();
167: path[n++].dir = file;
168: }
169: }
170: // Trim class path to exact size
171: this .path = new ClassPathEntry[n];
172: System.arraycopy((Object) path, 0, (Object) this .path, 0, n);
173: }
174:
175: /**
176: * Find the specified directory in the class path
177: */
178: public ClassFile getDirectory(String name) {
179: return getFile(name, true);
180: }
181:
182: /**
183: * Load the specified file from the class path
184: */
185: public ClassFile getFile(String name) {
186: return getFile(name, false);
187: }
188:
189: private final String fileSeparatorChar = "" + File.separatorChar;
190:
191: private ClassFile getFile(String name, boolean isDirectory) {
192: String subdir = name;
193: String basename = "";
194: if (!isDirectory) {
195: int i = name.lastIndexOf(File.separatorChar);
196: subdir = name.substring(0, i + 1);
197: basename = name.substring(i + 1);
198: } else if (!subdir.equals("")
199: && !subdir.endsWith(fileSeparatorChar)) {
200: // zip files are picky about "foo" vs. "foo/".
201: // also, the getFiles caches are keyed with a trailing /
202: subdir = subdir + File.separatorChar;
203: name = subdir; // Note: isDirectory==true & basename==""
204: }
205: for (int i = 0; i < path.length; i++) {
206: if (path[i].zip != null) {
207: String newname = name.replace(File.separatorChar, '/');
208: ZipEntry entry = path[i].zip.getEntry(newname);
209: if (entry != null) {
210: return new ClassFile(path[i].zip, entry);
211: }
212: } else {
213: File file = new File(path[i].dir.getPath(), name);
214: String list[] = path[i].getFiles(subdir);
215: if (isDirectory) {
216: if (list.length > 0) {
217: return new ClassFile(file);
218: }
219: } else {
220: for (int j = 0; j < list.length; j++) {
221: if (basename.equals(list[j])) {
222: // Don't bother checking !file.isDir,
223: // since we only look for names which
224: // cannot already be packages (foo.java, etc).
225: return new ClassFile(file);
226: }
227: }
228: }
229: }
230: }
231: return null;
232: }
233:
234: /**
235: * Returns list of files given a package name and extension.
236: */
237: public Enumeration getFiles(String pkg, String ext) {
238: Hashtable files = new Hashtable();
239: for (int i = path.length; --i >= 0;) {
240: if (path[i].zip != null) {
241: Enumeration e = path[i].zip.entries();
242: while (e.hasMoreElements()) {
243: ZipEntry entry = (ZipEntry) e.nextElement();
244: String name = entry.getName();
245: name = name.replace('/', File.separatorChar);
246: if (name.startsWith(pkg) && name.endsWith(ext)) {
247: files.put(name, new ClassFile(path[i].zip,
248: entry));
249: }
250: }
251: } else {
252: String[] list = path[i].getFiles(pkg);
253: for (int j = 0; j < list.length; j++) {
254: String name = list[j];
255: if (name.endsWith(ext)) {
256: name = pkg + File.separatorChar + name;
257: File file = new File(path[i].dir.getPath(),
258: name);
259: files.put(name, new ClassFile(file));
260: }
261: }
262: }
263: }
264: return files.elements();
265: }
266:
267: /**
268: * Release resources.
269: */
270: public void close() throws IOException {
271: for (int i = path.length; --i >= 0;) {
272: if (path[i].zip != null) {
273: path[i].zip.close();
274: }
275: }
276: }
277:
278: /**
279: * Returns original class path string
280: */
281: public String toString() {
282: return pathstr;
283: }
284: }
285:
286: /**
287: * A class path entry, which can either be a directory or an open zip file.
288: */
289: class ClassPathEntry {
290: File dir;
291: ZipFile zip;
292:
293: Hashtable subdirs = new Hashtable(29); // cache of sub-directory listings
294:
295: String[] getFiles(String subdir) {
296: String files[] = (String[]) subdirs.get(subdir);
297: if (files == null) {
298: // search the directory, exactly once
299: File sd = new File(dir.getPath(), subdir);
300: if (sd.isDirectory()) {
301: files = sd.list();
302: if (files == null) {
303: // should not happen, but just in case, fail silently
304: files = new String[0];
305: }
306: if (files.length == 0) {
307: String nonEmpty[] = { "" };
308: files = nonEmpty;
309: }
310: } else {
311: files = new String[0];
312: }
313: subdirs.put(subdir, files);
314: }
315: return files;
316: }
317:
318: }
|