comparison graal/com.oracle.max.base/src/com/sun/max/program/Classpath.java @ 3733:e233f5660da4

Added Java files from Maxine project.
author Thomas Wuerthinger <thomas.wuerthinger@oracle.com>
date Sat, 17 Dec 2011 19:59:18 +0100
parents
children bc8527f3071c
comparison
equal deleted inserted replaced
3732:3e2e8b8abdaf 3733:e233f5660da4
1 /*
2 * Copyright (c) 2007, 2011, Oracle and/or its affiliates. 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.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23 package com.sun.max.program;
24
25 import java.io.*;
26 import java.util.*;
27 import java.util.zip.*;
28
29 import com.sun.max.io.*;
30
31 /**
32 */
33 public class Classpath {
34
35 private static final List<Entry> EMPTY_LIST = Collections.emptyList();
36
37 public static final Classpath EMPTY = new Classpath(EMPTY_LIST);
38
39 private final List<Entry> entries;
40
41 /**
42 * An entry in a classpath is a file system path that denotes an existing {@linkplain Directory directory},
43 * an existing {@linkplain Archive zip/jar} file or a {@linkplain PlainFile neither}.
44 */
45 public abstract static class Entry {
46
47 /**
48 * Gets the string representing the underlying path of this entry.
49 */
50 public final String path() {
51 return file().getPath();
52 }
53
54 /**
55 * Gets the File object representing the underlying path of this entry.
56 */
57 public abstract File file();
58
59 /**
60 * Determines if a given resource can be found under this entry.
61 *
62 * @param path a file path relative to this classpath entry. This values uses the '/' character as the path
63 * separator regardless of the {@linkplain File#separatorChar default} for the underlying platform.
64 */
65 public abstract boolean contains(String path);
66
67 /**
68 * Gets the contents of a file denoted by a given path that is relative to this classpath entry. If the denoted
69 * file does not exist under this classpath entry then {@code null} is returned. Any IO exception that occurs
70 * when reading is silently ignored.
71 *
72 * @param path a file path relative to this classpath entry. This values uses the '/' character as the path
73 * separator regardless of the {@linkplain File#separatorChar default} for the underlying platform.
74 */
75 abstract ClasspathFile readFile(String path);
76
77 public boolean isDirectory() {
78 return false;
79 }
80
81 public boolean isArchive() {
82 return false;
83 }
84
85 public boolean isPlainFile() {
86 return false;
87 }
88
89 @Override
90 public String toString() {
91 return path();
92 }
93
94 public ZipFile zipFile() {
95 return null;
96 }
97 }
98
99 /**
100 * Represents a classpath entry that is neither an existing directory nor an existing zip/jar archive file.
101 */
102 static final class PlainFile extends Entry {
103
104 private final File file;
105
106 public PlainFile(File file) {
107 this.file = file;
108 }
109
110 @Override
111 ClasspathFile readFile(String path) {
112 return null;
113 }
114
115 @Override
116 public File file() {
117 return file;
118 }
119
120 @Override
121 public boolean isPlainFile() {
122 return true;
123 }
124
125 @Override
126 public boolean contains(String path) {
127 return false;
128 }
129 }
130
131 /**
132 * Represents a classpath entry that is a path to an existing directory.
133 */
134 static final class Directory extends Entry {
135 private final File directory;
136
137 public Directory(File directory) {
138 this.directory = directory;
139 }
140
141 @Override
142 ClasspathFile readFile(String path) {
143 final File file = new File(directory, File.separatorChar == '/' ? path : path.replace('/', File.separatorChar));
144 if (file.exists()) {
145 try {
146 return new ClasspathFile(Files.toBytes(file), this);
147 } catch (IOException ioException) {
148 ProgramWarning.message("Error reading from " + file + ": " + ioException);
149 return null;
150 }
151 }
152 return null;
153 }
154
155 @Override
156 public File file() {
157 return directory;
158 }
159
160 @Override
161 public boolean isDirectory() {
162 return true;
163 }
164
165 @Override
166 public boolean contains(String path) {
167 return new File(directory, File.separatorChar == '/' ? path : path.replace('/', File.separatorChar)).exists();
168 }
169 }
170
171 /**
172 * Represents a classpath entry that is a path to an existing zip/jar archive file.
173 */
174 static final class Archive extends Entry {
175
176 private final File file;
177 private ZipFile zipFile;
178
179 public Archive(File file) {
180 this.file = file;
181 }
182
183 @Override
184 public ZipFile zipFile() {
185 if (zipFile == null && file != null) {
186 try {
187 zipFile = new ZipFile(file);
188 } catch (IOException e) {
189 ProgramWarning.message("Error opening ZIP file: " + file.getPath());
190 e.printStackTrace();
191 }
192 }
193 return zipFile;
194 }
195
196 @Override
197 public boolean contains(String path) {
198 final ZipFile zf = zipFile();
199 if (zf == null) {
200 return false;
201 }
202 return zf.getEntry(path) != null;
203 }
204
205 @Override
206 ClasspathFile readFile(String path) {
207 final ZipFile zf = zipFile();
208 if (zf == null) {
209 return null;
210 }
211 try {
212 final ZipEntry zipEntry = zf.getEntry(path);
213 if (zipEntry != null) {
214 return new ClasspathFile(readZipEntry(zf, zipEntry), this);
215 }
216 } catch (IOException ioException) {
217 //ProgramWarning.message("could not read ZIP file: " + file);
218 }
219 return null;
220 }
221
222 @Override
223 public File file() {
224 return file;
225 }
226
227 @Override
228 public boolean isArchive() {
229 return true;
230 }
231 }
232
233 /**
234 * Gets the ordered entries from which this classpath is composed.
235 *
236 * @return a sequence of {@code Entry} objects
237 */
238 public List<Entry> entries() {
239 return entries;
240 }
241
242 /**
243 * Creates a classpath {@link Entry} from a given file system path.
244 *
245 * @param path a file system path denoting a classpath entry
246 */
247 public static Entry createEntry(String path) {
248 final File pathFile = new File(path);
249 if (pathFile.isDirectory()) {
250 return new Directory(pathFile);
251 } else if (path.endsWith(".zip") || path.endsWith(".jar")) {
252 if (pathFile.exists() && pathFile.isFile()) {
253 return new Archive(pathFile);
254 }
255 }
256 //ProgramWarning.message("Class path entry is neither a directory nor a JAR file: " + path);
257 return new PlainFile(pathFile);
258 }
259
260 /**
261 * Creates a new classpath from an array of classpath entries.
262 *
263 * @param paths an array of classpath entries
264 */
265 public Classpath(String[] paths) {
266 final Entry[] entryArray = new Entry[paths.length];
267 for (int i = 0; i < paths.length; ++i) {
268 final String path = paths[i];
269 entryArray[i] = createEntry(path);
270 }
271 this.entries = Arrays.asList(entryArray);
272 }
273
274 /**
275 * Creates a new classpath from a sequence of classpath entries.
276 *
277 * @param paths a sequence of classpath entries
278 */
279 public Classpath(List<Entry> entries) {
280 this.entries = entries;
281 }
282
283 /**
284 * Creates a new classpath by parsing a string of classpath entries separated by the system dependent
285 * {@linkplain File#pathSeparator path separator}.
286 *
287 * @param paths a string of classpath entries separated by ':' or ';'
288 */
289 public Classpath(String paths) {
290 this(paths.split(File.pathSeparator));
291 }
292
293 /**
294 * Gets the classpath derived from the value of the {@code "java.ext.dirs"} system property.
295 *
296 * @see "http://java.sun.com/javase/6/docs/technotes/guides/extensions/extensions.html"
297 */
298 private static String extensionClasspath() {
299 final String extDirs = System.getProperty("java.ext.dirs");
300 if (extDirs != null) {
301 final StringBuilder buf = new StringBuilder();
302 for (String extDirPath : extDirs.split(File.pathSeparator)) {
303 final File extDir = new File(extDirPath);
304 if (extDir.isDirectory()) {
305 for (File file : extDir.listFiles()) {
306 if (file.isDirectory() ||
307 (file.isFile() && (file.getName().endsWith(".jar") || file.getName().endsWith(".zip")))) {
308 if (buf.length() != 0) {
309 buf.append(File.pathSeparatorChar);
310 }
311 buf.append(file.getAbsolutePath());
312 }
313 }
314 } else {
315 // Ignore non-directory
316 }
317 }
318 if (buf.length() != 0) {
319 buf.append(File.pathSeparatorChar);
320 return buf.toString();
321 }
322 }
323 return "";
324 }
325
326 /**
327 * Gets a classpath corresponding to the class search order used by the application class loader.
328 */
329 public static Classpath fromSystem() {
330 final String value = System.getProperty("sun.boot.class.path") + File.pathSeparator + extensionClasspath() + System.getProperty("java.class.path");
331 return new Classpath(value.split(File.pathSeparator));
332 }
333
334 /**
335 * Gets a classpath corresponding to the class search order used by the boot class loader.
336 * */
337 public static Classpath bootClassPath() {
338 return bootClassPath(null);
339 }
340
341 /**
342 * Gets a classpath corresponding to the class search order used by the boot class loader.
343 * @param extraPath an additional path to append to the system property or null if none
344 */
345 public static Classpath bootClassPath(String extraPath) {
346 String value = System.getProperty("sun.boot.class.path");
347 if (value == null) {
348 return EMPTY;
349 }
350 if (extraPath != null) {
351 value = value + File.pathSeparator + extraPath;
352 }
353 return new Classpath(value.split(File.pathSeparator));
354 }
355
356 /**
357 * Gets a new classpath obtained by prepending a given classpath to this class classpath.
358 *
359 * @param classpath the classpath to prepend to this classpath
360 * @return the result of prepending {@code classpath} to this classpath
361 */
362 public Classpath prepend(Classpath classpath) {
363 ArrayList<Entry> entries = new ArrayList<Entry>(this.entries.size() + classpath.entries.size());
364 entries.addAll(classpath.entries);
365 entries.addAll(this.entries);
366 return new Classpath(entries);
367 }
368
369 /**
370 * Gets a new classpath obtained by prepending a given classpath to this class classpath.
371 *
372 * @param classpath the classpath to prepend to this classpath
373 * @return the result of prepending {@code classpath} to this classpath
374 */
375 public Classpath prepend(String path) {
376 ArrayList<Entry> entries = new ArrayList<Entry>(this.entries.size());
377 entries.add(createEntry(path));
378 entries.addAll(this.entries);
379 return new Classpath(entries);
380 }
381
382 /**
383 * Searches for a class file denoted by a given class name on this classpath and returns its contents in a byte array if
384 * found. Any IO exception that occurs when reading is silently ignored.
385 *
386 * @param className a fully qualified class name (e.g. "java.lang.Class")
387 * @return the contents of the file available on the classpath whose name is computed as
388 * {@code className.replace('.', '/')}. If no such file is available on this class path or if
389 * reading the file produces an IO exception, then null is returned.
390 */
391 public ClasspathFile readClassFile(String className) {
392 return readFile(className, ".class");
393 }
394
395 /**
396 * Searches for a file denoted by a given class name on this classpath and returns its contents in a byte array if
397 * found. Any IO exception that occurs when reading is silently ignored.
398 *
399 * @param className a fully qualified class name (e.g. "java.lang.Class")
400 * @param extension a file extension
401 * @return the contents of the file available on the classpath whose name is computed as
402 * {@code className.replace('.', '/') + extension}. If no such file is available on this class path or if
403 * reading the file produces an IO exception, then null is returned.
404 */
405 public ClasspathFile readFile(String className, String extension) {
406 final String path = className.replace('.', '/') + extension;
407 for (Entry entry : entries()) {
408 ClasspathFile classpathFile = entry.readFile(path);
409 if (classpathFile != null) {
410 return classpathFile;
411 }
412 }
413 return null;
414 }
415
416 /**
417 * Searches for an existing file corresponding to a directory entry in this classpath composed with a given path
418 * suffix.
419 *
420 * @param suffix a file path relative to a directory entry of this classpath
421 * @return a file corresponding to the {@linkplain File#File(File, String) composition} of the first directory entry
422 * of this classpath with {@code suffix} that denotes an existing file or null if so such file exists
423 */
424 public File findFile(String suffix) {
425 for (Entry entry : entries()) {
426 if (entry instanceof Directory) {
427 final File file = new File(((Directory) entry).directory, suffix);
428 if (file.exists()) {
429 return file;
430 }
431 }
432 }
433 return null;
434 }
435
436 public static byte[] readZipEntry(ZipFile zipFile, ZipEntry zipEntry) throws IOException {
437 final byte[] bytes = new byte[(int) zipEntry.getSize()];
438 final InputStream zipStream = new BufferedInputStream(zipFile.getInputStream(zipEntry), bytes.length);
439 try {
440 int offset = 0;
441 while (offset < bytes.length) {
442 final int n = zipStream.read(bytes, offset, bytes.length - offset);
443 if (n <= 0) {
444 //ProgramWarning.message("truncated ZIP file: " + zipFile);
445 }
446 offset += n;
447 }
448 } finally {
449 zipStream.close();
450 }
451 return bytes;
452 }
453
454 @Override
455 public String toString() {
456 if (entries == null || entries.isEmpty()) {
457 return "";
458 }
459 String s = entries.toString().replace(", ", File.pathSeparator);
460 return s.substring(1, s.length() - 1);
461 }
462
463 /**
464 * Converts this object to a String array with one array element for each classpath entry.
465 *
466 * @return the newly created String array with one element per classpath entry
467 */
468 public String[] toStringArray() {
469 final String[] result = new String[entries().size()];
470 int z = 0;
471 for (Classpath.Entry e : entries()) {
472 result[z] = e.path();
473 z++;
474 }
475 return result;
476 }
477 }