src/share/classes/com/sun/tools/javac/file/Paths.java

changeset 50
b9bcea8bbe24
parent 14
58039502942e
child 54
eaf608c64fec
equal deleted inserted replaced
48:c2abfb92ba69 50:b9bcea8bbe24
1 /*
2 * Copyright 2003-2006 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
26 package com.sun.tools.javac.file;
27
28 import java.io.File;
29 import java.io.IOException;
30 import java.util.HashMap;
31 import java.util.HashSet;
32 import java.util.Map;
33 import java.util.Set;
34 import java.util.jar.JarFile;
35 import java.util.jar.Manifest;
36 import java.util.jar.Attributes;
37 import java.util.Collection;
38 import java.util.Collections;
39 import java.util.LinkedHashSet;
40 import java.util.Iterator;
41 import java.util.StringTokenizer;
42 import java.util.zip.ZipFile;
43 import java.util.ArrayList;
44 import java.util.concurrent.ConcurrentHashMap;
45 import java.util.concurrent.locks.Lock;
46 import java.util.concurrent.locks.ReentrantLock;
47 import javax.tools.JavaFileManager.Location;
48
49 import com.sun.tools.javac.code.Lint;
50 import com.sun.tools.javac.util.Context;
51 import com.sun.tools.javac.util.Log;
52 import com.sun.tools.javac.util.Options;
53
54 import static javax.tools.StandardLocation.*;
55 import static com.sun.tools.javac.main.OptionName.*;
56
57 /** This class converts command line arguments, environment variables
58 * and system properties (in File.pathSeparator-separated String form)
59 * into a boot class path, user class path, and source path (in
60 * Collection<String> form).
61 *
62 * <p><b>This is NOT part of any API supported by Sun Microsystems. If
63 * you write code that depends on this, you do so at your own risk.
64 * This code and its internal interfaces are subject to change or
65 * deletion without notice.</b>
66 */
67 public class Paths {
68
69 /** The context key for the todo list */
70 protected static final Context.Key<Paths> pathsKey =
71 new Context.Key<Paths>();
72
73 /** Get the Paths instance for this context.
74 * @param context the context
75 * @return the Paths instance for this context
76 */
77 public static Paths instance(Context context) {
78 Paths instance = context.get(pathsKey);
79 if (instance == null)
80 instance = new Paths(context);
81 return instance;
82 }
83
84 /** The log to use for warning output */
85 private Log log;
86
87 /** Collection of command-line options */
88 private Options options;
89
90 /** Handler for -Xlint options */
91 private Lint lint;
92
93 private static boolean NON_BATCH_MODE = System.getProperty("nonBatchMode") != null;// TODO: Use -XD compiler switch for this.
94 private static Map<File, PathEntry> pathExistanceCache = new ConcurrentHashMap<File, PathEntry>();
95 private static Map<File, java.util.List<File>> manifestEntries = new ConcurrentHashMap<File, java.util.List<File>>();
96 private static Map<File, Boolean> isDirectory = new ConcurrentHashMap<File, Boolean>();
97 private static Lock lock = new ReentrantLock();
98
99 public static void clearPathExistanceCache() {
100 pathExistanceCache.clear();
101 }
102
103 static class PathEntry {
104 boolean exists = false;
105 boolean isFile = false;
106 File cannonicalPath = null;
107 }
108
109 protected Paths(Context context) {
110 context.put(pathsKey, this);
111 pathsForLocation = new HashMap<Location,Path>(16);
112 setContext(context);
113 }
114
115 void setContext(Context context) {
116 log = Log.instance(context);
117 options = Options.instance(context);
118 lint = Lint.instance(context);
119 }
120
121 /** Whether to warn about non-existent path elements */
122 private boolean warn;
123
124 private Map<Location, Path> pathsForLocation;
125
126 private boolean inited = false; // TODO? caching bad?
127
128 /**
129 * rt.jar as found on the default bootclass path. If the user specified a
130 * bootclasspath, null is used.
131 */
132 private File bootClassPathRtJar = null;
133
134 Path getPathForLocation(Location location) {
135 Path path = pathsForLocation.get(location);
136 if (path == null)
137 setPathForLocation(location, null);
138 return pathsForLocation.get(location);
139 }
140
141 void setPathForLocation(Location location, Iterable<? extends File> path) {
142 // TODO? if (inited) throw new IllegalStateException
143 // TODO: otherwise reset sourceSearchPath, classSearchPath as needed
144 Path p;
145 if (path == null) {
146 if (location == CLASS_PATH)
147 p = computeUserClassPath();
148 else if (location == PLATFORM_CLASS_PATH)
149 p = computeBootClassPath();
150 else if (location == ANNOTATION_PROCESSOR_PATH)
151 p = computeAnnotationProcessorPath();
152 else if (location == SOURCE_PATH)
153 p = computeSourcePath();
154 else
155 // no defaults for other paths
156 p = null;
157 } else {
158 p = new Path();
159 for (File f: path)
160 p.addFile(f, warn); // TODO: is use of warn appropriate?
161 }
162 pathsForLocation.put(location, p);
163 }
164
165 protected void lazy() {
166 if (!inited) {
167 warn = lint.isEnabled(Lint.LintCategory.PATH);
168
169 pathsForLocation.put(PLATFORM_CLASS_PATH, computeBootClassPath());
170 pathsForLocation.put(CLASS_PATH, computeUserClassPath());
171 pathsForLocation.put(SOURCE_PATH, computeSourcePath());
172
173 inited = true;
174 }
175 }
176
177 public Collection<File> bootClassPath() {
178 lazy();
179 return Collections.unmodifiableCollection(getPathForLocation(PLATFORM_CLASS_PATH));
180 }
181 public Collection<File> userClassPath() {
182 lazy();
183 return Collections.unmodifiableCollection(getPathForLocation(CLASS_PATH));
184 }
185 public Collection<File> sourcePath() {
186 lazy();
187 Path p = getPathForLocation(SOURCE_PATH);
188 return p == null || p.size() == 0
189 ? null
190 : Collections.unmodifiableCollection(p);
191 }
192
193 boolean isBootClassPathRtJar(File file) {
194 return file.equals(bootClassPathRtJar);
195 }
196
197 private static class PathIterator implements Iterable<String> {
198 private int pos = 0;
199 private final String path;
200 private final String emptyPathDefault;
201
202 public PathIterator(String path, String emptyPathDefault) {
203 this.path = path;
204 this.emptyPathDefault = emptyPathDefault;
205 }
206 public PathIterator(String path) { this(path, null); }
207 public Iterator<String> iterator() {
208 return new Iterator<String>() {
209 public boolean hasNext() {
210 return pos <= path.length();
211 }
212 public String next() {
213 int beg = pos;
214 int end = path.indexOf(File.pathSeparator, beg);
215 if (end == -1)
216 end = path.length();
217 pos = end + 1;
218
219 if (beg == end && emptyPathDefault != null)
220 return emptyPathDefault;
221 else
222 return path.substring(beg, end);
223 }
224 public void remove() {
225 throw new UnsupportedOperationException();
226 }
227 };
228 }
229 }
230
231 private class Path extends LinkedHashSet<File> {
232 private static final long serialVersionUID = 0;
233
234 private boolean expandJarClassPaths = false;
235 private Set<File> canonicalValues = new HashSet<File>();
236
237 public Path expandJarClassPaths(boolean x) {
238 expandJarClassPaths = x;
239 return this;
240 }
241
242 /** What to use when path element is the empty string */
243 private String emptyPathDefault = null;
244
245 public Path emptyPathDefault(String x) {
246 emptyPathDefault = x;
247 return this;
248 }
249
250 public Path() { super(); }
251
252 public Path addDirectories(String dirs, boolean warn) {
253 if (dirs != null)
254 for (String dir : new PathIterator(dirs))
255 addDirectory(dir, warn);
256 return this;
257 }
258
259 public Path addDirectories(String dirs) {
260 return addDirectories(dirs, warn);
261 }
262
263 private void addDirectory(String dir, boolean warn) {
264 if (! new File(dir).isDirectory()) {
265 if (warn)
266 log.warning("dir.path.element.not.found", dir);
267 return;
268 }
269
270 File[] files = new File(dir).listFiles();
271 if (files == null)
272 return;
273
274 for (File direntry : files) {
275 if (isArchive(direntry))
276 addFile(direntry, warn);
277 }
278 }
279
280 public Path addFiles(String files, boolean warn) {
281 if (files != null)
282 for (String file : new PathIterator(files, emptyPathDefault))
283 addFile(file, warn);
284 return this;
285 }
286
287 public Path addFiles(String files) {
288 return addFiles(files, warn);
289 }
290
291 public Path addFile(String file, boolean warn) {
292 addFile(new File(file), warn);
293 return this;
294 }
295
296 public void addFile(File file, boolean warn) {
297 boolean foundInCache = false;
298 PathEntry pe = null;
299 if (!NON_BATCH_MODE) {
300 pe = pathExistanceCache.get(file);
301 if (pe != null) {
302 foundInCache = true;
303 }
304 else {
305 pe = new PathEntry();
306 }
307 }
308 else {
309 pe = new PathEntry();
310 }
311
312 File canonFile;
313 try {
314 if (!foundInCache) {
315 pe.cannonicalPath = file.getCanonicalFile();
316 }
317 else {
318 canonFile = pe.cannonicalPath;
319 }
320 } catch (IOException e) {
321 pe.cannonicalPath = canonFile = file;
322 }
323
324 if (contains(file) || canonicalValues.contains(pe.cannonicalPath)) {
325 /* Discard duplicates and avoid infinite recursion */
326 return;
327 }
328
329 if (!foundInCache) {
330 pe.exists = file.exists();
331 pe.isFile = file.isFile();
332 if (!NON_BATCH_MODE) {
333 pathExistanceCache.put(file, pe);
334 }
335 }
336
337 if (! pe.exists) {
338 /* No such file or directory exists */
339 if (warn)
340 log.warning("path.element.not.found", file);
341 } else if (pe.isFile) {
342 /* File is an ordinary file. */
343 if (!isArchive(file)) {
344 /* Not a recognized extension; open it to see if
345 it looks like a valid zip file. */
346 try {
347 ZipFile z = new ZipFile(file);
348 z.close();
349 if (warn)
350 log.warning("unexpected.archive.file", file);
351 } catch (IOException e) {
352 // FIXME: include e.getLocalizedMessage in warning
353 if (warn)
354 log.warning("invalid.archive.file", file);
355 return;
356 }
357 }
358 }
359
360 /* Now what we have left is either a directory or a file name
361 confirming to archive naming convention */
362 super.add(file);
363 canonicalValues.add(pe.cannonicalPath);
364
365 if (expandJarClassPaths && file.exists() && file.isFile())
366 addJarClassPath(file, warn);
367 }
368
369 // Adds referenced classpath elements from a jar's Class-Path
370 // Manifest entry. In some future release, we may want to
371 // update this code to recognize URLs rather than simple
372 // filenames, but if we do, we should redo all path-related code.
373 private void addJarClassPath(File jarFile, boolean warn) {
374 try {
375 java.util.List<File> manifestsList = manifestEntries.get(jarFile);
376 if (!NON_BATCH_MODE) {
377 lock.lock();
378 try {
379 if (manifestsList != null) {
380 for (File entr : manifestsList) {
381 addFile(entr, warn);
382 }
383 return;
384 }
385 }
386 finally {
387 lock.unlock();
388 }
389 }
390
391 if (!NON_BATCH_MODE) {
392 manifestsList = new ArrayList<File>();
393 manifestEntries.put(jarFile, manifestsList);
394 }
395
396 String jarParent = jarFile.getParent();
397 JarFile jar = new JarFile(jarFile);
398
399 try {
400 Manifest man = jar.getManifest();
401 if (man == null) return;
402
403 Attributes attr = man.getMainAttributes();
404 if (attr == null) return;
405
406 String path = attr.getValue(Attributes.Name.CLASS_PATH);
407 if (path == null) return;
408
409 for (StringTokenizer st = new StringTokenizer(path);
410 st.hasMoreTokens();) {
411 String elt = st.nextToken();
412 File f = (jarParent == null ? new File(elt) : new File(jarParent, elt));
413 addFile(f, warn);
414
415 if (!NON_BATCH_MODE) {
416 lock.lock();
417 try {
418 manifestsList.add(f);
419 }
420 finally {
421 lock.unlock();
422 }
423 }
424 }
425 } finally {
426 jar.close();
427 }
428 } catch (IOException e) {
429 log.error("error.reading.file", jarFile, e.getLocalizedMessage());
430 }
431 }
432 }
433
434 private Path computeBootClassPath() {
435 bootClassPathRtJar = null;
436 String optionValue;
437 Path path = new Path();
438
439 path.addFiles(options.get(XBOOTCLASSPATH_PREPEND));
440
441 if ((optionValue = options.get(ENDORSEDDIRS)) != null)
442 path.addDirectories(optionValue);
443 else
444 path.addDirectories(System.getProperty("java.endorsed.dirs"), false);
445
446 if ((optionValue = options.get(BOOTCLASSPATH)) != null) {
447 path.addFiles(optionValue);
448 } else {
449 // Standard system classes for this compiler's release.
450 String files = System.getProperty("sun.boot.class.path");
451 path.addFiles(files, false);
452 File rt_jar = new File("rt.jar");
453 for (String file : new PathIterator(files, null)) {
454 File f = new File(file);
455 if (new File(f.getName()).equals(rt_jar))
456 bootClassPathRtJar = f;
457 }
458 }
459
460 path.addFiles(options.get(XBOOTCLASSPATH_APPEND));
461
462 // Strictly speaking, standard extensions are not bootstrap
463 // classes, but we treat them identically, so we'll pretend
464 // that they are.
465 if ((optionValue = options.get(EXTDIRS)) != null)
466 path.addDirectories(optionValue);
467 else
468 path.addDirectories(System.getProperty("java.ext.dirs"), false);
469
470 return path;
471 }
472
473 private Path computeUserClassPath() {
474 String cp = options.get(CLASSPATH);
475
476 // CLASSPATH environment variable when run from `javac'.
477 if (cp == null) cp = System.getProperty("env.class.path");
478
479 // If invoked via a java VM (not the javac launcher), use the
480 // platform class path
481 if (cp == null && System.getProperty("application.home") == null)
482 cp = System.getProperty("java.class.path");
483
484 // Default to current working directory.
485 if (cp == null) cp = ".";
486
487 return new Path()
488 .expandJarClassPaths(true) // Only search user jars for Class-Paths
489 .emptyPathDefault(".") // Empty path elt ==> current directory
490 .addFiles(cp);
491 }
492
493 private Path computeSourcePath() {
494 String sourcePathArg = options.get(SOURCEPATH);
495 if (sourcePathArg == null)
496 return null;
497
498 return new Path().addFiles(sourcePathArg);
499 }
500
501 private Path computeAnnotationProcessorPath() {
502 String processorPathArg = options.get(PROCESSORPATH);
503 if (processorPathArg == null)
504 return null;
505
506 return new Path().addFiles(processorPathArg);
507 }
508
509 /** The actual effective locations searched for sources */
510 private Path sourceSearchPath;
511
512 public Collection<File> sourceSearchPath() {
513 if (sourceSearchPath == null) {
514 lazy();
515 Path sourcePath = getPathForLocation(SOURCE_PATH);
516 Path userClassPath = getPathForLocation(CLASS_PATH);
517 sourceSearchPath = sourcePath != null ? sourcePath : userClassPath;
518 }
519 return Collections.unmodifiableCollection(sourceSearchPath);
520 }
521
522 /** The actual effective locations searched for classes */
523 private Path classSearchPath;
524
525 public Collection<File> classSearchPath() {
526 if (classSearchPath == null) {
527 lazy();
528 Path bootClassPath = getPathForLocation(PLATFORM_CLASS_PATH);
529 Path userClassPath = getPathForLocation(CLASS_PATH);
530 classSearchPath = new Path();
531 classSearchPath.addAll(bootClassPath);
532 classSearchPath.addAll(userClassPath);
533 }
534 return Collections.unmodifiableCollection(classSearchPath);
535 }
536
537 /** The actual effective locations for non-source, non-class files */
538 private Path otherSearchPath;
539
540 Collection<File> otherSearchPath() {
541 if (otherSearchPath == null) {
542 lazy();
543 Path userClassPath = getPathForLocation(CLASS_PATH);
544 Path sourcePath = getPathForLocation(SOURCE_PATH);
545 if (sourcePath == null)
546 otherSearchPath = userClassPath;
547 else {
548 otherSearchPath = new Path();
549 otherSearchPath.addAll(userClassPath);
550 otherSearchPath.addAll(sourcePath);
551 }
552 }
553 return Collections.unmodifiableCollection(otherSearchPath);
554 }
555
556 /** Is this the name of an archive file? */
557 private static boolean isArchive(File file) {
558 String n = file.getName().toLowerCase();
559 boolean isFile = false;
560 if (!NON_BATCH_MODE) {
561 Boolean isf = isDirectory.get(file);
562 if (isf == null) {
563 isFile = file.isFile();
564 isDirectory.put(file, isFile);
565 }
566 else {
567 isFile = isf;
568 }
569 }
570 else {
571 isFile = file.isFile();
572 }
573
574 return isFile
575 && (n.endsWith(".jar") || n.endsWith(".zip"));
576 }
577 }

mercurial