langtools/src/jdk.jshell/share/classes/jdk/jshell/SourceCodeAnalysisImpl.java
changeset 41865 3ef02797070d
parent 41527 e8f487b79e24
child 42258 a1aafd5ea6ec
equal deleted inserted replaced
41864:f7dbab23003a 41865:3ef02797070d
    40 import com.sun.source.tree.Scope;
    40 import com.sun.source.tree.Scope;
    41 import com.sun.source.tree.Tree;
    41 import com.sun.source.tree.Tree;
    42 import com.sun.source.tree.Tree.Kind;
    42 import com.sun.source.tree.Tree.Kind;
    43 import com.sun.source.tree.TypeParameterTree;
    43 import com.sun.source.tree.TypeParameterTree;
    44 import com.sun.source.tree.VariableTree;
    44 import com.sun.source.tree.VariableTree;
    45 import com.sun.source.util.JavacTask;
       
    46 import com.sun.source.util.SourcePositions;
    45 import com.sun.source.util.SourcePositions;
    47 import com.sun.source.util.TreePath;
    46 import com.sun.source.util.TreePath;
    48 import com.sun.source.util.TreePathScanner;
    47 import com.sun.source.util.TreePathScanner;
    49 import com.sun.source.util.Trees;
       
    50 import com.sun.tools.javac.api.JavacScope;
    48 import com.sun.tools.javac.api.JavacScope;
    51 import com.sun.tools.javac.api.JavacTaskImpl;
       
    52 import com.sun.tools.javac.code.Flags;
    49 import com.sun.tools.javac.code.Flags;
    53 import com.sun.tools.javac.code.Symbol.CompletionFailure;
    50 import com.sun.tools.javac.code.Symbol.CompletionFailure;
    54 import com.sun.tools.javac.code.Symbol.VarSymbol;
    51 import com.sun.tools.javac.code.Symbol.VarSymbol;
    55 import com.sun.tools.javac.code.Symtab;
    52 import com.sun.tools.javac.code.Symtab;
    56 import com.sun.tools.javac.code.Type;
    53 import com.sun.tools.javac.code.Type;
    57 import com.sun.tools.javac.code.Type.ClassType;
    54 import com.sun.tools.javac.code.Type.ClassType;
       
    55 import jdk.internal.shellsupport.doc.JavadocHelper;
    58 import com.sun.tools.javac.util.Name;
    56 import com.sun.tools.javac.util.Name;
    59 import com.sun.tools.javac.util.Names;
    57 import com.sun.tools.javac.util.Names;
    60 import com.sun.tools.javac.util.Pair;
    58 import com.sun.tools.javac.util.Pair;
    61 import jdk.jshell.CompletenessAnalyzer.CaInfo;
    59 import jdk.jshell.CompletenessAnalyzer.CaInfo;
    62 import jdk.jshell.TaskFactory.AnalyzeTask;
    60 import jdk.jshell.TaskFactory.AnalyzeTask;
   103 import java.util.function.Function;
   101 import java.util.function.Function;
   104 import java.util.regex.Matcher;
   102 import java.util.regex.Matcher;
   105 import java.util.regex.Pattern;
   103 import java.util.regex.Pattern;
   106 import java.util.stream.Collectors;
   104 import java.util.stream.Collectors;
   107 import static java.util.stream.Collectors.collectingAndThen;
   105 import static java.util.stream.Collectors.collectingAndThen;
       
   106 import static java.util.stream.Collectors.joining;
   108 import static java.util.stream.Collectors.toCollection;
   107 import static java.util.stream.Collectors.toCollection;
   109 import static java.util.stream.Collectors.toList;
   108 import static java.util.stream.Collectors.toList;
   110 import static java.util.stream.Collectors.toSet;
   109 import static java.util.stream.Collectors.toSet;
   111 import java.util.stream.Stream;
   110 import java.util.stream.Stream;
   112 import java.util.stream.StreamSupport;
   111 import java.util.stream.StreamSupport;
   121 import javax.lang.model.type.ArrayType;
   120 import javax.lang.model.type.ArrayType;
   122 import javax.lang.model.type.ExecutableType;
   121 import javax.lang.model.type.ExecutableType;
   123 import javax.lang.model.type.TypeKind;
   122 import javax.lang.model.type.TypeKind;
   124 import javax.lang.model.util.ElementFilter;
   123 import javax.lang.model.util.ElementFilter;
   125 import javax.lang.model.util.Types;
   124 import javax.lang.model.util.Types;
   126 import javax.tools.JavaCompiler;
       
   127 import javax.tools.JavaFileManager.Location;
   125 import javax.tools.JavaFileManager.Location;
   128 import javax.tools.JavaFileObject;
       
   129 import javax.tools.StandardJavaFileManager;
       
   130 import javax.tools.StandardLocation;
   126 import javax.tools.StandardLocation;
   131 import javax.tools.ToolProvider;
       
   132 
   127 
   133 import static jdk.jshell.Util.REPL_DOESNOTMATTER_CLASS_NAME;
   128 import static jdk.jshell.Util.REPL_DOESNOTMATTER_CLASS_NAME;
   134 import static java.util.stream.Collectors.joining;
       
   135 import static jdk.jshell.SourceCodeAnalysis.Completeness.DEFINITELY_INCOMPLETE;
   129 import static jdk.jshell.SourceCodeAnalysis.Completeness.DEFINITELY_INCOMPLETE;
   136 import static jdk.jshell.TreeDissector.printType;
   130 import static jdk.jshell.TreeDissector.printType;
   137 
   131 
   138 /**
   132 /**
   139  * The concrete implementation of SourceCodeAnalysis.
   133  * The concrete implementation of SourceCodeAnalysis.
   149         return t;
   143         return t;
   150     });
   144     });
   151 
   145 
   152     private final JShell proc;
   146     private final JShell proc;
   153     private final CompletenessAnalyzer ca;
   147     private final CompletenessAnalyzer ca;
       
   148     private final List<AutoCloseable> closeables = new ArrayList<>();
   154     private final Map<Path, ClassIndex> currentIndexes = new HashMap<>();
   149     private final Map<Path, ClassIndex> currentIndexes = new HashMap<>();
   155     private int indexVersion;
   150     private int indexVersion;
   156     private int classpathVersion;
   151     private int classpathVersion;
   157     private final Object suspendLock = new Object();
   152     private final Object suspendLock = new Object();
   158     private int suspend;
   153     private int suspend;
  1095 
  1090 
  1096         return candidateConstructors;
  1091         return candidateConstructors;
  1097     }
  1092     }
  1098 
  1093 
  1099     @Override
  1094     @Override
  1100     public String documentation(String code, int cursor) {
  1095     public List<Documentation> documentation(String code, int cursor, boolean computeJavadoc) {
  1101         suspendIndexing();
  1096         suspendIndexing();
  1102         try {
  1097         try {
  1103             return documentationImpl(code, cursor);
  1098             return documentationImpl(code, cursor, computeJavadoc);
  1104         } finally {
  1099         } finally {
  1105             resumeIndexing();
  1100             resumeIndexing();
  1106         }
  1101         }
  1107     }
  1102     }
  1108 
  1103 
  1110     //JDK's classes are stable for both release and fastdebug builds:
  1105     //JDK's classes are stable for both release and fastdebug builds:
  1111     private final String[] keepParameterNames = new String[] {
  1106     private final String[] keepParameterNames = new String[] {
  1112         "-parameters"
  1107         "-parameters"
  1113     };
  1108     };
  1114 
  1109 
  1115     private String documentationImpl(String code, int cursor) {
  1110     private List<Documentation> documentationImpl(String code, int cursor, boolean computeJavadoc) {
  1116         code = code.substring(0, cursor);
  1111         code = code.substring(0, cursor);
  1117         if (code.trim().isEmpty()) { //TODO: comment handling
  1112         if (code.trim().isEmpty()) { //TODO: comment handling
  1118             code += ";";
  1113             code += ";";
  1119         }
  1114         }
  1120 
  1115 
  1121         if (guessKind(code) == Kind.IMPORT)
  1116         if (guessKind(code) == Kind.IMPORT)
  1122             return null;
  1117             return Collections.<Documentation>emptyList();
  1123 
  1118 
  1124         OuterWrap codeWrap = proc.outerMap.wrapInTrialClass(Wrap.methodWrap(code));
  1119         OuterWrap codeWrap = proc.outerMap.wrapInTrialClass(Wrap.methodWrap(code));
  1125         AnalyzeTask at = proc.taskFactory.new AnalyzeTask(codeWrap, keepParameterNames);
  1120         AnalyzeTask at = proc.taskFactory.new AnalyzeTask(codeWrap, keepParameterNames);
  1126         SourcePositions sp = at.trees().getSourcePositions();
  1121         SourcePositions sp = at.trees().getSourcePositions();
  1127         CompilationUnitTree topLevel = at.firstCuTree();
  1122         CompilationUnitTree topLevel = at.firstCuTree();
  1128         TreePath tp = pathFor(topLevel, sp, codeWrap.snippetIndexToWrapIndex(cursor));
  1123         TreePath tp = pathFor(topLevel, sp, codeWrap.snippetIndexToWrapIndex(cursor));
  1129 
  1124 
  1130         if (tp == null)
  1125         if (tp == null)
  1131             return null;
  1126             return Collections.<Documentation>emptyList();
  1132 
  1127 
  1133         TreePath prevPath = null;
  1128         TreePath prevPath = null;
  1134         while (tp != null && tp.getLeaf().getKind() != Kind.METHOD_INVOCATION && tp.getLeaf().getKind() != Kind.NEW_CLASS) {
  1129         while (tp != null && tp.getLeaf().getKind() != Kind.METHOD_INVOCATION &&
       
  1130                tp.getLeaf().getKind() != Kind.NEW_CLASS && tp.getLeaf().getKind() != Kind.IDENTIFIER &&
       
  1131                tp.getLeaf().getKind() != Kind.MEMBER_SELECT) {
  1135             prevPath = tp;
  1132             prevPath = tp;
  1136             tp = tp.getParentPath();
  1133             tp = tp.getParentPath();
  1137         }
  1134         }
  1138 
  1135 
  1139         if (tp == null)
  1136         if (tp == null)
  1140             return null;
  1137             return Collections.<Documentation>emptyList();
  1141 
  1138 
       
  1139         Stream<Element> elements;
  1142         Iterable<Pair<ExecutableElement, ExecutableType>> candidates;
  1140         Iterable<Pair<ExecutableElement, ExecutableType>> candidates;
  1143         List<? extends ExpressionTree> arguments;
  1141         List<? extends ExpressionTree> arguments;
  1144 
  1142 
  1145         if (tp.getLeaf().getKind() == Kind.METHOD_INVOCATION) {
  1143         if (tp.getLeaf().getKind() == Kind.METHOD_INVOCATION || tp.getLeaf().getKind() == Kind.NEW_CLASS) {
  1146             MethodInvocationTree mit = (MethodInvocationTree) tp.getLeaf();
  1144             if (tp.getLeaf().getKind() == Kind.METHOD_INVOCATION) {
  1147             candidates = methodCandidates(at, tp);
  1145                 MethodInvocationTree mit = (MethodInvocationTree) tp.getLeaf();
  1148             arguments = mit.getArguments();
  1146                 candidates = methodCandidates(at, tp);
       
  1147                 arguments = mit.getArguments();
       
  1148             } else {
       
  1149                 NewClassTree nct = (NewClassTree) tp.getLeaf();
       
  1150                 candidates = newClassCandidates(at, tp);
       
  1151                 arguments = nct.getArguments();
       
  1152             }
       
  1153 
       
  1154             if (!isEmptyArgumentsContext(arguments)) {
       
  1155                 List<TypeMirror> actuals = computeActualInvocationTypes(at, arguments, prevPath);
       
  1156                 List<TypeMirror> fullActuals = actuals != null ? actuals : Collections.emptyList();
       
  1157 
       
  1158                 candidates =
       
  1159                         this.filterExecutableTypesByArguments(at, candidates, fullActuals)
       
  1160                             .stream()
       
  1161                             .filter(method -> parameterType(method.fst, method.snd, fullActuals.size(), true).findAny().isPresent())
       
  1162                             .collect(Collectors.toList());
       
  1163             }
       
  1164 
       
  1165             elements = Util.stream(candidates).map(method -> method.fst);
       
  1166         } else if (tp.getLeaf().getKind() == Kind.IDENTIFIER || tp.getLeaf().getKind() == Kind.MEMBER_SELECT) {
       
  1167             Element el = at.trees().getElement(tp);
       
  1168 
       
  1169             if (el == null ||
       
  1170                 el.asType().getKind() == TypeKind.ERROR ||
       
  1171                 (el.getKind() == ElementKind.PACKAGE && el.getEnclosedElements().isEmpty())) {
       
  1172                 //erroneous element:
       
  1173                 return Collections.<Documentation>emptyList();
       
  1174             }
       
  1175 
       
  1176             elements = Stream.of(el);
  1149         } else {
  1177         } else {
  1150             NewClassTree nct = (NewClassTree) tp.getLeaf();
  1178             return Collections.<Documentation>emptyList();
  1151             candidates = newClassCandidates(at, tp);
  1179         }
  1152             arguments = nct.getArguments();
  1180 
  1153         }
  1181         List<Documentation> result = Collections.emptyList();
  1154 
  1182 
  1155         if (!isEmptyArgumentsContext(arguments)) {
  1183         try (JavadocHelper helper = JavadocHelper.create(at.task, findSources())) {
  1156             List<TypeMirror> actuals = computeActualInvocationTypes(at, arguments, prevPath);
  1184             result = elements.map(el -> constructDocumentation(at, helper, el, computeJavadoc))
  1157             List<TypeMirror> fullActuals = actuals != null ? actuals : Collections.emptyList();
  1185                              .filter(r -> r != null)
  1158 
  1186                              .collect(Collectors.toList());
  1159             candidates =
  1187         } catch (IOException ex) {
  1160                     this.filterExecutableTypesByArguments(at, candidates, fullActuals)
  1188             proc.debug(ex, "JavadocHelper.close()");
  1161                         .stream()
  1189         }
  1162                         .filter(method -> parameterType(method.fst, method.snd, fullActuals.size(), true).findAny().isPresent())
  1190 
  1163                         .collect(Collectors.toList());
  1191         return result;
  1164         }
  1192     }
  1165 
  1193 
  1166         try (SourceCache sourceCache = new SourceCache(at)) {
  1194     private Documentation constructDocumentation(AnalyzeTask at, JavadocHelper helper, Element el, boolean computeJavadoc) {
  1167             return Util.stream(candidates)
  1195         String javadoc = null;
  1168                     .map(method -> Util.expunge(element2String(sourceCache, method.fst)))
  1196         try {
  1169                     .collect(joining("\n"));
  1197             if (hasSyntheticParameterNames(el)) {
  1170         }
  1198                 el = helper.getSourceElement(el);
       
  1199             }
       
  1200             if (computeJavadoc) {
       
  1201                 javadoc = helper.getResolvedDocComment(el);
       
  1202             }
       
  1203         } catch (IOException ex) {
       
  1204             proc.debug(ex, "SourceCodeAnalysisImpl.element2String(..., " + el + ")");
       
  1205         }
       
  1206         String signature = Util.expunge(elementHeader(at, el, !hasSyntheticParameterNames(el), true));
       
  1207         return new DocumentationImpl(signature,  javadoc);
       
  1208     }
       
  1209 
       
  1210     public void close() {
       
  1211         for (AutoCloseable closeable : closeables) {
       
  1212             try {
       
  1213                 closeable.close();
       
  1214             } catch (Exception ex) {
       
  1215                 proc.debug(ex, "SourceCodeAnalysisImpl.close()");
       
  1216             }
       
  1217         }
       
  1218     }
       
  1219 
       
  1220     private static final class DocumentationImpl implements Documentation {
       
  1221 
       
  1222         private final String signature;
       
  1223         private final String javadoc;
       
  1224 
       
  1225         public DocumentationImpl(String signature, String javadoc) {
       
  1226             this.signature = signature;
       
  1227             this.javadoc = javadoc;
       
  1228         }
       
  1229 
       
  1230         @Override
       
  1231         public String signature() {
       
  1232             return signature;
       
  1233         }
       
  1234 
       
  1235         @Override
       
  1236         public String javadoc() {
       
  1237             return javadoc;
       
  1238         }
       
  1239 
  1171     }
  1240     }
  1172 
  1241 
  1173     private boolean isEmptyArgumentsContext(List<? extends ExpressionTree> arguments) {
  1242     private boolean isEmptyArgumentsContext(List<? extends ExpressionTree> arguments) {
  1174         if (arguments.size() == 1) {
  1243         if (arguments.size() == 1) {
  1175             Tree firstArgument = arguments.get(0);
  1244             Tree firstArgument = arguments.get(0);
  1176             return firstArgument.getKind() == Kind.ERRONEOUS;
  1245             return firstArgument.getKind() == Kind.ERRONEOUS;
  1177         }
  1246         }
  1178         return false;
  1247         return false;
  1179     }
  1248     }
  1180 
  1249 
  1181     private String element2String(SourceCache sourceCache, Element el) {
       
  1182         try {
       
  1183             if (hasSyntheticParameterNames(el)) {
       
  1184                 el = sourceCache.getSourceMethod(el);
       
  1185             }
       
  1186         } catch (IOException ex) {
       
  1187             proc.debug(ex, "SourceCodeAnalysisImpl.element2String(..., " + el + ")");
       
  1188         }
       
  1189 
       
  1190         return Util.expunge(elementHeader(sourceCache.originalTask, el, !hasSyntheticParameterNames(el)));
       
  1191     }
       
  1192 
       
  1193     private boolean hasSyntheticParameterNames(Element el) {
  1250     private boolean hasSyntheticParameterNames(Element el) {
  1194         if (el.getKind() != ElementKind.CONSTRUCTOR && el.getKind() != ElementKind.METHOD)
  1251         if (el.getKind() != ElementKind.CONSTRUCTOR && el.getKind() != ElementKind.METHOD)
  1195             return false;
  1252             return false;
  1196 
  1253 
  1197         ExecutableElement ee = (ExecutableElement) el;
  1254         ExecutableElement ee = (ExecutableElement) el;
  1200             return false;
  1257             return false;
  1201 
  1258 
  1202         return ee.getParameters()
  1259         return ee.getParameters()
  1203                  .stream()
  1260                  .stream()
  1204                  .allMatch(param -> param.getSimpleName().toString().startsWith("arg"));
  1261                  .allMatch(param -> param.getSimpleName().toString().startsWith("arg"));
  1205     }
       
  1206 
       
  1207     private final class SourceCache implements AutoCloseable {
       
  1208         private final JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
       
  1209         private final Map<String, Map<String, Element>> topLevelName2Signature2Method = new HashMap<>();
       
  1210         private final AnalyzeTask originalTask;
       
  1211         private final StandardJavaFileManager fm;
       
  1212 
       
  1213         public SourceCache(AnalyzeTask originalTask) {
       
  1214             this.originalTask = originalTask;
       
  1215             List<Path> sources = findSources();
       
  1216             if (sources.iterator().hasNext()) {
       
  1217                 StandardJavaFileManager fm = compiler.getStandardFileManager(null, null, null);
       
  1218                 try {
       
  1219                     fm.setLocationFromPaths(StandardLocation.SOURCE_PATH, sources);
       
  1220                 } catch (IOException ex) {
       
  1221                     proc.debug(ex, "SourceCodeAnalysisImpl.SourceCache.<init>(...)");
       
  1222                     try {
       
  1223                         fm.close();
       
  1224                     } catch (IOException closeEx) {
       
  1225                         proc.debug(closeEx, "SourceCodeAnalysisImpl.SourceCache.close()");
       
  1226                     }
       
  1227                     fm = null;
       
  1228                 }
       
  1229                 this.fm = fm;
       
  1230             } else {
       
  1231                 //don't waste time if there are no sources
       
  1232                 this.fm = null;
       
  1233             }
       
  1234         }
       
  1235 
       
  1236         public Element getSourceMethod(Element method) throws IOException {
       
  1237             if (fm == null)
       
  1238                 return method;
       
  1239 
       
  1240             TypeElement type = topLevelType(method);
       
  1241 
       
  1242             if (type == null)
       
  1243                 return method;
       
  1244 
       
  1245             String binaryName = originalTask.task.getElements().getBinaryName(type).toString();
       
  1246 
       
  1247             Map<String, Element> cache = topLevelName2Signature2Method.get(binaryName);
       
  1248 
       
  1249             if (cache == null) {
       
  1250                 topLevelName2Signature2Method.put(binaryName, cache = createMethodCache(binaryName));
       
  1251             }
       
  1252 
       
  1253             String handle = elementHeader(originalTask, method, false);
       
  1254 
       
  1255             return cache.getOrDefault(handle, method);
       
  1256         }
       
  1257 
       
  1258         private TypeElement topLevelType(Element el) {
       
  1259             while (el != null && el.getEnclosingElement().getKind() != ElementKind.PACKAGE) {
       
  1260                 el = el.getEnclosingElement();
       
  1261             }
       
  1262 
       
  1263             return el != null && (el.getKind().isClass() || el.getKind().isInterface()) ? (TypeElement) el : null;
       
  1264         }
       
  1265 
       
  1266         private Map<String, Element> createMethodCache(String binaryName) throws IOException {
       
  1267             Pair<JavacTask, CompilationUnitTree> source = findSource(binaryName);
       
  1268 
       
  1269             if (source == null)
       
  1270                 return Collections.emptyMap();
       
  1271 
       
  1272             Map<String, Element> signature2Method = new HashMap<>();
       
  1273             Trees trees = Trees.instance(source.fst);
       
  1274 
       
  1275             new TreePathScanner<Void, Void>() {
       
  1276                 @Override
       
  1277                 public Void visitMethod(MethodTree node, Void p) {
       
  1278                     Element currentMethod = trees.getElement(getCurrentPath());
       
  1279 
       
  1280                     if (currentMethod != null) {
       
  1281                         signature2Method.put(elementHeader(originalTask, currentMethod, false), currentMethod);
       
  1282                     }
       
  1283 
       
  1284                     return null;
       
  1285                 }
       
  1286             }.scan(source.snd, null);
       
  1287 
       
  1288             return signature2Method;
       
  1289         }
       
  1290 
       
  1291         private Pair<JavacTask, CompilationUnitTree> findSource(String binaryName) throws IOException {
       
  1292             JavaFileObject jfo = fm.getJavaFileForInput(StandardLocation.SOURCE_PATH,
       
  1293                                                         binaryName,
       
  1294                                                         JavaFileObject.Kind.SOURCE);
       
  1295 
       
  1296             if (jfo == null)
       
  1297                 return null;
       
  1298 
       
  1299             List<JavaFileObject> jfos = Arrays.asList(jfo);
       
  1300             JavacTaskImpl task = (JavacTaskImpl) compiler.getTask(null, fm, d -> {}, null, null, jfos);
       
  1301             Iterable<? extends CompilationUnitTree> cuts = task.parse();
       
  1302 
       
  1303             task.enter();
       
  1304 
       
  1305             return Pair.of(task, cuts.iterator().next());
       
  1306         }
       
  1307 
       
  1308         @Override
       
  1309         public void close() {
       
  1310             try {
       
  1311                 if (fm != null) {
       
  1312                     fm.close();
       
  1313                 }
       
  1314             } catch (IOException ex) {
       
  1315                 proc.debug(ex, "SourceCodeAnalysisImpl.SourceCache.close()");
       
  1316             }
       
  1317         }
       
  1318     }
  1262     }
  1319 
  1263 
  1320     private List<Path> availableSources;
  1264     private List<Path> availableSources;
  1321 
  1265 
  1322     private List<Path> findSources() {
  1266     private List<Path> findSources() {
  1326         List<Path> result = new ArrayList<>();
  1270         List<Path> result = new ArrayList<>();
  1327         Path home = Paths.get(System.getProperty("java.home"));
  1271         Path home = Paths.get(System.getProperty("java.home"));
  1328         Path srcZip = home.resolve("src.zip");
  1272         Path srcZip = home.resolve("src.zip");
  1329         if (!Files.isReadable(srcZip))
  1273         if (!Files.isReadable(srcZip))
  1330             srcZip = home.getParent().resolve("src.zip");
  1274             srcZip = home.getParent().resolve("src.zip");
  1331         if (Files.isReadable(srcZip))
  1275         if (Files.isReadable(srcZip)) {
  1332             result.add(srcZip);
  1276             boolean keepOpen = false;
       
  1277             FileSystem zipFO = null;
       
  1278 
       
  1279             try {
       
  1280                 URI uri = URI.create("jar:" + srcZip.toUri());
       
  1281                 zipFO = FileSystems.newFileSystem(uri, Collections.emptyMap());
       
  1282                 Path root = zipFO.getRootDirectories().iterator().next();
       
  1283 
       
  1284                 if (Files.exists(root.resolve("java/lang/Object.java".replace("/", zipFO.getSeparator())))) {
       
  1285                     //non-modular format:
       
  1286                     result.add(srcZip);
       
  1287                 } else if (Files.exists(root.resolve("java.base/java/lang/Object.java".replace("/", zipFO.getSeparator())))) {
       
  1288                     //modular format:
       
  1289                     try (DirectoryStream<Path> ds = Files.newDirectoryStream(root)) {
       
  1290                         for (Path p : ds) {
       
  1291                             if (Files.isDirectory(p)) {
       
  1292                                 result.add(p);
       
  1293                             }
       
  1294                         }
       
  1295                     }
       
  1296 
       
  1297                     keepOpen = true;
       
  1298                 }
       
  1299             } catch (IOException ex) {
       
  1300                 proc.debug(ex, "SourceCodeAnalysisImpl.findSources()");
       
  1301             } finally {
       
  1302                 if (zipFO != null) {
       
  1303                     if (keepOpen) {
       
  1304                         closeables.add(zipFO);
       
  1305                     } else {
       
  1306                         try {
       
  1307                             zipFO.close();
       
  1308                         } catch (IOException ex) {
       
  1309                             proc.debug(ex, "SourceCodeAnalysisImpl.findSources()");
       
  1310                         }
       
  1311                     }
       
  1312                 }
       
  1313             }
       
  1314         }
  1333         return availableSources = result;
  1315         return availableSources = result;
  1334     }
  1316     }
  1335 
  1317 
  1336     private String elementHeader(AnalyzeTask at, Element el) {
  1318     private String elementHeader(AnalyzeTask at, Element el, boolean includeParameterNames, boolean useFQN) {
  1337         return elementHeader(at, el, true);
       
  1338     }
       
  1339 
       
  1340     private String elementHeader(AnalyzeTask at, Element el, boolean includeParameterNames) {
       
  1341         switch (el.getKind()) {
  1319         switch (el.getKind()) {
  1342             case ANNOTATION_TYPE: case CLASS: case ENUM: case INTERFACE: {
  1320             case ANNOTATION_TYPE: case CLASS: case ENUM: case INTERFACE: {
  1343                 TypeElement type = (TypeElement)el;
  1321                 TypeElement type = (TypeElement)el;
  1344                 String fullname = type.getQualifiedName().toString();
  1322                 String fullname = type.getQualifiedName().toString();
  1345                 Element pkg = at.getElements().getPackageOf(el);
  1323                 Element pkg = at.getElements().getPackageOf(el);
  1346                 String name = pkg == null ? fullname :
  1324                 String name = pkg == null || useFQN ? fullname :
  1347                         proc.maps.fullClassNameAndPackageToClass(fullname, ((PackageElement)pkg).getQualifiedName().toString());
  1325                         proc.maps.fullClassNameAndPackageToClass(fullname, ((PackageElement)pkg).getQualifiedName().toString());
  1348 
  1326 
  1349                 return name + typeParametersOpt(at, type.getTypeParameters());
  1327                 return name + typeParametersOpt(at, type.getTypeParameters(), includeParameterNames);
  1350             }
  1328             }
  1351             case TYPE_PARAMETER: {
  1329             case TYPE_PARAMETER: {
  1352                 TypeParameterElement tp = (TypeParameterElement)el;
  1330                 TypeParameterElement tp = (TypeParameterElement)el;
  1353                 String name = tp.getSimpleName().toString();
  1331                 String name = tp.getSimpleName().toString();
  1354 
  1332 
  1361                         : name + " extends " + bounds.stream()
  1339                         : name + " extends " + bounds.stream()
  1362                                 .map(bound -> printType(at, proc, bound))
  1340                                 .map(bound -> printType(at, proc, bound))
  1363                                 .collect(joining(" & "));
  1341                                 .collect(joining(" & "));
  1364             }
  1342             }
  1365             case FIELD:
  1343             case FIELD:
  1366                 return elementHeader(at, el.getEnclosingElement()) + "." + el.getSimpleName() + ":" + el.asType();
  1344                 return elementHeader(at, el.getEnclosingElement(), includeParameterNames, false) + "." + el.getSimpleName() + ":" + el.asType();
  1367             case ENUM_CONSTANT:
  1345             case ENUM_CONSTANT:
  1368                 return elementHeader(at, el.getEnclosingElement()) + "." + el.getSimpleName();
  1346                 return elementHeader(at, el.getEnclosingElement(), includeParameterNames, false) + "." + el.getSimpleName();
  1369             case EXCEPTION_PARAMETER: case LOCAL_VARIABLE: case PARAMETER: case RESOURCE_VARIABLE:
  1347             case EXCEPTION_PARAMETER: case LOCAL_VARIABLE: case PARAMETER: case RESOURCE_VARIABLE:
  1370                 return el.getSimpleName() + ":" + el.asType();
  1348                 return el.getSimpleName() + ":" + el.asType();
  1371             case CONSTRUCTOR: case METHOD: {
  1349             case CONSTRUCTOR: case METHOD: {
  1372                 StringBuilder header = new StringBuilder();
  1350                 StringBuilder header = new StringBuilder();
  1373 
  1351 
  1377                 if (isMethod) {
  1355                 if (isMethod) {
  1378                     // return type
  1356                     // return type
  1379                     header.append(printType(at, proc, method.getReturnType())).append(" ");
  1357                     header.append(printType(at, proc, method.getReturnType())).append(" ");
  1380                 } else {
  1358                 } else {
  1381                     // type parameters for the constructor
  1359                     // type parameters for the constructor
  1382                     String typeParameters = typeParametersOpt(at, method.getTypeParameters());
  1360                     String typeParameters = typeParametersOpt(at, method.getTypeParameters(), includeParameterNames);
  1383                     if (!typeParameters.isEmpty()) {
  1361                     if (!typeParameters.isEmpty()) {
  1384                         header.append(typeParameters).append(" ");
  1362                         header.append(typeParameters).append(" ");
  1385                     }
  1363                     }
  1386                 }
  1364                 }
  1387 
  1365 
  1388                 // receiver type
  1366                 // receiver type
  1389                 String clazz = elementHeader(at, el.getEnclosingElement());
  1367                 String clazz = elementHeader(at, el.getEnclosingElement(), includeParameterNames, false);
  1390                 header.append(clazz);
  1368                 header.append(clazz);
  1391 
  1369 
  1392                 if (isMethod) {
  1370                 if (isMethod) {
  1393                     //method name with type parameters
  1371                     //method name with type parameters
  1394                     (clazz.isEmpty() ? header : header.append("."))
  1372                     (clazz.isEmpty() ? header : header.append("."))
  1395                             .append(typeParametersOpt(at, method.getTypeParameters()))
  1373                             .append(typeParametersOpt(at, method.getTypeParameters(), includeParameterNames))
  1396                             .append(el.getSimpleName());
  1374                             .append(el.getSimpleName());
  1397                 }
  1375                 }
  1398 
  1376 
  1399                 // arguments
  1377                 // arguments
  1400                 header.append("(");
  1378                 header.append("(");
  1433         if (arrayType.getKind() == TypeKind.ARRAY) {
  1411         if (arrayType.getKind() == TypeKind.ARRAY) {
  1434             return ((ArrayType)arrayType).getComponentType();
  1412             return ((ArrayType)arrayType).getComponentType();
  1435         }
  1413         }
  1436         return arrayType;
  1414         return arrayType;
  1437     }
  1415     }
  1438     private String typeParametersOpt(AnalyzeTask at, List<? extends TypeParameterElement> typeParameters) {
  1416     private String typeParametersOpt(AnalyzeTask at, List<? extends TypeParameterElement> typeParameters, boolean includeParameterNames) {
  1439         return typeParameters.isEmpty() ? ""
  1417         return typeParameters.isEmpty() ? ""
  1440                 : typeParameters.stream()
  1418                 : typeParameters.stream()
  1441                         .map(tp -> elementHeader(at, tp))
  1419                         .map(tp -> elementHeader(at, tp, includeParameterNames, false))
  1442                         .collect(joining(", ", "<", ">"));
  1420                         .collect(joining(", ", "<", ">"));
  1443     }
  1421     }
  1444 
  1422 
  1445     @Override
  1423     @Override
  1446     public String analyzeType(String code, int cursor) {
  1424     public String analyzeType(String code, int cursor) {