Skip to content

Commit 73433c6

Browse files
authored
🐛 Do special search for on-demand imports (#143)
* Do special search for on-demand imports Signed-off-by: Juan Manuel Leflet Estrada <[email protected]> * Calculate compilation units only if there is a * import search Signed-off-by: Juan Manuel Leflet Estrada <[email protected]> * Improvements Signed-off-by: Juan Manuel Leflet Estrada <[email protected]> * Fix and simplify getLocationForImport Signed-off-by: Juan Manuel Leflet Estrada <[email protected]> * Add missing import Signed-off-by: Juan Manuel Leflet Estrada <[email protected]> * Do not addAll and add explanatory comment Signed-off-by: Juan Manuel Leflet Estrada <[email protected]> --------- Signed-off-by: Juan Manuel Leflet Estrada <[email protected]>
1 parent a072cb0 commit 73433c6

File tree

1 file changed

+136
-69
lines changed

1 file changed

+136
-69
lines changed

java-analyzer-bundle.core/src/main/java/io/konveyor/tackle/core/internal/SampleDelegateCommandHandler.java

Lines changed: 136 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
import java.util.ArrayList;
77
import java.util.List;
8+
import java.util.regex.Matcher;
89
import java.util.regex.Pattern;
910

1011
import org.eclipse.core.runtime.IPath;
@@ -14,18 +15,26 @@
1415
import org.eclipse.jdt.core.IJavaElement;
1516
import org.eclipse.jdt.core.IJavaProject;
1617
import org.eclipse.jdt.core.IPackageFragment;
18+
import org.eclipse.jdt.core.dom.AST;
19+
import org.eclipse.jdt.core.dom.ASTParser;
20+
import org.eclipse.jdt.core.dom.CompilationUnit;
21+
import org.eclipse.jdt.core.dom.ImportDeclaration;
1722
import org.eclipse.jdt.core.search.IJavaSearchConstants;
1823
import org.eclipse.jdt.core.search.IJavaSearchScope;
1924
import org.eclipse.jdt.core.search.SearchEngine;
2025
import org.eclipse.jdt.core.search.SearchParticipant;
2126
import org.eclipse.jdt.core.search.SearchPattern;
2227
import org.eclipse.jdt.internal.core.search.JavaSearchParticipant;
2328
import org.eclipse.jdt.ls.core.internal.IDelegateCommandHandler;
29+
import org.eclipse.jdt.core.IPackageFragmentRoot;
2430
import org.eclipse.jdt.ls.core.internal.JavaLanguageServerPlugin;
31+
import org.eclipse.jdt.ls.core.internal.JDTUtils;
2532
import org.eclipse.jdt.ls.core.internal.JobHelpers;
2633
import org.eclipse.jdt.ls.core.internal.ProjectUtils;
2734
import org.eclipse.jdt.ls.core.internal.ResourceUtils;
35+
import org.eclipse.lsp4j.Location;
2836
import org.eclipse.lsp4j.SymbolInformation;
37+
import org.eclipse.lsp4j.SymbolKind;
2938

3039
import io.konveyor.tackle.core.internal.query.AnnotationQuery;
3140
import io.konveyor.tackle.core.internal.util.OpenSourceFilteredSearchScope;
@@ -77,11 +86,11 @@ private static SearchPattern mapLocationToSearchPatternLocation(int location, St
7786
var startListIndex = query.indexOf("(");
7887
var endListIndex = query.indexOf(")");
7988
var startQuery = query.substring(0, startListIndex);
80-
var endQuery = query.substring(endListIndex+1, query.length());
89+
var endQuery = query.substring(endListIndex + 1, query.length());
8190
var optionalList = endQuery.startsWith("?");
8291

8392
// This should strip the ( ) chars
84-
var listString = query.substring(startListIndex+1, endListIndex);
93+
var listString = query.substring(startListIndex + 1, endListIndex);
8594
var list = listString.split("\\|");
8695
ArrayList<SearchPattern> l = new ArrayList<SearchPattern>();
8796

@@ -91,11 +100,11 @@ private static SearchPattern mapLocationToSearchPatternLocation(int location, St
91100
l.add(mapLocationToSearchPatternLocation(location, startQuery + endQuery));
92101
}
93102

94-
for (String s: list) {
103+
for (String s : list) {
95104
var p = mapLocationToSearchPatternLocation(location, startQuery + s + endQuery);
96105
l.add(p);
97106
}
98-
107+
99108
// Get the end pattern
100109
SearchPattern p = l.subList(1, l.size()).stream().reduce(l.get(0), (SearchPattern::createOrPattern));
101110
return p;
@@ -130,26 +139,26 @@ private static SearchPattern mapLocationToSearchPatternLocation(int location, St
130139

131140
/**
132141
* Location correspondence from java provider (TYPE is the default):
133-
* "": 0,
134-
* "inheritance": 1,
135-
* "method_call": 2,
136-
* "constructor_call": 3,
137-
* "annotation": 4,
138-
* "implements_type": 5,
139-
* "enum_constant": 6,
140-
* "return_type": 7,
141-
* "import": 8,
142-
* "variable_declaration": 9,
143-
* "type": 10,
144-
* "package": 11,
145-
* "field": 12,
146-
* "method_declaration": 13,
147-
* "class_declaration": 14,
142+
* "": 0,
143+
* "inheritance": 1,
144+
* "method_call": 2,
145+
* "constructor_call": 3,
146+
* "annotation": 4,
147+
* "implements_type": 5,
148+
* "enum_constant": 6,
149+
* "return_type": 7,
150+
* "import": 8,
151+
* "variable_declaration": 9,
152+
* "type": 10,
153+
* "package": 11,
154+
* "field": 12,
155+
* "method_declaration": 13,
156+
* "class_declaration": 14,
148157
*
149158
* @param location
150159
* @param query
151160
* @return
152-
* @throws Exception
161+
* @throws Exception TODO: move these to enums
153162
*/
154163
private static SearchPattern getPatternSingleQuery(int location, String query) throws Exception {
155164
var pattern = SearchPattern.R_PATTERN_MATCH;
@@ -158,52 +167,52 @@ private static SearchPattern getPatternSingleQuery(int location, String query) t
158167
pattern = SearchPattern.R_EXACT_MATCH | SearchPattern.R_CASE_SENSITIVE;
159168
}
160169
switch (location) {
161-
// Using type for both type and annotation.
162-
// Type and annotation
163-
case 10:
164-
case 4:
165-
case 8:
166-
return SearchPattern.createPattern(query, IJavaSearchConstants.TYPE, IJavaSearchConstants.ALL_OCCURRENCES, pattern);
167-
case 5:
168-
case 1:
169-
return SearchPattern.createPattern(query, IJavaSearchConstants.TYPE, IJavaSearchConstants.IMPLEMENTORS, pattern);
170-
case 7:
171-
case 9:
172-
return SearchPattern.createPattern(query, IJavaSearchConstants.TYPE, IJavaSearchConstants.REFERENCES, pattern);
173-
case 2:
174-
if (query.contains(".")) {
175-
return SearchPattern.createPattern(query, IJavaSearchConstants.METHOD, IJavaSearchConstants.QUALIFIED_REFERENCE, SearchPattern.R_PATTERN_MATCH | SearchPattern.R_ERASURE_MATCH);
176-
}
177-
// Switched back to referenced
178-
return SearchPattern.createPattern(query, IJavaSearchConstants.METHOD, IJavaSearchConstants.REFERENCES, SearchPattern.R_PATTERN_MATCH | SearchPattern.R_ERASURE_MATCH);
179-
case 3:
180-
return SearchPattern.createPattern(query, IJavaSearchConstants.CONSTRUCTOR, IJavaSearchConstants.ALL_OCCURRENCES, pattern);
181-
case 11:
182-
return SearchPattern.createPattern(query, IJavaSearchConstants.PACKAGE, IJavaSearchConstants.ALL_OCCURRENCES, pattern);
183-
case 12:
184-
return SearchPattern.createPattern(query, IJavaSearchConstants.TYPE, IJavaSearchConstants.FIELD_DECLARATION_TYPE_REFERENCE, pattern);
185-
case 13:
186-
return SearchPattern.createPattern(query, IJavaSearchConstants.METHOD, IJavaSearchConstants.DECLARATIONS, SearchPattern.R_EXACT_MATCH | SearchPattern.R_PATTERN_MATCH);
187-
case 14:
188-
return SearchPattern.createPattern(query, IJavaSearchConstants.CLASS, IJavaSearchConstants.DECLARATIONS, SearchPattern.R_EXACT_MATCH | SearchPattern.R_PATTERN_MATCH);
170+
// Using type for both type and annotation.
171+
// Type and annotation
172+
case 10:
173+
case 4:
174+
case 8:
175+
return SearchPattern.createPattern(query, IJavaSearchConstants.TYPE, IJavaSearchConstants.ALL_OCCURRENCES, pattern);
176+
case 5:
177+
case 1:
178+
return SearchPattern.createPattern(query, IJavaSearchConstants.TYPE, IJavaSearchConstants.IMPLEMENTORS, pattern);
179+
case 7:
180+
case 9:
181+
return SearchPattern.createPattern(query, IJavaSearchConstants.TYPE, IJavaSearchConstants.REFERENCES, pattern);
182+
case 2:
183+
if (query.contains(".")) {
184+
return SearchPattern.createPattern(query, IJavaSearchConstants.METHOD, IJavaSearchConstants.QUALIFIED_REFERENCE, SearchPattern.R_PATTERN_MATCH | SearchPattern.R_ERASURE_MATCH);
185+
}
186+
// Switched back to referenced
187+
return SearchPattern.createPattern(query, IJavaSearchConstants.METHOD, IJavaSearchConstants.REFERENCES, SearchPattern.R_PATTERN_MATCH | SearchPattern.R_ERASURE_MATCH);
188+
case 3:
189+
return SearchPattern.createPattern(query, IJavaSearchConstants.CONSTRUCTOR, IJavaSearchConstants.ALL_OCCURRENCES, pattern);
190+
case 11:
191+
return SearchPattern.createPattern(query, IJavaSearchConstants.PACKAGE, IJavaSearchConstants.ALL_OCCURRENCES, pattern);
192+
case 12:
193+
return SearchPattern.createPattern(query, IJavaSearchConstants.TYPE, IJavaSearchConstants.FIELD_DECLARATION_TYPE_REFERENCE, pattern);
194+
case 13:
195+
return SearchPattern.createPattern(query, IJavaSearchConstants.METHOD, IJavaSearchConstants.DECLARATIONS, SearchPattern.R_EXACT_MATCH | SearchPattern.R_PATTERN_MATCH);
196+
case 14:
197+
return SearchPattern.createPattern(query, IJavaSearchConstants.CLASS, IJavaSearchConstants.DECLARATIONS, SearchPattern.R_EXACT_MATCH | SearchPattern.R_PATTERN_MATCH);
189198
}
190-
throw new Exception("unable to create search pattern");
199+
throw new Exception("unable to create search pattern");
191200
}
192201

193202
private static List<SymbolInformation> search(String projectName, ArrayList<String> includedPaths, String query, AnnotationQuery annotationQuery, int location, String analysisMode,
194-
boolean includeOpenSourceLibraries, String mavenLocalRepoPath, String mavenIndexPath, IProgressMonitor monitor) throws Exception {
203+
boolean includeOpenSourceLibraries, String mavenLocalRepoPath, String mavenIndexPath, IProgressMonitor monitor) throws Exception {
195204
IJavaProject[] targetProjects;
196205
IJavaProject project = ProjectUtils.getJavaProject(projectName);
197206
if (project != null) {
198-
targetProjects = new IJavaProject[] { project };
199-
} else {
200-
targetProjects= ProjectUtils.getJavaProjects();
201-
}
202-
207+
targetProjects = new IJavaProject[]{project};
208+
} else {
209+
targetProjects = ProjectUtils.getJavaProjects();
210+
}
211+
203212
logInfo("Searching in target project: " + targetProjects);
204213

205214
// For Partial results, we are going to filter out based on a list in the engine
206-
int s = IJavaSearchScope.SOURCES | IJavaSearchScope.REFERENCED_PROJECTS | IJavaSearchScope.APPLICATION_LIBRARIES;
215+
int s = IJavaSearchScope.SOURCES | IJavaSearchScope.REFERENCED_PROJECTS | IJavaSearchScope.APPLICATION_LIBRARIES;
207216
if (analysisMode.equals(sourceOnlyAnalysisMode)) {
208217
logInfo("KONVEYOR_LOG: source-only analysis mode only scoping to Sources");
209218
s = IJavaSearchScope.SOURCES;
@@ -217,17 +226,19 @@ private static List<SymbolInformation> search(String projectName, ArrayList<Stri
217226
var errors = ResourceUtils.getErrorMarkers(iJavaProject.getProject());
218227
var warnings = ResourceUtils.getWarningMarkers(iJavaProject.getProject());
219228
logInfo("KONVEYOR_LOG:" +
220-
" found errors: " + errors.toString().replace("\n", " ") +
221-
" warnings: " + warnings.toString().replace("\n", " "));
229+
" found errors: " + errors.toString().replace("\n", " ") +
230+
" warnings: " + warnings.toString().replace("\n", " "));
222231
}
223232

224-
IJavaSearchScope scope;
233+
IJavaSearchScope scope;
225234
var workspaceDirectoryLocation = JavaLanguageServerPlugin.getPreferencesManager().getPreferences().getRootPaths();
226235
if (workspaceDirectoryLocation == null || workspaceDirectoryLocation.size() == 0) {
227236
logInfo("unable to find workspace directory location");
228237
return new ArrayList<>();
229238
}
230-
239+
240+
List<ICompilationUnit> units = new ArrayList<>();
241+
231242
if (includedPaths != null && includedPaths.size() > 0) {
232243
ArrayList<IJavaElement> includedFragments = new ArrayList<IJavaElement>();
233244
for (IJavaProject proj : targetProjects) {
@@ -292,9 +303,14 @@ private static List<SymbolInformation> search(String projectName, ArrayList<Stri
292303
// instead of comparing path strings, comparing segments is better for 2 reasons:
293304
// - we don't have to worry about redundant . / etc in input
294305
// - matching sub-trees is easier with segments than strings
295-
if (includedIPath.segmentCount() <= fragmentPath.segmentCount() &&
296-
includedIPath.matchingFirstSegments(fragmentPath) == includedIPath.segmentCount()) {
306+
if (includedIPath.segmentCount() <= fragmentPath.segmentCount() &&
307+
includedIPath.matchingFirstSegments(fragmentPath) == includedIPath.segmentCount()) {
297308
includedFragments.add(fragment);
309+
310+
// Get all compilation units for included fragments
311+
if (fragment.getKind() == IPackageFragmentRoot.K_SOURCE) {
312+
units.addAll(List.of(fragment.getCompilationUnits()));
313+
}
298314
}
299315
}
300316
}
@@ -313,6 +329,8 @@ private static List<SymbolInformation> search(String projectName, ArrayList<Stri
313329
}
314330
logInfo("scope: " + scope);
315331

332+
List<SymbolInformation> symbols = new ArrayList<SymbolInformation>();
333+
316334
SearchPattern pattern;
317335
try {
318336
pattern = mapLocationToSearchPatternLocation(location, query);
@@ -325,8 +343,6 @@ private static List<SymbolInformation> search(String projectName, ArrayList<Stri
325343

326344
SearchEngine searchEngine = new SearchEngine();
327345

328-
List<SymbolInformation> symbols = new ArrayList<SymbolInformation>();
329-
330346
SymbolInformationTypeRequestor requestor = new SymbolInformationTypeRequestor(symbols, 0, monitor, location, query, annotationQuery);
331347

332348
//Use the default search participents
@@ -340,12 +356,63 @@ private static List<SymbolInformation> search(String projectName, ArrayList<Stri
340356
logInfo("KONVEYOR_LOG: unable to get search " + e.toString().replace("\n", " "));
341357
}
342358

359+
// For on-demand imports (things like <code>import javax.ejb.*;</code>) we need to
360+
// do a special search, since on-demand imports are only resolved at compilation time
361+
if (location == 8) {
362+
Matcher matcher = Pattern.compile("[^A-Z*]+\\*").matcher(query);
363+
if (matcher.matches()) {
364+
// Get all compilation units in scope
365+
for (IJavaProject p : targetProjects) {
366+
for (IPackageFragment pkg : p.getPackageFragments()) {
367+
if (pkg.getKind() == IPackageFragmentRoot.K_SOURCE) {
368+
for (ICompilationUnit unit : pkg.getCompilationUnits()) {
369+
if (scope.encloses(unit)) {
370+
units.add(unit);
371+
}
372+
}
373+
}
374+
}
375+
}
376+
377+
// Now run ImportScanner only on units in scope
378+
ASTParser parser = ASTParser.newParser(AST.getJLSLatest());
379+
Pattern regex = Pattern.compile(query);
380+
for (ICompilationUnit unit : units) {
381+
parser.setSource(unit);
382+
CompilationUnit cu = (CompilationUnit) parser.createAST(null);
383+
for (Object o : cu.imports()) {
384+
ImportDeclaration imp = (ImportDeclaration) o;
385+
if (imp.isOnDemand()) {
386+
if (regex.matcher(imp.getName().getFullyQualifiedName()).matches()) {
387+
SymbolInformation symbol = new SymbolInformation();
388+
symbol.setName(imp.getName().getFullyQualifiedName());
389+
symbol.setKind(SymbolKind.Namespace);
390+
symbol.setContainerName(unit.getElementName());
391+
symbol.setLocation(getLocationForImport(unit, imp, cu));
392+
System.out.println("Found in " + unit.getElementName());
393+
symbols.add(symbol);
394+
}
395+
}
396+
}
397+
}
398+
}
399+
}
400+
343401
logInfo("KONVEYOR_LOG: got: " + requestor.getAllSearchMatches() +
344-
" search matches for " + query +
345-
" location " + location
346-
+ " matches" + requestor.getSymbols().size());
402+
" search matches for " + query +
403+
" location " + location
404+
+ " matches" + requestor.getSymbols().size());
405+
406+
return symbols;
347407

348-
return requestor.getSymbols();
408+
}
349409

410+
public static Location getLocationForImport(ICompilationUnit icu, ImportDeclaration imp, CompilationUnit cuAst) {
411+
try {
412+
return JDTUtils.toLocation(icu, imp.getStartPosition(), imp.getLength());
413+
} catch (Exception e) {
414+
logInfo("Unable to get location for import: " + e);
415+
return null;
416+
}
350417
}
351-
}
418+
}

0 commit comments

Comments
 (0)