blob: 41e87b7db7183cbdefaca8851b7b31552fd9a6bc [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 1994-2007 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26package sun.tools.java;
27
28import java.util.Enumeration;
29import java.util.Hashtable;
30import java.io.File;
31import java.io.IOException;
32import java.util.zip.*;
33
34/**
35 * This class is used to represent a class path, which can contain both
36 * directories and zip files.
37 *
38 * WARNING: The contents of this source file are not part of any
39 * supported API. Code that depends on them does so at its own risk:
40 * they are subject to change or removal without notice.
41 */
42public
43class ClassPath {
44 static final char dirSeparator = File.pathSeparatorChar;
45
46 /**
47 * The original class path string
48 */
49 String pathstr;
50
51 /**
52 * List of class path entries
53 */
54 private ClassPathEntry[] path;
55
56 /**
57 * Build a class path from the specified path string
58 */
59 public ClassPath(String pathstr) {
60 init(pathstr);
61 }
62
63 /**
64 * Build a class path from the specified array of class path
65 * element strings. This constructor, and the corresponding
66 * "init" method, were added as part of the fix for 6473331, which
67 * adds support for Class-Path manifest entries in JAR files to
68 * rmic. It is conceivable that the value of a Class-Path
69 * manifest entry will contain a path separator, which would cause
70 * incorrect behavior if the expanded path were passed to the
71 * previous constructor as a single path-separator-delimited
72 * string; use of this constructor avoids that problem.
73 */
74 public ClassPath(String[] patharray) {
75 init(patharray);
76 }
77
78 /**
79 * Build a default class path from the path strings specified by
80 * the properties sun.boot.class.path and env.class.path, in that
81 * order.
82 */
83 public ClassPath() {
84 String syscp = System.getProperty("sun.boot.class.path");
85 String envcp = System.getProperty("env.class.path");
86 if (envcp == null) envcp = ".";
87 String cp = syscp + File.pathSeparator + envcp;
88 init(cp);
89 }
90
91 private void init(String pathstr) {
92 int i, j, n;
93 // Save original class path string
94 this.pathstr = pathstr;
95
96 if (pathstr.length() == 0) {
97 this.path = new ClassPathEntry[0];
98 }
99
100 // Count the number of path separators
101 i = n = 0;
102 while ((i = pathstr.indexOf(dirSeparator, i)) != -1) {
103 n++; i++;
104 }
105 // Build the class path
106 ClassPathEntry[] path = new ClassPathEntry[n+1];
107 int len = pathstr.length();
108 for (i = n = 0; i < len; i = j + 1) {
109 if ((j = pathstr.indexOf(dirSeparator, i)) == -1) {
110 j = len;
111 }
112 if (i == j) {
113 path[n] = new ClassPathEntry();
114 path[n++].dir = new File(".");
115 } else {
116 File file = new File(pathstr.substring(i, j));
117 if (file.isFile()) {
118 try {
119 ZipFile zip = new ZipFile(file);
120 path[n] = new ClassPathEntry();
121 path[n++].zip = zip;
122 } catch (ZipException e) {
123 } catch (IOException e) {
124 // Ignore exceptions, at least for now...
125 }
126 } else {
127 path[n] = new ClassPathEntry();
128 path[n++].dir = file;
129 }
130 }
131 }
132 // Trim class path to exact size
133 this.path = new ClassPathEntry[n];
134 System.arraycopy((Object)path, 0, (Object)this.path, 0, n);
135 }
136
137 private void init(String[] patharray) {
138 // Save original class path string
139 if (patharray.length == 0) {
140 this.pathstr = "";
141 } else {
142 StringBuilder sb = new StringBuilder(patharray[0]);
143 for (int i = 1; i < patharray.length; i++) {
144 sb.append(File.separator);
145 sb.append(patharray[i]);
146 }
147 this.pathstr = sb.toString();
148 }
149
150 // Build the class path
151 ClassPathEntry[] path = new ClassPathEntry[patharray.length];
152 int n = 0;
153 for (String name : patharray) {
154 File file = new File(name);
155 if (file.isFile()) {
156 try {
157 ZipFile zip = new ZipFile(file);
158 path[n] = new ClassPathEntry();
159 path[n++].zip = zip;
160 } catch (ZipException e) {
161 } catch (IOException e) {
162 // Ignore exceptions, at least for now...
163 }
164 } else {
165 path[n] = new ClassPathEntry();
166 path[n++].dir = file;
167 }
168 }
169 // Trim class path to exact size
170 this.path = new ClassPathEntry[n];
171 System.arraycopy((Object)path, 0, (Object)this.path, 0, n);
172 }
173
174 /**
175 * Find the specified directory in the class path
176 */
177 public ClassFile getDirectory(String name) {
178 return getFile(name, true);
179 }
180
181 /**
182 * Load the specified file from the class path
183 */
184 public ClassFile getFile(String name) {
185 return getFile(name, false);
186 }
187
188 private final String fileSeparatorChar = "" + File.separatorChar;
189
190 private ClassFile getFile(String name, boolean isDirectory) {
191 String subdir = name;
192 String basename = "";
193 if (!isDirectory) {
194 int i = name.lastIndexOf(File.separatorChar);
195 subdir = name.substring(0, i + 1);
196 basename = name.substring(i + 1);
197 } else if (!subdir.equals("")
198 && !subdir.endsWith(fileSeparatorChar)) {
199 // zip files are picky about "foo" vs. "foo/".
200 // also, the getFiles caches are keyed with a trailing /
201 subdir = subdir + File.separatorChar;
202 name = subdir; // Note: isDirectory==true & basename==""
203 }
204 for (int i = 0; i < path.length; i++) {
205 if (path[i].zip != null) {
206 String newname = name.replace(File.separatorChar, '/');
207 ZipEntry entry = path[i].zip.getEntry(newname);
208 if (entry != null) {
209 return new ClassFile(path[i].zip, entry);
210 }
211 } else {
212 File file = new File(path[i].dir.getPath(), name);
213 String list[] = path[i].getFiles(subdir);
214 if (isDirectory) {
215 if (list.length > 0) {
216 return new ClassFile(file);
217 }
218 } else {
219 for (int j = 0; j < list.length; j++) {
220 if (basename.equals(list[j])) {
221 // Don't bother checking !file.isDir,
222 // since we only look for names which
223 // cannot already be packages (foo.java, etc).
224 return new ClassFile(file);
225 }
226 }
227 }
228 }
229 }
230 return null;
231 }
232
233 /**
234 * Returns list of files given a package name and extension.
235 */
236 public Enumeration getFiles(String pkg, String ext) {
237 Hashtable files = new Hashtable();
238 for (int i = path.length; --i >= 0; ) {
239 if (path[i].zip != null) {
240 Enumeration e = path[i].zip.entries();
241 while (e.hasMoreElements()) {
242 ZipEntry entry = (ZipEntry)e.nextElement();
243 String name = entry.getName();
244 name = name.replace('/', File.separatorChar);
245 if (name.startsWith(pkg) && name.endsWith(ext)) {
246 files.put(name, new ClassFile(path[i].zip, entry));
247 }
248 }
249 } else {
250 String[] list = path[i].getFiles(pkg);
251 for (int j = 0; j < list.length; j++) {
252 String name = list[j];
253 if (name.endsWith(ext)) {
254 name = pkg + File.separatorChar + name;
255 File file = new File(path[i].dir.getPath(), name);
256 files.put(name, new ClassFile(file));
257 }
258 }
259 }
260 }
261 return files.elements();
262 }
263
264 /**
265 * Release resources.
266 */
267 public void close() throws IOException {
268 for (int i = path.length; --i >= 0; ) {
269 if (path[i].zip != null) {
270 path[i].zip.close();
271 }
272 }
273 }
274
275 /**
276 * Returns original class path string
277 */
278 public String toString() {
279 return pathstr;
280 }
281}
282
283/**
284 * A class path entry, which can either be a directory or an open zip file.
285 */
286class ClassPathEntry {
287 File dir;
288 ZipFile zip;
289
290 Hashtable subdirs = new Hashtable(29); // cache of sub-directory listings
291 String[] getFiles(String subdir) {
292 String files[] = (String[]) subdirs.get(subdir);
293 if (files == null) {
294 // search the directory, exactly once
295 File sd = new File(dir.getPath(), subdir);
296 if (sd.isDirectory()) {
297 files = sd.list();
298 if (files == null) {
299 // should not happen, but just in case, fail silently
300 files = new String[0];
301 }
302 if (files.length == 0) {
303 String nonEmpty[] = { "" };
304 files = nonEmpty;
305 }
306 } else {
307 files = new String[0];
308 }
309 subdirs.put(subdir, files);
310 }
311 return files;
312 }
313
314}