diff --git a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/hover/BootJavaHoverProvider.java b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/hover/BootJavaHoverProvider.java index 8771d2dd5..6e0b0f5c9 100644 --- a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/hover/BootJavaHoverProvider.java +++ b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/hover/BootJavaHoverProvider.java @@ -41,6 +41,13 @@ public class BootJavaHoverProvider implements HoverHandler { private static final String SPRING_VALUE = "org.springframework.beans.factory.annotation.Value"; + private static final String SPRING_REQUEST_MAPPING = "org.springframework.web.bind.annotation.RequestMapping"; + private static final String SPRING_GET_MAPPING = "org.springframework.web.bind.annotation.GetMapping"; + private static final String SPRING_POST_MAPPING = "org.springframework.web.bind.annotation.PostMapping"; + private static final String SPRING_PUT_MAPPING = "org.springframework.web.bind.annotation.PutMapping"; + private static final String SPRING_DELETE_MAPPING = "org.springframework.web.bind.annotation.DeleteMapping"; + private static final String SPRING_PATCH_MAPPING = "org.springframework.web.bind.annotation.PatchMapping"; + private JavaProjectFinder projectFinder; private SimpleLanguageServer server; @@ -123,6 +130,13 @@ public class BootJavaHoverProvider implements HoverHandler { private CompletableFuture provideHoverForSpringAnnotation(ASTNode node, Annotation annotation, ITypeBinding type, int offset, TextDocument doc) { if (type.getQualifiedName().equals(SPRING_VALUE)) { return new ValueHoverProvider().provideHoverForValueAnnotation(node, annotation, type, offset, doc); + } else if (type.getQualifiedName().equals(SPRING_REQUEST_MAPPING) + || type.getQualifiedName().equals(SPRING_GET_MAPPING) + || type.getQualifiedName().equals(SPRING_POST_MAPPING) + || type.getQualifiedName().equals(SPRING_PUT_MAPPING) + || type.getQualifiedName().equals(SPRING_DELETE_MAPPING) + || type.getQualifiedName().equals(SPRING_PATCH_MAPPING)) { + return new RequestMappingHoverProvider().provideHoverForRequestMappingAnnotation(node, annotation, type, offset, doc); } return null; diff --git a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/hover/RequestMappingHoverProvider.java b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/hover/RequestMappingHoverProvider.java new file mode 100644 index 000000000..d24509701 --- /dev/null +++ b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/hover/RequestMappingHoverProvider.java @@ -0,0 +1,136 @@ +/******************************************************************************* + * Copyright (c) 2017 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.boot.java.hover; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +import org.eclipse.jdt.core.dom.ASTNode; +import org.eclipse.jdt.core.dom.Annotation; +import org.eclipse.jdt.core.dom.Expression; +import org.eclipse.jdt.core.dom.ITypeBinding; +import org.eclipse.jdt.core.dom.MemberValuePair; +import org.eclipse.jdt.core.dom.NormalAnnotation; +import org.eclipse.jdt.core.dom.SingleMemberAnnotation; +import org.eclipse.jdt.core.dom.StringLiteral; +import org.eclipse.lsp4j.Hover; +import org.eclipse.lsp4j.MarkedString; +import org.eclipse.lsp4j.Range; +import org.eclipse.lsp4j.jsonrpc.messages.Either; +import org.json.JSONObject; +import org.springframework.ide.vscode.commons.boot.app.cli.SpringBootApp; +import org.springframework.ide.vscode.commons.util.text.TextDocument; + +/** + * @author Martin Lippert + */ +public class RequestMappingHoverProvider { + + public CompletableFuture provideHoverForRequestMappingAnnotation(ASTNode node, Annotation annotation, + ITypeBinding type, int offset, TextDocument doc) { + return provideHover(annotation, doc); + } + + private CompletableFuture provideHover(Annotation annotation, TextDocument doc) { + + try { + JSONObject[] mappings = getRequestMappingsFromProcesses(); + + List> hoverContent = new ArrayList<>(); + + for (int i = 0; i < mappings.length; i++) { + addHoverContent(mappings[i], hoverContent, annotation); + } + + + Range hoverRange = doc.toRange(annotation.getStartPosition(), annotation.getLength()); + Hover hover = new Hover(); + + hover.setContents(hoverContent); + hover.setRange(hoverRange); + + return CompletableFuture.completedFuture(hover); + } + catch (Exception e) { + e.printStackTrace(); + } + + return null; + } + + private void addHoverContent(JSONObject jsonObject, List> hoverContent, Annotation annotation) { + Iterator keys = jsonObject.keys(); + while (keys.hasNext()) { + String key = keys.next(); + if (doesMatch(annotation, key)) { + hoverContent.add(Either.forLeft(key)); + hoverContent.add(Either.forLeft(jsonObject.get(key).toString())); + } + } + } + + private boolean doesMatch(Annotation annotation, String key) { + String mappingPath = null; + if (annotation instanceof SingleMemberAnnotation) { + Expression valueContent = ((SingleMemberAnnotation) annotation).getValue(); + if (valueContent instanceof StringLiteral) { + mappingPath = ((StringLiteral)valueContent).getLiteralValue(); + } + } + else if (annotation instanceof NormalAnnotation) { + List values = ((NormalAnnotation) annotation).values(); + for (Object value : values) { + if (value instanceof MemberValuePair) { + String name = ((MemberValuePair)value).getName().toString(); + if (name != null && name.equals("value")) { + Expression valueContent = ((MemberValuePair)value).getValue(); + if (valueContent instanceof StringLiteral) { + mappingPath = ((StringLiteral)valueContent).getLiteralValue(); + } + } + } + } + + } + + return mappingPath != null ? key.contains(mappingPath) : false; + } + + public JSONObject[] getRequestMappingsFromProcesses() { + List result = new ArrayList<>(); + + try { + Map apps = SpringBootApp.getAllRunningJavaApps(); + Iterator appsIter = apps.values().iterator(); + while (appsIter.hasNext()) { + SpringBootApp app = appsIter.next(); + if (app.isSpringBootApp()) { + String mappings = app.getRequestMappings(); + if (mappings != null) { + JSONObject requestMappings = new JSONObject(mappings); + if (requestMappings != null) { + result.add(requestMappings); + } + } + } + } + } + catch (Exception e) { + e.printStackTrace(); + } + + return result.toArray(new JSONObject[result.size()]); + } + +}