1 /*
2 * Copyright (c) 1999, 2024, 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. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 package com.sun.tools.javac.code;
27
28 import java.io.IOException;
29 import java.nio.file.Path;
30 import java.util.EnumSet;
31 import java.util.HashMap;
32 import java.util.Iterator;
33 import java.util.Map;
34 import java.util.NoSuchElementException;
35 import java.util.Set;
36 import java.util.function.Supplier;
37
38 import javax.lang.model.SourceVersion;
39 import javax.tools.JavaFileManager;
40 import javax.tools.JavaFileManager.Location;
41 import javax.tools.JavaFileObject;
42 import javax.tools.JavaFileObject.Kind;
43 import javax.tools.StandardJavaFileManager;
44 import javax.tools.StandardLocation;
45
46 import com.sun.tools.javac.code.Scope.WriteableScope;
47 import com.sun.tools.javac.code.Symbol.ClassSymbol;
48 import com.sun.tools.javac.code.Symbol.Completer;
49 import com.sun.tools.javac.code.Symbol.CompletionFailure;
50 import com.sun.tools.javac.code.Symbol.ModuleSymbol;
51 import com.sun.tools.javac.code.Symbol.PackageSymbol;
52 import com.sun.tools.javac.code.Symbol.TypeSymbol;
53 import com.sun.tools.javac.comp.Annotate;
54 import com.sun.tools.javac.file.JRTIndex;
55 import com.sun.tools.javac.file.JavacFileManager;
56 import com.sun.tools.javac.jvm.ClassReader;
57 import com.sun.tools.javac.jvm.Profile;
58 import com.sun.tools.javac.main.Option;
59 import com.sun.tools.javac.platform.PlatformDescription;
60 import com.sun.tools.javac.resources.CompilerProperties.Fragments;
61 import com.sun.tools.javac.util.*;
62
63 import static javax.tools.StandardLocation.*;
64
65 import static com.sun.tools.javac.code.Flags.*;
66 import static com.sun.tools.javac.code.Kinds.Kind.*;
67 import com.sun.tools.javac.code.Symbol;
68 import com.sun.tools.javac.code.Symbol.CompletionFailure;
69 import com.sun.tools.javac.main.DelegatingJavaFileManager;
70
71 import com.sun.tools.javac.util.Dependencies.CompletionCause;
72
73 /**
74 * This class provides operations to locate class definitions
75 * from the source and class files on the paths provided to javac.
76 *
77 * <p><b>This is NOT part of any supported API.
78 * If you write code that depends on this, you do so at your own risk.
79 * This code and its internal interfaces are subject to change or
80 * deletion without notice.</b>
81 */
82 public class ClassFinder {
83 /** The context key for the class finder. */
84 protected static final Context.Key<ClassFinder> classFinderKey = new Context.Key<>();
85
86 ClassReader reader;
87
88 private final Annotate annotate;
89
90 /** Switch: verbose output.
91 */
92 boolean verbose;
93
94 /**
95 * Switch: cache completion failures unless -XDdev is used
96 */
97 private boolean cacheCompletionFailure;
98
99 /**
100 * Switch: prefer source files instead of newer when both source
101 * and class are available
102 **/
103 protected boolean preferSource;
104
105 /**
106 * Switch: Search classpath and sourcepath for classes before the
107 * bootclasspath
108 */
109 protected boolean userPathsFirst;
110
111 /** The log to use for verbose output
112 */
113 final Log log;
114
115 /** The symbol table. */
116 Symtab syms;
117
118 /** The name table. */
119 final Names names;
120
121 /** Force a completion failure on this name
122 */
123 final Name completionFailureName;
124
125 /** Access to files
126 */
127 private final JavaFileManager fileManager;
128
129 /** Dependency tracker
130 */
131 private final Dependencies dependencies;
132
133 /** Factory for diagnostics
134 */
135 JCDiagnostic.Factory diagFactory;
136
137 final DeferredCompletionFailureHandler dcfh;
138
139 /** Can be reassigned from outside:
140 * the completer to be used for ".java" files. If this remains unassigned
141 * ".java" files will not be loaded.
142 */
143 public Completer sourceCompleter = Completer.NULL_COMPLETER;
144
145 /** The path name of the class file currently being read.
146 */
147 protected JavaFileObject currentClassFile = null;
148
149 /** The class or method currently being read.
150 */
151 protected Symbol currentOwner = null;
152
153 /**
154 * The currently selected profile.
155 */
156 private final Profile profile;
157
158 /**
159 * Use direct access to the JRTIndex to access the temporary
160 * replacement for the info that used to be in ct.sym.
161 * In time, this will go away and be replaced by the module system.
162 */
163 private final JRTIndex jrtIndex;
164
165 /**
166 * Completer that delegates to the complete-method of this class.
167 */
168 private final Completer thisCompleter = this::complete;
169
170 public Completer getCompleter() {
171 return thisCompleter;
172 }
173
174 /** Get the ClassFinder instance for this invocation. */
175 public static ClassFinder instance(Context context) {
176 ClassFinder instance = context.get(classFinderKey);
177 if (instance == null)
178 instance = new ClassFinder(context);
179 return instance;
180 }
181
182 /** Construct a new class finder. */
183 @SuppressWarnings("this-escape")
184 protected ClassFinder(Context context) {
185 context.put(classFinderKey, this);
186 reader = ClassReader.instance(context);
187 names = Names.instance(context);
188 syms = Symtab.instance(context);
189 fileManager = context.get(JavaFileManager.class);
190 dependencies = Dependencies.instance(context);
191 if (fileManager == null)
192 throw new AssertionError("FileManager initialization error");
193 diagFactory = JCDiagnostic.Factory.instance(context);
194 dcfh = DeferredCompletionFailureHandler.instance(context);
195
196 log = Log.instance(context);
197 annotate = Annotate.instance(context);
198
199 Options options = Options.instance(context);
200 verbose = options.isSet(Option.VERBOSE);
201 cacheCompletionFailure = options.isUnset("dev");
202 preferSource = "source".equals(options.get("-Xprefer"));
203 userPathsFirst = options.isSet(Option.XXUSERPATHSFIRST);
204
205 completionFailureName =
206 options.isSet("failcomplete")
207 ? names.fromString(options.get("failcomplete"))
208 : null;
209
210 // Temporary, until more info is available from the module system.
211 boolean useCtProps;
212 JavaFileManager fm = context.get(JavaFileManager.class);
213 if (fm instanceof DelegatingJavaFileManager delegatingJavaFileManager) {
214 fm = delegatingJavaFileManager.getBaseFileManager();
215 }
216 if (fm instanceof JavacFileManager javacFileManager) {
217 useCtProps = javacFileManager.isDefaultBootClassPath() && javacFileManager.isSymbolFileEnabled();
218 } else {
219 useCtProps = false;
220 }
221 jrtIndex = useCtProps && JRTIndex.isAvailable() ? JRTIndex.getSharedInstance() : null;
222
223 profile = Profile.instance(context);
224 cachedCompletionFailure = new CompletionFailure(null, () -> null, dcfh);
225 cachedCompletionFailure.setStackTrace(new StackTraceElement[0]);
226 }
227
228
229 /* **********************************************************************
230 * Temporary ct.sym replacement
231 *
232 * The following code is a temporary substitute for the ct.sym mechanism
233 * used in JDK 6 thru JDK 8.
234 * This mechanism will eventually be superseded by the Jigsaw module system.
235 ***********************************************************************/
236
237 /**
238 * Returns any extra flags for a class symbol.
239 * This information used to be provided using private annotations
240 * in the class file in ct.sym; in time, this information will be
241 * available from the module system.
242 */
243 long getSupplementaryFlags(ClassSymbol c) {
244 if (jrtIndex == null || !jrtIndex.isInJRT(c.classfile) || c.name == names.module_info) {
245 return 0;
246 }
247
248 if (supplementaryFlags == null) {
249 supplementaryFlags = new HashMap<>();
250 }
251
252 PackageSymbol packge = c.packge();
253
254 Long flags = supplementaryFlags.get(packge);
255 if (flags == null) {
256 long newFlags = 0;
257 try {
258 ModuleSymbol owningModule = packge.modle;
259 if (owningModule == syms.noModule) {
260 JRTIndex.CtSym ctSym = jrtIndex.getCtSym(packge.flatName());
261 Profile minProfile = Profile.DEFAULT;
262 if (ctSym.proprietary)
263 newFlags |= PROPRIETARY;
264 if (ctSym.minProfile != null)
265 minProfile = Profile.lookup(ctSym.minProfile);
266 if (profile != Profile.DEFAULT && minProfile.value > profile.value) {
267 newFlags |= NOT_IN_PROFILE;
268 }
269 } else if (owningModule.name == names.jdk_unsupported) {
270 newFlags |= PROPRIETARY;
271 }
272 } catch (IOException ignore) {
273 }
274 supplementaryFlags.put(packge, flags = newFlags);
275 }
276 return flags;
277 }
278
279 private Map<PackageSymbol, Long> supplementaryFlags;
280
281 /* **********************************************************************
282 * Loading Classes
283 ***********************************************************************/
284
285 /** Completion for classes to be loaded. Before a class is loaded
286 * we make sure its enclosing class (if any) is loaded.
287 */
288 private void complete(Symbol sym) throws CompletionFailure {
289 if (sym.kind == TYP) {
290 try {
291 ClassSymbol c = (ClassSymbol) sym;
292 dependencies.push(c, CompletionCause.CLASS_READER);
293 annotate.blockAnnotations();
294 Scope.ErrorScope members = new Scope.ErrorScope(c);
295 c.members_field = members; // make sure it's always defined
296 completeOwners(c.owner);
297 completeEnclosing(c);
298 //if an enclosing class is completed from the source,
299 //this class might have been completed already as well,
300 //avoid attempts to re-complete it:
301 if (c.members_field == members) {
302 fillIn(c);
303 }
304 } finally {
305 annotate.unblockAnnotationsNoFlush();
306 dependencies.pop();
307 }
308 } else if (sym.kind == PCK) {
309 PackageSymbol p = (PackageSymbol)sym;
310 try {
311 fillIn(p);
312 } catch (IOException ex) {
313 throw new CompletionFailure(
314 sym,
315 () -> diagFactory.fragment(
316 Fragments.ExceptionMessage(ex.getLocalizedMessage())),
317 dcfh)
318 .initCause(ex);
319 }
320 }
321 if (!reader.filling)
322 annotate.flush(); // finish attaching annotations
323 }
324
325 /** complete up through the enclosing package. */
326 private void completeOwners(Symbol o) {
327 if (o.kind != PCK) completeOwners(o.owner);
328 o.complete();
329 }
330
331 /**
332 * Tries to complete lexically enclosing classes if c looks like a
333 * nested class. This is similar to completeOwners but handles
334 * the situation when a nested class is accessed directly as it is
335 * possible with the Tree API or javax.lang.model.*.
336 */
337 private void completeEnclosing(ClassSymbol c) {
338 if (c.owner.kind == PCK) {
339 Symbol owner = c.owner;
340 for (Name name : Convert.enclosingCandidates(Convert.shortName(c.name))) {
341 Symbol encl = owner.members().findFirst(name);
342 if (encl == null)
343 encl = syms.getClass(c.packge().modle, TypeSymbol.formFlatName(name, owner));
344 if (encl != null)
345 encl.complete();
346 }
347 }
348 }
349
350 /** Fill in definition of class `c' from corresponding class or
351 * source file.
352 */
353 void fillIn(ClassSymbol c) {
354 if (completionFailureName == c.fullname) {
355 throw new CompletionFailure(
356 c, () -> diagFactory.fragment(Fragments.UserSelectedCompletionFailure), dcfh);
357 }
358 currentOwner = c;
359 JavaFileObject classfile = c.classfile;
360 if (classfile != null) {
361 JavaFileObject previousClassFile = currentClassFile;
362 Symbol prevOwner = c.owner;
363 Name prevName = c.fullname;
364 try {
365 if (reader.filling) {
366 Assert.error("Filling " + classfile.toUri() + " during " + previousClassFile);
367 }
368 currentClassFile = classfile;
369 if (verbose) {
370 log.printVerbose("loading", currentClassFile.getName());
371 }
372 if (classfile.getKind() == JavaFileObject.Kind.CLASS) {
373 reader.readClassFile(c);
374 c.flags_field |= getSupplementaryFlags(c);
375 } else {
376 if (!sourceCompleter.isTerminal()) {
377 sourceCompleter.complete(c);
378 } else {
379 throw new IllegalStateException("Source completer required to read "
380 + classfile.toUri());
381 }
382 }
383 } catch (BadClassFile cf) {
384 //the symbol may be partially initialized, purge it:
385 c.owner = prevOwner;
386 c.members_field.getSymbols(sym -> sym.kind == TYP).forEach(sym -> {
387 ClassSymbol csym = (ClassSymbol) sym;
388 csym.owner = sym.packge();
389 csym.owner.members().enter(sym);
390 csym.fullname = sym.flatName();
391 csym.name = Convert.shortName(sym.flatName());
392 csym.reset();
393 });
394 c.fullname = prevName;
395 c.name = Convert.shortName(prevName);
396 c.reset();
397 throw cf;
398 } finally {
399 currentClassFile = previousClassFile;
400 }
401 } else {
402 throw classFileNotFound(c);
403 }
404 }
405 // where
406 private CompletionFailure classFileNotFound(ClassSymbol c) {
407 return newCompletionFailure(
408 c, () -> diagFactory.fragment(Fragments.ClassFileNotFound(c.flatname)));
409 }
410 /** Static factory for CompletionFailure objects.
411 * In practice, only one can be used at a time, so we share one
412 * to reduce the expense of allocating new exception objects.
413 */
414 private CompletionFailure newCompletionFailure(TypeSymbol c,
415 Supplier<JCDiagnostic> diag) {
416 if (!cacheCompletionFailure) {
417 // log.warning("proc.messager",
418 // Log.getLocalizedString("class.file.not.found", c.flatname));
419 // c.debug.printStackTrace();
420 return new CompletionFailure(c, diag, dcfh);
421 } else {
422 CompletionFailure result = cachedCompletionFailure;
423 result.sym = c;
424 result.resetDiagnostic(diag);
425 return result;
426 }
427 }
428 private final CompletionFailure cachedCompletionFailure;
429
430
431 /** Load a toplevel class with given fully qualified name
432 * The class is entered into `classes' only if load was successful.
433 */
434 public ClassSymbol loadClass(ModuleSymbol msym, Name flatname) throws CompletionFailure {
435 Assert.checkNonNull(msym);
436 Name packageName = Convert.packagePart(flatname);
437 PackageSymbol ps = syms.lookupPackage(msym, packageName);
438
439 Assert.checkNonNull(ps.modle, () -> "msym=" + msym + "; flatName=" + flatname);
440
441 boolean absent = syms.getClass(ps.modle, flatname) == null;
442 ClassSymbol c = syms.enterClass(ps.modle, flatname);
443
444 if (c.members_field == null) {
445 try {
446 c.complete();
447 } catch (CompletionFailure ex) {
448 if (absent) {
449 syms.removeClass(ps.modle, flatname);
450 ex.dcfh.classSymbolRemoved(c);
451 }
452 throw ex;
453 }
454 }
455 return c;
456 }
457
458 /* **********************************************************************
459 * Loading Packages
460 ***********************************************************************/
461
462 /** Include class corresponding to given class file in package,
463 * unless (1) we already have one the same kind (.class or .java), or
464 * (2) we have one of the other kind, and the given class file
465 * is older.
466 */
467 protected void includeClassFile(PackageSymbol p, JavaFileObject file) {
468 if ((p.flags_field & EXISTS) == 0)
469 for (Symbol q = p; q != null && q.kind == PCK; q = q.owner)
470 q.flags_field |= EXISTS;
471 JavaFileObject.Kind kind = file.getKind();
472 int seen;
473 if (kind == JavaFileObject.Kind.CLASS)
474 seen = CLASS_SEEN;
475 else
476 seen = SOURCE_SEEN;
477 String binaryName = fileManager.inferBinaryName(currentLoc, file);
478 int lastDot = binaryName.lastIndexOf(".");
479 Name classname = names.fromString(binaryName.substring(lastDot + 1));
480 boolean isPkgInfo = classname == names.package_info;
481 ClassSymbol c = isPkgInfo
482 ? p.package_info
483 : (ClassSymbol) p.members_field.findFirst(classname);
484 if (c == null) {
485 c = syms.enterClass(p.modle, classname, p);
486 if (c.classfile == null) // only update the file if's it's newly created
487 c.classfile = file;
488 if (isPkgInfo) {
489 p.package_info = c;
490 } else {
491 if (c.owner == p) // it might be an inner class
492 p.members_field.enter(c);
493 }
494 } else if (!preferCurrent && c.classfile != null && (c.flags_field & seen) == 0) {
495 // if c.classfile == null, we are currently compiling this class
496 // and no further action is necessary.
497 // if (c.flags_field & seen) != 0, we have already encountered
498 // a file of the same kind; again no further action is necessary.
499 if ((c.flags_field & (CLASS_SEEN | SOURCE_SEEN)) != 0)
500 c.classfile = preferredFileObject(file, c.classfile);
501 }
502 c.flags_field |= seen;
503 }
504
505 /** Implement policy to choose to derive information from a source
506 * file or a class file when both are present. May be overridden
507 * by subclasses.
508 */
509 protected JavaFileObject preferredFileObject(JavaFileObject a,
510 JavaFileObject b) {
511
512 if (preferSource)
513 return (a.getKind() == JavaFileObject.Kind.SOURCE) ? a : b;
514 else {
515 long adate = a.getLastModified();
516 long bdate = b.getLastModified();
517 // 6449326: policy for bad lastModifiedTime in ClassReader
518 //assert adate >= 0 && bdate >= 0;
519 return (adate > bdate) ? a : b;
520 }
521 }
522
523 /**
524 * specifies types of files to be read when filling in a package symbol
525 */
526 // Note: overridden by JavadocClassFinder
527 protected EnumSet<JavaFileObject.Kind> getPackageFileKinds() {
528 return EnumSet.of(JavaFileObject.Kind.CLASS, JavaFileObject.Kind.SOURCE);
529 }
530
531 /**
532 * this is used to support javadoc
533 */
534 protected void extraFileActions(PackageSymbol pack, JavaFileObject fe) {
535 }
536
537 protected Location currentLoc; // FIXME
538
539 private boolean verbosePath = true;
540
541 // Set to true when the currently selected file should be kept
542 private boolean preferCurrent;
543
544 /** Load directory of package into members scope.
545 */
546 private void fillIn(PackageSymbol p) throws IOException {
547 if (p.members_field == null)
548 p.members_field = WriteableScope.create(p);
549
550 ModuleSymbol msym = p.modle;
551
552 Assert.checkNonNull(msym, p::toString);
553
554 msym.complete();
555
556 if (msym == syms.noModule) {
557 preferCurrent = false;
558 if (userPathsFirst) {
559 scanUserPaths(p, true);
560 preferCurrent = true;
561 scanPlatformPath(p);
562 } else {
563 scanPlatformPath(p);
564 scanUserPaths(p, true);
565 }
566 } else if (msym.classLocation == StandardLocation.CLASS_PATH) {
567 scanUserPaths(p, msym.sourceLocation == StandardLocation.SOURCE_PATH);
568 } else {
569 scanModulePaths(p, msym);
570 }
571 }
572
573 // TODO: for now, this is a much simplified form of scanUserPaths
574 // and (deliberately) does not default sourcepath to classpath.
575 // But, we need to think about retaining existing behavior for
576 // -classpath and -sourcepath for single module mode.
577 // One plausible solution is to detect if the module's sourceLocation
578 // is the same as the module's classLocation.
579 private void scanModulePaths(PackageSymbol p, ModuleSymbol msym) throws IOException {
580 Set<JavaFileObject.Kind> kinds = getPackageFileKinds();
581
582 Set<JavaFileObject.Kind> classKinds = EnumSet.copyOf(kinds);
583 classKinds.remove(JavaFileObject.Kind.SOURCE);
584 boolean wantClassFiles = !classKinds.isEmpty();
585
586 Set<JavaFileObject.Kind> sourceKinds = EnumSet.copyOf(kinds);
587 sourceKinds.remove(JavaFileObject.Kind.CLASS);
588 boolean wantSourceFiles = !sourceKinds.isEmpty();
589
590 String packageName = p.fullname.toString();
591
592 Location classLocn = msym.classLocation;
593 Location sourceLocn = msym.sourceLocation;
594 Location patchLocn = msym.patchLocation;
595 Location patchOutLocn = msym.patchOutputLocation;
596
597 boolean prevPreferCurrent = preferCurrent;
598
599 try {
600 preferCurrent = false;
601 if (wantClassFiles && (patchOutLocn != null)) {
602 fillIn(p, patchOutLocn,
603 list(patchOutLocn,
604 p,
605 packageName,
606 classKinds));
607 }
608 if ((wantClassFiles || wantSourceFiles) && (patchLocn != null)) {
609 Set<JavaFileObject.Kind> combined = EnumSet.noneOf(JavaFileObject.Kind.class);
610 combined.addAll(classKinds);
611 combined.addAll(sourceKinds);
612 fillIn(p, patchLocn,
613 list(patchLocn,
614 p,
615 packageName,
616 combined));
617 }
618 preferCurrent = true;
619 if (wantClassFiles && (classLocn != null)) {
620 fillIn(p, classLocn,
621 list(classLocn,
622 p,
623 packageName,
624 classKinds));
625 }
626 if (wantSourceFiles && (sourceLocn != null)) {
627 fillIn(p, sourceLocn,
628 list(sourceLocn,
629 p,
630 packageName,
631 sourceKinds));
632 }
633 } finally {
634 preferCurrent = prevPreferCurrent;
635 }
636 }
637
638 /**
639 * Scans class path and source path for files in given package.
640 */
641 private void scanUserPaths(PackageSymbol p, boolean includeSourcePath) throws IOException {
642 Set<JavaFileObject.Kind> kinds = getPackageFileKinds();
643
644 Set<JavaFileObject.Kind> classKinds = EnumSet.copyOf(kinds);
645 classKinds.remove(JavaFileObject.Kind.SOURCE);
646 boolean wantClassFiles = !classKinds.isEmpty();
647
648 Set<JavaFileObject.Kind> sourceKinds = EnumSet.copyOf(kinds);
649 sourceKinds.remove(JavaFileObject.Kind.CLASS);
650 boolean wantSourceFiles = !sourceKinds.isEmpty();
651
652 boolean haveSourcePath = includeSourcePath && fileManager.hasLocation(SOURCE_PATH);
653
654 if (verbose && verbosePath) {
655 verbosePath = false; // print once per compile
656 if (fileManager instanceof StandardJavaFileManager standardJavaFileManager) {
657 if (haveSourcePath && wantSourceFiles) {
658 List<Path> path = List.nil();
659 for (Path sourcePath : standardJavaFileManager.getLocationAsPaths(SOURCE_PATH)) {
660 path = path.prepend(sourcePath);
661 }
662 log.printVerbose("sourcepath", path.reverse().toString());
663 } else if (wantSourceFiles) {
664 List<Path> path = List.nil();
665 for (Path classPath : standardJavaFileManager.getLocationAsPaths(CLASS_PATH)) {
666 path = path.prepend(classPath);
667 }
668 log.printVerbose("sourcepath", path.reverse().toString());
669 }
670 if (wantClassFiles) {
671 List<Path> path = List.nil();
672 for (Path platformPath : standardJavaFileManager.getLocationAsPaths(PLATFORM_CLASS_PATH)) {
673 path = path.prepend(platformPath);
674 }
675 for (Path classPath : standardJavaFileManager.getLocationAsPaths(CLASS_PATH)) {
676 path = path.prepend(classPath);
677 }
678 log.printVerbose("classpath", path.reverse().toString());
679 }
680 }
681 }
682
683 String packageName = p.fullname.toString();
684 if (wantSourceFiles && !haveSourcePath) {
685 fillIn(p, CLASS_PATH,
686 list(CLASS_PATH,
687 p,
688 packageName,
689 kinds));
690 } else {
691 if (wantClassFiles)
692 fillIn(p, CLASS_PATH,
693 list(CLASS_PATH,
694 p,
695 packageName,
696 classKinds));
697 if (wantSourceFiles)
698 fillIn(p, SOURCE_PATH,
699 list(SOURCE_PATH,
700 p,
701 packageName,
702 sourceKinds));
703 }
704 }
705
706 /**
707 * Scans platform class path for files in given package.
708 */
709 private void scanPlatformPath(PackageSymbol p) throws IOException {
710 fillIn(p, PLATFORM_CLASS_PATH,
711 list(PLATFORM_CLASS_PATH,
712 p,
713 p.fullname.toString(),
714 EnumSet.of(JavaFileObject.Kind.CLASS)));
715 }
716 // where
717 private void fillIn(PackageSymbol p,
718 Location location,
719 Iterable<JavaFileObject> files)
720 {
721 currentLoc = location;
722 for (JavaFileObject fo : files) {
723 switch (fo.getKind()) {
724 case OTHER:
725 extraFileActions(p, fo);
726 break;
727 case CLASS:
728 case SOURCE: {
729 // TODO pass binaryName to includeClassFile
730 String binaryName = fileManager.inferBinaryName(currentLoc, fo);
731 String simpleName = binaryName.substring(binaryName.lastIndexOf(".") + 1);
732 if (SourceVersion.isIdentifier(simpleName) ||
733 simpleName.equals("package-info"))
734 includeClassFile(p, fo);
735 break;
736 }
737 default:
738 extraFileActions(p, fo);
739 break;
740 }
741 }
742 }
743
744 Iterable<JavaFileObject> list(Location location,
745 PackageSymbol p,
746 String packageName,
747 Set<Kind> kinds) throws IOException {
748 Iterable<JavaFileObject> listed = fileManager.list(location,
749 packageName,
750 EnumSet.allOf(Kind.class),
751 false);
752 return () -> new Iterator<JavaFileObject>() {
753 private final Iterator<JavaFileObject> original = listed.iterator();
754 private JavaFileObject next;
755 @Override
756 public boolean hasNext() {
757 if (next == null) {
758 while (original.hasNext()) {
759 JavaFileObject fo = original.next();
760
761 if (fo.getKind() != Kind.CLASS &&
762 fo.getKind() != Kind.SOURCE) {
763 p.flags_field |= Flags.HAS_RESOURCE;
764 }
765
766 if (kinds.contains(fo.getKind())) {
767 next = fo;
768 break;
769 }
770 }
771 }
772 return next != null;
773 }
774
775 @Override
776 public JavaFileObject next() {
777 if (!hasNext())
778 throw new NoSuchElementException();
779 JavaFileObject result = next;
780 next = null;
781 return result;
782 }
783
784 };
785 }
786
787 /**
788 * Used for bad class definition files, such as bad .class files or
789 * for .java files with unexpected package or class names.
790 */
791 public static class BadClassFile extends CompletionFailure {
792 private static final long serialVersionUID = 0;
793
794 public BadClassFile(TypeSymbol sym, JavaFileObject file, JCDiagnostic diag,
795 JCDiagnostic.Factory diagFactory, DeferredCompletionFailureHandler dcfh) {
796 super(sym, () -> createBadClassFileDiagnostic(file, diag, diagFactory), dcfh);
797 }
798 // where
799 private static JCDiagnostic createBadClassFileDiagnostic(
800 JavaFileObject file, JCDiagnostic diag, JCDiagnostic.Factory diagFactory) {
801 String key = (file.getKind() == JavaFileObject.Kind.SOURCE
802 ? "bad.source.file.header" : "bad.class.file.header");
803 return diagFactory.fragment(key, file, diag);
804 }
805 }
806
807 public static class BadEnclosingMethodAttr extends BadClassFile {
808 private static final long serialVersionUID = 0;
809
810 public BadEnclosingMethodAttr(TypeSymbol sym, JavaFileObject file, JCDiagnostic diag,
811 JCDiagnostic.Factory diagFactory, DeferredCompletionFailureHandler dcfh) {
812 super(sym, file, diag, diagFactory, dcfh);
813 }
814 }
815 }