PT #156965730: Get Javadoc from JDT LS or eclipse client via sts/javadoc

This commit is contained in:
BoykoAlex
2018-05-30 17:10:34 -04:00
parent d27856da64
commit 1b33036794
63 changed files with 1555 additions and 590 deletions

View File

@@ -20,10 +20,10 @@ import org.springframework.ide.vscode.commons.java.IMemberValuePair;
import org.springframework.ide.vscode.commons.javadoc.IJavadoc;
public class AnnotationImpl implements IAnnotation {
private AnnotationInstance annotation;
private IJavadocProvider javadocProvider;
AnnotationImpl(AnnotationInstance annotation, IJavadocProvider javadocProvider) {
this.annotation = annotation;
this.javadocProvider = javadocProvider;
@@ -50,7 +50,7 @@ public class AnnotationImpl implements IAnnotation {
return Wrappers.wrap(av);
});
}
@Override
public String toString() {
return annotation.toString();
@@ -69,4 +69,9 @@ public class AnnotationImpl implements IAnnotation {
return super.equals(obj);
}
@Override
public String getBindingKey() {
return BindingKeyUtils.getBindingKey(annotation);
}
}

View File

@@ -0,0 +1,282 @@
/*******************************************************************************
* Copyright (c) 2018 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.commons.jandex;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
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.stream.Collectors;
import java.util.stream.Stream;
import org.jboss.jandex.ClassInfo;
import org.jboss.jandex.DotName;
import org.jboss.jandex.IndexReader;
import org.jboss.jandex.IndexView;
import org.jboss.jandex.Indexer;
import org.jboss.jandex.JarIndexer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ide.vscode.commons.util.FuzzyMatcher;
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import reactor.core.publisher.Flux;
import reactor.core.scheduler.Schedulers;
import reactor.util.function.Tuple2;
import reactor.util.function.Tuple3;
import reactor.util.function.Tuples;
/**
* Basic Jandex Index using only Reactor Flux and Jandex only constructs
* independent of Javadoc provider logic. Thus this index can be shared via a
* static object
*
* @author Alex Boyko
*
*/
public class BasicJandexIndex {
private static final Logger log = LoggerFactory.getLogger(BasicJandexIndex.class);
private static final String JAVA_IO_TMPDIR = "java.io.tmpdir";
@FunctionalInterface
public static interface IndexFileFinder {
File findIndexFile(File jarFile);
}
public static File getIndexFolder() {
File folder = new File(System.getProperty(JAVA_IO_TMPDIR), "jandex");
if (!folder.isDirectory()) {
folder.mkdirs();
}
return folder;
}
private Map<File, Supplier<Optional<IndexView>>> index;
private Map<File, Supplier<List<Tuple3<String, File, ClassInfo>>>> knownTypes;
private Map<File, Supplier<List<String>>> knownPackages;
private BasicJandexIndex[] baseIndex;
BasicJandexIndex(Collection<File> classpathEntries, IndexFileFinder indexFileFinder,
BasicJandexIndex... baseIndex) {
this.baseIndex = baseIndex;
this.index = new ConcurrentHashMap<>();
this.knownTypes = new HashMap<>();
this.knownPackages = new HashMap<>();
classpathEntries.forEach(file -> {
index.put(file, /*Suppliers.synchronizedSupplier(*/Suppliers.memoize(() -> createIndex(file, indexFileFinder))/*)*/);
knownTypes.put(file, Suppliers.memoize(() -> getKnownTypesStream(file).collect(Collectors.toList())));
knownPackages.put(file, Suppliers.memoize(() -> getKnownPackages(file).collect(Collectors.toList())));
});
}
private Optional<IndexView> createIndex(File file, IndexFileFinder indexFileFinder) {
if (file != null && file.isFile() && file.getName().endsWith(".jar")) {
return indexJar(file, indexFileFinder);
} else if (file != null && file.isDirectory()) {
return indexFolder(file);
} else {
return Optional.empty();
}
}
private static Optional<IndexView> indexFolder(File folder) {
Indexer indexer = new Indexer();
for (Iterator<File> itr = com.google.common.io.Files.fileTreeTraverser().breadthFirstTraversal(folder)
.iterator(); itr.hasNext();) {
File file = itr.next();
if (file.isFile() && file.getName().endsWith(".class")) {
try {
final InputStream stream = new FileInputStream(file);
try {
indexer.index(stream);
} finally {
try {
stream.close();
} catch (Exception ignore) {
}
}
} catch (Exception e) {
log.error("Failed to index file " + file, e);
}
}
}
return Optional.of(indexer.complete());
}
private static Optional<IndexView> indexJar(File file, IndexFileFinder indexFileFinder) {
File indexFile = indexFileFinder.findIndexFile(file);
if (indexFile != null) {
try {
if (!indexFile.getParentFile().exists()) {
indexFile.getParentFile().mkdirs();
}
if (indexFile.createNewFile()) {
try {
return Optional.of(JarIndexer.createJarIndex(file, new Indexer(), indexFile, false, false,
false, System.out, System.err).getIndex());
} catch (IOException e) {
log.error("Failed to index '" + file + "'", e);
}
} else {
try {
return Optional.of(new IndexReader(new FileInputStream(indexFile)).read());
} catch (IOException e) {
log.error("Failed to read index file '" + indexFile + "'. Creating new index file.", e);
if (indexFile.delete()) {
return indexJar(file, indexFileFinder);
} else {
log.error("Failed to read index file '" + indexFile);
}
}
}
} catch (IOException e) {
log.error("Unable to create index file '" + indexFile + "'", e);
}
} else {
try {
return Optional.of(JarIndexer
.createJarIndex(file, new Indexer(), file.canWrite(), file.getParentFile().canWrite(), false)
.getIndex());
} catch (IOException e) {
log.error("Failed to index '" + file + "'", e);
}
}
return Optional.empty();
}
Tuple2<File, ClassInfo> getClassByName(DotName fqName) {
// First look for type in the base index array
return (baseIndex == null ? Stream.<Tuple2<File, ClassInfo>>empty()
: Arrays.stream(
baseIndex)
.filter(
jandexIndex -> jandexIndex != null)
.map(jandexIndex -> jandexIndex.getClassByName(fqName))).filter(type -> type != null)
.findFirst()
// If not found look at indices owned by this
// JandexIndex instance
.orElseGet(() -> streamOfIndices()
.map(e -> Tuples.of(e.getT1(), Optional.ofNullable(e.getT2().getClassByName(fqName))))
.filter(t -> t.getT2().isPresent())
.map(e -> Tuples.of(e.getT1(), e.getT2().get())).findFirst()
.orElse(null));
}
private Optional<Tuple2<File, ClassInfo>> findMatch(DotName fqName) {
return (baseIndex == null ? Stream.<Optional<Tuple2<File, ClassInfo>>>empty()
: Arrays.stream(
baseIndex)
.filter(
jandexIndex -> jandexIndex != null)
.map(jandexIndex -> jandexIndex.findMatch(fqName))).filter(o -> o.isPresent())
.findFirst()
// If not found look at indices owned by this
// JandexIndex instance
.orElseGet(() -> streamOfIndices()
.map(e -> {
IndexView view = e.getT2();
ClassInfo info = view.getClassByName(fqName);
return Tuples.of(e.getT1(), Optional.ofNullable(info));
// return Tuples.of(e.getT1(), Optional.ofNullable(e.getT2().getClassByName(fqName)));
})
.filter(t -> t.getT2().isPresent())
.map(e -> Tuples.of(e.getT1(), e.getT2().get())).findFirst());
}
public Optional<File> findClasspathResourceForType(String fqName) {
Optional<Tuple2<File, ClassInfo>> match = findMatch(DotName.createSimple(fqName));
return Optional.ofNullable(match.isPresent() ? match.get().getT1() : null);
}
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<Tuple3<String, File, ClassInfo>> 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(), file, info));
}
return Stream.empty();
}
private final Stream<String> getKnownPackages(File file) {
Optional<IndexView> indexView = index.get(file).get();
if (indexView.isPresent()) {
return indexView.get().getKnownClasses().parallelStream().map(info -> {
String name = info.name().toString();
return name.substring(0, name.lastIndexOf('.'));
}).distinct();
}
return Stream.empty();
}
Flux<Tuple3<File, ClassInfo, Double>> fuzzySearchTypes(String searchTerm) {
Flux<Tuple3<File, ClassInfo, Double>> flux = Flux.fromIterable(knownTypes.values()).publishOn(Schedulers.parallel())
.flatMap(s -> Flux.fromIterable(s.get()))
.map(t -> Tuples.of(t.getT2(), t.getT3(), FuzzyMatcher.matchScore(searchTerm, t.getT1())))
.filter(t -> t.getT3() != 0.0);
if (baseIndex == null) {
return flux;
} else {
return Flux.merge(flux,
Flux.fromArray(baseIndex).flatMap(index -> index.fuzzySearchTypes(searchTerm)));
}
}
public Flux<Tuple2<String, Double>> fuzzySearchPackages(String searchTerm) {
Flux<Tuple2<String, Double>> flux = Flux.fromIterable(knownPackages.values()).publishOn(Schedulers.parallel())
.flatMap(s -> Flux.fromIterable(s.get()))
.map(pkg -> Tuples.of(pkg, FuzzyMatcher.matchScore(searchTerm, pkg))).filter(t -> t.getT2() != 0.0);
if (baseIndex == null) {
return flux;
} else {
return Flux.merge(flux, Flux.fromArray(baseIndex).flatMap(index -> index.fuzzySearchPackages(searchTerm)));
}
}
Flux<Tuple2<File, ClassInfo>> allSubtypesOf(DotName name, boolean isInterface) {
Flux<Tuple2<File, ClassInfo>> flux = Flux.fromIterable(index.keySet()).publishOn(Schedulers.parallel()).flatMap(file -> {
Optional<IndexView> optional = index.get(file).get();
if (optional.isPresent()) {
return Flux
.fromIterable(isInterface ? optional.get().getAllKnownImplementors(name)
: optional.get().getAllKnownSubclasses(name))
.publishOn(Schedulers.parallel()).map(info -> 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(name, isInterface)));
}
}
}

View File

@@ -0,0 +1,165 @@
/*******************************************************************************
* Copyright (c) 2018 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.commons.jandex;
import java.util.stream.Collectors;
import org.jboss.jandex.AnnotationInstance;
import org.jboss.jandex.ArrayType;
import org.jboss.jandex.ClassInfo;
import org.jboss.jandex.ClassType;
import org.jboss.jandex.FieldInfo;
import org.jboss.jandex.MethodInfo;
import org.jboss.jandex.ParameterizedType;
import org.jboss.jandex.PrimitiveType;
import org.jboss.jandex.Type;
import org.jboss.jandex.TypeVariable;
import org.jboss.jandex.UnresolvedTypeVariable;
import org.jboss.jandex.VoidType;
import org.jboss.jandex.WildcardType;
class BindingKeyUtils {
public static String getBindingKey(ClassInfo info) {
StringBuilder sb = new StringBuilder();
sb.append('L');
sb.append(info.toString().replace('.', '/'));
sb.append(';');
return sb.toString();
}
public static String getBindingKey(AnnotationInstance annotation) {
return annotation.name().toString();
}
public static String getBindingKey(FieldInfo field) {
StringBuilder sb = new StringBuilder();
sb.append(getBindingKey(field.declaringClass()));
sb.append('.');
sb.append(field.name());
return sb.toString();
}
public static String getBindingKey(MethodInfo method) {
StringBuilder sb = new StringBuilder();
sb.append(getBindingKey(method.declaringClass()));
sb.append('.');
sb.append(method.name());
sb.append('(');
for (Type parameter : method.parameters()) {
sb.append(getGeneralTypeBindingKey(parameter));
}
sb.append(')');
sb.append(getGeneralTypeBindingKey(method.returnType()));
return method.name().toString();
}
public static String getGeneralTypeBindingKey(Type type) {
switch (type.kind()) {
case ARRAY:
return getBindingKey(type.asArrayType());
case CLASS:
return getBindingKey(type.asClassType());
case PARAMETERIZED_TYPE:
return getBindingKey(type.asParameterizedType());
case PRIMITIVE:
return getBindingKey(type.asPrimitiveType());
case TYPE_VARIABLE:
return getBindingKey(type.asTypeVariable());
case UNRESOLVED_TYPE_VARIABLE:
return getBindingKey(type.asUnresolvedTypeVariable());
case VOID:
return getBindingKey(type.asVoidType());
case WILDCARD_TYPE:
return getBindingKey(type.asWildcardType());
default:
break;
}
return "";
}
private static String getBindingKey(WildcardType type) {
if (type.extendsBound() != null) {
return "+" + getGeneralTypeBindingKey(type.extendsBound());
} else if (type.superBound() != null) {
return "-" + getGeneralTypeBindingKey(type.superBound());
} else {
return "*";
}
}
private static String getBindingKey(ParameterizedType type) {
StringBuilder sb = new StringBuilder();
sb.append(type.owner() == null ? "L" + type.name().toString().replace('.', '/') : getGeneralTypeBindingKey(type.owner()));
sb.append('<');
for (Type argument : type.arguments()) {
sb.append(getGeneralTypeBindingKey(argument));
}
sb.append('>');
return sb.toString();
}
private static String getBindingKey(TypeVariable type) {
StringBuilder sb = new StringBuilder();
sb.append('T');
sb.append(type.identifier());
sb.append(type.bounds().stream().map(BindingKeyUtils::getGeneralTypeBindingKey).collect(Collectors.joining(":")));
sb.append(';');
return sb.toString();
}
private static String getBindingKey(ArrayType type) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < type.dimensions(); i++) {
sb.append('[');
}
sb.append(getGeneralTypeBindingKey(type.component()));
return null;
}
private static String getBindingKey(ClassType type) {
StringBuilder sb = new StringBuilder();
sb.append('L');
sb.append(type.name().toString().replace('.', '/'));
sb.append(';');
return sb.toString();
}
private static String getBindingKey(PrimitiveType primitive) {
if (primitive == PrimitiveType.BYTE) {
return "B";
} else if (primitive == PrimitiveType.CHAR) {
return "C";
} else if (primitive == PrimitiveType.DOUBLE) {
return "D";
} else if (primitive == PrimitiveType.FLOAT) {
return "F";
} else if (primitive == PrimitiveType.INT) {
return "I";
} else if (primitive == PrimitiveType.LONG) {
return "J";
} else if (primitive == PrimitiveType.SHORT) {
return "S";
}
// BOOLEAN
return "Z";
}
private static String getBindingKey(UnresolvedTypeVariable type) {
return "Q" + type.name().toString();
}
private static String getBindingKey(VoidType type) {
return "V";
}
}

View File

@@ -22,13 +22,13 @@ import org.springframework.ide.vscode.commons.java.IType;
import org.springframework.ide.vscode.commons.javadoc.IJavadoc;
class FieldImpl implements IField {
private JandexIndex index;
private FieldInfo field;
private IJavadocProvider javadocProvider;
FieldImpl(JandexIndex index, FieldInfo field, IJavadocProvider javadocProvider) {
this.index = index;
private IType declaringType;
FieldImpl(IType declaringType, FieldInfo field, IJavadocProvider javadocProvider) {
this.declaringType = declaringType;
this.field = field;
this.javadocProvider = javadocProvider;
}
@@ -40,7 +40,7 @@ class FieldImpl implements IField {
@Override
public IType getDeclaringType() {
return Wrappers.wrap(index, field.declaringClass(), javadocProvider);
return declaringType;
}
@Override
@@ -69,7 +69,7 @@ class FieldImpl implements IField {
public boolean isEnumConstant() {
return Flags.isEnum(field.flags());
}
@Override
public String toString() {
return field.toString();
@@ -87,7 +87,10 @@ class FieldImpl implements IField {
}
return super.equals(obj);
}
@Override
public String getBindingKey() {
return BindingKeyUtils.getBindingKey(field);
}
}

View File

@@ -23,12 +23,11 @@ import java.util.stream.Stream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ide.vscode.commons.jandex.JandexIndex.JavadocProviderFactory;
import org.springframework.ide.vscode.commons.java.ClasspathIndex;
import org.springframework.ide.vscode.commons.java.IClasspath;
import org.springframework.ide.vscode.commons.java.IClasspathUtil;
import org.springframework.ide.vscode.commons.java.IJavadocProvider;
import org.springframework.ide.vscode.commons.java.IType;
import org.springframework.ide.vscode.commons.javadoc.JavaDocProviders;
import org.springframework.ide.vscode.commons.languageserver.jdt.ls.Classpath;
import org.springframework.ide.vscode.commons.languageserver.jdt.ls.Classpath.CPE;
import org.springframework.ide.vscode.commons.util.CollectorUtil;
@@ -53,8 +52,6 @@ public final class JandexClasspath implements ClasspathIndex {
public static final Logger log = LoggerFactory.getLogger(JandexClasspath.class);
public static JavadocProviderTypes providerType = JavadocProviderTypes.HTML;
public enum JavadocProviderTypes {
// JAVA_PARSER, //Used to be based on githb java parser. If need something back that can extract docs from source code, we have to implement
// based on JDT parser. But at the moment this wasn't being used so just got removed.
@@ -64,10 +61,12 @@ public final class JandexClasspath implements ClasspathIndex {
private Supplier<JandexIndex> javaIndex;
private final IClasspath classpath;
private final FileObserver fileObserver;
private final JavadocProviderFactory javadocProviderFactory;
public JandexClasspath(IClasspath classpath, FileObserver fileObserver) {
public JandexClasspath(IClasspath classpath, FileObserver fileObserver, JavadocProviderFactory javadocProviderFactory) {
this.fileObserver = fileObserver;
this.classpath = classpath;
this.javadocProviderFactory = javadocProviderFactory;
this.javaIndex = Suppliers.synchronizedSupplier(Suppliers.memoize(() -> createIndex()));
}
@@ -80,16 +79,7 @@ public final class JandexClasspath implements ClasspathIndex {
} catch (Exception e) {
log.error("Cannot obtain binary root from classpath entries for " + classpath.getName(), e);
}
return new JandexIndex(classpathEntries, jarFile -> findIndexFile(jarFile), classpathResource -> {
switch (providerType) {
// case JAVA_PARSER:
// return createParserJavadocProvider(classpathResource);
case HTML:
return createHtmlJavdocProvider(classpathResource);
default:
throw new IllegalStateException("Missing switch case?");
}
}, getBaseIndices());
return new JandexIndex(classpathEntries, jarFile -> findIndexFile(jarFile), javadocProviderFactory, getBaseIndices());
}
private Disposable.Composite subscriptions = Disposables.composite();
@@ -115,18 +105,13 @@ public final class JandexClasspath implements ClasspathIndex {
}
}
private IJavadocProvider createHtmlJavdocProvider(File binaryClasspathRoot) {
CPE cpe = IClasspathUtil.findEntryForBinaryRoot(classpath, binaryClasspathRoot);
return JavaDocProviders.createFor(cpe);
}
@Override
public Optional<URL> sourceContainer(File binaryClasspathRoot) {
CPE cpe = IClasspathUtil.findEntryForBinaryRoot(classpath, binaryClasspathRoot);
return cpe == null ? Optional.empty() : Optional.ofNullable(cpe.getSourceContainerUrl());
}
protected JandexIndex[] getBaseIndices() {
protected BasicJandexIndex[] getBaseIndices() {
return JandexSystemLibsIndex.getInstance().fromJars(IClasspathUtil.getBinaryRoots(classpath, CPE::isSystem));
}

View File

@@ -12,28 +12,12 @@
package org.springframework.ide.vscode.commons.jandex;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
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.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.jboss.jandex.ClassInfo;
import org.jboss.jandex.DotName;
import org.jboss.jandex.IndexReader;
import org.jboss.jandex.IndexView;
import org.jboss.jandex.Indexer;
import org.jboss.jandex.JarIndexer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ide.vscode.commons.java.IAnnotation;
@@ -42,42 +26,23 @@ 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.javadoc.IJavadoc;
import org.springframework.ide.vscode.commons.util.FuzzyMatcher;
import com.google.common.base.Supplier;
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 {
public class JandexIndex extends BasicJandexIndex {
private static final Logger log = LoggerFactory.getLogger(JandexIndex.class);
private static final String JAVA_IO_TMPDIR = "java.io.tmpdir";
@FunctionalInterface
public static interface IndexFileFinder {
File findIndexFile(File jarFile);
}
@FunctionalInterface
public static interface JavadocProviderFactory {
IJavadocProvider createJavadocProvider(File jarContainer);
}
public static File getIndexFolder() {
File folder = new File(System.getProperty(JAVA_IO_TMPDIR), "jandex");
if (!folder.isDirectory()) {
folder.mkdirs();
}
return folder;
}
private static final IJavadocProvider ABSENT_JAVADOC_PROVIDER = new IJavadocProvider() {
@Override
@@ -102,18 +67,10 @@ public class JandexIndex {
};
private Map<File, Supplier<Optional<IndexView>>> index;
private JavadocProviderFactory javadocProviderFactory;
private Map<File, Supplier<List<Tuple2<String, IType>>>> knownTypes;
private Map<File, Supplier<List<String>>> knownPackages;
private Cache<File, IJavadocProvider> javadocProvidersCache = CacheBuilder.newBuilder().build();
private JandexIndex[] baseIndex;
public void setJvadocProviderFactory(JavadocProviderFactory sourceContainerProvider) {
this.javadocProviderFactory = sourceContainerProvider;
}
@@ -123,144 +80,19 @@ public class JandexIndex {
}
public JandexIndex(Collection<File> classpathEntries, IndexFileFinder indexFileFinder,
JavadocProviderFactory javadocProviderFactory, JandexIndex... baseIndex) {
this.baseIndex = baseIndex;
this.index = new ConcurrentHashMap<>();
this.knownTypes = new HashMap<>();
this.knownPackages = new HashMap<>();
JavadocProviderFactory javadocProviderFactory, BasicJandexIndex... baseIndex) {
super(classpathEntries, indexFileFinder, baseIndex);
this.javadocProviderFactory = javadocProviderFactory;
classpathEntries.forEach(file -> {
index.put(file, /*Suppliers.synchronizedSupplier(*/Suppliers.memoize(() -> createIndex(file, indexFileFinder))/*)*/);
knownTypes.put(file, Suppliers.memoize(() -> getKnownTypesStream(file).collect(Collectors.toList())));
knownPackages.put(file, Suppliers.memoize(() -> getKnownPackages(file).collect(Collectors.toList())));
});
}
private Optional<IndexView> createIndex(File file, IndexFileFinder indexFileFinder) {
if (file != null && file.isFile() && file.getName().endsWith(".jar")) {
return indexJar(file, indexFileFinder);
} else if (file != null && file.isDirectory()) {
return indexFolder(file);
} else {
return Optional.empty();
}
}
private static Optional<IndexView> indexFolder(File folder) {
Indexer indexer = new Indexer();
for (Iterator<File> itr = com.google.common.io.Files.fileTreeTraverser().breadthFirstTraversal(folder)
.iterator(); itr.hasNext();) {
File file = itr.next();
if (file.isFile() && file.getName().endsWith(".class")) {
try {
final InputStream stream = new FileInputStream(file);
try {
indexer.index(stream);
} finally {
try {
stream.close();
} catch (Exception ignore) {
}
}
} catch (Exception e) {
log.error("Failed to index file " + file, e);
}
}
}
return Optional.of(indexer.complete());
}
private static Optional<IndexView> indexJar(File file, IndexFileFinder indexFileFinder) {
File indexFile = indexFileFinder.findIndexFile(file);
if (indexFile != null) {
try {
if (!indexFile.getParentFile().exists()) {
indexFile.getParentFile().mkdirs();
}
if (indexFile.createNewFile()) {
try {
return Optional.of(JarIndexer.createJarIndex(file, new Indexer(), indexFile, false, false,
false, System.out, System.err).getIndex());
} catch (IOException e) {
log.error("Failed to index '" + file + "'", e);
}
} else {
try {
return Optional.of(new IndexReader(new FileInputStream(indexFile)).read());
} catch (IOException e) {
log.error("Failed to read index file '" + indexFile + "'. Creating new index file.", e);
if (indexFile.delete()) {
return indexJar(file, indexFileFinder);
} else {
log.error("Failed to read index file '" + indexFile);
}
}
}
} catch (IOException e) {
log.error("Unable to create index file '" + indexFile + "'", e);
}
} else {
try {
return Optional.of(JarIndexer
.createJarIndex(file, new Indexer(), file.canWrite(), file.getParentFile().canWrite(), false)
.getIndex());
} catch (IOException e) {
log.error("Failed to index '" + file + "'", e);
}
}
return Optional.empty();
}
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()
: Arrays.stream(
baseIndex)
.filter(
jandexIndex -> jandexIndex != null)
.map(jandexIndex -> jandexIndex.getClassByName(fqName))).filter(type -> type != null)
.findFirst()
// If not found look at indices owned by this
// JandexIndex instance
.orElseGet(() -> streamOfIndices()
.map(e -> Tuples.of(e.getT1(), Optional.ofNullable(e.getT2().getClassByName(fqName))))
.filter(t -> t.getT2().isPresent())
.map(e -> createType(Tuples.of(e.getT1(), e.getT2().get()))).findFirst()
.orElse(null));
}
private Optional<Tuple2<File, ClassInfo>> findMatch(DotName fqName) {
return (baseIndex == null ? Stream.<Optional<Tuple2<File, ClassInfo>>>empty()
: Arrays.stream(
baseIndex)
.filter(
jandexIndex -> jandexIndex != null)
.map(jandexIndex -> jandexIndex.findMatch(fqName))).filter(o -> o.isPresent())
.findFirst()
// If not found look at indices owned by this
// JandexIndex instance
.orElseGet(() -> streamOfIndices()
.map(e -> {
IndexView view = e.getT2();
ClassInfo info = view.getClassByName(fqName);
return Tuples.of(e.getT1(), Optional.ofNullable(info));
// return Tuples.of(e.getT1(), Optional.ofNullable(e.getT2().getClassByName(fqName)));
})
.filter(t -> t.getT2().isPresent())
.map(e -> Tuples.of(e.getT1(), e.getT2().get())).findFirst());
}
public Optional<File> findClasspathResourceForType(String fqName) {
Optional<Tuple2<File, ClassInfo>> match = findMatch(DotName.createSimple(fqName));
return Optional.ofNullable(match.isPresent() ? match.get().getT1() : null);
return createType(getClassByName(DotName.createSimple(fqName)));
}
private IType createType(Tuple2<File, ClassInfo> match) {
if (match == null) {
return null;
}
File classpathResource = match.getT1();
IJavadocProvider javadocProvider = null;
try {
@@ -274,76 +106,18 @@ public class JandexIndex {
} catch (ExecutionException e) {
log.error("Failed to retrieve javadoc provider for resource " + classpathResource, e);
}
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();
}
private Stream<String> getKnownPackages(File file) {
Optional<IndexView> indexView = index.get(file).get();
if (indexView.isPresent()) {
return indexView.get().getKnownClasses().parallelStream().map(info -> {
String name = info.name().toString();
return name.substring(0, name.lastIndexOf('.'));
}).distinct();
}
return Stream.empty();
return Wrappers.wrap(this, match.getT1(), match.getT2(), javadocProvider);
}
public Flux<Tuple2<IType, Double>> fuzzySearchTypes(String searchTerm, Predicate<IType> typeFilter) {
Flux<Tuple2<IType, Double>> flux = Flux.fromIterable(knownTypes.values()).publishOn(Schedulers.parallel())
.flatMap(s -> Flux.fromIterable(s.get())).filter(t -> typeFilter == null || typeFilter.test(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<Tuple2<String, Double>> fuzzySearchPackages(String searchTerm) {
Flux<Tuple2<String, Double>> flux = Flux.fromIterable(knownPackages.values()).publishOn(Schedulers.parallel())
.flatMap(s -> Flux.fromIterable(s.get()))
.map(pkg -> Tuples.of(pkg, FuzzyMatcher.matchScore(searchTerm, pkg))).filter(t -> t.getT2() != 0.0);
if (baseIndex == null) {
return flux;
} else {
return Flux.merge(flux, Flux.fromArray(baseIndex).flatMap(index -> index.fuzzySearchPackages(searchTerm)));
}
return fuzzySearchTypes(searchTerm)
.map(match -> Tuples.of(createType(Tuples.of(match.getT1(), match.getT2())), match.getT3()))
.filter(t -> typeFilter == null || typeFilter.test(t.getT1()));
}
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)));
}
return allSubtypesOf(name, type.isInterface()).map(match -> createType(match));
}
}

View File

@@ -10,12 +10,8 @@
*******************************************************************************/
package org.springframework.ide.vscode.commons.jandex;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.channels.IllegalSelectorException;
import java.nio.file.Files;
import java.nio.file.Path;
@@ -32,8 +28,6 @@ import java.util.stream.Collectors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ide.vscode.commons.javadoc.HtmlJavadocProvider;
import org.springframework.ide.vscode.commons.javadoc.TypeUrlProviderFromContainerUrl;
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
@@ -58,13 +52,13 @@ public class JandexSystemLibsIndex {
private static final Supplier<JandexSystemLibsIndex> INSTANCE = Suppliers.memoize(() -> new JandexSystemLibsIndex());
private Cache<Path, JandexIndex> cache;
private Cache<Path, BasicJandexIndex> cache;
private JandexSystemLibsIndex() {
this.cache = CacheBuilder.newBuilder().build(new CacheLoader<Path, JandexIndex>() {
this.cache = CacheBuilder.newBuilder().build(new CacheLoader<Path, BasicJandexIndex>() {
@Override
public JandexIndex load(Path key) throws Exception {
public BasicJandexIndex load(Path key) throws Exception {
return createIndex(key);
}
@@ -76,7 +70,7 @@ public class JandexSystemLibsIndex {
* @param path the path containing jars
* @return Jandex Index of the jars contained in the folder
*/
public JandexIndex index(Path path) {
public BasicJandexIndex index(Path path) {
try {
return cache.get(path, () -> createIndex(path));
} catch (ExecutionException e) {
@@ -90,15 +84,15 @@ public class JandexSystemLibsIndex {
* @param jars system lib jars
* @return Jandex Indexs for jars
*/
public JandexIndex[] fromJars(Collection<File> jars) {
return jars.stream().map(jar -> jar.toPath().getParent()).distinct().map(folder -> index(folder)).filter(Objects::nonNull).toArray(JandexIndex[]::new);
public BasicJandexIndex[] fromJars(Collection<File> jars) {
return jars.stream().map(jar -> jar.toPath().getParent()).distinct().map(folder -> index(folder)).filter(Objects::nonNull).toArray(BasicJandexIndex[]::new);
}
public static JandexSystemLibsIndex getInstance() {
return INSTANCE.get();
}
private JandexIndex createIndex(Path path) {
private BasicJandexIndex createIndex(Path path) {
List<File> jars = Collections.emptyList();
try {
jars = Files.list(path).filter(p -> p.getFileName().toString().endsWith(".jar") && Files.isRegularFile(p)).map(p -> p.toFile()).collect(Collectors.toList());
@@ -106,7 +100,7 @@ public class JandexSystemLibsIndex {
// Shouldn't happen - there should at least be one jar file
log.error("Cannot list files in folder " + path, e);
}
return new JandexIndex(jars, jarFile -> findIndexFile(jarFile), (classpathResource) -> createHtmlJavadocProvider(path));
return new BasicJandexIndex(jars, jarFile -> findIndexFile(jarFile));
}
private File findIndexFile(File jarFile) {
@@ -126,46 +120,36 @@ public class JandexSystemLibsIndex {
}
}
private static HtmlJavadocProvider createHtmlJavadocProvider(Path path) {
try {
String javaVersion = getJavaVersion(path);
URL javadocUrl = new URL("https://docs.oracle.com/javase/" + extractVersionForJavadoc(javaVersion) + "/docs/api/");
return new HtmlJavadocProvider((type) -> TypeUrlProviderFromContainerUrl.JAVADOC_FOLDER_URL_SUPPLIER.url(javadocUrl, type.getFullyQualifiedName()));
} catch (MalformedURLException e) {
return null;
}
}
private static String getJavaVersion(Path path) {
// Find valid /bin folder
for (; path != null && !(Files.isDirectory(path.resolve("bin")) && Files.isReadable(path.resolve("bin"))); path = path.getParent());
// If found, assume it's the java home bin folder
if (path != null) {
Path javaBin = path.resolve("bin");
try {
Process p = new ProcessBuilder().directory(javaBin.toFile()).command("./java", "-version").start();
BufferedReader buffer = new BufferedReader(new InputStreamReader(p.getErrorStream()));
int exitCode = p.waitFor();
if (exitCode == 0) {
return buffer.lines().map(l -> JAVA_VERSION_PATTERN.matcher(l)).filter(m -> m.find()).findFirst().map(m -> m.group(1)).orElse(DEFAULT_JAVA_VERSION);
} else {
log.error("Failed to compute java version in folder: " + javaBin + ". 'java -version' exit code is " + exitCode);
}
} catch (IOException | InterruptedException e) {
log.error("Failed to compute java version in folder: " + javaBin, e);
}
}
return DEFAULT_JAVA_VERSION;
}
private static String extractVersionForJavadoc(String javaVersion) {
if (javaVersion.startsWith("1.")) {
int idx = javaVersion.indexOf('.', 2);
return idx >= 0 ? javaVersion.substring(2, idx) : javaVersion.substring(2);
} else {
int idx = javaVersion.indexOf('.');
return idx >= 0 ? javaVersion.substring(0, idx) : javaVersion;
}
}
// private static String getJavaVersion(Path path) {
// // Find valid /bin folder
// for (; path != null && !(Files.isDirectory(path.resolve("bin")) && Files.isReadable(path.resolve("bin"))); path = path.getParent());
// // If found, assume it's the java home bin folder
// if (path != null) {
// Path javaBin = path.resolve("bin");
// try {
// Process p = new ProcessBuilder().directory(javaBin.toFile()).command("./java", "-version").start();
// BufferedReader buffer = new BufferedReader(new InputStreamReader(p.getErrorStream()));
// int exitCode = p.waitFor();
// if (exitCode == 0) {
// return buffer.lines().map(l -> JAVA_VERSION_PATTERN.matcher(l)).filter(m -> m.find()).findFirst().map(m -> m.group(1)).orElse(DEFAULT_JAVA_VERSION);
// } else {
// log.error("Failed to compute java version in folder: " + javaBin + ". 'java -version' exit code is " + exitCode);
// }
// } catch (IOException | InterruptedException e) {
// log.error("Failed to compute java version in folder: " + javaBin, e);
// }
// }
// return DEFAULT_JAVA_VERSION;
// }
//
// private static String extractVersionForJavadoc(String javaVersion) {
// if (javaVersion.startsWith("1.")) {
// int idx = javaVersion.indexOf('.', 2);
// return idx >= 0 ? javaVersion.substring(2, idx) : javaVersion.substring(2);
// } else {
// int idx = javaVersion.indexOf('.');
// return idx >= 0 ? javaVersion.substring(0, idx) : javaVersion;
// }
// }
}

View File

@@ -22,16 +22,15 @@ import org.springframework.ide.vscode.commons.java.IType;
import org.springframework.ide.vscode.commons.javadoc.IJavadoc;
public class MethodImpl implements IMethod {
private static final String JANDEX_CONTRUCTOR_NAME = "<init>";
private JandexIndex index;
private IType declaringType;
private MethodInfo method;
private IJavadocProvider javadocProvider;
MethodImpl(JandexIndex index, MethodInfo method, IJavadocProvider javadocProvider) {
this.index = index;
MethodImpl(IType declaringType, MethodInfo method, IJavadocProvider javadocProvider) {
this.declaringType = declaringType;
this.method = method;
this.javadocProvider =javadocProvider;
}
@@ -40,7 +39,7 @@ public class MethodImpl implements IMethod {
public int getFlags() {
return method.flags();
}
@Override
public boolean isConstructor() {
return method.name().equals(JANDEX_CONTRUCTOR_NAME);
@@ -48,7 +47,7 @@ public class MethodImpl implements IMethod {
@Override
public IType getDeclaringType() {
return Wrappers.wrap(index, method.declaringClass(), javadocProvider);
return declaringType;
}
@Override
@@ -85,7 +84,7 @@ public class MethodImpl implements IMethod {
// sb.append(getReturnType());
// return sb.toString();
// }
@Override
public String toString() {
return method.toString();
@@ -109,5 +108,9 @@ public class MethodImpl implements IMethod {
return super.equals(obj);
}
@Override
public String getBindingKey() {
return BindingKeyUtils.getBindingKey(method);
}
}

View File

@@ -11,6 +11,7 @@
package org.springframework.ide.vscode.commons.jandex;
import java.io.File;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@@ -29,14 +30,16 @@ import org.springframework.ide.vscode.commons.java.IType;
import org.springframework.ide.vscode.commons.javadoc.IJavadoc;
class TypeImpl implements IType {
private ClassInfo info;
private JandexIndex index;
private IJavadocProvider javadocProvider;
TypeImpl(JandexIndex index, ClassInfo info, IJavadocProvider javadocProvider) {
private File classpathContainer;
TypeImpl(JandexIndex index, File classpathContainer, ClassInfo info, IJavadocProvider javadocProvider) {
this.info = info;
this.index = index;
this.classpathContainer = classpathContainer;
this.javadocProvider = javadocProvider;
}
@@ -48,7 +51,7 @@ class TypeImpl implements IType {
@Override
public IType getDeclaringType() {
DotName enclosingClass = info.enclosingClass();
return enclosingClass == null ? null : index.getClassByName(enclosingClass);
return enclosingClass == null ? null : index.findType(enclosingClass.toString());
}
@Override
@@ -92,7 +95,7 @@ class TypeImpl implements IType {
public boolean isAnnotation() {
return Flags.isAnnotation(info.flags());
}
@Override
public String getFullyQualifiedName() {
return info.name().toString();
@@ -100,26 +103,26 @@ class TypeImpl implements IType {
@Override
public IField getField(String name) {
return Wrappers.wrap(index, info.field(name), javadocProvider);
return Wrappers.wrap(this, info.field(name), javadocProvider);
}
@Override
public Stream<IField> getFields() {
return info.fields().stream().map(f -> {
return Wrappers.wrap(index, f, javadocProvider);
return Wrappers.wrap(this, f, javadocProvider);
});
}
@Override
public IMethod getMethod(String name, Stream<IJavaType> parameters) {
List<Type> typeParameters = parameters.map(Wrappers::from).collect(Collectors.toList());
return Wrappers.wrap(index, info.method(name, typeParameters.toArray(new Type[typeParameters.size()])), javadocProvider);
return Wrappers.wrap(this, info.method(name, typeParameters.toArray(new Type[typeParameters.size()])), javadocProvider);
}
@Override
public Stream<IMethod> getMethods() {
return info.methods().stream().map(m -> {
return Wrappers.wrap(index, m, javadocProvider);
return Wrappers.wrap(this, m, javadocProvider);
});
}
@@ -141,5 +144,14 @@ class TypeImpl implements IType {
return super.equals(obj);
}
@Override
public String getBindingKey() {
return BindingKeyUtils.getBindingKey(info);
}
@Override
public File classpathContainer() {
return classpathContainer;
}
}

View File

@@ -13,6 +13,8 @@ package org.springframework.ide.vscode.commons.jandex;
import static org.springframework.ide.vscode.commons.util.Assert.isNotNull;
import java.io.File;
import org.jboss.jandex.AnnotationInstance;
import org.jboss.jandex.AnnotationValue;
import org.jboss.jandex.ClassInfo;
@@ -32,32 +34,32 @@ import org.springframework.ide.vscode.commons.java.IType;
import org.springframework.ide.vscode.commons.java.IVoidType;
public class Wrappers {
public static IType wrap(JandexIndex index, ClassInfo info, IJavadocProvider javadocProvider) {
public static IType wrap(JandexIndex index, File classpathContainer, ClassInfo info, IJavadocProvider javadocProvider) {
if (info == null) {
return null;
}
return new TypeImpl(index, info, javadocProvider);
return new TypeImpl(index, classpathContainer, info, javadocProvider);
}
public static IField wrap(JandexIndex index, FieldInfo field, IJavadocProvider javadocProvider) {
public static IField wrap(IType declaringType, FieldInfo field, IJavadocProvider javadocProvider) {
if (field == null) {
return null;
}
return new FieldImpl(index, field, javadocProvider);
return new FieldImpl(declaringType, field, javadocProvider);
}
public static IMethod wrap(JandexIndex index, MethodInfo method, IJavadocProvider javadocProvider) {
isNotNull(index);
public static IMethod wrap(IType declaringType, MethodInfo method, IJavadocProvider javadocProvider) {
isNotNull(declaringType);
isNotNull(method);
return new MethodImpl(index, method, javadocProvider);
return new MethodImpl(declaringType, method, javadocProvider);
}
public static IAnnotation wrap(AnnotationInstance annotation, IJavadocProvider javadocProvider) {
isNotNull(annotation);
return new AnnotationImpl(annotation, javadocProvider);
}
public static IMemberValuePair wrap(AnnotationValue annotationValue) {
if (annotationValue == null) {
return null;
@@ -73,14 +75,14 @@ public class Wrappers {
public Object getValue() {
return annotationValue.value();
}
@Override
public String toString() {
return annotationValue.toString();
}
};
}
public static IPrimitiveType wrap(PrimitiveType type) {
switch (type.primitive()) {
case SHORT:
@@ -102,7 +104,7 @@ public class Wrappers {
}
throw new IllegalArgumentException("Invalid Java primitive type! " + type.toString());
}
@SuppressWarnings("unchecked")
static Type from(IJavaType type) {
if (type == IPrimitiveType.BOOLEAN) {
@@ -128,7 +130,7 @@ public class Wrappers {
}
throw new IllegalArgumentException("Not a Jandex wrapped typed!");
}
public static IJavaType wrap(Type type) {
switch (type.kind()) {
case ARRAY:
@@ -150,5 +152,5 @@ public class Wrappers {
}
throw new IllegalArgumentException("Invalid Java Type " + type.toString());
}
}

View File

@@ -10,6 +10,14 @@
*******************************************************************************/
package org.springframework.ide.vscode.commons.java;
import java.io.File;
public interface IField extends IMember {
boolean isEnumConstant();
@Override
default File classpathContainer() {
return getDeclaringType().classpathContainer();
}
}

View File

@@ -15,5 +15,6 @@ import org.springframework.ide.vscode.commons.javadoc.IJavadoc;
public interface IJavaElement {
String getElementName();
IJavadoc getJavaDoc();
String getBindingKey();
boolean exists();
}

View File

@@ -17,20 +17,18 @@ import java.util.List;
import java.util.Optional;
import java.util.function.Predicate;
import org.springframework.ide.vscode.commons.javadoc.IJavadoc;
import reactor.core.publisher.Flux;
import reactor.util.function.Tuple2;
public interface IJavaProject extends IJavaElement {
public interface IJavaProject {
final static String PROJECT_CACHE_FOLDER = ".sts4-cache";
IClasspath getClasspath();
ClasspathIndex getIndex();
URI getLocationUri();
boolean exists();
@Override
default String getElementName() {
return getClasspath().getName();
}
@@ -59,10 +57,4 @@ public interface IJavaProject extends IJavaElement {
return getIndex().findClasspathResourceContainer(fqName);
}
@Override
default IJavadoc getJavaDoc() {
//?? why is this here ??
return null;
}
}

View File

@@ -11,6 +11,8 @@
*******************************************************************************/
package org.springframework.ide.vscode.commons.java;
import java.io.File;
public interface IMember extends IJavaElement, IAnnotatable {
/**
@@ -32,7 +34,7 @@ public interface IMember extends IJavaElement, IAnnotatable {
* @see Flags
*/
int getFlags();
/**
* Returns the type in which this member is declared, or <code>null</code>
* if this member is not declared in a type (for example, a top-level type).
@@ -43,4 +45,6 @@ public interface IMember extends IJavaElement, IAnnotatable {
*/
IType getDeclaringType();
File classpathContainer();
}

View File

@@ -12,6 +12,7 @@
*******************************************************************************/
package org.springframework.ide.vscode.commons.java;
import java.io.File;
import java.util.stream.Stream;
public interface IMethod extends IMember {
@@ -57,13 +58,18 @@ public interface IMethod extends IMember {
// * @see Signature
// */
// String getSignature();
/**
* Returns parameter types of this method
* @return
*/
Stream<IJavaType> parameters();
boolean isConstructor();
@Override
default File classpathContainer() {
return getDeclaringType().classpathContainer();
}
}

View File

@@ -14,6 +14,7 @@ import java.io.File;
import java.net.URI;
import org.springframework.ide.vscode.commons.jandex.JandexClasspath;
import org.springframework.ide.vscode.commons.jandex.JandexIndex.JavadocProviderFactory;
import org.springframework.ide.vscode.commons.util.FileObserver;
import reactor.core.Disposable;
@@ -24,12 +25,14 @@ public class JavaProject implements IJavaProject, Disposable {
private ClasspathIndex index;
private URI uri;
private final FileObserver fileObserver;
private final JavadocProviderFactory javadocProviderFactory;
public JavaProject(FileObserver fileObserver, URI uri, IClasspath classpath) {
public JavaProject(FileObserver fileObserver, URI uri, IClasspath classpath, JavadocProviderFactory javadocProviderFactory) {
super();
this.classpath = classpath;
this.fileObserver = fileObserver;
this.uri = uri;
this.javadocProviderFactory = javadocProviderFactory;
}
@Override
@@ -40,7 +43,7 @@ public class JavaProject implements IJavaProject, Disposable {
@Override
public synchronized ClasspathIndex getIndex() {
if (index==null) {
index = new JandexClasspath(classpath, fileObserver);
index = new JandexClasspath(classpath, fileObserver, javadocProviderFactory);
}
return index;
}

View File

@@ -13,20 +13,28 @@ package org.springframework.ide.vscode.commons.java;
import java.net.URI;
import java.nio.file.Path;
import org.springframework.ide.vscode.commons.javadoc.JavaDocProviders;
import org.springframework.ide.vscode.commons.languageserver.jdt.ls.Classpath.CPE;
import org.springframework.ide.vscode.commons.util.FileObserver;
/**
* Abstract java project. Has a folder to store some project calculated data to speed up access
* Legacy java project. Base implementation for projects calculating classpath
* and other Java related data locally on this LS. Data calculation is
* expensive, hence there is a folder to store some project calculated data to
* speed up access
*
* @author Alex Boyko
*
*/
public abstract class AbstractJavaProject extends JavaProject {
public class LegacyJavaProject extends JavaProject {
final protected Path projectDataCache;
public AbstractJavaProject(FileObserver fileObserver, URI loactionUri, Path projectDataCache, IClasspath classpath) {
super(fileObserver, loactionUri, classpath);
public LegacyJavaProject(FileObserver fileObserver, URI loactionUri, Path projectDataCache, IClasspath classpath) {
super(fileObserver, loactionUri, classpath, classpathResource -> {
CPE cpe = IClasspathUtil.findEntryForBinaryRoot(classpath, classpathResource);
return JavaDocProviders.createFor(cpe);
});
this.projectDataCache = projectDataCache;
}

View File

@@ -0,0 +1,95 @@
/*******************************************************************************
* Copyright (c) 2018 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.commons.javadoc;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ide.vscode.commons.java.IAnnotation;
import org.springframework.ide.vscode.commons.java.IField;
import org.springframework.ide.vscode.commons.java.IJavaElement;
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.languageserver.JavadocParams;
import org.springframework.ide.vscode.commons.languageserver.JavadocResponse;
import org.springframework.ide.vscode.commons.languageserver.STS4LanguageClient;
import org.springframework.ide.vscode.commons.util.Renderable;
import org.springframework.ide.vscode.commons.util.Renderables;
public class JdtLsJavadocProvider implements IJavadocProvider {
private static final Logger log = LoggerFactory.getLogger(JdtLsJavadocProvider.class);
private STS4LanguageClient client;
private String projectUri;
public JdtLsJavadocProvider(STS4LanguageClient client, String projectUri) {
super();
this.client = client;
this.projectUri = projectUri;
}
private IJavadoc produceJavadocFromMd(JavadocResponse response) {
String md = response.getContent();
if (md != null) {
final Renderable renderableDoc = Renderables.mdBlob(md);
return new IJavadoc() {
@Override
public String raw() {
throw new UnsupportedOperationException("Raw content unavailable");
}
@Override
public Renderable getRenderable() {
return renderableDoc;
}
};
}
return null;
}
private IJavadoc javadoc(IJavaElement element) {
try {
JavadocResponse response = client.javadoc(new JavadocParams(projectUri, element.getBindingKey())).get(10, TimeUnit.SECONDS);
return produceJavadocFromMd(response);
} catch (InterruptedException | ExecutionException | TimeoutException e) {
log.error("", e);
return null;
}
}
@Override
public IJavadoc getJavadoc(IType type) {
return javadoc(type);
}
@Override
public IJavadoc getJavadoc(IField field) {
return javadoc(field);
}
@Override
public IJavadoc getJavadoc(IMethod method) {
return javadoc(method);
}
@Override
public IJavadoc getJavadoc(IAnnotation annotation) {
return javadoc(annotation);
}
}

View File

@@ -60,7 +60,7 @@ public class JandexClasspathTest {
}
JandexClasspath getJandexClasspath() {
return new JandexClasspath(getClasspath(), fileObserver);
return new JandexClasspath(getClasspath(), fileObserver, null);
}
public void deleteClass(String fqName, BiConsumer<BasicFileObserver, String> eventNoficator) {