Class Reference content assist for boot application properties and yaml

This commit is contained in:
BoykoAlex
2016-11-30 13:14:27 -05:00
parent 6d68de4c8f
commit 5792f523fc
16 changed files with 331 additions and 54 deletions

View File

@@ -51,5 +51,11 @@
<version>${roaster.version}</version>
<scope>runtime</scope>
</dependency>
<!-- Reactor -->
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-core</artifactId>
<version>${reactor-version}</version>
</dependency>
</dependencies>
</project>

View File

@@ -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, V> {
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<List<Entry<File, IndexView>>> index;
private Map<File, Supplier<Optional<IndexView>>> index;
private JavadocProviderFactory javadocProviderFactory;
private Map<File, Supplier<List<Tuple2<String, IType>>>> knownTypes;
private Cache<File, IJavadocProvider> javadocProvidersCache = CacheBuilder.newBuilder().build();
private JandexIndex[] baseIndex;
@@ -93,26 +96,25 @@ public class JandexIndex {
return javadocProviderFactory;
}
public JandexIndex(Stream<Path> classpathEntries, IndexFileFinder indexFileFinder, JavadocProviderFactory javadocProviderFactory, JandexIndex... baseIndex) {
public JandexIndex(Collection<File> 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<Entry<File, IndexView>> buildIndex(Stream<Path> classpathEntries, IndexFileFinder indexFileFinder) {
return classpathEntries
.map(entry -> entry.toFile())
.map(file -> {
Optional<IndexView> 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<IndexView> 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<IndexView> 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.<IType>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<File, ClassInfo> match) {
File classpathResource = match.key;
private IType createType(Tuple2<File, ClassInfo> 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<Tuple2<File, IndexView>> 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<Tuple2<String, IType>> getKnownTypesStream(File file) {
Optional<IndexView> 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<Tuple2<IType, Double>> fuzzySearchTypes(String searchTerm, TypeFilter typeFilter) {
Flux<Tuple2<IType, Double>> 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<IType> allSubtypesOf(IType type) {
DotName name = DotName.createSimple(type.getFullyQualifiedName());
Flux<IType> flux = Flux.fromIterable(index.keySet())
.publishOn(Schedulers.parallel())
.flatMap(file -> {
Optional<IndexView> 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)));
}
}
}

View File

@@ -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<IAnnotation> getAnnotations() {
// TODO: check correctness!
return info.annotations().get(info.name()).stream().map(a -> Wrappers.wrap(a, javadocProvider));
List<AnnotationInstance> 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);
}

View File

@@ -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<Tuple2<IType, Double>> fuzzySearchTypes(String searchTerm, TypeFilter typeFilter);
Flux<IType> allSubtypesOf(IType type);
IClasspath getClasspath();
}