Skip to content

Commit

Permalink
Provide codeLens participant
Browse files Browse the repository at this point in the history
See redhat-developer#229

Signed-off-by: azerr <azerr@redhat.com>
  • Loading branch information
angelozerr committed Mar 2, 2020
1 parent 9878c12 commit 9d68de1
Show file tree
Hide file tree
Showing 23 changed files with 669 additions and 195 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ Bundle-ActivationPolicy: lazy
Export-Package: com.redhat.microprofile.commons,
com.redhat.microprofile.commons.metadata,
com.redhat.microprofile.jdt.core,
com.redhat.microprofile.jdt.core.java,
com.redhat.microprofile.jdt.core.jaxrs,
com.redhat.microprofile.jdt.core.project,
com.redhat.microprofile.jdt.core.utils,
io.quarkus.runtime.util
Expand Down
6 changes: 6 additions & 0 deletions microprofile.jdt/com.redhat.microprofile.jdt.core/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -79,4 +79,10 @@
<diagnostics class="com.redhat.microprofile.jdt.internal.restclient.java.MicroProfileRestClientDiagnosticsParticipant" />
</extension>

<!-- JAX-RS support -->

<extension point="com.redhat.microprofile.jdt.core.javaFeatureParticipants">
<!-- Java URL codeLens for JAX-RS -->
<codeLens class="com.redhat.microprofile.jdt.internal.jaxrs.java.JaxRsCodeLensParticipant" />
</extension>
</plugin>
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,9 @@
<complexType>
<sequence minOccurs="1" maxOccurs="unbounded">
<choice>
<element ref="hover" minOccurs="0" maxOccurs="unbounded"/>
<element ref="codeLens" minOccurs="0" maxOccurs="unbounded"/>
<element ref="diagnostics" minOccurs="0" maxOccurs="unbounded"/>
<element ref="hover" minOccurs="0" maxOccurs="unbounded"/>
</choice>
</sequence>
<attribute name="point" type="string" use="required">
Expand Down Expand Up @@ -50,6 +51,26 @@
</complexType>
</element>

<element name="codeLens">
<annotation>
<documentation>
Java codeLens participant.
</documentation>
</annotation>
<complexType>
<attribute name="class" type="string" use="required">
<annotation>
<documentation>
Name of a class that implements IJavaCodeLensParticipant.
</documentation>
<appinfo>
<meta.attribute kind="java" basedOn=":com.redhat.microprofile.jdt.core.java.IJavaCodeLensParticipant"/>
</appinfo>
</annotation>
</attribute>
</complexType>
</element>

<element name="diagnostics">
<annotation>
<documentation>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,49 +9,30 @@
*******************************************************************************/
package com.redhat.microprofile.jdt.core;

import static com.redhat.microprofile.jdt.core.utils.AnnotationUtils.getAnnotation;
import static com.redhat.microprofile.jdt.core.utils.AnnotationUtils.getAnnotationMemberValue;
import static com.redhat.microprofile.jdt.core.utils.AnnotationUtils.hasAnnotation;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.stream.Collectors;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jdt.core.Flags;
import org.eclipse.jdt.core.IAnnotatable;
import org.eclipse.jdt.core.IAnnotation;
import org.eclipse.jdt.core.IClassFile;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.ISourceRange;
import org.eclipse.jdt.core.ISourceReference;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.ITypeRoot;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.lsp4j.CodeLens;
import org.eclipse.lsp4j.Command;
import org.eclipse.lsp4j.Diagnostic;
import org.eclipse.lsp4j.Hover;
import org.eclipse.lsp4j.Position;
import org.eclipse.lsp4j.PublishDiagnosticsParams;
import org.eclipse.lsp4j.Range;

import com.redhat.microprofile.commons.DocumentFormat;
import com.redhat.microprofile.commons.MicroProfileJavaCodeLensParams;
import com.redhat.microprofile.commons.MicroProfileJavaDiagnosticsParams;
import com.redhat.microprofile.commons.MicroProfileJavaHoverParams;
import com.redhat.microprofile.jdt.core.java.JavaCodeLensContext;
import com.redhat.microprofile.jdt.core.java.JavaDiagnosticsContext;
import com.redhat.microprofile.jdt.core.java.JavaHoverContext;
import com.redhat.microprofile.jdt.core.project.JDTMicroProfileProjectManager;
import com.redhat.microprofile.jdt.core.utils.IJDTUtils;
import com.redhat.microprofile.jdt.internal.core.java.JavaFeatureDefinition;
import com.redhat.microprofile.jdt.internal.core.java.JavaFeaturesRegistry;
Expand All @@ -64,16 +45,6 @@
*/
public class PropertiesManagerForJava {

private static final String LOCALHOST = "localhost";

private static final int PING_TIMEOUT = 2000;

private static final String JAVAX_WS_RS_PATH_ANNOTATION = "javax.ws.rs.Path";

private static final String JAVAX_WS_RS_GET_ANNOTATION = "javax.ws.rs.GET";

private static final String PATH_VALUE = "value";

private static final PropertiesManagerForJava INSTANCE = new PropertiesManagerForJava();

public static PropertiesManagerForJava getInstance() {
Expand All @@ -99,18 +70,29 @@ public List<? extends CodeLens> codeLens(MicroProfileJavaCodeLensParams params,
if (typeRoot == null) {
return Collections.emptyList();
}
IJavaElement[] elements = typeRoot.getChildren();
Collection<CodeLens> lenses = new LinkedHashSet<>(elements.length);
if (params.isUrlCodeLensEnabled()) {
int serverPort = JDTMicroProfileProjectManager.getInstance()
.getJDTMicroProfileProject(typeRoot.getJavaProject()).getServerPort();
params.setLocalServerPort(serverPort);
collectURLCodeLenses(typeRoot, elements, null, lenses, params, utils, monitor);
}
List<CodeLens> lenses = new ArrayList<>();
collectCodeLens(uri, typeRoot, utils, params, lenses, monitor);
if (monitor.isCanceled()) {
lenses.clear();
return Collections.emptyList();
}
return new ArrayList<>(lenses);
return lenses;
}

private void collectCodeLens(String uri, ITypeRoot typeRoot, IJDTUtils utils, MicroProfileJavaCodeLensParams params,
List<CodeLens> lenses, IProgressMonitor monitor) {
// Collect all adapted codeLens participant
JavaCodeLensContext context = new JavaCodeLensContext(uri, typeRoot, utils, params, lenses);
List<JavaFeatureDefinition> definitions = JavaFeaturesRegistry.getInstance().getJavaFeatureDefinitions()
.stream().filter(definition -> definition.isAdaptedForCodeLens(context, monitor))
.collect(Collectors.toList());
if (definitions.isEmpty()) {
return;
}

// Begin, collect, end participants
definitions.forEach(definition -> definition.beginCodeLens(context, monitor));
definitions.forEach(definition -> definition.collectCodeLens(context, monitor));
definitions.forEach(definition -> definition.endCodeLens(context, monitor));
}

/**
Expand Down Expand Up @@ -139,120 +121,6 @@ private static ITypeRoot resolveTypeRoot(String uri, IJDTUtils utils, IProgressM
return unit != null ? unit : classFile;
}

private void collectURLCodeLenses(ITypeRoot typeRoot, IJavaElement[] elements, String rootPath,
Collection<CodeLens> lenses, MicroProfileJavaCodeLensParams params, IJDTUtils utils,
IProgressMonitor monitor) throws JavaModelException {
for (IJavaElement element : elements) {
if (monitor.isCanceled()) {
return;
}
if (element.getElementType() == IJavaElement.TYPE) {
IType type = (IType) element;
// Get value of JAX-RS @Path annotation from the class
String pathValue = getJaxRsPathValue(type);
if (pathValue != null) {
// Class is annotated with @Path
// Display code lens only if local server is available.
if (!params.isCheckServerAvailable()
|| isServerAvailable(LOCALHOST, params.getLocalServerPort(), PING_TIMEOUT)) {
// Loop for each method annotated with @Path to generate URL code lens per
// method.
collectURLCodeLenses(typeRoot, type.getChildren(), pathValue, lenses, params, utils, monitor);
}
}
continue;
} else if (element.getElementType() == IJavaElement.METHOD) {
if (utils.isHiddenGeneratedElement(element)) {
continue;
}
// ignore element if method range overlaps the type range, happens for generated
// bytcode, i.e. with lombok
IJavaElement parentType = element.getAncestor(IJavaElement.TYPE);
if (parentType != null && overlaps(((ISourceReference) parentType).getNameRange(),
((ISourceReference) element).getNameRange())) {
continue;
}
} else {// neither a type nor a method, we bail
continue;
}

// Here java element is a method
if (rootPath != null) {
IMethod method = (IMethod) element;
// A JAX-RS method is a public method annotated with @GET @POST, @DELETE, @PUT
// JAX-RS
// annotation
if (isJaxRsRequestMethod(method) && Flags.isPublic(method.getFlags())) {
CodeLens lens = createCodeLens(element, typeRoot, utils);
if (lens != null) {
String baseURL = params.getLocalBaseURL();
String pathValue = getJaxRsPathValue(method);
String url = buildURL(baseURL, rootPath, pathValue);
String openURICommandId = params.getOpenURICommand();
lens.setCommand(new Command(url, openURICommandId != null ? openURICommandId : "",
Collections.singletonList(url)));
lenses.add(lens);
}
}
}
}
}

private static String buildURL(String... paths) {
StringBuilder url = new StringBuilder();
for (String path : paths) {
if (path != null && !path.isEmpty()) {
if (!url.toString().isEmpty() && path.charAt(0) != '/' && url.charAt(url.length() - 1) != '/') {
url.append('/');
}
url.append(path);
}
}
return url.toString();
}

private static String getJaxRsPathValue(IAnnotatable annotatable) throws JavaModelException {
IAnnotation annotationPath = getAnnotation(annotatable, JAVAX_WS_RS_PATH_ANNOTATION);
return annotationPath != null ? getAnnotationMemberValue(annotationPath, PATH_VALUE) : null;
}

private static boolean isJaxRsRequestMethod(IAnnotatable annotatable) throws JavaModelException {
return hasAnnotation(annotatable, JAVAX_WS_RS_GET_ANNOTATION);
}

private boolean overlaps(ISourceRange typeRange, ISourceRange methodRange) {
if (typeRange == null || methodRange == null) {
return false;
}
// method range is overlapping if it appears before or actually overlaps the
// type's range
return methodRange.getOffset() < typeRange.getOffset() || methodRange.getOffset() >= typeRange.getOffset()
&& methodRange.getOffset() <= (typeRange.getOffset() + typeRange.getLength());
}

private static CodeLens createCodeLens(IJavaElement element, ITypeRoot typeRoot, IJDTUtils utils)
throws JavaModelException {
ISourceRange r = ((ISourceReference) element).getNameRange();
if (r == null) {
return null;
}
CodeLens lens = new CodeLens();
final Range range = utils.toRange(typeRoot, r.getOffset(), r.getLength());
lens.setRange(range);
String uri = utils.toClientUri(utils.toUri(typeRoot));
lens.setData(Arrays.asList(uri, range.getStart()));
return lens;
}

private static boolean isServerAvailable(String host, int port, int timeout) {
try (Socket socket = new Socket()) {
socket.connect(new InetSocketAddress(host, port), timeout);
return true;
} catch (IOException e) {
return false;
}
}

/**
* Returns the hover information according to the given <code>params</code>
*
Expand Down Expand Up @@ -282,6 +150,9 @@ public Hover hover(MicroProfileJavaHoverParams params, IJDTUtils utils, IProgres
if (hovers.isEmpty()) {
return null;
}
if (monitor.isCanceled()) {
return null;
}
// TODO : aggregate the hover
return hovers.get(0);
}
Expand Down Expand Up @@ -331,6 +202,9 @@ public List<PublishDiagnosticsParams> diagnostics(MicroProfileJavaDiagnosticsPar
publishDiagnostics.add(publishDiagnostic);
collectDiagnostics(uri, utils, documentFormat, diagnostics, monitor);
}
if (monitor.isCanceled()) {
return Collections.emptyList();
}
return publishDiagnostics;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@
*******************************************************************************/
package com.redhat.microprofile.jdt.core.java;

import java.util.HashMap;
import java.util.Map;

import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.ITypeRoot;

Expand All @@ -30,6 +33,8 @@ public abstract class AbtractJavaContext {

private final IJDTUtils utils;

private Map<String, Object> cache;

public AbtractJavaContext(String uri, ITypeRoot typeRoot, IJDTUtils utils) {
this.uri = uri;
this.typeRoot = typeRoot;
Expand All @@ -51,4 +56,34 @@ public IJavaProject getJavaProject() {
public IJDTUtils getUtils() {
return utils;
}


/**
* Associates the specified value with the specified key in the cache.
*
* @param key the key.
* @param value the value.
*/
public void put(String key, Object value) {
if (cache == null) {
cache = new HashMap<>();
}
cache.put(key, value);
}

/**
* Returns the value to which the specified key is mapped, or {@code null} if
* this map contains no mapping for the key.
*
* @param key the key.
* @return the value to which the specified key is mapped, or {@code null} if
* this map contains no mapping for the key.
*/
public Object get(String key) {
if (cache == null) {
return null;
}
return cache.get(key);
}

}
Loading

0 comments on commit 9d68de1

Please sign in to comment.