From 5792f523fcb66909bb843400d05efa07627366de Mon Sep 17 00:00:00 2001 From: BoykoAlex Date: Wed, 30 Nov 2016 13:14:27 -0500 Subject: [PATCH] Class Reference content assist for boot application properties and yaml --- .../metadata/CachingValueProvider.java | 6 +- .../metadata/ClassReferenceProvider.java | 135 ++++++++++++++++++ .../metadata/ResourceHintProvider.java | 2 +- .../metadata/ValueProviderRegistry.java | 2 +- .../commons/commons-java/pom.xml | 6 + .../vscode/commons/jandex/JandexIndex.java | 121 +++++++++++----- .../ide/vscode/commons/jandex/TypeImpl.java | 8 +- .../ide/vscode/commons/java/IJavaProject.java | 14 ++ .../ide/vscode/commons/maven/MavenCore.java | 11 +- .../commons/maven/java/MavenJavaProject.java | 18 ++- .../maven/java/MavenProjectClasspath.java | 16 ++- .../JavaProjectWithClasspathFile.java | 14 +- .../vscode/commons/maven/JavaIndexTest.java | 25 ++++ .../src/test/resources/.gitignore | 3 + .../test/ApplicationPropertiesEditorTest.java | 2 +- .../yaml/ApplicationYamlEditorTest.java | 2 +- 16 files changed, 331 insertions(+), 54 deletions(-) create mode 100644 vscode-extensions/commons/application-properties-metadata/src/main/java/org/springframework/ide/vscode/application/properties/metadata/ClassReferenceProvider.java create mode 100644 vscode-extensions/commons/commons-maven/src/test/resources/.gitignore diff --git a/vscode-extensions/commons/application-properties-metadata/src/main/java/org/springframework/ide/vscode/application/properties/metadata/CachingValueProvider.java b/vscode-extensions/commons/application-properties-metadata/src/main/java/org/springframework/ide/vscode/application/properties/metadata/CachingValueProvider.java index a3be8be31..bb7d59c3a 100644 --- a/vscode-extensions/commons/application-properties-metadata/src/main/java/org/springframework/ide/vscode/application/properties/metadata/CachingValueProvider.java +++ b/vscode-extensions/commons/application-properties-metadata/src/main/java/org/springframework/ide/vscode/application/properties/metadata/CachingValueProvider.java @@ -128,17 +128,17 @@ public abstract class CachingValueProvider implements ValueProviderStrategy { } } // debug("full search for: "+query); - return getValuesAsycn(javaProject, query); + return getValuesAsync(javaProject, query); } - protected abstract Flux getValuesAsycn(IJavaProject javaProject, String query); + protected abstract Flux getValuesAsync(IJavaProject javaProject, String query); private Tuple2 key(IJavaProject javaProject, String query) { return Tuples.of(javaProject==null?null:javaProject.getElementName(), query); } protected Cache createCache() { - return CacheBuilder.newBuilder().expireAfterWrite(1, TimeUnit.MINUTES).build(); + return CacheBuilder.newBuilder().expireAfterWrite(1, TimeUnit.MINUTES).expireAfterAccess(1, TimeUnit.MINUTES).build(); } public static void restoreDefaults() { diff --git a/vscode-extensions/commons/application-properties-metadata/src/main/java/org/springframework/ide/vscode/application/properties/metadata/ClassReferenceProvider.java b/vscode-extensions/commons/application-properties-metadata/src/main/java/org/springframework/ide/vscode/application/properties/metadata/ClassReferenceProvider.java new file mode 100644 index 000000000..e50f95a46 --- /dev/null +++ b/vscode-extensions/commons/application-properties-metadata/src/main/java/org/springframework/ide/vscode/application/properties/metadata/ClassReferenceProvider.java @@ -0,0 +1,135 @@ +package org.springframework.ide.vscode.application.properties.metadata; + +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.function.Function; +import java.util.stream.Collectors; + +import org.springframework.ide.vscode.application.properties.metadata.ValueProviderRegistry.ValueProviderStrategy; +import org.springframework.ide.vscode.application.properties.metadata.hints.StsValueHint; +import org.springframework.ide.vscode.commons.java.Flags; +import org.springframework.ide.vscode.commons.java.IJavaProject; +import org.springframework.ide.vscode.commons.java.IType; +import org.springframework.ide.vscode.commons.util.Log; +import org.springframework.ide.vscode.commons.util.StringUtil; + +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; + +import reactor.core.publisher.Flux; + +public class ClassReferenceProvider extends CachingValueProvider { + + /** + * Default value for the 'concrete' parameter. + */ + private static final boolean DEFAULT_CONCRETE = true; + + private static final ClassReferenceProvider UNTARGETTED_INSTANCE = new ClassReferenceProvider(null, DEFAULT_CONCRETE); + + public static final Function, ValueProviderStrategy> FACTORY = applyOn( + 1, TimeUnit.MINUTES, + (params) -> { + String target = getTarget(params); + Boolean concrete = getConcrete(params); + if (target!=null || concrete!=null) { + if (concrete==null) { + concrete = DEFAULT_CONCRETE; + } + return new ClassReferenceProvider(target, concrete); + } + return UNTARGETTED_INSTANCE; + } + ); + + public static Function applyOn(long duration, TimeUnit unit, Function func) { + Cache cache = CacheBuilder.newBuilder().expireAfterAccess(duration, unit).expireAfterWrite(duration, unit).build(); + return (k) -> { + try { + return cache.get(k, () -> func.apply(k)); + } catch (ExecutionException e) { + Log.log(e); + return null; + } + }; + } + + private static String getTarget(Map params) { + if (params!=null) { + Object obj = params.get("target"); + if (obj instanceof String) { + String target = (String) obj; + if (StringUtil.hasText(target)) { + return target; + } + } + } + return null; + } + + private static boolean isAbstract(IType type) { + try { + return type.isInterface() || Flags.isAbstract(type.getFlags()); + } catch (Exception e) { + Log.log(e); + return false; + } + } + + private static Boolean getConcrete(Map params) { + try { + if (params!=null) { + Object obj = params.get("concrete"); + if (obj instanceof String) { + String concrete = (String) obj; + return Boolean.valueOf(concrete); + } else if (obj instanceof Boolean) { + return (Boolean) obj; + } + } + } catch (Exception e) { + Log.log(e); + } + return null; + } + + /** + * Optional, fully qualified name of the 'target' type. Suggested hints should be a subtype of this type. + */ + private String target; + + /** + * Optional parameter, whether only concrete types should be suggested. Default value is true. + */ + private boolean concrete; + + private ClassReferenceProvider(String target, boolean concrete) { + this.target = target; + this.concrete = concrete; + } + + @Override + protected Flux getValuesAsync(IJavaProject javaProject, String query) { + IType targetType = target == null || target.isEmpty() ? javaProject.findType("java.lang.Object") : javaProject.findType(target); + if (targetType == null) { + return Flux.empty(); + } + Set allSubclasses = javaProject + .allSubtypesOf(targetType) + .filter(t -> Flags.isPublic(t.getFlags()) && !concrete || !isAbstract(t)) + .collect(Collectors.toSet()) + .block(); + if (allSubclasses.isEmpty()) { + return Flux.empty(); + } else { + return javaProject + .fuzzySearchTypes(query, type -> allSubclasses.contains(type)) + .collectSortedList((o1, o2) -> o2.getT2().compareTo(o1.getT2())) + .flatMap(l -> Flux.fromIterable(l)) + .map(t -> StsValueHint.create(t.getT1())); + } + } + +} diff --git a/vscode-extensions/commons/application-properties-metadata/src/main/java/org/springframework/ide/vscode/application/properties/metadata/ResourceHintProvider.java b/vscode-extensions/commons/application-properties-metadata/src/main/java/org/springframework/ide/vscode/application/properties/metadata/ResourceHintProvider.java index 3200b2a01..a9e00af9b 100644 --- a/vscode-extensions/commons/application-properties-metadata/src/main/java/org/springframework/ide/vscode/application/properties/metadata/ResourceHintProvider.java +++ b/vscode-extensions/commons/application-properties-metadata/src/main/java/org/springframework/ide/vscode/application/properties/metadata/ResourceHintProvider.java @@ -51,7 +51,7 @@ public class ResourceHintProvider implements ValueProviderStrategy { private static class ClasspathHints extends CachingValueProvider { @Override - protected Flux getValuesAsycn(IJavaProject javaProject, String query) { + protected Flux getValuesAsync(IJavaProject javaProject, String query) { return Flux.fromStream(javaProject.getClasspath().getClasspathResources().distinct().map(StsValueHint::create)); } } diff --git a/vscode-extensions/commons/application-properties-metadata/src/main/java/org/springframework/ide/vscode/application/properties/metadata/ValueProviderRegistry.java b/vscode-extensions/commons/application-properties-metadata/src/main/java/org/springframework/ide/vscode/application/properties/metadata/ValueProviderRegistry.java index 49c404567..a7b488d10 100644 --- a/vscode-extensions/commons/application-properties-metadata/src/main/java/org/springframework/ide/vscode/application/properties/metadata/ValueProviderRegistry.java +++ b/vscode-extensions/commons/application-properties-metadata/src/main/java/org/springframework/ide/vscode/application/properties/metadata/ValueProviderRegistry.java @@ -49,7 +49,7 @@ public class ValueProviderRegistry { protected void initializeDefaults(ValueProviderRegistry r) { // def("logger-name", LoggerNameProvider.FACTORY); -// def("class-reference", ClassReferenceProvider.FACTORY); + def("class-reference", ClassReferenceProvider.FACTORY); } private Map, ValueProviderStrategy>> registry = new HashMap<>(); diff --git a/vscode-extensions/commons/commons-java/pom.xml b/vscode-extensions/commons/commons-java/pom.xml index 558fca823..2d90305ae 100644 --- a/vscode-extensions/commons/commons-java/pom.xml +++ b/vscode-extensions/commons/commons-java/pom.xml @@ -51,5 +51,11 @@ ${roaster.version} runtime + + + io.projectreactor + reactor-core + ${reactor-version} + \ No newline at end of file diff --git a/vscode-extensions/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/jandex/JandexIndex.java b/vscode-extensions/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/jandex/JandexIndex.java index 4bd7f7a70..bb834d1f8 100644 --- a/vscode-extensions/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/jandex/JandexIndex.java +++ b/vscode-extensions/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/jandex/JandexIndex.java @@ -4,11 +4,14 @@ import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; -import java.nio.file.Path; import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; import java.util.Iterator; import java.util.List; +import java.util.Map; import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutionException; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -24,7 +27,9 @@ import org.springframework.ide.vscode.commons.java.IField; import org.springframework.ide.vscode.commons.java.IJavadocProvider; import org.springframework.ide.vscode.commons.java.IMethod; import org.springframework.ide.vscode.commons.java.IType; +import org.springframework.ide.vscode.commons.java.IJavaProject.TypeFilter; import org.springframework.ide.vscode.commons.javadoc.IJavadoc; +import org.springframework.ide.vscode.commons.util.FuzzyMatcher; import org.springframework.ide.vscode.commons.util.Log; import com.google.common.base.Supplier; @@ -32,17 +37,13 @@ import com.google.common.base.Suppliers; import com.google.common.cache.Cache; import com.google.common.cache.CacheBuilder; +import reactor.core.publisher.Flux; +import reactor.core.scheduler.Schedulers; +import reactor.util.function.Tuple2; +import reactor.util.function.Tuples; + public class JandexIndex { - private static class Entry { - K key; - V value; - Entry(K key, V value) { - this.key = key; - this.value = value; - } - } - @FunctionalInterface public static interface IndexFileFinder { File findIndexFile(File jarFile); @@ -77,10 +78,12 @@ public class JandexIndex { }; - private Supplier>> index; + private Map>> index; private JavadocProviderFactory javadocProviderFactory; + private Map>>> knownTypes; + private Cache javadocProvidersCache = CacheBuilder.newBuilder().build(); private JandexIndex[] baseIndex; @@ -93,26 +96,25 @@ public class JandexIndex { return javadocProviderFactory; } - public JandexIndex(Stream classpathEntries, IndexFileFinder indexFileFinder, JavadocProviderFactory javadocProviderFactory, JandexIndex... baseIndex) { + public JandexIndex(Collection classpathEntries, IndexFileFinder indexFileFinder, JavadocProviderFactory javadocProviderFactory, JandexIndex... baseIndex) { this.baseIndex = baseIndex; - index = Suppliers.memoize(() -> buildIndex(classpathEntries, indexFileFinder).collect(Collectors.toList())); + this.index = new ConcurrentHashMap<>(); + this.knownTypes = new HashMap<>(); this.javadocProviderFactory = javadocProviderFactory; + classpathEntries.forEach(file -> { + index.put(file, Suppliers.memoize(() -> createIndex(file, indexFileFinder))); + knownTypes.put(file, Suppliers.memoize(() -> getKnownTypesStream(file).collect(Collectors.toList()))); + }); } - private Stream> buildIndex(Stream classpathEntries, IndexFileFinder indexFileFinder) { - return classpathEntries - .map(entry -> entry.toFile()) - .map(file -> { - Optional index = Optional.empty(); - if (file.isFile() && file.getName().endsWith(".jar")) { - index = indexJar(file, indexFileFinder); - } else if (file.isDirectory()) { - index = indexFolder(file); - } - return new Entry<>(file, index); - }) - .filter(e -> e.value.isPresent()) - .map(e -> new Entry<>(e.key, e.value.get())); + private Optional createIndex(File file, IndexFileFinder indexFileFinder) { + if (file.isFile() && file.getName().endsWith(".jar")) { + return indexJar(file, indexFileFinder); + } else if (file.isDirectory()) { + return indexFolder(file); + } else { + return Optional.empty(); + } } private static Optional indexFolder(File folder) { @@ -181,7 +183,7 @@ public class JandexIndex { public IType findType(String fqName) { return getClassByName(DotName.createSimple(fqName)); } - + IType getClassByName(DotName fqName) { // First look for type in the base index array return (baseIndex == null ? Stream.empty() @@ -191,24 +193,71 @@ public class JandexIndex { .filter(type -> type != null) .findFirst() // If not found look at indices owned by this JandexIndex instance - .orElseGet(() -> index.get().stream() - .map(e -> new Entry<>(e.key, e.value.getClassByName(fqName))) - .filter(e -> e.value != null) + .orElseGet(() -> streamOfIndices() + .map(e -> Tuples.of(e.getT1(), e.getT2().getClassByName(fqName))) + .filter(e -> e.getT2() != null) .map(e -> createType(e)) .findFirst() .orElse(null)); } - private IType createType(Entry match) { - File classpathResource = match.key; + private IType createType(Tuple2 match) { + File classpathResource = match.getT1(); IJavadocProvider javadocProvider = null; try { - javadocProvider = javadocProvidersCache.get(match.key, () -> javadocProviderFactory == null ? ABSENT_JAVADOC_PROVIDER : javadocProviderFactory.createJavadocProvider(classpathResource)); + javadocProvider = javadocProvidersCache.get(classpathResource, () -> javadocProviderFactory == null ? ABSENT_JAVADOC_PROVIDER : javadocProviderFactory.createJavadocProvider(classpathResource)); } catch (ExecutionException e) { Log.log(e); } - return Wrappers.wrap(this, match.value, javadocProvider); + return Wrappers.wrap(this, match.getT2(), javadocProvider); } - + + private Stream> streamOfIndices() { + return index.entrySet().parallelStream().map(e -> Tuples.of(e.getKey(), e.getValue().get())).filter(t -> t.getT2().isPresent()).map(t -> Tuples.of(t.getT1(), t.getT2().get())); + } + + private Stream> getKnownTypesStream(File file) { + Optional indexView = index.get(file).get(); + if (indexView.isPresent()) { + return indexView.get().getKnownClasses().parallelStream().map(info -> Tuples.of(info.name().toString(), createType(Tuples.of(file, info)))); + } + return Stream.empty(); + } + + public Flux> fuzzySearchTypes(String searchTerm, TypeFilter typeFilter) { + Flux> flux = Flux.fromIterable(knownTypes.values()) + .publishOn(Schedulers.parallel()) + .flatMap(s -> Flux.fromIterable(s.get())) + .filter(t -> typeFilter == null || typeFilter.accept(t.getT2())) + .map(t -> Tuples.of(t.getT2(), FuzzyMatcher.matchScore(searchTerm, t.getT1()))) + .filter(t -> t.getT2() != 0.0); + if (baseIndex == null) { + return flux; + } else { + return Flux.merge(flux, Flux.fromArray(baseIndex).flatMap(index -> index.fuzzySearchTypes(searchTerm, typeFilter))); + } + } + + public Flux allSubtypesOf(IType type) { + DotName name = DotName.createSimple(type.getFullyQualifiedName()); + Flux flux = Flux.fromIterable(index.keySet()) + .publishOn(Schedulers.parallel()) + .flatMap(file -> { + Optional optional = index.get(file).get(); + if (optional.isPresent()) { + return Flux.fromIterable(type.isInterface() ? optional.get().getAllKnownImplementors(name) : optional.get().getAllKnownSubclasses(name)) + .publishOn(Schedulers.parallel()) + .map(info -> createType(Tuples.of(file, info))); + } else { + return Flux.empty(); + } + }); + if (baseIndex == null) { + return flux; + } else { + return Flux.merge(flux, Flux.fromArray(baseIndex).flatMap(index -> index.allSubtypesOf(type))); + } + } + } diff --git a/vscode-extensions/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/jandex/TypeImpl.java b/vscode-extensions/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/jandex/TypeImpl.java index c20051a05..a8c1a65ef 100644 --- a/vscode-extensions/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/jandex/TypeImpl.java +++ b/vscode-extensions/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/jandex/TypeImpl.java @@ -4,6 +4,7 @@ import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; +import org.jboss.jandex.AnnotationInstance; import org.jboss.jandex.ClassInfo; import org.jboss.jandex.DotName; import org.jboss.jandex.Type; @@ -57,7 +58,8 @@ class TypeImpl implements IType { @Override public Stream getAnnotations() { // TODO: check correctness! - return info.annotations().get(info.name()).stream().map(a -> Wrappers.wrap(a, javadocProvider)); + List annotations = info.annotations().get(info.name()); + return annotations == null ? Stream.empty() : annotations.stream().map(a -> Wrappers.wrap(a, javadocProvider)); } @Override @@ -117,13 +119,13 @@ class TypeImpl implements IType { @Override public int hashCode() { - return info.toString().hashCode(); + return info.hashCode(); } @Override public boolean equals(Object obj) { if (obj instanceof TypeImpl) { - return info.toString().equals(((TypeImpl)obj).info.toString()); + return info.equals(((TypeImpl)obj).info); } return super.equals(obj); } diff --git a/vscode-extensions/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/IJavaProject.java b/vscode-extensions/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/IJavaProject.java index 9aefc9e0a..c76860c49 100644 --- a/vscode-extensions/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/IJavaProject.java +++ b/vscode-extensions/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/IJavaProject.java @@ -1,6 +1,20 @@ package org.springframework.ide.vscode.commons.java; +import reactor.core.publisher.Flux; +import reactor.util.function.Tuple2; + public interface IJavaProject extends IJavaElement { + + @FunctionalInterface + public static interface TypeFilter { + boolean accept(IType type); + } + IType findType(String fqName); + + Flux> fuzzySearchTypes(String searchTerm, TypeFilter typeFilter); + + Flux allSubtypesOf(IType type); + IClasspath getClasspath(); } diff --git a/vscode-extensions/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/MavenCore.java b/vscode-extensions/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/MavenCore.java index c9e5465f8..0a5be0d27 100644 --- a/vscode-extensions/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/MavenCore.java +++ b/vscode-extensions/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/MavenCore.java @@ -84,7 +84,7 @@ public class MavenCore { private Supplier javaCoreIndex = Suppliers.memoize(() -> { try { - return new JandexIndex(getJreLibs(), jarFile -> findIndexFile(jarFile), (classpathResource) -> { + return new JandexIndex(getJreLibs().map(path -> path.toFile()).collect(Collectors.toList()), jarFile -> findIndexFile(jarFile), (classpathResource) -> { try { String javaVersion = getJavaRuntimeMinorVersion(); if (javaVersion == null) { @@ -283,7 +283,14 @@ public class MavenCore { private File findIndexFile(File jarFile) { String suffix = null; try { - if (jarFile.toString().startsWith(getJavaHome())) { + String javaHome = getJavaHome(); + if (javaHome != null) { + int index = javaHome.lastIndexOf('/'); + if (index != -1) { + javaHome = javaHome.substring(0, index); + } + } + if (jarFile.toString().startsWith(javaHome)) { suffix = getJavaRuntimeVersion(); } } catch (MavenException e) { diff --git a/vscode-extensions/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/java/MavenJavaProject.java b/vscode-extensions/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/java/MavenJavaProject.java index 1acc33dd1..9205747a6 100644 --- a/vscode-extensions/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/java/MavenJavaProject.java +++ b/vscode-extensions/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/java/MavenJavaProject.java @@ -15,12 +15,14 @@ import java.nio.file.Path; import java.nio.file.Paths; import org.apache.maven.project.MavenProject; -import org.springframework.ide.vscode.commons.java.IClasspath; import org.springframework.ide.vscode.commons.java.IJavaProject; import org.springframework.ide.vscode.commons.java.IType; import org.springframework.ide.vscode.commons.javadoc.IJavadoc; import org.springframework.ide.vscode.commons.maven.MavenCore; +import reactor.core.publisher.Flux; +import reactor.util.function.Tuple2; + /** * Wrapper for Maven Core project * @@ -60,12 +62,22 @@ public class MavenJavaProject implements IJavaProject { } @Override - public IClasspath getClasspath() { + public Flux> fuzzySearchTypes(String searchTerm, TypeFilter typeFilter) { + return classpath.fuzzySearchType(searchTerm, typeFilter); + } + + @Override + public Flux allSubtypesOf(IType type) { + return classpath.allSubtypesOf(type); + } + + @Override + public MavenProjectClasspath getClasspath() { return classpath; } public Path getOutputFolder() { return Paths.get(new File(mavenProject.getBuild().getOutputDirectory()).toURI()); } - + } diff --git a/vscode-extensions/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/java/MavenProjectClasspath.java b/vscode-extensions/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/java/MavenProjectClasspath.java index d2459f691..2fd892e56 100644 --- a/vscode-extensions/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/java/MavenProjectClasspath.java +++ b/vscode-extensions/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/java/MavenProjectClasspath.java @@ -16,6 +16,7 @@ import java.net.URL; import java.nio.file.Path; import java.util.Arrays; import java.util.Optional; +import java.util.stream.Collectors; import java.util.stream.Stream; import org.apache.maven.artifact.Artifact; @@ -25,6 +26,7 @@ import org.springframework.ide.vscode.commons.jandex.JandexIndex; import org.springframework.ide.vscode.commons.java.IClasspath; import org.springframework.ide.vscode.commons.java.IJavadocProvider; import org.springframework.ide.vscode.commons.java.IType; +import org.springframework.ide.vscode.commons.java.IJavaProject.TypeFilter; import org.springframework.ide.vscode.commons.java.parser.ParserJavadocProvider; import org.springframework.ide.vscode.commons.java.roaster.RoasterJavadocProvider; import org.springframework.ide.vscode.commons.javadoc.HtmlJavadocProvider; @@ -36,6 +38,9 @@ import org.springframework.ide.vscode.commons.util.Log; import com.google.common.base.Supplier; import com.google.common.base.Suppliers; +import reactor.core.publisher.Flux; +import reactor.util.function.Tuple2; + /** * Classpath for a maven project * @@ -70,7 +75,7 @@ public class MavenProjectClasspath implements IClasspath { } catch (Exception e) { Log.log(e); } - return new JandexIndex(classpathEntries, jarFile -> findIndexFile(jarFile), classpathResource -> { + return new JandexIndex(classpathEntries.map(p -> p.toFile()).collect(Collectors.toList()), jarFile -> findIndexFile(jarFile), classpathResource -> { switch (providerType) { case JAVA_PARSER: return createParserJavadocProvider(classpathResource); @@ -95,6 +100,14 @@ public class MavenProjectClasspath implements IClasspath { return javaIndex.get().findType(fqName); } + public Flux> fuzzySearchType(String searchTerm, TypeFilter typeFilter) { + return javaIndex.get().fuzzySearchTypes(searchTerm, typeFilter); + } + + public Flux allSubtypesOf(IType type) { + return javaIndex.get().allSubtypesOf(type); + } + private File findIndexFile(File jarFile) { return new File(maven.getIndexFolder().toString(), jarFile.getName() + "-" + jarFile.lastModified() + ".jdx"); } @@ -219,5 +232,4 @@ public class MavenProjectClasspath implements IClasspath { } } - } diff --git a/vscode-extensions/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/java/classpathfile/JavaProjectWithClasspathFile.java b/vscode-extensions/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/java/classpathfile/JavaProjectWithClasspathFile.java index 7df72c797..499fa8df2 100644 --- a/vscode-extensions/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/java/classpathfile/JavaProjectWithClasspathFile.java +++ b/vscode-extensions/commons/commons-maven/src/main/java/org/springframework/ide/vscode/commons/maven/java/classpathfile/JavaProjectWithClasspathFile.java @@ -18,6 +18,9 @@ import org.springframework.ide.vscode.commons.java.IJavaProject; import org.springframework.ide.vscode.commons.java.IType; import org.springframework.ide.vscode.commons.javadoc.IJavadoc; +import reactor.core.publisher.Flux; +import reactor.util.function.Tuple2; + /** * Java project that contains classpath text file * @@ -55,6 +58,16 @@ public class JavaProjectWithClasspathFile implements IJavaProject { return null; } + @Override + public Flux> fuzzySearchTypes(String searchTerm, TypeFilter typeFilter) { + return Flux.empty(); + } + + @Override + public Flux allSubtypesOf(IType type) { + return Flux.empty(); + } + @Override public IClasspath getClasspath() { return classpath; @@ -90,5 +103,4 @@ public class JavaProjectWithClasspathFile implements IJavaProject { return true; } - } \ No newline at end of file diff --git a/vscode-extensions/commons/commons-maven/src/test/java/org/springframework/ide/vscode/commons/maven/JavaIndexTest.java b/vscode-extensions/commons/commons-maven/src/test/java/org/springframework/ide/vscode/commons/maven/JavaIndexTest.java index 168d885b7..6b1fbc74f 100644 --- a/vscode-extensions/commons/commons-maven/src/test/java/org/springframework/ide/vscode/commons/maven/JavaIndexTest.java +++ b/vscode-extensions/commons/commons-maven/src/test/java/org/springframework/ide/vscode/commons/maven/JavaIndexTest.java @@ -3,15 +3,18 @@ package org.springframework.ide.vscode.commons.maven; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Collections; +import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; import org.junit.Assume; import org.junit.Test; +import org.springframework.ide.vscode.commons.java.Flags; import org.springframework.ide.vscode.commons.java.IField; import org.springframework.ide.vscode.commons.java.IMethod; import org.springframework.ide.vscode.commons.java.IPrimitiveType; @@ -26,6 +29,8 @@ import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; +import reactor.util.function.Tuple2; + public class JavaIndexTest { private static LoadingCache projectsCache = CacheBuilder.newBuilder().build(new CacheLoader() { @@ -62,6 +67,26 @@ public class JavaIndexTest { } } + @Test + public void fuzzySearchNoFilter() throws Exception { + List> results = MavenCore.getInstance().getJavaIndexForJreLibs() + .fuzzySearchTypes("util.Map", null) + .collectSortedList((o1, o2) -> o2.getT2().compareTo(o1.getT2())) + .block(); + assertTrue(results.size() > 10); + assertEquals("java.util.Map", results.get(0).getT1().getFullyQualifiedName()); + } + + @Test + public void fuzzySearchWithFilter() throws Exception { + List> results = MavenCore.getInstance().getJavaIndexForJreLibs() + .fuzzySearchTypes("util.Map", (type) -> Flags.isPrivate(type.getFlags())) + .collectSortedList((o1, o2) -> o2.getT2().compareTo(o1.getT2())) + .block(); + assertTrue(results.size() > 10); + assertEquals("java.util.EnumMap$KeySet", results.get(0).getT1().getFullyQualifiedName()); + } + @Test public void findClassInJar() throws Exception { MavenJavaProject project = mavenProjectsCache.get("gs-rest-service-cors-boot-1.4.1-with-classpath-file"); diff --git a/vscode-extensions/commons/commons-maven/src/test/resources/.gitignore b/vscode-extensions/commons/commons-maven/src/test/resources/.gitignore new file mode 100644 index 000000000..a4b4df396 --- /dev/null +++ b/vscode-extensions/commons/commons-maven/src/test/resources/.gitignore @@ -0,0 +1,3 @@ +**/classpath.txt +**/bin/** +**/*.log.* \ No newline at end of file diff --git a/vscode-extensions/vscode-application-properties/src/test/java/org/springframework/ide/vscode/application/properties/test/ApplicationPropertiesEditorTest.java b/vscode-extensions/vscode-application-properties/src/test/java/org/springframework/ide/vscode/application/properties/test/ApplicationPropertiesEditorTest.java index 994981b9b..0a5ca3200 100644 --- a/vscode-extensions/vscode-application-properties/src/test/java/org/springframework/ide/vscode/application/properties/test/ApplicationPropertiesEditorTest.java +++ b/vscode-extensions/vscode-application-properties/src/test/java/org/springframework/ide/vscode/application/properties/test/ApplicationPropertiesEditorTest.java @@ -1331,7 +1331,7 @@ public class ApplicationPropertiesEditorTest extends AbstractPropsEditorTest { } } - @Ignore @Test public void testClassReferenceCompletion() throws Exception { + @Test public void testClassReferenceCompletion() throws Exception { CachingValueProvider.TIMEOUT = Duration.ofSeconds(20); useProject(createPredefinedMavenProject("empty-boot-1.3.0-with-mongo")); diff --git a/vscode-extensions/vscode-application-yaml/src/test/java/org/springframework/ide/vscode/application/yaml/ApplicationYamlEditorTest.java b/vscode-extensions/vscode-application-yaml/src/test/java/org/springframework/ide/vscode/application/yaml/ApplicationYamlEditorTest.java index 93f02c82b..aecb348e1 100644 --- a/vscode-extensions/vscode-application-yaml/src/test/java/org/springframework/ide/vscode/application/yaml/ApplicationYamlEditorTest.java +++ b/vscode-extensions/vscode-application-yaml/src/test/java/org/springframework/ide/vscode/application/yaml/ApplicationYamlEditorTest.java @@ -3247,7 +3247,7 @@ public class ApplicationYamlEditorTest extends AbstractPropsEditorTest { ); } - @Ignore @Test public void testClassReferenceCompletion() throws Exception { + @Test public void testClassReferenceCompletion() throws Exception { CachingValueProvider.TIMEOUT = Duration.ofSeconds(20); useProject(createPredefinedMavenProject("empty-boot-1.3.0-with-mongo"));