PT #164474609: Light TypeDescriptorData and caching super, sub-types
This commit is contained in:
@@ -17,6 +17,7 @@ import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Queue;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -36,7 +37,6 @@ import reactor.core.Disposable;
|
||||
import reactor.core.Disposables;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.util.function.Tuple2;
|
||||
import reactor.util.function.Tuples;
|
||||
|
||||
/**
|
||||
* Classpath with Jandex Java index for searching types
|
||||
@@ -101,8 +101,8 @@ public final class JandexClasspath implements ClasspathIndex {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<Tuple2<String, Double>> fuzzySearchTypes(String searchTerm, boolean includeBinaries, boolean includeSystemLibs) {
|
||||
return javaIndex.get().fuzzySearchTypes(searchTerm).map(m -> Tuples.of(m.getT2().name().toString(), m.getT3()));
|
||||
public Flux<Tuple2<IType, Double>> fuzzySearchTypes(String searchTerm, boolean includeBinaries, boolean includeSystemLibs) {
|
||||
return javaIndex.get().fuzzySearchITypes(searchTerm);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -111,8 +111,13 @@ public final class JandexClasspath implements ClasspathIndex {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<IType> allSubtypesOf(IType type) {
|
||||
return javaIndex.get().allSubtypesOf(type);
|
||||
public Flux<IType> allSubtypesOf(String fqName, boolean includeFocusType) {
|
||||
IType type = javaIndex.get().findType(fqName);
|
||||
if (type == null) {
|
||||
return Flux.empty();
|
||||
} else {
|
||||
return Flux.concat(includeFocusType ? Flux.fromStream(Stream.of(type)) : Flux.empty(), javaIndex.get().allSubtypesOf(type));
|
||||
}
|
||||
}
|
||||
|
||||
private File findIndexFile(File jarFile) {
|
||||
@@ -152,25 +157,25 @@ public final class JandexClasspath implements ClasspathIndex {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<IType> allSuperTypesOf(IType type) {
|
||||
public Flux<IType> allSuperTypesOf(String fqName, boolean includeFocusType) {
|
||||
Queue<String> queue = new LinkedList<>();
|
||||
HashSet<String> visited = new HashSet<>();
|
||||
updateQueue(queue, visited, type);
|
||||
return Flux.generate(() -> queue, (state, sink) -> {
|
||||
IType nextType = null;
|
||||
while (nextType == null && state.peek() != null) {
|
||||
queue.add(fqName);
|
||||
visited.add(fqName);
|
||||
Flux<IType> typesFlux = Flux.generate(() -> queue, (state, sink) -> {
|
||||
if (state.peek() == null) {
|
||||
sink.complete();
|
||||
} else {
|
||||
String typeName = state.poll();
|
||||
nextType = findType(typeName);
|
||||
IType nextType = findType(typeName);
|
||||
if (nextType != null) {
|
||||
sink.next(nextType);
|
||||
updateQueue(state, visited, nextType);
|
||||
}
|
||||
}
|
||||
if (state.peek() == null) {
|
||||
sink.complete();
|
||||
}
|
||||
return state;
|
||||
});
|
||||
return includeFocusType ? typesFlux : typesFlux.skip(1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ import com.google.common.cache.CacheBuilder;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.util.function.Tuple2;
|
||||
import reactor.util.function.Tuples;
|
||||
|
||||
public class JandexIndex extends BasicJandexIndex {
|
||||
|
||||
@@ -81,6 +82,10 @@ public class JandexIndex extends BasicJandexIndex {
|
||||
return Wrappers.wrap(this, match.getT1(), match.getT2(), javadocProvider);
|
||||
}
|
||||
|
||||
Flux<Tuple2<IType, Double>> fuzzySearchITypes(String searchTerm) {
|
||||
return fuzzySearchTypes(searchTerm).map(m -> Tuples.of(createType(Tuples.of(m.getT1(), m.getT2())), m.getT3()));
|
||||
}
|
||||
|
||||
public Flux<IType> allSubtypesOf(IType type) {
|
||||
DotName name = DotName.createSimple(type.getFullyQualifiedName());
|
||||
return allSubtypesOf(name, type.isInterface()).map(match -> createType(match));
|
||||
|
||||
@@ -17,10 +17,10 @@ import reactor.util.function.Tuple2;
|
||||
public interface ClasspathIndex extends Disposable {
|
||||
|
||||
IType findType(String fqName);
|
||||
Flux<Tuple2<String, Double>> fuzzySearchTypes(String searchTerm, boolean includeBinaries, boolean includeSystemLibs);
|
||||
Flux<Tuple2<IType, Double>> fuzzySearchTypes(String searchTerm, boolean includeBinaries, boolean includeSystemLibs);
|
||||
Flux<Tuple2<String, Double>> fuzzySearchPackages(String searchTerm, boolean includeBinaries, boolean includeSystemLibs);
|
||||
Flux<IType> allSubtypesOf(IType type);
|
||||
Flux<IType> allSuperTypesOf(IType type);
|
||||
Flux<IType> allSubtypesOf(String fqName, boolean includeFocusType);
|
||||
Flux<IType> allSuperTypesOf(String fqName, boolean includeFocusType);
|
||||
IJavaModuleData findClasspathResourceContainer(String fqName);
|
||||
|
||||
}
|
||||
|
||||
@@ -127,4 +127,11 @@ public class JavaUtils {
|
||||
return bindingKey == null ? null : bindingKey.substring(1, bindingKey.length() - 1).replace('/', '.');
|
||||
}
|
||||
|
||||
public static String typeFqNametoBindingKey(String fqName) {
|
||||
StringBuilder sb = new StringBuilder('L');
|
||||
sb.append(fqName.replace('.', '/'));
|
||||
sb.append(';');
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -11,8 +11,11 @@
|
||||
package org.springframework.ide.vscode.commons.jdtls;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
@@ -29,6 +32,7 @@ import org.springframework.ide.vscode.commons.protocol.java.JavaDataParams;
|
||||
import org.springframework.ide.vscode.commons.protocol.java.JavaSearchParams;
|
||||
import org.springframework.ide.vscode.commons.protocol.java.JavaTypeHierarchyParams;
|
||||
import org.springframework.ide.vscode.commons.protocol.java.TypeData;
|
||||
import org.springframework.ide.vscode.commons.protocol.java.TypeDescriptorData;
|
||||
import org.springframework.ide.vscode.commons.util.FuzzyMatcher;
|
||||
|
||||
import com.google.common.base.Suppliers;
|
||||
@@ -51,7 +55,9 @@ public class JdtLsIndex implements ClasspathIndex {
|
||||
private final URI projectUri;
|
||||
private final JdtLsJavadocProvider javadocProvider;
|
||||
|
||||
private Cache<String, Optional<IType>> cache = CacheBuilder.newBuilder().expireAfterAccess(10, TimeUnit.SECONDS).build();
|
||||
final private Cache<String, Optional<IType>> typeCache = CacheBuilder.newBuilder().expireAfterAccess(10, TimeUnit.SECONDS).build();
|
||||
final private Cache<JavaTypeHierarchyParams, CompletableFuture<List<IType>>> supertypesCache = CacheBuilder.newBuilder().expireAfterAccess(10, TimeUnit.SECONDS).build();
|
||||
final private Cache<JavaTypeHierarchyParams, CompletableFuture<List<IType>>> subtypesCache = CacheBuilder.newBuilder().expireAfterAccess(10, TimeUnit.SECONDS).build();
|
||||
|
||||
public JdtLsIndex(STS4LanguageClient client, URI projectUri) {
|
||||
this.client = client;
|
||||
@@ -69,10 +75,16 @@ public class JdtLsIndex implements ClasspathIndex {
|
||||
return Wrappers.wrap(data, Suppliers.memoize(() -> declaringTypeFqName == null ? null : findType(declaringTypeFqName)), javadocProvider);
|
||||
}
|
||||
|
||||
private IType toTypeFromDescriptor(TypeDescriptorData data) {
|
||||
String declaringTypeBindingKey = data.getDeclaringType();
|
||||
String declaringTypeFqName = JavaUtils.typeBindingKeyToFqName(declaringTypeBindingKey);
|
||||
return Wrappers.wrap(data, Suppliers.memoize(() -> findType(data.getFqName())), Suppliers.memoize(() -> declaringTypeFqName == null ? null : findType(declaringTypeFqName)), javadocProvider);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IType findType(String fqName) {
|
||||
try {
|
||||
return cache.get(fqName, () -> {
|
||||
return typeCache.get(fqName, () -> {
|
||||
JavaDataParams params = new JavaDataParams(projectUri.toString(), "L" + fqName.replace('.', '/') + ";", false);
|
||||
try {
|
||||
TypeData data = client.javaType(params).get(500, TimeUnit.MILLISECONDS);
|
||||
@@ -91,12 +103,12 @@ public class JdtLsIndex implements ClasspathIndex {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<Tuple2<String, Double>> fuzzySearchTypes(String searchTerm, boolean includeBinaries, boolean includeSystemLibs) {
|
||||
public Flux<Tuple2<IType, Double>> fuzzySearchTypes(String searchTerm, boolean includeBinaries, boolean includeSystemLibs) {
|
||||
JavaSearchParams searchParams = new JavaSearchParams(projectUri.toString(), searchTerm, includeBinaries, includeSystemLibs, SEARCH_TIMEOUT);
|
||||
return Mono.fromFuture(client.javaSearchTypes(searchParams))
|
||||
.flatMapMany(results -> Flux.fromIterable(results).publishOn(Schedulers.parallel()))
|
||||
.filter(Objects::nonNull)
|
||||
.map(type -> Tuples.of(type, FuzzyMatcher.matchScore(searchTerm, type)))
|
||||
.map(type -> Tuples.of(toTypeFromDescriptor(type), FuzzyMatcher.matchScore(searchTerm, type.getFqName())))
|
||||
.filter(tuple -> tuple.getT2() != 0.0);
|
||||
}
|
||||
|
||||
@@ -110,22 +122,42 @@ public class JdtLsIndex implements ClasspathIndex {
|
||||
.filter(tuple -> tuple.getT2() != 0.0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<IType> allSubtypesOf(IType type) {
|
||||
JavaTypeHierarchyParams searchParams = new JavaTypeHierarchyParams(projectUri.toString(), type.getFullyQualifiedName());
|
||||
return Mono.fromFuture(client.javaSubTypes(searchParams))
|
||||
.flatMapMany(results -> Flux.fromIterable(results).publishOn(Schedulers.parallel()))
|
||||
.filter(Objects::nonNull)
|
||||
.map(this::toType);
|
||||
private List<IType> convertTypeDescriptors(List<TypeDescriptorData> descriptors) {
|
||||
List<IType> types = new ArrayList<>(descriptors.size());
|
||||
for (TypeDescriptorData data : descriptors) {
|
||||
if (data != null) {
|
||||
types.add(toTypeFromDescriptor(data));
|
||||
}
|
||||
}
|
||||
return types;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<IType> allSuperTypesOf(IType type) {
|
||||
JavaTypeHierarchyParams searchParams = new JavaTypeHierarchyParams(projectUri.toString(), type.getFullyQualifiedName());
|
||||
return Mono.fromFuture(client.javaSuperTypes(searchParams))
|
||||
.flatMapMany(results -> Flux.fromIterable(results).publishOn(Schedulers.parallel()))
|
||||
.filter(Objects::nonNull)
|
||||
.map(this::toType);
|
||||
public Flux<IType> allSubtypesOf(String fqName, boolean includeFocusType) {
|
||||
JavaTypeHierarchyParams searchParams = new JavaTypeHierarchyParams(projectUri.toString(), fqName, includeFocusType);
|
||||
try {
|
||||
CompletableFuture<List<IType>> future = subtypesCache.get(searchParams, () -> client
|
||||
.javaSubTypes(searchParams).handle((results, exception) -> convertTypeDescriptors(results)));
|
||||
return Mono.fromFuture(future)
|
||||
.flatMapMany(results -> Flux.fromIterable(results));
|
||||
} catch (ExecutionException e) {
|
||||
log.error("{}", e);
|
||||
return Flux.empty();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<IType> allSuperTypesOf(String fqName, boolean includeFocusType) {
|
||||
JavaTypeHierarchyParams searchParams = new JavaTypeHierarchyParams(projectUri.toString(), fqName, includeFocusType);
|
||||
try {
|
||||
CompletableFuture<List<IType>> future = supertypesCache.get(searchParams, () -> client
|
||||
.javaSuperTypes(searchParams).handle((results, exception) -> convertTypeDescriptors(results)));
|
||||
return Mono.fromFuture(future)
|
||||
.flatMapMany(results -> Flux.fromIterable(results));
|
||||
} catch (ExecutionException e) {
|
||||
log.error("{}", e);
|
||||
return Flux.empty();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -32,12 +32,14 @@ import org.springframework.ide.vscode.commons.java.ITypeVariable;
|
||||
import org.springframework.ide.vscode.commons.java.IUnresolvedTypeVariable;
|
||||
import org.springframework.ide.vscode.commons.java.IVoidType;
|
||||
import org.springframework.ide.vscode.commons.java.IWildcardType;
|
||||
import org.springframework.ide.vscode.commons.java.JavaUtils;
|
||||
import org.springframework.ide.vscode.commons.javadoc.IJavadoc;
|
||||
import org.springframework.ide.vscode.commons.protocol.java.JavaTypeData;
|
||||
import org.springframework.ide.vscode.commons.protocol.java.TypeData;
|
||||
import org.springframework.ide.vscode.commons.protocol.java.TypeData.AnnotationData;
|
||||
import org.springframework.ide.vscode.commons.protocol.java.TypeData.FieldData;
|
||||
import org.springframework.ide.vscode.commons.protocol.java.TypeData.MethodData;
|
||||
import org.springframework.ide.vscode.commons.protocol.java.TypeDescriptorData;
|
||||
|
||||
import com.google.common.base.Supplier;
|
||||
|
||||
@@ -491,4 +493,111 @@ public class Wrappers {
|
||||
};
|
||||
}
|
||||
|
||||
public static IType wrap(TypeDescriptorData descriptor, Supplier<IType> lazyType, Supplier<IType> declaringTypeSupplier, IJavadocProvider javadocProvider) {
|
||||
return new IType() {
|
||||
|
||||
@Override
|
||||
public int getFlags() {
|
||||
return descriptor.getFlags();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IType getDeclaringType() {
|
||||
return declaringTypeSupplier.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IJavaModuleData classpathContainer() {
|
||||
return lazyType.get().classpathContainer();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String signature() {
|
||||
return descriptor.getLabel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getElementName() {
|
||||
return descriptor.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IJavadoc getJavaDoc() {
|
||||
return javadocProvider.getJavadoc(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getBindingKey() {
|
||||
return JavaUtils.typeFqNametoBindingKey(getFullyQualifiedName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean exists() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<IAnnotation> getAnnotations() {
|
||||
return lazyType.get().getAnnotations();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isClass() {
|
||||
return descriptor.isClass();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnum() {
|
||||
return descriptor.isEnum();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInterface() {
|
||||
return descriptor.isInterface();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAnnotation() {
|
||||
return descriptor.isAnnotation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFullyQualifiedName() {
|
||||
return descriptor.getFqName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IField getField(String name) {
|
||||
return getFields().filter(f -> name.equals(f.getElementName())).findFirst().orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<IField> getFields() {
|
||||
return lazyType.get().getFields();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IMethod getMethod(String name, Stream<IJavaType> parameters) {
|
||||
return lazyType.get().getMethod(name, parameters);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<IMethod> getMethods() {
|
||||
return lazyType.get().getMethods();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSuperclassName() {
|
||||
return descriptor.getSuperClassName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getSuperInterfaceNames() {
|
||||
return descriptor.getSuperInterfaceNames();
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ import org.springframework.ide.vscode.commons.java.IType;
|
||||
import org.springframework.ide.vscode.commons.jdtls.JdtLsIndex;
|
||||
import org.springframework.ide.vscode.commons.protocol.STS4LanguageClient;
|
||||
import org.springframework.ide.vscode.commons.protocol.java.TypeData;
|
||||
import org.springframework.ide.vscode.commons.protocol.java.TypeDescriptorData;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
@@ -51,9 +52,9 @@ public class JdtLsIndexTest {
|
||||
return gson.fromJson(new FileReader(jsonFile), TypeData.class);
|
||||
}
|
||||
|
||||
private List<String> loadJsonSearchTypeResults(String fileName) throws Exception {
|
||||
private List<TypeDescriptorData> loadJsonSearchTypeResults(String fileName) throws Exception {
|
||||
File jsonFile = new File(JdtLsIndexTest.class.getResource("/java-data-json/" + fileName).toURI());
|
||||
Type listType = new TypeToken<List<String>>(){}.getType();
|
||||
Type listType = new TypeToken<List<TypeDescriptorData>>(){}.getType();
|
||||
return gson.fromJson(new FileReader(jsonFile), listType);
|
||||
}
|
||||
|
||||
@@ -138,9 +139,9 @@ public class JdtLsIndexTest {
|
||||
}));
|
||||
// Some valid URI necessary for URI#toString() to succeed
|
||||
JdtLsIndex index = new JdtLsIndex(client, URI.create(System.getProperty("java.io.tmpdir")));
|
||||
List<Tuple2<String, Double>> results = index.fuzzySearchTypes("util.Map", true, false).collectSortedList((o1, o2) -> o2.getT2().compareTo(o1.getT2())).block();
|
||||
String type = results.get(0).getT1();
|
||||
assertEquals("io.netty.util.Mapping", type);
|
||||
List<Tuple2<IType, Double>> results = index.fuzzySearchTypes("util.Map", true, false).collectSortedList((o1, o2) -> o2.getT2().compareTo(o1.getT2())).block();
|
||||
IType type = results.get(0).getT1();
|
||||
assertEquals("io.netty.util.Mapping", type.getFullyQualifiedName());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user