diff --git a/vscode-extensions/commons/application-properties-metadata/src/main/java/org/springframework/ide/vscode/application/properties/metadata/hints/StsValueHint.java b/vscode-extensions/commons/application-properties-metadata/src/main/java/org/springframework/ide/vscode/application/properties/metadata/hints/StsValueHint.java index 7d5909824..8fa72ba9c 100644 --- a/vscode-extensions/commons/application-properties-metadata/src/main/java/org/springframework/ide/vscode/application/properties/metadata/hints/StsValueHint.java +++ b/vscode-extensions/commons/application-properties-metadata/src/main/java/org/springframework/ide/vscode/application/properties/metadata/hints/StsValueHint.java @@ -117,7 +117,7 @@ public class StsValueHint { public static Provider javaDocSnippet(IJavaElement je) { return () -> { try { - HtmlSnippet jdoc = je.getJavaDoc(); + HtmlSnippet jdoc = HtmlSnippet.raw(je.getJavaDoc().html()); if (jdoc!=null) { return jdoc; } 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 871a6e7f0..c4143aaaf 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 @@ -8,6 +8,7 @@ import java.nio.file.Path; import java.util.Iterator; import java.util.List; import java.util.Optional; +import java.util.concurrent.ExecutionException; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -17,11 +18,14 @@ import org.jboss.jandex.IndexReader; import org.jboss.jandex.IndexView; import org.jboss.jandex.Indexer; import org.jboss.jandex.JarIndexer; +import org.springframework.ide.vscode.commons.java.IJavadocProvider; import org.springframework.ide.vscode.commons.java.IType; import org.springframework.ide.vscode.commons.util.Log; import com.google.common.base.Supplier; import com.google.common.base.Suppliers; +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; public class JandexIndex { @@ -40,35 +44,29 @@ public class JandexIndex { } @FunctionalInterface - public static interface SourceContainerProvider { - File getSourceContainer(File container); + public static interface JavadocProviderFactory { + IJavadocProvider createJavadocProvider(File jarContainer); } private Supplier>> index; - private SourceContainerProvider sourceContainerProvider; + private JavadocProviderFactory javadocProviderFactory; - public JandexIndex(Stream classpathEntries) { - this(classpathEntries, jarFile -> null, Optional.empty()); + private Cache javadocProvidersCache = CacheBuilder.newBuilder().build(); + + public JandexIndex(Stream classpathEntries, IndexFileFinder indexFileFinder, JavadocProviderFactory javadocProviderFactory) { + this(classpathEntries, indexFileFinder, Optional.empty(), javadocProviderFactory); } - public JandexIndex(Stream classpathEntries, IndexFileFinder indexFileFinder) { - this(classpathEntries, indexFileFinder, Optional.empty()); + public void setJvadocProviderFactory(JavadocProviderFactory sourceContainerProvider) { + this.javadocProviderFactory = sourceContainerProvider; } - public JandexIndex(Stream classpathEntries, Optional baseIndex) { - this(classpathEntries, jarFile -> null, baseIndex); + public JavadocProviderFactory getJavadocProviderFactory() { + return javadocProviderFactory; } - public void setSourceContainerProvider(SourceContainerProvider sourceContainerProvider) { - this.sourceContainerProvider = sourceContainerProvider; - } - - public SourceContainerProvider getSourceContainerProvider() { - return sourceContainerProvider; - } - - public JandexIndex(Stream classpathEntries, IndexFileFinder indexFileFinder, Optional baseIndex) { + public JandexIndex(Stream classpathEntries, IndexFileFinder indexFileFinder, Optional baseIndex, JavadocProviderFactory javadocProviderFactory) { index = Suppliers.memoize(() -> { List> indices = buildIndex(classpathEntries, indexFileFinder).collect(Collectors.toList()); if (baseIndex.isPresent()) { @@ -76,6 +74,7 @@ public class JandexIndex { } return indices; }); + this.javadocProviderFactory = javadocProviderFactory; } private Stream> buildIndex(Stream classpathEntries, IndexFileFinder indexFileFinder) { @@ -164,7 +163,14 @@ public class JandexIndex { IType getClassByName(DotName className) { Optional> pair = index.get().stream().map(e -> new Entry<>(e.key, e.value.getClassByName(className))).filter(e -> e.value != null).findFirst(); if (pair.isPresent()) { - return Wrappers.wrap(this, pair.get().value, pair.get().key); + File classpathResource = pair.get().key; + IJavadocProvider javadocProvider = null; + try { + javadocProvider = javadocProvidersCache.get(pair.get().key, () -> javadocProviderFactory == null ? null : javadocProviderFactory.createJavadocProvider(classpathResource)); + } catch (ExecutionException e) { + Log.log(e); + } + return Wrappers.wrap(this, pair.get().value, javadocProvider); } else { return null; } diff --git a/vscode-extensions/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/jandex/Wrappers.java b/vscode-extensions/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/jandex/Wrappers.java index 0d9e65476..89baaf7e0 100644 --- a/vscode-extensions/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/jandex/Wrappers.java +++ b/vscode-extensions/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/jandex/Wrappers.java @@ -2,7 +2,6 @@ package org.springframework.ide.vscode.commons.jandex; import static org.springframework.ide.vscode.commons.util.Assert.isNotNull; -import java.io.File; import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -21,16 +20,17 @@ import org.springframework.ide.vscode.commons.java.Flags; import org.springframework.ide.vscode.commons.java.IAnnotation; import org.springframework.ide.vscode.commons.java.IField; import org.springframework.ide.vscode.commons.java.IJavaType; +import org.springframework.ide.vscode.commons.java.IJavadoc; +import org.springframework.ide.vscode.commons.java.IJavadocProvider; import org.springframework.ide.vscode.commons.java.IMemberValuePair; import org.springframework.ide.vscode.commons.java.IMethod; import org.springframework.ide.vscode.commons.java.IPrimitiveType; import org.springframework.ide.vscode.commons.java.IType; import org.springframework.ide.vscode.commons.java.IVoidType; -import org.springframework.ide.vscode.commons.util.HtmlSnippet; public class Wrappers { - public static IType wrap(JandexIndex index, ClassInfo info, File container) { + public static IType wrap(JandexIndex index, ClassInfo info, IJavadocProvider javadocProvider) { if (info == null) { return null; } @@ -49,12 +49,12 @@ public class Wrappers { @Override public String getElementName() { - return info.simpleName(); + return info.simpleName() == null ? info.name().local() : info.simpleName(); } @Override - public HtmlSnippet getJavaDoc() { - throw new UnsupportedOperationException("Not yet implemented"); + public IJavadoc getJavaDoc() { + return javadocProvider == null ? null : javadocProvider.getJavadoc(this); } @Override @@ -65,7 +65,7 @@ public class Wrappers { @Override public Stream getAnnotations() { // TODO: check correctness! - return info.annotations().get(info.name()).stream().map(Wrappers::wrap); + return info.annotations().get(info.name()).stream().map(a -> wrap(a, javadocProvider)); } @Override @@ -90,26 +90,26 @@ public class Wrappers { @Override public IField getField(String name) { - return wrap(index, info.field(name), container); + return wrap(index, info.field(name), javadocProvider); } @Override public Stream getFields() { return info.fields().stream().map(f -> { - return wrap(index, f, container); + return wrap(index, f, javadocProvider); }); } @Override public IMethod getMethod(String name, Stream parameters) { List typeParameters = parameters.map(Wrappers::from).collect(Collectors.toList()); - return wrap(index, info.method(name, typeParameters.toArray(new Type[typeParameters.size()])), container); + return wrap(index, info.method(name, typeParameters.toArray(new Type[typeParameters.size()])), javadocProvider); } @Override public Stream getMethods() { return info.methods().stream().map(m -> { - return wrap(index, m, container); + return wrap(index, m, javadocProvider); }); } @@ -121,7 +121,7 @@ public class Wrappers { }; } - public static IField wrap(JandexIndex index, FieldInfo field, File container) { + public static IField wrap(JandexIndex index, FieldInfo field, IJavadocProvider javadocProvider) { if (field == null) { return null; } @@ -134,7 +134,7 @@ public class Wrappers { @Override public IType getDeclaringType() { - return wrap(index, field.declaringClass(), container); + return wrap(index, field.declaringClass(), javadocProvider); } @Override @@ -143,8 +143,8 @@ public class Wrappers { } @Override - public HtmlSnippet getJavaDoc() { - throw new UnsupportedOperationException("Not yet implemented"); + public IJavadoc getJavaDoc() { + return javadocProvider == null ? null : javadocProvider.getJavadoc(this); } @Override @@ -155,7 +155,7 @@ public class Wrappers { @Override public Stream getAnnotations() { return field.annotations().stream().map(a -> { - return wrap(a); + return wrap(a, javadocProvider); }); } @@ -171,7 +171,7 @@ public class Wrappers { }; } - public static IMethod wrap(JandexIndex index, MethodInfo method, File container) { + public static IMethod wrap(JandexIndex index, MethodInfo method, IJavadocProvider javadocProvider) { isNotNull(index); isNotNull(method); return new IMethod() { @@ -183,7 +183,7 @@ public class Wrappers { @Override public IType getDeclaringType() { - return wrap(index, method.declaringClass(), container); + return wrap(index, method.declaringClass(), javadocProvider); } @Override @@ -192,8 +192,8 @@ public class Wrappers { } @Override - public HtmlSnippet getJavaDoc() { - throw new UnsupportedOperationException("Not yet implemented"); + public IJavadoc getJavaDoc() { + return javadocProvider == null ? null : javadocProvider.getJavadoc(this); } @Override @@ -203,7 +203,7 @@ public class Wrappers { @Override public Stream getAnnotations() { - return method.annotations().stream().map(Wrappers::wrap); + return method.annotations().stream().map(a -> wrap(a, javadocProvider)); } @Override @@ -233,7 +233,7 @@ public class Wrappers { }; } - public static IAnnotation wrap(AnnotationInstance annotation) { + public static IAnnotation wrap(AnnotationInstance annotation, IJavadocProvider javadocProvider) { isNotNull(annotation); return new IAnnotation() { @@ -243,8 +243,8 @@ public class Wrappers { } @Override - public HtmlSnippet getJavaDoc() { - throw new UnsupportedOperationException("Not yet implemented"); + public IJavadoc getJavaDoc() { + return javadocProvider == null ? null : javadocProvider.getJavadoc(this); } @Override diff --git a/vscode-extensions/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/IJavaElement.java b/vscode-extensions/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/IJavaElement.java index 387253e79..5be8993a0 100644 --- a/vscode-extensions/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/IJavaElement.java +++ b/vscode-extensions/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/IJavaElement.java @@ -1,9 +1,7 @@ package org.springframework.ide.vscode.commons.java; -import org.springframework.ide.vscode.commons.util.HtmlSnippet; - public interface IJavaElement { String getElementName(); - HtmlSnippet getJavaDoc(); + IJavadoc getJavaDoc(); boolean exists(); } diff --git a/vscode-extensions/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/IJavadoc.java b/vscode-extensions/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/IJavadoc.java new file mode 100644 index 000000000..51bb2d893 --- /dev/null +++ b/vscode-extensions/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/IJavadoc.java @@ -0,0 +1,13 @@ +package org.springframework.ide.vscode.commons.java; + +public interface IJavadoc { + + String raw(); + + String plainText(); + + String html(); + + String markdown(); + +} diff --git a/vscode-extensions/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/IJavadocProvider.java b/vscode-extensions/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/IJavadocProvider.java new file mode 100644 index 000000000..3eb42027a --- /dev/null +++ b/vscode-extensions/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/IJavadocProvider.java @@ -0,0 +1,12 @@ +package org.springframework.ide.vscode.commons.java; + +public interface IJavadocProvider { + + IJavadoc getJavadoc(IType type); + + IJavadoc getJavadoc(IField field); + + IJavadoc getJavadoc(IMethod method); + + IJavadoc getJavadoc(IAnnotation method); +} diff --git a/vscode-extensions/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/parser/AbstractJavadocProvider.java b/vscode-extensions/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/parser/AbstractJavadocProvider.java new file mode 100644 index 000000000..749b06bc8 --- /dev/null +++ b/vscode-extensions/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/parser/AbstractJavadocProvider.java @@ -0,0 +1,160 @@ +package org.springframework.ide.vscode.commons.java.parser; + +import java.net.MalformedURLException; +import java.net.URL; +import java.util.Optional; + +import org.springframework.ide.vscode.commons.java.IAnnotation; +import org.springframework.ide.vscode.commons.java.IField; +import org.springframework.ide.vscode.commons.java.IJavadoc; +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.util.Log; + +import com.github.javaparser.ast.CompilationUnit; +import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration; +import com.github.javaparser.ast.body.EnumDeclaration; +import com.github.javaparser.ast.body.FieldDeclaration; +import com.github.javaparser.ast.body.MethodDeclaration; +import com.github.javaparser.ast.body.VariableDeclarator; +import com.github.javaparser.ast.visitor.GenericVisitorAdapter; + +public abstract class AbstractJavadocProvider implements IJavadocProvider { + + public IJavadoc getJavadoc(IType type) { + if (type.isEnum()) { + EnumDeclaration declaration = getEnumDeclaration(type); + return declaration.getJavaDoc() == null ? null : new Javadoc(declaration.getJavaDoc()); + } else { + ClassOrInterfaceDeclaration declaration = getClassOrInterfaceDeclaration(type); + return declaration.getJavaDoc() == null ? null : new Javadoc(declaration.getJavaDoc()); + } + } + + public IJavadoc getJavadoc(IField field) { + IType declaringType = field.getDeclaringType(); + if (declaringType.isEnum()) { + FieldDeclaration declaration = createVisitorToFindField(field).visit(getEnumDeclaration(declaringType), null); + return declaration.getJavaDoc() == null ? null : new Javadoc(declaration.getJavaDoc()); + } else { + FieldDeclaration declaration = createVisitorToFindField(field).visit(getClassOrInterfaceDeclaration(declaringType), null); + return declaration.getJavaDoc() == null ? null : new Javadoc(declaration.getJavaDoc()); + } + } + + public IJavadoc getJavadoc(IMethod method) { + if (method.parameters().findFirst().isPresent()) { + throw new UnsupportedOperationException("Only methods with no parameters are supported"); + } + IType declaringType = method.getDeclaringType(); + if (declaringType.isEnum()) { + MethodDeclaration declaration = createVisitorToFindMethod(method).visit(getEnumDeclaration(declaringType), null); + return declaration.getJavaDoc() == null ? null : new Javadoc(declaration.getJavaDoc()); + } else { + MethodDeclaration declaration = createVisitorToFindMethod(method).visit(getClassOrInterfaceDeclaration(declaringType), null); + return declaration.getJavaDoc() == null ? null : new Javadoc(declaration.getJavaDoc()); + } + } + + public IJavadoc getJavadoc(IAnnotation annotation) { + throw new UnsupportedOperationException("Not yet implemented"); + } + + private CompilationUnit getCompilationUnit(IType type) { + try { + URL sourceUrl = createSourceUrl(type); + return CompilationUnitIndex.DEFAULT.getCompilationUnit(sourceUrl); + } catch (MalformedURLException e) { + Log.log("Invalid source URL for type " + type, e); + return null; + } + } + + private EnumDeclaration getEnumDeclaration(IType type) { + IType parent = type.getDeclaringType(); + if (parent == null) { + CompilationUnit cu = getCompilationUnit(type); + return createVisitorToFindEnum(type).visit(cu, null); + } else { + if (parent.isEnum()) { + EnumDeclaration declaration = getEnumDeclaration(parent); + return createVisitorToFindEnum(type).visit(declaration, null); + } else { + ClassOrInterfaceDeclaration declaration = getClassOrInterfaceDeclaration(parent); + return createVisitorToFindEnum(type).visit(declaration, null); + } + } + } + + private ClassOrInterfaceDeclaration getClassOrInterfaceDeclaration(IType type) { + IType parent = type.getDeclaringType(); + if (parent == null) { + CompilationUnit cu = getCompilationUnit(type); + return createVisitorToFindClassOrInterface(type).visit(cu, null); + } else { + if (parent.isEnum()) { + EnumDeclaration declaration = getEnumDeclaration(parent); + return createVisitorToFindClassOrInterface(type).visit(declaration, null); + } else { + ClassOrInterfaceDeclaration declaration = getClassOrInterfaceDeclaration(parent); + return createVisitorToFindClassOrInterface(type).visit(declaration, null); + } + } + } + + private GenericVisitorAdapter createVisitorToFindClassOrInterface(IType type) { + return new GenericVisitorAdapter() { + + @Override + public ClassOrInterfaceDeclaration visit(ClassOrInterfaceDeclaration n, Object arg) { + if (n.getName().equals(type.getElementName())) { + return n; + } else { + return super.visit(n, arg); + } + } + + }; + } + + private GenericVisitorAdapter createVisitorToFindEnum(IType type) { + return new GenericVisitorAdapter() { + + @Override + public EnumDeclaration visit(EnumDeclaration n, Object arg) { + if (n.getName().equals(type.getElementName())) { + return n; + } else { + return super.visit(n, arg); + } + } + + }; + } + + private GenericVisitorAdapter createVisitorToFindMethod(IMethod method) { + return new GenericVisitorAdapter() { + @Override + public MethodDeclaration visit(MethodDeclaration n, Object arg) { + if (n.getParameters().isEmpty() && n.getName().equals(method.getElementName())) { + return n; + } + return null; + } + }; + } + + private GenericVisitorAdapter createVisitorToFindField(IField field) { + return new GenericVisitorAdapter() { + @Override + public FieldDeclaration visit(FieldDeclaration n, Object arg) { + Optional variable = n.getVariables().stream().filter(v -> v.getId().getName().equals(field.getElementName())).findFirst(); + return variable.isPresent() ? n : null; + } + }; + } + + abstract protected URL createSourceUrl(IType type) throws MalformedURLException; + +} diff --git a/vscode-extensions/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/parser/JarSourcesJavadocProvider.java b/vscode-extensions/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/parser/JarSourcesJavadocProvider.java new file mode 100644 index 000000000..5a2396ee7 --- /dev/null +++ b/vscode-extensions/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/parser/JarSourcesJavadocProvider.java @@ -0,0 +1,31 @@ +package org.springframework.ide.vscode.commons.java.parser; + +import java.net.MalformedURLException; +import java.net.URL; + +import org.springframework.ide.vscode.commons.java.IType; + +import com.google.common.base.Supplier; + +public class JarSourcesJavadocProvider extends AbstractJavadocProvider { + + private Supplier sourcesJarUrl; + + public JarSourcesJavadocProvider(Supplier sourcesJarUrl) { + super(); + this.sourcesJarUrl = sourcesJarUrl; + } + + @Override + protected URL createSourceUrl(IType type) throws MalformedURLException { + StringBuilder sourceUrlStr = new StringBuilder(); + sourceUrlStr.append("jar:"); + sourceUrlStr.append(sourcesJarUrl.get()); + sourceUrlStr.append("!"); + sourceUrlStr.append('/'); + sourceUrlStr.append(type.getFullyQualifiedName().replaceAll("\\.", "/")); + sourceUrlStr.append(".java"); + return new URL(sourceUrlStr.toString()); + } + +} diff --git a/vscode-extensions/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/parser/Javadoc.java b/vscode-extensions/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/parser/Javadoc.java new file mode 100644 index 000000000..cbc1315a4 --- /dev/null +++ b/vscode-extensions/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/parser/Javadoc.java @@ -0,0 +1,35 @@ +package org.springframework.ide.vscode.commons.java.parser; + +import org.springframework.ide.vscode.commons.java.IJavadoc; + +import com.github.javaparser.ast.comments.JavadocComment; + +final class Javadoc implements IJavadoc { + + private JavadocComment javadocComment; + + Javadoc(JavadocComment javadocComment) { + this.javadocComment = javadocComment; + } + + @Override + public String raw() { + return javadocComment.getContent(); + } + + @Override + public String plainText() { + throw new UnsupportedOperationException("Not yet implemnted"); + } + + @Override + public String html() { + throw new UnsupportedOperationException("Not yet implemnted"); + } + + @Override + public String markdown() { + throw new UnsupportedOperationException("Not yet implemnted"); + } + +} diff --git a/vscode-extensions/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/parser/SourceFolderJavadocProvider.java b/vscode-extensions/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/parser/SourceFolderJavadocProvider.java new file mode 100644 index 000000000..0f2ce7983 --- /dev/null +++ b/vscode-extensions/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/parser/SourceFolderJavadocProvider.java @@ -0,0 +1,23 @@ +package org.springframework.ide.vscode.commons.java.parser; + +import java.io.File; +import java.net.MalformedURLException; +import java.net.URL; + +import org.springframework.ide.vscode.commons.java.IType; + +public class SourceFolderJavadocProvider extends AbstractJavadocProvider { + + private File sourceFolder; + + public SourceFolderJavadocProvider(File sourceFolder) { + super(); + this.sourceFolder = sourceFolder; + } + + @Override + protected URL createSourceUrl(IType type) throws MalformedURLException { + return new File(sourceFolder, type.getFullyQualifiedName().replaceAll("\\.", "/") + ".java").toURI().toURL(); + } + +} 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 acf5d7ea1..cb1d1d8c6 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 @@ -83,7 +83,7 @@ public class MavenCore { private Supplier> javaCoreIndex = Suppliers.memoize(() -> { try { - return Optional.of(new JandexIndex(getJreLibs(), jarFile -> findIndexFile(jarFile))); + return Optional.of(new JandexIndex(getJreLibs(), jarFile -> findIndexFile(jarFile), null)); } catch (MavenException e) { return Optional.empty(); } 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 3d577cf5a..6e951cc32 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 @@ -17,9 +17,9 @@ 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.IJavadoc; import org.springframework.ide.vscode.commons.java.IType; import org.springframework.ide.vscode.commons.maven.MavenCore; -import org.springframework.ide.vscode.commons.util.HtmlSnippet; /** * Wrapper for Maven Core project @@ -45,7 +45,7 @@ public class MavenJavaProject implements IJavaProject { } @Override - public HtmlSnippet getJavaDoc() { + public IJavadoc getJavaDoc() { return null; } 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 6d18e39d8..d32b770c2 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 @@ -11,17 +11,24 @@ package org.springframework.ide.vscode.commons.maven.java; import java.io.File; +import java.net.MalformedURLException; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Arrays; +import java.util.Optional; import java.util.stream.Stream; +import org.apache.maven.artifact.Artifact; import org.apache.maven.project.MavenProject; import org.codehaus.plexus.util.DirectoryScanner; 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.parser.JarSourcesJavadocProvider; +import org.springframework.ide.vscode.commons.java.parser.SourceFolderJavadocProvider; import org.springframework.ide.vscode.commons.maven.MavenCore; +import org.springframework.ide.vscode.commons.maven.MavenException; import org.springframework.ide.vscode.commons.util.Log; import com.google.common.base.Supplier; @@ -53,7 +60,7 @@ public class MavenProjectClasspath implements IClasspath { } catch (Exception e) { Log.log(e); } - return new JandexIndex(classpathEntries, jarFile -> findIndexFile(jarFile), maven.getJavaIndexForJreLibs()); + return new JandexIndex(classpathEntries, jarFile -> findIndexFile(jarFile), maven.getJavaIndexForJreLibs(), classpathResource -> createJavadocProvider(classpathResource)); }); } @@ -72,6 +79,36 @@ public class MavenProjectClasspath implements IClasspath { private File findIndexFile(File jarFile) { return new File(maven.getIndexFolder().toString(), jarFile.getName() + "-" + jarFile.lastModified() + ".jdx"); } + + private Optional getArtifactFromJarFile(File file) throws MavenException { + return maven.resolveDependencies(project, null).stream().filter(a -> file.equals(a.getFile())).findFirst(); + } + + private IJavadocProvider createJavadocProvider(File classpathResource) { + System.out.println("--------> creating javadoc provider for " + classpathResource); + if (classpathResource.isDirectory()) { + if (classpathResource.toString().startsWith(project.getBuild().getOutputDirectory())) { + return new SourceFolderJavadocProvider(new File(project.getBuild().getSourceDirectory())); + } else if (classpathResource.toString().startsWith(project.getBuild().getTestOutputDirectory())) { + return new SourceFolderJavadocProvider(new File(project.getBuild().getTestSourceDirectory())); + } else { + throw new IllegalArgumentException("Cannot find source folder for " + classpathResource); + } + } else { + // Assume it's a JAR file + return new JarSourcesJavadocProvider(Suppliers.memoize(() -> { + try { + Artifact artifact = getArtifactFromJarFile(classpathResource).get(); + return maven.getSources(artifact).getFile().toURI().toURL(); + } catch (MavenException e) { + Log.log("Failed to find sources JAR for " + classpathResource, e); + } catch (MalformedURLException e) { + Log.log("Invalid URL for sources JAR for " + classpathResource, e); + } + return null; + })); + } + } @Override public Stream getClasspathResources() { 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 0a3458f53..3e24c8c1e 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 @@ -1,94 +1,94 @@ -/******************************************************************************* - * Copyright (c) 2016 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.maven.java.classpathfile; - -import java.io.File; -import java.nio.file.Paths; - -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.util.HtmlSnippet; - -/** - * Java project that contains classpath text file - * - * @author Alex Boyko - * - */ -public class JavaProjectWithClasspathFile implements IJavaProject { - - private File cpFile; - private FileClasspath classpath; - - public JavaProjectWithClasspathFile(File cpFile) { - this.cpFile = cpFile; - this.classpath = new FileClasspath(Paths.get(cpFile.toURI())); - } - - @Override - public String getElementName() { - return cpFile.getParentFile().getName(); - } - - @Override - public HtmlSnippet getJavaDoc() { - return null; - } - - @Override - public boolean exists() { - return cpFile.exists(); - } - - @Override - public IType findType(String fqName) { - //TODO: implement - return null; - } - - @Override - public IClasspath getClasspath() { - return classpath; - } - - @Override - public String toString() { - return "JavaProjectWithClasspathFile("+cpFile+")"; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((cpFile == null) ? 0 : cpFile.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - JavaProjectWithClasspathFile other = (JavaProjectWithClasspathFile) obj; - if (cpFile == null) { - if (other.cpFile != null) - return false; - } else if (!cpFile.equals(other.cpFile)) - return false; - return true; - } - - +/******************************************************************************* + * Copyright (c) 2016 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.maven.java.classpathfile; + +import java.io.File; +import java.nio.file.Paths; + +import org.springframework.ide.vscode.commons.java.IClasspath; +import org.springframework.ide.vscode.commons.java.IJavaProject; +import org.springframework.ide.vscode.commons.java.IJavadoc; +import org.springframework.ide.vscode.commons.java.IType; + +/** + * Java project that contains classpath text file + * + * @author Alex Boyko + * + */ +public class JavaProjectWithClasspathFile implements IJavaProject { + + private File cpFile; + private FileClasspath classpath; + + public JavaProjectWithClasspathFile(File cpFile) { + this.cpFile = cpFile; + this.classpath = new FileClasspath(Paths.get(cpFile.toURI())); + } + + @Override + public String getElementName() { + return cpFile.getParentFile().getName(); + } + + @Override + public IJavadoc getJavaDoc() { + return null; + } + + @Override + public boolean exists() { + return cpFile.exists(); + } + + @Override + public IType findType(String fqName) { + //TODO: implement + return null; + } + + @Override + public IClasspath getClasspath() { + return classpath; + } + + @Override + public String toString() { + return "JavaProjectWithClasspathFile("+cpFile+")"; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((cpFile == null) ? 0 : cpFile.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + JavaProjectWithClasspathFile other = (JavaProjectWithClasspathFile) obj; + if (cpFile == null) { + if (other.cpFile != null) + return false; + } else if (!cpFile.equals(other.cpFile)) + return false; + 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 48383a330..6bd927e9c 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 @@ -11,6 +11,7 @@ import java.util.stream.Collectors; import java.util.stream.Stream; import org.junit.Test; +import org.springframework.ide.vscode.commons.java.IField; import org.springframework.ide.vscode.commons.java.IMethod; import org.springframework.ide.vscode.commons.java.IPrimitiveType; import org.springframework.ide.vscode.commons.java.IType; @@ -85,7 +86,71 @@ public class JavaIndexTest { IMethod m = type.getMethod("", Stream.of(IPrimitiveType.INT)); assertEquals("", m.getElementName()); assertEquals(IVoidType.DEFAULT, m.getReturnType()); - assertEquals(Collections.singletonList(IPrimitiveType.INT), m.parameters().collect(Collectors.toList())); + assertEquals(Collections.singletonList(IPrimitiveType.INT), m.parameters().collect(Collectors.toList())); + } + + @Test + public void testClassJavadocForOutputFolder() throws Exception { + MavenJavaProject project = projectsCache.get("gs-rest-service-cors-boot-1.4.1-with-classpath-file"); + IType type = project.findType("hello.Greeting"); + + assertNotNull(type); + assertEquals("* Comment for Greeting class", type.getJavaDoc().raw().trim()); + + IField field = type.getField("id"); + assertNotNull(field); + assertEquals("* Comment for id field", field.getJavaDoc().raw().trim()); + + IMethod method = type.getMethod("getId", Stream.empty()); + assertNotNull(method); + assertEquals("* Comment for getId()", method.getJavaDoc().raw().trim()); } + @Test + public void testInnerClassJavadocForOutputFolder() throws Exception { + MavenJavaProject project = projectsCache.get("gs-rest-service-cors-boot-1.4.1-with-classpath-file"); + IType type = project.findType("hello.Greeting$TestInnerClass"); + assertNotNull(type); + assertEquals("* Comment for inner class", type.getJavaDoc().raw().trim()); + + IField field = type.getField("innerField"); + assertNotNull(field); + assertEquals("* Comment for inner field", field.getJavaDoc().raw().trim()); + + IMethod method = type.getMethod("getInnerField", Stream.empty()); + assertNotNull(method); + assertEquals("* Comment for method inside nested class", method.getJavaDoc().raw().trim()); + } + + @Test + public void testClassJavadocForJar() throws Exception { + MavenJavaProject project = projectsCache.get("gs-rest-service-cors-boot-1.4.1-with-classpath-file"); + + IType type = project.findType("org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener"); + assertNotNull(type); + String expectedPrefix = "* {@link ApplicationListener} that replaces the liquibase {@link ServiceLocator} with a"; + assertEquals(expectedPrefix, type.getJavaDoc().raw().trim().substring(0, expectedPrefix.length())); + + type = project.findType("org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener$LiquibasePresent"); + assertNotNull(type); + assertEquals("* Inner class to prevent class not found issues.", type.getJavaDoc().raw().trim()); + } + + @Test + public void testFieldAndMethodJavadocForJar() throws Exception { + MavenJavaProject project = projectsCache.get("gs-rest-service-cors-boot-1.4.1-with-classpath-file"); + + IType type = project.findType("org.springframework.boot.SpringApplication"); + assertNotNull(type); + + IField field = type.getField("BANNER_LOCATION_PROPERTY_VALUE"); + assertNotNull(field); + assertEquals("* Default banner location.", field.getJavaDoc().raw().trim()); + + IMethod method = type.getMethod("getListeners", Stream.empty()); + assertNotNull(method); + String expectedPrefix = "* Returns read-only ordered Set of the {@link ApplicationListener}s that will be"; + assertEquals(expectedPrefix, method.getJavaDoc().raw().trim().substring(0, expectedPrefix.length())); + } + } diff --git a/vscode-extensions/commons/commons-maven/src/test/resources/gs-rest-service-cors-boot-1.4.1-with-classpath-file/src/main/java/hello/Greeting.java b/vscode-extensions/commons/commons-maven/src/test/resources/gs-rest-service-cors-boot-1.4.1-with-classpath-file/src/main/java/hello/Greeting.java index 7e631d9ec..7baa70971 100644 --- a/vscode-extensions/commons/commons-maven/src/test/resources/gs-rest-service-cors-boot-1.4.1-with-classpath-file/src/main/java/hello/Greeting.java +++ b/vscode-extensions/commons/commons-maven/src/test/resources/gs-rest-service-cors-boot-1.4.1-with-classpath-file/src/main/java/hello/Greeting.java @@ -1,7 +1,13 @@ package hello; +/** + * Comment for Greeting class + */ public class Greeting { + /** + * Comment for id field + */ private final long id; private final String content; @@ -15,6 +21,9 @@ public class Greeting { this.content = content; } + /** + * Comment for getId() + */ public long getId() { return id; } @@ -22,4 +31,23 @@ public class Greeting { public String getContent() { return content; } + + /** + * Comment for inner class + */ + private static class TestInnerClass { + + /** + * Comment for inner field + */ + int innerField; + + /** + * Comment for method inside nested class + */ + public int getInnerField() { + return innerField; + } + + } }