From 29c2a6e2b7d3ed63d301828616b7c64ca55b9e43 Mon Sep 17 00:00:00 2001 From: BoykoAlex Date: Thu, 26 Oct 2017 11:43:16 -0400 Subject: [PATCH 01/10] Refine request mappings --- .../LiveAppURLSymbolProvider.java | 28 +- .../RequestMappingHoverProvider.java | 233 +++-- .../boot/java/requestmapping/UrlUtil.java | 61 -- .../test/RequestMappingLiveHoverTest.java | 798 ++++++++++++++++++ .../harness/MockRunningAppProvider.java | 5 +- .../commons/commons-boot-app-cli/pom.xml | 5 + .../commons/boot/app/cli/SpringBootApp.java | 22 +- .../cli/requestmappings/RequestMapping.java | 23 + .../requestmappings/RequestMappingImpl1.java | 170 ++++ .../boot/app/cli/RequestMappingImp1Test.java} | 24 +- .../boot/app/cli/SpringBootAppTest.java | 6 +- .../commons/java/parser/JLRMethodParser.java | 62 ++ 12 files changed, 1244 insertions(+), 193 deletions(-) create mode 100644 headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/requestmappings/RequestMapping.java create mode 100644 headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/requestmappings/RequestMappingImpl1.java rename headless-services/{boot-java-language-server/src/test/java/org/springframework/ide/vscode/boot/java/requestmapping/test/UrlUtilTest.java => commons/commons-boot-app-cli/src/test/java/org/springframework/ide/vscode/commons/boot/app/cli/RequestMappingImp1Test.java} (63%) diff --git a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/LiveAppURLSymbolProvider.java b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/LiveAppURLSymbolProvider.java index fd14519a8..f0e1efba4 100644 --- a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/LiveAppURLSymbolProvider.java +++ b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/LiveAppURLSymbolProvider.java @@ -11,15 +11,15 @@ package org.springframework.ide.vscode.boot.java.requestmapping; import java.util.ArrayList; -import java.util.Iterator; +import java.util.Arrays; import java.util.List; +import java.util.stream.Stream; import org.eclipse.lsp4j.Location; import org.eclipse.lsp4j.Position; import org.eclipse.lsp4j.Range; import org.eclipse.lsp4j.SymbolInformation; import org.eclipse.lsp4j.SymbolKind; -import org.json.JSONObject; import org.springframework.ide.vscode.boot.java.handlers.RunningAppProvider; import org.springframework.ide.vscode.commons.boot.app.cli.SpringBootApp; import org.springframework.ide.vscode.commons.util.Log; @@ -44,7 +44,12 @@ public class LiveAppURLSymbolProvider { SpringBootApp[] runningApps = runningAppProvider.getAllRunningSpringApps().toArray(new SpringBootApp[0]); for (SpringBootApp app : runningApps) { try { - collectLiveAppSymbols(result, app); + String host = app.getHost(); + String port = app.getPort(); + Stream urls = app.getRequestMappings().stream() + .flatMap(rm -> Arrays.stream(rm.getSplitPath())) + .map(path -> UrlUtil.createUrl(host, port, path)); + urls.forEach(url -> result.add(new SymbolInformation(url, SymbolKind.Method, new Location(url, new Range(new Position(0, 0), new Position(0, 1)))))); } catch (Exception e) { Log.log(e); @@ -57,21 +62,4 @@ public class LiveAppURLSymbolProvider { return result; } - private void collectLiveAppSymbols(List result, SpringBootApp app) throws Exception { - String mappings = app.getRequestMappings(); - JSONObject requestMappings = new JSONObject(mappings); - Iterator keys = requestMappings.keys(); - while (keys.hasNext()) { - String key = keys.next(); - String extractedPath = UrlUtil.extractPath(key); - if (extractedPath != null) { - String[] splitPath = UrlUtil.splitPath(extractedPath); - for (String path : splitPath) { - String url = UrlUtil.createUrl(app.getHost(), app.getPort(), path); - result.add(new SymbolInformation(url, SymbolKind.Method, new Location(url, new Range(new Position(0, 0), new Position(0, 1))))); - } - } - } - } - } diff --git a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/RequestMappingHoverProvider.java b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/RequestMappingHoverProvider.java index c870a55a3..d39e0baff 100644 --- a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/RequestMappingHoverProvider.java +++ b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/RequestMappingHoverProvider.java @@ -11,20 +11,27 @@ package org.springframework.ide.vscode.boot.java.requestmapping; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; -import java.util.Iterator; +import java.util.HashSet; import java.util.List; +import java.util.Objects; import java.util.Optional; +import java.util.Set; import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; import org.eclipse.jdt.core.dom.ASTNode; import org.eclipse.jdt.core.dom.Annotation; +import org.eclipse.jdt.core.dom.ArrayInitializer; import org.eclipse.jdt.core.dom.Expression; import org.eclipse.jdt.core.dom.IMethodBinding; import org.eclipse.jdt.core.dom.ITypeBinding; import org.eclipse.jdt.core.dom.MemberValuePair; import org.eclipse.jdt.core.dom.MethodDeclaration; import org.eclipse.jdt.core.dom.NormalAnnotation; +import org.eclipse.jdt.core.dom.QualifiedName; +import org.eclipse.jdt.core.dom.SimpleName; import org.eclipse.jdt.core.dom.SingleMemberAnnotation; import org.eclipse.jdt.core.dom.StringLiteral; import org.eclipse.jdt.core.dom.TypeDeclaration; @@ -32,17 +39,18 @@ 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.boot.java.handlers.HoverProvider; import org.springframework.ide.vscode.commons.boot.app.cli.SpringBootApp; -import org.springframework.ide.vscode.commons.java.parser.JLRMethodParser; -import org.springframework.ide.vscode.commons.java.parser.JLRMethodParser.JLRMethod; +import org.springframework.ide.vscode.commons.boot.app.cli.requestmappings.RequestMapping; import org.springframework.ide.vscode.commons.util.BadLocationException; import org.springframework.ide.vscode.commons.util.Log; import org.springframework.ide.vscode.commons.util.text.TextDocument; import com.google.common.collect.ImmutableList; +import reactor.util.function.Tuple2; +import reactor.util.function.Tuples; + /** * @author Martin Lippert */ @@ -58,7 +66,7 @@ public class RequestMappingHoverProvider implements HoverProvider { public Collection getLiveHoverHints(Annotation annotation, TextDocument doc, SpringBootApp[] runningApps) { try { if (runningApps.length > 0) { - Optional val = getRequestMappingMethodFromRunningApp(annotation, runningApps); + Optional> val = getRequestMappingMethodFromRunningApp(annotation, runningApps); if (val.isPresent()) { Range hoverRange = doc.toRange(annotation.getStartPosition(), annotation.getLength()); return ImmutableList.of(hoverRange); @@ -77,7 +85,7 @@ public class RequestMappingHoverProvider implements HoverProvider { try { List> hoverContent = new ArrayList<>(); - Optional val = getRequestMappingMethodFromRunningApp(annotation, runningApps); + Optional> val = getRequestMappingMethodFromRunningApp(annotation, runningApps); if (val.isPresent()) { addHoverContent(val.get(), hoverContent); @@ -97,25 +105,18 @@ public class RequestMappingHoverProvider implements HoverProvider { return null; } - private Optional getRequestMappingMethodFromRunningApp(Annotation annotation, + private Optional> getRequestMappingMethodFromRunningApp(Annotation annotation, SpringBootApp[] runningApps) { try { for (SpringBootApp app : runningApps) { - String mappings = app.getRequestMappings(); - if (mappings!=null) { - JSONObject requestMappings = new JSONObject(mappings); - String rawPath = getRawPath(annotation, requestMappings); - if (rawPath != null) { - String path = UrlUtil.extractPath(rawPath); - if (path != null) { - String rawMethod = getRawMethod(annotation, requestMappings); - JLRMethod parsedMethod = JLRMethodParser.parse(rawMethod); - if (methodMatchesAnnotation(annotation, parsedMethod)) { - return Optional.of(new RequestMappingMethod(path, parsedMethod, app)); - } - } - } + Collection mappings = app.getRequestMappings(); + if (mappings!=null && !mappings.isEmpty()) { + return mappings.stream() + .filter(rm -> matchesAnnotation(annotation, rm)) + .filter(rm -> methodMatchesAnnotation(annotation, rm)) + .map(rm -> Tuples.of(rm, app)) + .findFirst(); } } } catch (Exception e) { @@ -124,16 +125,19 @@ public class RequestMappingHoverProvider implements HoverProvider { return Optional.empty(); } - private boolean methodMatchesAnnotation(Annotation annotation, JLRMethod requestMappingMethod) { - String rqClassName = requestMappingMethod.getFQClassName(); - String rqMethod = requestMappingMethod.getMethodName(); + private boolean methodMatchesAnnotation(Annotation annotation, RequestMapping rm) { + String rqClassName = rm.getFullyQualifiedClassName(); ASTNode parent = annotation.getParent(); if (parent instanceof MethodDeclaration) { MethodDeclaration methodDec = (MethodDeclaration) parent; IMethodBinding binding = methodDec.resolveBinding(); - return binding.getDeclaringClass().getQualifiedName().equals(rqClassName) && - binding.getName().equals(rqMethod); + return binding.getDeclaringClass().getQualifiedName().equals(rqClassName) + && binding.getName().equals(rm.getMethodName()) + && Arrays.equals(Arrays.stream(binding.getParameterTypes()) + .map(t -> t.getQualifiedName()) + .toArray(String[]::new), + rm.getMethodParameters()); } else if (parent instanceof TypeDeclaration) { TypeDeclaration typeDec = (TypeDeclaration) parent; return typeDec.resolveBinding().getQualifiedName().equals(rqClassName); @@ -141,113 +145,156 @@ public class RequestMappingHoverProvider implements HoverProvider { return false; } - private void addHoverContent(RequestMappingMethod mappingMethod, List> hoverContent) throws Exception { - String processId = mappingMethod.app.getProcessID(); - String processName = mappingMethod.app.getProcessName(); - String path = mappingMethod.requestMappingPath; - + private void addHoverContent(Tuple2 mappingMethod, List> hoverContent) throws Exception { + String processId = mappingMethod.getT2().getProcessID(); + String processName = mappingMethod.getT2().getProcessName(); + String port = mappingMethod.getT2().getPort(); + String host = mappingMethod.getT2().getHost(); StringBuilder builder = new StringBuilder(); - String port = mappingMethod.app.getPort(); - String host = mappingMethod.app.getHost(); - String url = UrlUtil.createUrl(host, port, path); + Arrays.stream(mappingMethod.getT1().getSplitPath()).forEach(path -> { - builder.append("Path: "); - builder.append("["); - builder.append(path); - builder.append("]"); - builder.append("("); - builder.append(url); - builder.append(")"); + String url = UrlUtil.createUrl(host, port, path); + + if (builder.length() > 0) { + builder.append("\n"); + } + builder.append("Path: "); + builder.append("["); + builder.append(path); + builder.append("]"); + builder.append("("); + builder.append(url); + builder.append(")"); + + }); hoverContent.add(Either.forLeft(builder.toString())); hoverContent.add(Either.forLeft("Process ID: " + processId)); hoverContent.add(Either.forLeft("Process Name: " + processName)); } - private String getRawMethod(Annotation annotation, JSONObject mappings) { - Iterator keys = mappings.keys(); - while (keys.hasNext()) { - String key = keys.next(); - if (matchesAnnotation(annotation, key)) { - Object ob= mappings.get(key); - if (ob instanceof JSONObject) { - JSONObject methodMap = (JSONObject) ob; - return methodMap.getString("method"); - } + protected String getRequestMethod(SingleMemberAnnotation annotation) { + ITypeBinding type = annotation.resolveTypeBinding(); + if (type != null) { + switch (type.getQualifiedName()) { + case Constants.SPRING_GET_MAPPING: + return "GET"; + case Constants.SPRING_POST_MAPPING: + return "POST"; + case Constants.SPRING_DELETE_MAPPING: + return "DELETE"; + case Constants.SPRING_PUT_MAPPING: + return "PUT"; + case Constants.SPRING_PATCH_MAPPING: + return "PATCH"; } } return null; } - private String getRawPath(Annotation annotation, JSONObject mappings) { - Iterator keys = mappings.keys(); - while (keys.hasNext()) { - String key = keys.next(); - if (matchesAnnotation(annotation, key)) { - return key; - } - } - return null; - } - - private boolean matchesAnnotation(Annotation annotation, String jsonKey) { - String mappingPath = null; + private boolean matchesAnnotation(Annotation annotation, RequestMapping rm) { + String[] mappingPath = null; + Set methods = null; if (annotation instanceof SingleMemberAnnotation) { - Expression valueContent = ((SingleMemberAnnotation) annotation).getValue(); + SingleMemberAnnotation singleAnnotation = (SingleMemberAnnotation) annotation; + Expression valueContent = singleAnnotation.getValue(); if (valueContent instanceof StringLiteral) { - mappingPath = ((StringLiteral)valueContent).getLiteralValue(); + mappingPath = new String[] { ((StringLiteral)valueContent).getLiteralValue() }; + } + String method = getRequestMethod(singleAnnotation); + if (method != null) { + methods = new HashSet<>(); + methods.add(method); } } 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(); - } + MemberValuePair pair = (MemberValuePair)value; + String name = pair.getName().toString(); + switch (name) { + case "value": + case "path": + mappingPath = getPaths(pair.getValue()); + break; + case "method": + methods = getRequestMethod(pair.getValue()); + break; } } } } - return mappingPath != null ? jsonKey.contains(mappingPath) : false; + if (mappingPath != null) { + if (Arrays.equals(mappingPath, rm.getSplitPath())) { + if (methods == null || methods.isEmpty()) { + return true; + } else { + return methods.equals(rm.getRequestMethods()); + } + } + } + return false; + } - public JSONObject[] getRequestMappingsFromProcesses(SpringBootApp[] runningApps) { - List result = new ArrayList<>(); + private static String getExpressionValueAsString(Expression exp) { + if (exp instanceof StringLiteral) { + return ((StringLiteral)exp).getLiteralValue(); + } else if (exp instanceof QualifiedName) { + return getExpressionValueAsString(((QualifiedName)exp).getName()); + } else if (exp instanceof SimpleName) { + return ((SimpleName)exp).getIdentifier(); + } else { + return null; + } + } - try { - for (SpringBootApp app : runningApps) { - String mappings = app.getRequestMappings(); - if (mappings != null) { - JSONObject requestMappings = new JSONObject(mappings); - if (requestMappings != null) { - result.add(requestMappings); - } - } + @SuppressWarnings("unchecked") + private static String[] getPaths(Expression exp) { + if (exp instanceof ArrayInitializer) { + ArrayInitializer array = (ArrayInitializer) exp; + return ((List)array.expressions()).stream() + .map(e -> getExpressionValueAsString(e)) + .filter(Objects::nonNull) + .toArray(String[]::new); + } else { + String rm = getExpressionValueAsString(exp); + if (rm != null) { + return new String[] { rm }; } } - catch (Exception e) { - Log.log(e); - } + return null; + } - return result.toArray(new JSONObject[result.size()]); + @SuppressWarnings("unchecked") + private static Set getRequestMethod(Expression exp) { + if (exp instanceof ArrayInitializer) { + ArrayInitializer array = (ArrayInitializer) exp; + return ((List)array.expressions()).stream() + .map(e -> getExpressionValueAsString(e)) + .filter(Objects::nonNull) + .collect(Collectors.toSet()); + } else { + String rm = getExpressionValueAsString(exp); + if (rm != null) { + HashSet methods = new HashSet<>(); + methods.add(rm); + } + } + return null; } static class RequestMappingMethod { public final SpringBootApp app; - public final String requestMappingPath; - public final JLRMethod requestMappingMethod; + public final RequestMapping requestMapping; - public RequestMappingMethod(String requestMappingPath, JLRMethod requestMappingMethod, SpringBootApp app) { - this.requestMappingPath = requestMappingPath; - this.requestMappingMethod = requestMappingMethod; + public RequestMappingMethod(RequestMapping requestMapping, SpringBootApp app) { + this.requestMapping = requestMapping; this.app = app; } } diff --git a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/UrlUtil.java b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/UrlUtil.java index e56501510..f961f10f7 100644 --- a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/UrlUtil.java +++ b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/UrlUtil.java @@ -10,41 +10,8 @@ *******************************************************************************/ package org.springframework.ide.vscode.boot.java.requestmapping; -import java.util.ArrayList; -import java.util.List; -import java.util.StringTokenizer; -import java.util.regex.Pattern; -import java.util.stream.Stream; - public class UrlUtil { - - public static Stream processOrPaths(String pathExp) { - if (pathExp.contains("||")) { - String[] paths = pathExp.split(Pattern.quote("||")); - return Stream.of(paths).map(String::trim); - } else { - return Stream.of(pathExp); - } - } - - - public static String extractPath(String key) { - if (key.startsWith("{[")) { //Case 2 (see above) - //An almost json string. Unfortunately not really json so we can't - //use org.json or jackson Mapper to properly parse this. - int start = 2; //right after first '[' - int end = key.indexOf(']'); - if (end>=2) { - return key.substring(start, end); - } - } - //Case 1, or some unanticipated stuff. - //Assume the key is the path, which is right for Case 1 - // and probably more useful than null for 'unanticipated stuff'. - return key; - } - /** * Creates http URL string based on host, port and path * @param host @@ -67,32 +34,4 @@ public class UrlUtil { return null; } - - public static String[] splitPath(String path) { - if (path.contains("||")) { - List result = new ArrayList<>(); - - String basePath = path.substring(0, path.indexOf("||")).trim(); - result.add(basePath); - - if (basePath.lastIndexOf('/') > 0) { - basePath = basePath.substring(0, basePath.lastIndexOf('/')); - } - - String additionalPaths = path.substring(path.indexOf("||")); - StringTokenizer tokenizer = new StringTokenizer(additionalPaths, "||"); - while (tokenizer.hasMoreTokens()) { - String token = tokenizer.nextToken().trim(); - if (token.length() > 0) { - result.add(basePath + "/" + token); - } - } - - return result.toArray(new String[result.size()]); - } - else { - return new String[] {path}; - } - } - } diff --git a/headless-services/boot-java-language-server/src/test/java/org/springframework/ide/vscode/boot/java/requestmapping/test/RequestMappingLiveHoverTest.java b/headless-services/boot-java-language-server/src/test/java/org/springframework/ide/vscode/boot/java/requestmapping/test/RequestMappingLiveHoverTest.java index 0fd57e088..8ab9ea978 100644 --- a/headless-services/boot-java-language-server/src/test/java/org/springframework/ide/vscode/boot/java/requestmapping/test/RequestMappingLiveHoverTest.java +++ b/headless-services/boot-java-language-server/src/test/java/org/springframework/ide/vscode/boot/java/requestmapping/test/RequestMappingLiveHoverTest.java @@ -132,4 +132,802 @@ public class RequestMappingLiveHoverTest { } + @Test + public void testDeleteMappingHoverHintMethod1() throws Exception { + + File directory = new File( + ProjectsHarness.class.getResource("/test-projects/test-request-mapping-live-hover/").toURI()); + String docUri = "file://" +directory.getAbsolutePath() + "/src/main/java/example/RestApi.java"; + + + // Build a mock running boot app + mockAppProvider.builder() + .isSpringBootApp(true) + .port("999") + .processId("76543") + .host("cfapps.io") + .processName("test-request-mapping-live-hover") + // Ugly, but this is real JSON copied from a real live running app. We want the + // mock app to return realistic results if possible + .getRequestMappings( + "{\"{[/greetings],methods=[DELETE]}\": {\"bean\": \"requestMappingHandlerMapping\", \"method\":\"public void com.example.RestApi.deleteGreetings()\"}}") + . build(); + + harness.intialize(directory); + + Editor editor = harness.newEditor(LanguageId.JAVA, + "package com.example;\n" + + "\n" + + "import org.springframework.stereotype.Controller;\n" + + "import org.springframework.web.bind.annotation.DeleteMapping;\n" + + "\n" + + "@Controller\n" + + "public class RestApi {\n" + + "\n" + + "@DeleteMapping(\"/greetings\")\n" + + "public void deleteGreetings() {\n" + + "}\n" + + "\n" + + "}", + docUri); + + editor.assertHoverContains("@DeleteMapping(\"/greetings\")", "Path: [/greetings](http://cfapps.io:999/greetings)\n" + + "\n" + + "Process ID: 76543\n" + + "\n" + + "Process Name: test-request-mapping-live-hover"); + + } + + + @Test + public void testDeleteMappingHoverHintMethod2() throws Exception { + + File directory = new File( + ProjectsHarness.class.getResource("/test-projects/test-request-mapping-live-hover/").toURI()); + String docUri = "file://" +directory.getAbsolutePath() + "/src/main/java/example/RestApi.java"; + + + // Build a mock running boot app + mockAppProvider.builder() + .isSpringBootApp(true) + .port("999") + .processId("76543") + .host("cfapps.io") + .processName("test-request-mapping-live-hover") + // Ugly, but this is real JSON copied from a real live running app. We want the + // mock app to return realistic results if possible + .getRequestMappings( + "{\"{[/greetings],methods=[GET]}\": {\"bean\": \"requestMappingHandlerMapping\", \"method\":\"public java.lang.String com.example.RestApi.deleteGreetings()\"}}") + . build(); + + harness.intialize(directory); + + Editor editor = harness.newEditor(LanguageId.JAVA, + "package com.example;\n" + + "\n" + + "import org.springframework.stereotype.Controller;\n" + + "import org.springframework.web.bind.annotation.DeleteMapping;\n" + + "\n" + + "@Controller\n" + + "public class RestApi {\n" + + "\n" + + "@DeleteMapping(\"/greetings\")\n" + + "public void deleteGreetings() {\n" + + "}\n" + + "\n" + + "}", + docUri); + + editor.assertNoHover("@DeleteMapping(\"/greetings\")"); + + } + + @Test + public void testGetMappingHoverHintMethod1() throws Exception { + + File directory = new File( + ProjectsHarness.class.getResource("/test-projects/test-request-mapping-live-hover/").toURI()); + String docUri = "file://" +directory.getAbsolutePath() + "/src/main/java/example/RestApi.java"; + + + // Build a mock running boot app + mockAppProvider.builder() + .isSpringBootApp(true) + .port("999") + .processId("76543") + .host("cfapps.io") + .processName("test-request-mapping-live-hover") + // Ugly, but this is real JSON copied from a real live running app. We want the + // mock app to return realistic results if possible + .getRequestMappings( + "{\"{[/greetings],methods=[GET]}\": {\"bean\": \"requestMappingHandlerMapping\", \"method\":\"public java.lang.String com.example.RestApi.greetings()\"}}") + . build(); + + harness.intialize(directory); + + Editor editor = harness.newEditor(LanguageId.JAVA, + "package com.example;\n" + + "\n" + + "import java.lang.String;\n" + + "import org.springframework.stereotype.Controller;\n" + + "import org.springframework.web.bind.annotation.GetMapping;\n" + + "\n" + + "@Controller\n" + + "public class RestApi {\n" + + "\n" + + "@GetMapping(\"/greetings\")\n" + + "public String greetings() {\n" + + "return \"Greetings!\";\n" + + "}\n" + + "\n" + + "}", + docUri); + + editor.assertHoverContains("@GetMapping(\"/greetings\")", "Path: [/greetings](http://cfapps.io:999/greetings)\n" + + "\n" + + "Process ID: 76543\n" + + "\n" + + "Process Name: test-request-mapping-live-hover"); + + } + + @Test + public void testGetMappingHoverHintMethod2() throws Exception { + + File directory = new File( + ProjectsHarness.class.getResource("/test-projects/test-request-mapping-live-hover/").toURI()); + String docUri = "file://" +directory.getAbsolutePath() + "/src/main/java/example/RestApi.java"; + + + // Build a mock running boot app + mockAppProvider.builder() + .isSpringBootApp(true) + .port("999") + .processId("76543") + .host("cfapps.io") + .processName("test-request-mapping-live-hover") + // Ugly, but this is real JSON copied from a real live running app. We want the + // mock app to return realistic results if possible + .getRequestMappings( + "{\"{[/greetings],methods=[DELETE]}\": {\"bean\": \"requestMappingHandlerMapping\", \"method\":\"public java.lang.String com.example.RestApi.greetings()\"}}") + . build(); + + harness.intialize(directory); + + Editor editor = harness.newEditor(LanguageId.JAVA, + "package com.example;\n" + + "\n" + + "import java.lang.String;\n" + + "import org.springframework.stereotype.Controller;\n" + + "import org.springframework.web.bind.annotation.GetMapping;\n" + + "\n" + + "@Controller\n" + + "public class RestApi {\n" + + "\n" + + "@GetMapping(\"/greetings\")\n" + + "public String greetings() {\n" + + "return \"Greetings!\";\n" + + "}\n" + + "\n" + + "}", + docUri); + + editor.assertNoHover("@GetMapping(\"/greetings\")"); + + } + + @Test + public void testPostMappingHoverHintMethod1() throws Exception { + + File directory = new File( + ProjectsHarness.class.getResource("/test-projects/test-request-mapping-live-hover/").toURI()); + String docUri = "file://" +directory.getAbsolutePath() + "/src/main/java/example/RestApi.java"; + + + // Build a mock running boot app + mockAppProvider.builder() + .isSpringBootApp(true) + .port("999") + .processId("76543") + .host("cfapps.io") + .processName("test-request-mapping-live-hover") + // Ugly, but this is real JSON copied from a real live running app. We want the + // mock app to return realistic results if possible + .getRequestMappings( + "{\"{[/greetings],methods=[POST]}\": {\"bean\": \"requestMappingHandlerMapping\", \"method\":\"public void com.example.RestApi.createGreetings()\"}}") + . build(); + + harness.intialize(directory); + + Editor editor = harness.newEditor(LanguageId.JAVA, + "package com.example;\n" + + "\n" + + "import org.springframework.stereotype.Controller;\n" + + "import org.springframework.web.bind.annotation.PostMapping;\n" + + "\n" + + "@Controller\n" + + "public class RestApi {\n" + + "\n" + + "@PostMapping(\"/greetings\")\n" + + "public void createGreetings() {\n" + + "}\n" + + "\n" + + "}", + docUri); + + editor.assertHoverContains("@PostMapping(\"/greetings\")", "Path: [/greetings](http://cfapps.io:999/greetings)\n" + + "\n" + + "Process ID: 76543\n" + + "\n" + + "Process Name: test-request-mapping-live-hover"); + + } + + @Test + public void testPostMappingHoverHintMethod2() throws Exception { + + File directory = new File( + ProjectsHarness.class.getResource("/test-projects/test-request-mapping-live-hover/").toURI()); + String docUri = "file://" +directory.getAbsolutePath() + "/src/main/java/example/RestApi.java"; + + + // Build a mock running boot app + mockAppProvider.builder() + .isSpringBootApp(true) + .port("999") + .processId("76543") + .host("cfapps.io") + .processName("test-request-mapping-live-hover") + // Ugly, but this is real JSON copied from a real live running app. We want the + // mock app to return realistic results if possible + .getRequestMappings( + "{\"{[/greetings],methods=[GET]}\": {\"bean\": \"requestMappingHandlerMapping\", \"method\":\"public java.lang.String com.example.RestApi.createGreetings()\"}}") + . build(); + + harness.intialize(directory); + + Editor editor = harness.newEditor(LanguageId.JAVA, + "package com.example;\n" + + "\n" + + "import org.springframework.stereotype.Controller;\n" + + "import org.springframework.web.bind.annotation.PostMapping;\n" + + "\n" + + "@Controller\n" + + "public class RestApi {\n" + + "\n" + + "@PostMapping(\"/greetings\")\n" + + "public void createGreetings() {\n" + + "}\n" + + "\n" + + "}", + docUri); + + editor.assertNoHover("@PostMapping(\"/greetings\")"); + + } + + @Test + public void testPutMappingHoverHintMethod1() throws Exception { + + File directory = new File( + ProjectsHarness.class.getResource("/test-projects/test-request-mapping-live-hover/").toURI()); + String docUri = "file://" +directory.getAbsolutePath() + "/src/main/java/example/RestApi.java"; + + + // Build a mock running boot app + mockAppProvider.builder() + .isSpringBootApp(true) + .port("999") + .processId("76543") + .host("cfapps.io") + .processName("test-request-mapping-live-hover") + // Ugly, but this is real JSON copied from a real live running app. We want the + // mock app to return realistic results if possible + .getRequestMappings( + "{\"{[/greetings],methods=[PUT]}\": {\"bean\": \"requestMappingHandlerMapping\", \"method\":\"public void com.example.RestApi.updateGreetings()\"}}") + . build(); + + harness.intialize(directory); + + Editor editor = harness.newEditor(LanguageId.JAVA, + "package com.example;\n" + + "\n" + + "import org.springframework.stereotype.Controller;\n" + + "import org.springframework.web.bind.annotation.PutMapping;\n" + + "\n" + + "@Controller\n" + + "public class RestApi {\n" + + "\n" + + "@PutMapping(\"/greetings\")\n" + + "public void updateGreetings() {\n" + + "}\n" + + "\n" + + "}", + docUri); + + editor.assertHoverContains("@PutMapping(\"/greetings\")", "Path: [/greetings](http://cfapps.io:999/greetings)\n" + + "\n" + + "Process ID: 76543\n" + + "\n" + + "Process Name: test-request-mapping-live-hover"); + + } + + @Test + public void testPutMappingHoverHintMethod2() throws Exception { + + File directory = new File( + ProjectsHarness.class.getResource("/test-projects/test-request-mapping-live-hover/").toURI()); + String docUri = "file://" +directory.getAbsolutePath() + "/src/main/java/example/RestApi.java"; + + + // Build a mock running boot app + mockAppProvider.builder() + .isSpringBootApp(true) + .port("999") + .processId("76543") + .host("cfapps.io") + .processName("test-request-mapping-live-hover") + // Ugly, but this is real JSON copied from a real live running app. We want the + // mock app to return realistic results if possible + .getRequestMappings( + "{\"{[/greetings],methods=[GET]}\": {\"bean\": \"requestMappingHandlerMapping\", \"method\":\"public java.lang.String com.example.RestApi.updateGreetings()\"}}") + . build(); + + harness.intialize(directory); + + Editor editor = harness.newEditor(LanguageId.JAVA, + "package com.example;\n" + + "\n" + + "import org.springframework.stereotype.Controller;\n" + + "import org.springframework.web.bind.annotation.PutMapping;\n" + + "\n" + + "@Controller\n" + + "public class RestApi {\n" + + "\n" + + "@PutMapping(\"/greetings\")\n" + + "public void updateGreetings() {\n" + + "}\n" + + "\n" + + "}", + docUri); + + editor.assertNoHover("@PutMapping(\"/greetings\")"); + + } + + @Test + public void testPatchMappingHoverHintMethod1() throws Exception { + + File directory = new File( + ProjectsHarness.class.getResource("/test-projects/test-request-mapping-live-hover/").toURI()); + String docUri = "file://" +directory.getAbsolutePath() + "/src/main/java/example/RestApi.java"; + + + // Build a mock running boot app + mockAppProvider.builder() + .isSpringBootApp(true) + .port("999") + .processId("76543") + .host("cfapps.io") + .processName("test-request-mapping-live-hover") + // Ugly, but this is real JSON copied from a real live running app. We want the + // mock app to return realistic results if possible + .getRequestMappings( + "{\"{[/greetings],methods=[PATCH]}\": {\"bean\": \"requestMappingHandlerMapping\", \"method\":\"public void com.example.RestApi.patchGreetings()\"}}") + . build(); + + harness.intialize(directory); + + Editor editor = harness.newEditor(LanguageId.JAVA, + "package com.example;\n" + + "\n" + + "import org.springframework.stereotype.Controller;\n" + + "import org.springframework.web.bind.annotation.PatchMapping;\n" + + "\n" + + "@Controller\n" + + "public class RestApi {\n" + + "\n" + + "@PatchMapping(\"/greetings\")\n" + + "public void patchGreetings() {\n" + + "}\n" + + "\n" + + "}", + docUri); + + editor.assertHoverContains("@PatchMapping(\"/greetings\")", "Path: [/greetings](http://cfapps.io:999/greetings)\n" + + "\n" + + "Process ID: 76543\n" + + "\n" + + "Process Name: test-request-mapping-live-hover"); + + } + + @Test + public void testPatchMappingHoverHintMethod2() throws Exception { + + File directory = new File( + ProjectsHarness.class.getResource("/test-projects/test-request-mapping-live-hover/").toURI()); + String docUri = "file://" +directory.getAbsolutePath() + "/src/main/java/example/RestApi.java"; + + + // Build a mock running boot app + mockAppProvider.builder() + .isSpringBootApp(true) + .port("999") + .processId("76543") + .host("cfapps.io") + .processName("test-request-mapping-live-hover") + // Ugly, but this is real JSON copied from a real live running app. We want the + // mock app to return realistic results if possible + .getRequestMappings( + "{\"{[/greetings],methods=[GET]}\": {\"bean\": \"requestMappingHandlerMapping\", \"method\":\"public java.lang.String com.example.RestApi.patchGreetings()\"}}") + . build(); + + harness.intialize(directory); + + Editor editor = harness.newEditor(LanguageId.JAVA, + "package com.example;\n" + + "\n" + + "import org.springframework.stereotype.Controller;\n" + + "import org.springframework.web.bind.annotation.PatchMapping;\n" + + "\n" + + "@Controller\n" + + "public class RestApi {\n" + + "\n" + + "@PatchMapping(\"/greetings\")\n" + + "public void patchGreetings() {\n" + + "}\n" + + "\n" + + "}", + docUri); + + editor.assertNoHover("@PatchMapping(\"/greetings\")"); + + } + + @Test + public void testMultiRequestMethodMappingHoverHintMethod1() throws Exception { + + File directory = new File( + ProjectsHarness.class.getResource("/test-projects/test-request-mapping-live-hover/").toURI()); + String docUri = "file://" +directory.getAbsolutePath() + "/src/main/java/example/RestApi.java"; + + + // Build a mock running boot app + mockAppProvider.builder() + .isSpringBootApp(true) + .port("999") + .processId("76543") + .host("cfapps.io") + .processName("test-request-mapping-live-hover") + // Ugly, but this is real JSON copied from a real live running app. We want the + // mock app to return realistic results if possible + .getRequestMappings( + "{\"{[/greetings],methods=[POST,PUT]}\": {\"bean\": \"requestMappingHandlerMapping\", \"method\":\"public void com.example.RestApi.updateGreetings()\"}}") + . build(); + + harness.intialize(directory); + + Editor editor = harness.newEditor(LanguageId.JAVA, + "package com.example;\n" + + "\n" + + "import org.springframework.stereotype.Controller;\n" + + "import org.springframework.web.bind.annotation.RequestMapping;\n" + + "import org.springframework.web.bind.annotation.RequestMethod.*;\n" + + "\n" + + "@Controller\n" + + "public class RestApi {\n" + + "\n" + + "@RequestMapping(value=\"/greetings\", method={POST, PUT})\n" + + "public void updateGreetings() {\n" + + "}\n" + + "\n" + + "}", + docUri); + + editor.assertHoverContains("@RequestMapping(value=\"/greetings\", method={POST, PUT})", "Path: [/greetings](http://cfapps.io:999/greetings)\n" + + "\n" + + "Process ID: 76543\n" + + "\n" + + "Process Name: test-request-mapping-live-hover"); + + } + + @Test + public void testMultiRequestMethodMappingHoverHintMethod2() throws Exception { + + File directory = new File( + ProjectsHarness.class.getResource("/test-projects/test-request-mapping-live-hover/").toURI()); + String docUri = "file://" +directory.getAbsolutePath() + "/src/main/java/example/RestApi.java"; + + + // Build a mock running boot app + mockAppProvider.builder() + .isSpringBootApp(true) + .port("999") + .processId("76543") + .host("cfapps.io") + .processName("test-request-mapping-live-hover") + // Ugly, but this is real JSON copied from a real live running app. We want the + // mock app to return realistic results if possible + .getRequestMappings( + "{\"{[/greetings],methods=[POST,PUT,PATCH]}\": {\"bean\": \"requestMappingHandlerMapping\", \"method\":\"public java.lang.String com.example.RestApi.updateGreetings()\"}}") + . build(); + + harness.intialize(directory); + + Editor editor = harness.newEditor(LanguageId.JAVA, + "package com.example;\n" + + "\n" + + "import org.springframework.stereotype.Controller;\n" + + "import org.springframework.web.bind.annotation.RequestMapping;\n" + + "import org.springframework.web.bind.annotation.RequestMethod.*;\n" + + "\n" + + "@Controller\n" + + "public class RestApi {\n" + + "\n" + + "@RequestMapping(value=\"/greetings\", method={POST, PUT})\n" + + "public void updateGreetings() {\n" + + "}\n" + + "\n" + + "}", + docUri); + + editor.assertNoHover("@RequestMapping(value=\"/greetings\", method={POST, PUT})"); + + } + + @Test + public void testMultiPathMappingHoverHintMethod1() throws Exception { + + File directory = new File( + ProjectsHarness.class.getResource("/test-projects/test-request-mapping-live-hover/").toURI()); + String docUri = "file://" +directory.getAbsolutePath() + "/src/main/java/example/RestApi.java"; + + + // Build a mock running boot app + mockAppProvider.builder() + .isSpringBootApp(true) + .port("999") + .processId("76543") + .host("cfapps.io") + .processName("test-request-mapping-live-hover") + // Ugly, but this is real JSON copied from a real live running app. We want the + // mock app to return realistic results if possible + .getRequestMappings( + "{\"{[/greetings || /hello],methods=[GET]}\": {\"bean\": \"requestMappingHandlerMapping\", \"method\":\"public java.lang.String com.example.RestApi.greetings()\"}}") + . build(); + + harness.intialize(directory); + + Editor editor = harness.newEditor(LanguageId.JAVA, + "package com.example;\n" + + "\n" + + "import org.springframework.stereotype.Controller;\n" + + "import org.springframework.web.bind.annotation.RequestMapping;\n" + + "import org.springframework.web.bind.annotation.RequestMethod.*;\n" + + "\n" + + "@Controller\n" + + "public class RestApi {\n" + + "\n" + + "@RequestMapping(value={\"/greetings\", \"/hello\"}, method=GET)\n" + + "public String greetings() {\n" + + "}\n" + + "\n" + + "}", + docUri); + + editor.assertHoverContains("@RequestMapping(value={\"/greetings\", \"/hello\"}, method=GET)", "Path: [/greetings](http://cfapps.io:999/greetings)\n" + + "Path: [/hello](http://cfapps.io:999/hello)\n" + + "\n" + + "Process ID: 76543\n" + + "\n" + + "Process Name: test-request-mapping-live-hover"); + + } + + @Test + public void testMultiPathMappingHoverHintMethod2() throws Exception { + + File directory = new File( + ProjectsHarness.class.getResource("/test-projects/test-request-mapping-live-hover/").toURI()); + String docUri = "file://" +directory.getAbsolutePath() + "/src/main/java/example/RestApi.java"; + + + // Build a mock running boot app + mockAppProvider.builder() + .isSpringBootApp(true) + .port("999") + .processId("76543") + .host("cfapps.io") + .processName("test-request-mapping-live-hover") + // Ugly, but this is real JSON copied from a real live running app. We want the + // mock app to return realistic results if possible + .getRequestMappings( + "{\"{[/greetings || /hello || /helloAgain],methods=[GET]}\": {\"bean\": \"requestMappingHandlerMapping\", \"method\":\"public java.lang.String com.example.RestApi.greetings()\"}}") + . build(); + + harness.intialize(directory); + + Editor editor = harness.newEditor(LanguageId.JAVA, + "package com.example;\n" + + "\n" + + "import org.springframework.stereotype.Controller;\n" + + "import org.springframework.web.bind.annotation.RequestMapping;\n" + + "import org.springframework.web.bind.annotation.RequestMethod.*;\n" + + "\n" + + "@Controller\n" + + "public class RestApi {\n" + + "\n" + + "@RequestMapping(value={\"/greetings\", \"/hello\"}, method=GET)\n" + + "public String greetings() {\n" + + "}\n" + + "\n" + + "}", + docUri); + + editor.assertNoHover("@RequestMapping(value={\"/greetings\", \"/hello\"}, method=GET)"); + + } + + @Test + public void testMethodMatchingHoverHintMethod1() throws Exception { + + File directory = new File( + ProjectsHarness.class.getResource("/test-projects/test-request-mapping-live-hover/").toURI()); + String docUri = "file://" +directory.getAbsolutePath() + "/src/main/java/example/RestApi.java"; + + + // Build a mock running boot app + mockAppProvider.builder() + .isSpringBootApp(true) + .port("999") + .processId("76543") + .host("cfapps.io") + .processName("test-request-mapping-live-hover") + // Ugly, but this is real JSON copied from a real live running app. We want the + // mock app to return realistic results if possible + .getRequestMappings( + "{\"{[/find],methods=[GET]}\": {\"bean\": \"requestMappingHandlerMapping\", \"method\":\"public org.springframework.http.ResponseEntity com.example.RestApi.find(java.lang.String,java.util.Date,java.lang.String)\"}}") + . build(); + + harness.intialize(directory); + + Editor editor = harness.newEditor(LanguageId.JAVA, + "package com.example;\n" + + "\n" + + "import org.springframework.http.ResponseEntity;\n" + + "import java.lang.String;\n" + + "import java.util.Date;\n" + + "import org.springframework.stereotype.Controller;\n" + + "import org.springframework.web.bind.annotation.RequestMapping;\n" + + "import org.springframework.web.bind.annotation.RequestMethod.*;\n" + + "\n" + + "@Controller\n" + + "public class RestApi {\n" + + "\n" + + "@RequestMapping(value=\"/find\", method=GET)\n" + + "public ResponseEntity find(String p1, Date p2, String p3) {\n" + + "return null;\n" + + "}\n" + + "\n" + + "}", + docUri); + + editor.assertHoverContains("@RequestMapping(value=\"/find\", method=GET)", "Path: [/find](http://cfapps.io:999/find)\n" + + "\n" + + "Process ID: 76543\n" + + "\n" + + "Process Name: test-request-mapping-live-hover"); + + } + + @Test + public void testMethodMatchingHoverHintMethod2() throws Exception { + + File directory = new File( + ProjectsHarness.class.getResource("/test-projects/test-request-mapping-live-hover/").toURI()); + String docUri = "file://" +directory.getAbsolutePath() + "/src/main/java/example/RestApi.java"; + + + // Build a mock running boot app + mockAppProvider.builder() + .isSpringBootApp(true) + .port("999") + .processId("76543") + .host("cfapps.io") + .processName("test-request-mapping-live-hover") + // Ugly, but this is real JSON copied from a real live running app. We want the + // mock app to return realistic results if possible + .getRequestMappings( + "{\"{[/find],methods=[GET]}\": {\"bean\": \"requestMappingHandlerMapping\", \"method\":\"public java.lang.Object com.example.RestApi.set(java.lang.String,java.util.Map)\"}}") + . build(); + + harness.intialize(directory); + + Editor editor = harness.newEditor(LanguageId.JAVA, + "package com.example;\n" + + "\n" + + "import org.springframework.http.ResponseEntity;\n" + + "import java.lang.String;\n" + + "import java.util.Map;\n" + + "import org.springframework.stereotype.Controller;\n" + + "import org.springframework.web.bind.annotation.RequestMapping;\n" + + "import org.springframework.web.bind.annotation.RequestMethod.*;\n" + + "\n" + + "@Controller\n" + + "public class RestApi {\n" + + "\n" + + "@RequestMapping(value=\"/find\", method=GET)\n" + + "public Object set(String p1, Map p2) {\n" + + "return null;\n" + + "}\n" + + "\n" + + "}", + docUri); + + editor.assertHoverContains("@RequestMapping(value=\"/find\", method=GET)", "Path: [/find](http://cfapps.io:999/find)\n" + + "\n" + + "Process ID: 76543\n" + + "\n" + + "Process Name: test-request-mapping-live-hover"); + + } + + @Test + public void testMethodMatchingHoverHintMethod3() throws Exception { + + File directory = new File( + ProjectsHarness.class.getResource("/test-projects/test-request-mapping-live-hover/").toURI()); + String docUri = "file://" +directory.getAbsolutePath() + "/src/main/java/example/RestApi.java"; + + + // Build a mock running boot app + mockAppProvider.builder() + .isSpringBootApp(true) + .port("999") + .processId("76543") + .host("cfapps.io") + .processName("test-request-mapping-live-hover") + // Ugly, but this is real JSON copied from a real live running app. We want the + // mock app to return realistic results if possible + .getRequestMappings( + "{\"{[/find],methods=[GET]}\": {\"bean\": \"requestMappingHandlerMapping\", \"method\":\"public java.lang.Object com.example.RestApi.set(java.lang.String,java.util.Map>)\"}}") + . build(); + + harness.intialize(directory); + + Editor editor = harness.newEditor(LanguageId.JAVA, + "package com.example;\n" + + "\n" + + "import org.springframework.http.ResponseEntity;\n" + + "import java.lang.String;\n" + + "import java.lang.Integer;\n" + + "import java.util.Map;\n" + + "import org.springframework.stereotype.Controller;\n" + + "import org.springframework.web.bind.annotation.RequestMapping;\n" + + "import org.springframework.web.bind.annotation.RequestMethod.*;\n" + + "\n" + + "@Controller\n" + + "public class RestApi {\n" + + "\n" + + "@RequestMapping(value=\"/find\", method=GET)\n" + + "public Object set(String p1, Map> p2) {\n" + + "return null;\n" + + "}\n" + + "\n" + + "}", + docUri); + + editor.assertHoverContains("@RequestMapping(value=\"/find\", method=GET)", "Path: [/find](http://cfapps.io:999/find)\n" + + "\n" + + "Process ID: 76543\n" + + "\n" + + "Process Name: test-request-mapping-live-hover"); + + } + + } diff --git a/headless-services/boot-java-language-server/src/test/java/org/springframework/ide/vscode/project/harness/MockRunningAppProvider.java b/headless-services/boot-java-language-server/src/test/java/org/springframework/ide/vscode/project/harness/MockRunningAppProvider.java index 048afeaeb..718e6319f 100644 --- a/headless-services/boot-java-language-server/src/test/java/org/springframework/ide/vscode/project/harness/MockRunningAppProvider.java +++ b/headless-services/boot-java-language-server/src/test/java/org/springframework/ide/vscode/project/harness/MockRunningAppProvider.java @@ -19,8 +19,8 @@ import java.util.Collection; import org.mockito.Mockito; import org.springframework.ide.vscode.boot.java.handlers.RunningAppProvider; import org.springframework.ide.vscode.commons.boot.app.cli.SpringBootApp; +import org.springframework.ide.vscode.commons.boot.app.cli.requestmappings.RequestMapping; import org.springframework.ide.vscode.commons.util.ExceptionUtil; -import org.springframework.ide.vscode.project.harness.BootLanguageServerHarness.Builder; import com.google.common.collect.ImmutableList; @@ -97,7 +97,8 @@ public class MockRunningAppProvider { } public MockAppBuilder getRequestMappings(String mappings) throws Exception { - when(app.getRequestMappings()).thenReturn(mappings); + Collection requestMappings = SpringBootApp.parseRequestMappingsJson(mappings); + when(app.getRequestMappings()).thenReturn(requestMappings); return this; } diff --git a/headless-services/commons/commons-boot-app-cli/pom.xml b/headless-services/commons/commons-boot-app-cli/pom.xml index 17847e03a..d5c478053 100644 --- a/headless-services/commons/commons-boot-app-cli/pom.xml +++ b/headless-services/commons/commons-boot-app-cli/pom.xml @@ -37,6 +37,11 @@ org.springframework.ide.vscode ${project.version} + + commons-java + org.springframework.ide.vscode + ${project.version} + diff --git a/headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/SpringBootApp.java b/headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/SpringBootApp.java index b1cc28225..d69e5d1d2 100644 --- a/headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/SpringBootApp.java +++ b/headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/SpringBootApp.java @@ -13,7 +13,9 @@ package org.springframework.ide.vscode.commons.boot.app.cli; import java.io.File; import java.io.IOException; import java.util.ArrayList; +import java.util.Collection; import java.util.HashMap; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; @@ -30,6 +32,8 @@ import javax.management.remote.JMXServiceURL; import org.json.JSONArray; import org.json.JSONObject; +import org.springframework.ide.vscode.commons.boot.app.cli.requestmappings.RequestMapping; +import org.springframework.ide.vscode.commons.boot.app.cli.requestmappings.RequestMappingImpl1; import org.springframework.ide.vscode.commons.util.Log; import com.fasterxml.jackson.databind.ObjectMapper; @@ -186,17 +190,29 @@ public class SpringBootApp { return null; } - public String getRequestMappings() throws Exception { + public static Collection parseRequestMappingsJson(String json) { + JSONObject obj = new JSONObject(json); + Iterator keys = obj.keys(); + List result = new ArrayList<>(); + while (keys.hasNext()) { + String rawKey = keys.next(); + JSONObject value = obj.getJSONObject(rawKey); + result.add(new RequestMappingImpl1(rawKey, value)); + } + return result; + } + + public Collection getRequestMappings() throws Exception { Object result = getActuatorDataFromAttribute("org.springframework.boot:type=Endpoint,name=requestMappingEndpoint", "Data"); if (result != null) { String mappings = new ObjectMapper().writeValueAsString(result); - return mappings; + return parseRequestMappingsJson(mappings); } result = getActuatorDataFromOperation("org.springframework.boot:type=Endpoint,name=Mappings", "mappings"); if (result != null) { String mappings = new ObjectMapper().writeValueAsString(result); - return mappings; + return parseRequestMappingsJson(mappings); } return null; diff --git a/headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/requestmappings/RequestMapping.java b/headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/requestmappings/RequestMapping.java new file mode 100644 index 000000000..41c7c5bd2 --- /dev/null +++ b/headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/requestmappings/RequestMapping.java @@ -0,0 +1,23 @@ +/******************************************************************************* + * 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.commons.boot.app.cli.requestmappings; + +import java.util.Set; + +public interface RequestMapping { + String getPath(); + String[] getSplitPath(); + String getFullyQualifiedClassName(); + String getMethodName(); + String[] getMethodParameters(); + String getMethodString(); + Set getRequestMethods(); +} diff --git a/headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/requestmappings/RequestMappingImpl1.java b/headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/requestmappings/RequestMappingImpl1.java new file mode 100644 index 000000000..d3d7b290d --- /dev/null +++ b/headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/requestmappings/RequestMappingImpl1.java @@ -0,0 +1,170 @@ +/******************************************************************************* + * 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.commons.boot.app.cli.requestmappings; + +import java.util.Arrays; +import java.util.Collections; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +import org.json.JSONObject; +import org.springframework.ide.vscode.commons.java.parser.JLRMethodParser; +import org.springframework.ide.vscode.commons.java.parser.JLRMethodParser.JLRMethod; +import org.springframework.ide.vscode.commons.util.Log; + +import com.google.common.base.Objects; +import com.google.common.base.Supplier; +import com.google.common.base.Suppliers; + +public class RequestMappingImpl1 implements RequestMapping { + + private static final Pattern REQUEST_METHODS_PATTERN = Pattern.compile(".*methods=\\[(.*)\\].*"); + + /* +There are two styles of entries: + +1) key is a 'path' String. May contain patters like "**" + "/** /favicon.ico":{ + "bean":"faviconHandlerMapping" + } + +2) key is a 'almost json' String + "{[/bye],methods=[],params=[],headers=[],consumes=[],produces=[],custom=[]}":{ + "bean":"requestMappingHandlerMapping", + "method":"public java.lang.String demo.MyController.bye()" + } + */ + + private JSONObject beanInfo; + private String pathKey; + private Supplier methodDataSupplier; + private Supplier> requestMethodsSupplier; + private Supplier requestPathSupplier; + + public RequestMappingImpl1(String pathKey, JSONObject beanInfo) { + this.pathKey = pathKey; + this.beanInfo = beanInfo; + this.requestMethodsSupplier = Suppliers.memoize(() -> parseRequestMethods()); + this.requestPathSupplier = Suppliers.memoize(() -> parseRequestPath()); + this.methodDataSupplier = Suppliers.memoize(() -> JLRMethodParser.parse(getMethodString())); + } + + @Override + public String getPath() { + return requestPathSupplier.get(); + } + + @Override + public String toString() { + return "RequestMapping("+pathKey+")"; + } + + @Override + public String getFullyQualifiedClassName() { + JLRMethod m = getMethodData(); + if (m!=null) { + return m.getFQClassName(); + } + return null; + } + + @Override + public String getMethodName() { + JLRMethod m = getMethodData(); + if (m!=null) { + return m.getMethodName(); + } + return null; + } + + /** + * Returns the raw string found in the requestmapping info. This is a 'toString' value + * of java.lang.reflect.Method object. + */ + @Override + public String getMethodString() { + try { + if (beanInfo!=null) { + if (beanInfo.has("method")) { + return beanInfo.getString("method"); + } + } + } catch (Exception e) { + Log.log(e); + } + return null; + } + + private JLRMethod getMethodData() { + return methodDataSupplier.get(); + } + + @Override + public int hashCode() { + return pathKey.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + RequestMappingImpl1 other = (RequestMappingImpl1) obj; + return Objects.equal(this.pathKey, other.pathKey) + && Objects.equal(this.getMethodString(), other.getMethodString()); + } + + protected Set parseRequestMethods() { + Matcher matcher = REQUEST_METHODS_PATTERN.matcher(pathKey); + if (matcher.matches()) { + return Arrays.stream(matcher.group(1).split("\\s*,\\s*")).collect(Collectors.toSet()); + } + return Collections.emptySet(); + } + + protected String parseRequestPath() { + if (pathKey.startsWith("{[")) { //Case 2 (see above) + //An almost json string. Unfortunately not really json so we can't + //use org.json or jackson Mapper to properly parse this. + int start = 2; //right after first '[' + int end = pathKey.indexOf(']'); + if (end>=2) { + return pathKey.substring(start, end); + } + } + //Case 1, or some unanticipated stuff. + //Assume the key is the path, which is right for Case 1 + // and probably more useful than null for 'unanticipated stuff'. + return pathKey; + } + + @Override + public Set getRequestMethods() { + return requestMethodsSupplier.get(); + } + + @Override + public String[] getSplitPath() { + String paths = requestPathSupplier.get(); + return Arrays.stream(paths.split("\\|\\|")).map(s -> s.trim()).toArray(String[]::new); + } + + @Override + public String[] getMethodParameters() { + return getMethodData().getParameters(); + } + +} \ No newline at end of file diff --git a/headless-services/boot-java-language-server/src/test/java/org/springframework/ide/vscode/boot/java/requestmapping/test/UrlUtilTest.java b/headless-services/commons/commons-boot-app-cli/src/test/java/org/springframework/ide/vscode/commons/boot/app/cli/RequestMappingImp1Test.java similarity index 63% rename from headless-services/boot-java-language-server/src/test/java/org/springframework/ide/vscode/boot/java/requestmapping/test/UrlUtilTest.java rename to headless-services/commons/commons-boot-app-cli/src/test/java/org/springframework/ide/vscode/commons/boot/app/cli/RequestMappingImp1Test.java index 7ddf386f9..803cff043 100644 --- a/headless-services/boot-java-language-server/src/test/java/org/springframework/ide/vscode/boot/java/requestmapping/test/UrlUtilTest.java +++ b/headless-services/commons/commons-boot-app-cli/src/test/java/org/springframework/ide/vscode/commons/boot/app/cli/RequestMappingImp1Test.java @@ -8,38 +8,38 @@ * Contributors: * Pivotal, Inc. - initial API and implementation *******************************************************************************/ -package org.springframework.ide.vscode.boot.java.requestmapping.test; +package org.springframework.ide.vscode.commons.boot.app.cli; -import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; import org.junit.Test; -import org.springframework.ide.vscode.boot.java.requestmapping.UrlUtil; +import org.springframework.ide.vscode.commons.boot.app.cli.requestmappings.RequestMappingImpl1; /** * @author Martin Lippert */ -public class UrlUtilTest { +public class RequestMappingImp1Test { @Test public void testSplitPathWithoutDuplicate() { - String path = "/superpath"; - String[] splitPath = UrlUtil.splitPath(path); + RequestMappingImpl1 rm = new RequestMappingImpl1("/superpath", null); + String[] splitPath = rm.getSplitPath(); assertEquals(1, splitPath.length); assertEquals("/superpath", splitPath[0]); } @Test public void testSplitPathSimpleCaseWithEmptyOr() { - String path = "/superpath/mypath || "; - String[] splitPath = UrlUtil.splitPath(path); + RequestMappingImpl1 rm = new RequestMappingImpl1("/superpath/mypath || ", null); + String[] splitPath = rm.getSplitPath(); assertEquals(1, splitPath.length); assertEquals("/superpath/mypath", splitPath[0]); } @Test public void testSplitPathSimpleCase() { - String path = "/superpath/mypath || mypath.json"; - String[] splitPath = UrlUtil.splitPath(path); + RequestMappingImpl1 rm = new RequestMappingImpl1("/superpath/mypath || mypath.json", null); + String[] splitPath = rm.getSplitPath(); assertEquals(2, splitPath.length); assertEquals("/superpath/mypath", splitPath[0]); assertEquals("/superpath/mypath.json", splitPath[1]); @@ -47,8 +47,8 @@ public class UrlUtilTest { @Test public void testSplitPathMultipleCases() { - String path = "/superpath/mypath || mypath.json || somethingelse.what"; - String[] splitPath = UrlUtil.splitPath(path); + RequestMappingImpl1 rm = new RequestMappingImpl1("/superpath/mypath || mypath.json || somethingelse.what", null); + String[] splitPath = rm.getSplitPath(); assertEquals(3, splitPath.length); assertEquals("/superpath/mypath", splitPath[0]); assertEquals("/superpath/mypath.json", splitPath[1]); diff --git a/headless-services/commons/commons-boot-app-cli/src/test/java/org/springframework/ide/vscode/commons/boot/app/cli/SpringBootAppTest.java b/headless-services/commons/commons-boot-app-cli/src/test/java/org/springframework/ide/vscode/commons/boot/app/cli/SpringBootAppTest.java index 3247697de..29ef8566e 100644 --- a/headless-services/commons/commons-boot-app-cli/src/test/java/org/springframework/ide/vscode/commons/boot/app/cli/SpringBootAppTest.java +++ b/headless-services/commons/commons-boot-app-cli/src/test/java/org/springframework/ide/vscode/commons/boot/app/cli/SpringBootAppTest.java @@ -18,6 +18,7 @@ import static org.junit.Assert.assertTrue; import java.io.File; import java.net.URL; import java.time.Duration; +import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Optional; @@ -26,6 +27,7 @@ import org.json.JSONObject; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; +import org.springframework.ide.vscode.commons.boot.app.cli.requestmappings.RequestMapping; import org.springframework.ide.vscode.commons.util.AsyncProcess; import org.springframework.ide.vscode.commons.util.ExternalCommand; import org.springframework.ide.vscode.commons.util.StringUtil; @@ -139,8 +141,8 @@ public class SpringBootAppTest { @Test public void getRequestMappings() throws Exception { ACondition.waitFor(TIMEOUT, () -> { - String result = testApp.getRequestMappings(); - assertNonEmptyJsonObject(result); + Collection result = testApp.getRequestMappings(); + assertTrue(result != null && !result.isEmpty()); // System.out.println("requestMappings = "+result); }); } diff --git a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/parser/JLRMethodParser.java b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/parser/JLRMethodParser.java index 406eb6b24..970480e8c 100644 --- a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/parser/JLRMethodParser.java +++ b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/parser/JLRMethodParser.java @@ -10,9 +10,11 @@ *******************************************************************************/ package org.springframework.ide.vscode.commons.java.parser; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; +import java.util.List; import java.util.Set; /** @@ -32,6 +34,8 @@ public class JLRMethodParser { private String fqClass; private String methodName; + private String returnType; + private String[] parameters; //TODO: parsing arguments to handle overloading @@ -49,6 +53,7 @@ public class JLRMethodParser { while (modifiersEnd=modifiersEnd+2) { methodString = pieces[modifiersEnd+1]; int methodNameEnd = methodString.indexOf('('); @@ -63,6 +68,55 @@ public class JLRMethodParser { } } } + + int parametersStart = rawString.indexOf('('); + int parametersEnd = rawString.indexOf(')'); + if (parametersStart < parametersEnd) { + String parametersString = rawString.substring(parametersStart + 1, parametersEnd); + this.parameters = parseParameters(parametersString); + } else { + this.parameters = new String[0]; + } + } + + private String[] parseParameters(String parameterString) { + List parameters = new ArrayList<>(); + int openTemplateParameters = 0; + StringBuilder currentParameter = new StringBuilder(); + for (int i = 0; i < parameterString.length(); i++) { + char ch = parameterString.charAt(i); + switch (ch) { + case '.': + currentParameter.append(ch); + break; + case '<': + openTemplateParameters++; + currentParameter.append(ch); + break; + case '>': + openTemplateParameters--; + currentParameter.append(ch); + break; + case ',' : + if (openTemplateParameters == 0) { + if (currentParameter.length() != 0) { + parameters.add(currentParameter.toString()); + currentParameter = new StringBuilder(); + } + } else { + currentParameter.append(ch); + } + break; + default: + if (Character.isJavaIdentifierPart(ch)) { + currentParameter.append(ch); + } + } + } + if (currentParameter.length() > 0) { + parameters.add(currentParameter.toString()); + } + return parameters.toArray(new String[parameters.size()]); } @Override @@ -77,6 +131,14 @@ public class JLRMethodParser { public String getMethodName() { return methodName; } + + public String getReturnType() { + return returnType; + } + + public String[] getParameters() { + return parameters; + } } From 55458a369e52dbd22e6901f8c850fd8fc4fd6107 Mon Sep 17 00:00:00 2001 From: nsingh Date: Thu, 26 Oct 2017 12:22:04 -0700 Subject: [PATCH 02/10] Reduce process information to just one line --- .../conditionals/ConditionalsLiveHoverProvider.java | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/conditionals/ConditionalsLiveHoverProvider.java b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/conditionals/ConditionalsLiveHoverProvider.java index a5a1b1c45..5bc30a96e 100644 --- a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/conditionals/ConditionalsLiveHoverProvider.java +++ b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/conditionals/ConditionalsLiveHoverProvider.java @@ -39,8 +39,8 @@ import com.google.common.collect.ImmutableList; /** * - * Provides live hovers and hints for @ConditionalOn... Spring Boot annotations from running - * spring boot apps. + * Provides live hovers and hints for @ConditionalOn... Spring Boot annotations + * from running spring boot apps. */ public class ConditionalsLiveHoverProvider implements HoverProvider { @@ -100,12 +100,8 @@ public class ConditionalsLiveHoverProvider implements HoverProvider { RunningAppConditional condition = conditions.get(i); hoverContent.add(Either.forLeft("Condition: " + condition.condition)); hoverContent.add(Either.forLeft("Message: " + condition.message)); - - // If there is more than one instances show process information - if (conditions.size() > 1) { - hoverContent.add(Either.forLeft("Process ID: " + condition.app.getProcessID())); - hoverContent.add(Either.forLeft("Process Name: " + condition.app.getProcessName())); - } + hoverContent.add(Either + .forLeft("Process " + condition.app.getProcessID() + ": " + condition.app.getProcessName())); if (i < conditions.size() - 1) { hoverContent.add(Either.forLeft("---")); From 2f468b512e5c29f45620489e029c87719530d1b9 Mon Sep 17 00:00:00 2001 From: nsingh Date: Thu, 26 Oct 2017 12:38:50 -0700 Subject: [PATCH 03/10] Reduce conditional message to one line --- .../boot/java/conditionals/ConditionalsLiveHoverProvider.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/conditionals/ConditionalsLiveHoverProvider.java b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/conditionals/ConditionalsLiveHoverProvider.java index 5bc30a96e..0f5d951cf 100644 --- a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/conditionals/ConditionalsLiveHoverProvider.java +++ b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/conditionals/ConditionalsLiveHoverProvider.java @@ -98,8 +98,7 @@ public class ConditionalsLiveHoverProvider implements HoverProvider { List> hoverContent) throws Exception { for (int i = 0; i < conditions.size(); i++) { RunningAppConditional condition = conditions.get(i); - hoverContent.add(Either.forLeft("Condition: " + condition.condition)); - hoverContent.add(Either.forLeft("Message: " + condition.message)); + hoverContent.add(Either.forLeft(condition.message)); hoverContent.add(Either .forLeft("Process " + condition.app.getProcessID() + ": " + condition.app.getProcessName())); From 008182979f76b8ad7e06a445cd0635f98d48904e Mon Sep 17 00:00:00 2001 From: nsingh Date: Thu, 26 Oct 2017 12:42:29 -0700 Subject: [PATCH 04/10] Commenting out conditional tests until they can be fixed --- .../test/ConditionalsLiveHoverTest.java | 278 +++++++++--------- 1 file changed, 139 insertions(+), 139 deletions(-) diff --git a/headless-services/boot-java-language-server/src/test/java/org/springframework/ide/vscode/boot/java/conditionals/test/ConditionalsLiveHoverTest.java b/headless-services/boot-java-language-server/src/test/java/org/springframework/ide/vscode/boot/java/conditionals/test/ConditionalsLiveHoverTest.java index cb887f67c..291f30135 100644 --- a/headless-services/boot-java-language-server/src/test/java/org/springframework/ide/vscode/boot/java/conditionals/test/ConditionalsLiveHoverTest.java +++ b/headless-services/boot-java-language-server/src/test/java/org/springframework/ide/vscode/boot/java/conditionals/test/ConditionalsLiveHoverTest.java @@ -56,145 +56,145 @@ public class ConditionalsLiveHoverTest { Editor editorWithMethodLiveHover = harness.newEditorFromFileUri(docUri, LanguageId.JAVA); editorWithMethodLiveHover.assertNoHover("@ConditionalOnMissingBean"); } - - @Test - public void testLiveHoverConditionalOnBean() throws Exception { - - File directory = new File( - ProjectsHarness.class.getResource("/test-projects/test-conditionals-live-hover/").toURI()); - String docUri = "file://" + directory.getAbsolutePath() + "/src/main/java/example/ConditionalOnBeanConfig.java"; - - // Build a mock running boot app - mockAppProvider.builder().isSpringBootApp(true).port("1111").processId("22022").host("cfapps.io") - .processName("test-conditionals-live-hover") - .getAutoConfigReport( - "{\"positiveMatches\":{\"ConditionalOnBeanConfig#hi\":[{\"condition\":\"OnBeanCondition\",\"message\":\"@ConditionalOnBean (types: example.Hello; SearchStrategy: all) found bean 'missing'\"}]}}") - .build(); - - harness.intialize(directory); - - Editor editor = harness.newEditorFromFileUri(docUri, LanguageId.JAVA); - editor.assertHoverContains("@ConditionalOnBean", "Condition: OnBeanCondition\n" + "\n" - + "Message: @ConditionalOnBean (types: example.Hello; SearchStrategy: all) found bean 'missing'"); - - } - - @Test - public void testLiveHoverConditionalOnMissingBean() throws Exception { - - File directory = new File( - ProjectsHarness.class.getResource("/test-projects/test-conditionals-live-hover/").toURI()); - String docUri = "file://" + directory.getAbsolutePath() - + "/src/main/java/example/ConditionalOnMissingBeanConfig.java"; - - // Build a mock running boot app - mockAppProvider.builder().isSpringBootApp(true).port("1111").processId("22022").host("cfapps.io") - .processName("test-conditionals-live-hover") - .getAutoConfigReport( - "{\"positiveMatches\":{\"ConditionalOnMissingBeanConfig#missing\":[{\"condition\":\"OnBeanCondition\",\"message\":\"@ConditionalOnMissingBean (types: example.Hello; SearchStrategy: all) did not find any beans\"}]}}") - .build(); - - harness.intialize(directory); - - Editor editor = harness.newEditorFromFileUri(docUri, LanguageId.JAVA); - editor.assertHoverContains("@ConditionalOnMissingBean", "Condition: OnBeanCondition\n" + "\n" - + "Message: @ConditionalOnMissingBean (types: example.Hello; SearchStrategy: all) did not find any beans"); - - } - - @Test - public void testMultipleLiveHoverContentRealProject() throws Exception { - - File directory = new File( - ProjectsHarness.class.getResource("/test-projects/test-conditionals-live-hover/").toURI()); - String docUri = "file://" + directory.getAbsolutePath() + "/src/main/java/example/MultipleConditionals.java"; - - // Build a mock running boot app - mockAppProvider.builder().isSpringBootApp(true).port("1111").processId("22022").host("cfapps.io") - .processName("test-conditionals-live-hover") - .getAutoConfigReport( - "{\"positiveMatches\":{\"HelloConfig#missing\":[{\"condition\":\"OnBeanCondition\",\"message\":\"@ConditionalOnMissingBean (types: example.Hello; SearchStrategy: all) did not find any beans\"}],\"HelloConfig2#hi\":[{\"condition\":\"OnBeanCondition\",\"message\":\"@ConditionalOnBean (types: example.Hello; SearchStrategy: all) found bean 'missing'\"}],\"MultipleConditionals#hi\":[{\"condition\":\"OnClassCondition\",\"message\":\"@ConditionalOnClass found required class; @ConditionalOnMissingClass did not find unwanted class\"},{\"condition\":\"OnWebApplicationCondition\",\"message\":\"@ConditionalOnWebApplication (required) found StandardServletEnvironment\"},{\"condition\":\"OnJavaCondition\",\"message\":\"@ConditionalOnJava (1.8 or newer) found 1.8\"},{\"condition\":\"OnExpressionCondition\",\"message\":\"@ConditionalOnExpression (#{true}) resulted in true\"},{\"condition\":\"OnBeanCondition\",\"message\":\"@ConditionalOnBean (types: example.Hello; SearchStrategy: all) found beans 'hi', 'missing'\"}]}}") - .build(); - - harness.intialize(directory); - - Editor editor = harness.newEditorFromFileUri(docUri, LanguageId.JAVA); - - editor.assertHoverContains("@ConditionalOnBean", "Condition: OnBeanCondition\n" + "\n" - + "Message: @ConditionalOnBean (types: example.Hello; SearchStrategy: all) found beans 'hi', 'missing'"); - - editor.assertHoverContains("@ConditionalOnWebApplication", "Condition: OnWebApplicationCondition\n" + "\n" - + "Message: @ConditionalOnWebApplication (required) found StandardServletEnvironment"); - - editor.assertHoverContains("@ConditionalOnJava(value=ConditionalOnJava.JavaVersion.EIGHT)", - "Condition: OnJavaCondition\n" + "\n" + "Message: @ConditionalOnJava (1.8 or newer) found 1.8"); - - editor.assertHoverContains("@ConditionalOnMissingClass", "Condition: OnClassCondition\n" + "\n" - + "Message: @ConditionalOnClass found required class; @ConditionalOnMissingClass did not find unwanted class"); - - editor.assertHoverContains("@ConditionalOnExpression", "Condition: OnExpressionCondition\n" + "\n" - + "Message: @ConditionalOnExpression (#{true}) resulted in true"); - } - - - @Test - public void testMultipleAppsLiveHover() throws Exception { - - File directory = new File( - ProjectsHarness.class.getResource("/test-projects/test-conditionals-live-hover/").toURI()); - String docUri = "file://" + directory.getAbsolutePath() - + "/src/main/java/example/ConditionalOnMissingBeanConfig.java"; - - // Build a mock running boot app - mockAppProvider.builder().isSpringBootApp(true).port("1000").processId("70000").host("cfapps.io") - .processName("test-conditionals-live-hover") - .getAutoConfigReport( - "{\"positiveMatches\":{\"ConditionalOnMissingBeanConfig#missing\":[{\"condition\":\"OnBeanCondition\",\"message\":\"@ConditionalOnMissingBean (types: example.Hello; SearchStrategy: all) did not find any beans\"}]}}") - .build(); - - mockAppProvider.builder().isSpringBootApp(true).port("1001").processId("80000").host("cfapps.io") - .processName("test-conditionals-live-hover") - .getAutoConfigReport( - "{\"positiveMatches\":{\"ConditionalOnMissingBeanConfig#missing\":[{\"condition\":\"OnBeanCondition\",\"message\":\"@ConditionalOnMissingBean (types: example.Hello; SearchStrategy: all) did not find any beans\"}]}}") - .build(); - - mockAppProvider.builder().isSpringBootApp(true).port("1002").processId("90000").host("cfapps.io") - .processName("test-conditionals-live-hover") - .getAutoConfigReport( - "{\"positiveMatches\":{\"ConditionalOnMissingBeanConfig#missing\":[{\"condition\":\"OnBeanCondition\",\"message\":\"@ConditionalOnMissingBean (types: example.Hello; SearchStrategy: all) did not find any beans\"}]}}") - .build(); - - harness.intialize(directory); - - Editor editor = harness.newEditorFromFileUri(docUri, LanguageId.JAVA); - - - editor.assertHoverContains("@ConditionalOnMissingBean", "Condition: OnBeanCondition\n" + "\n" - + "Message: @ConditionalOnMissingBean (types: example.Hello; SearchStrategy: all) did not find any beans\n" + - "\n" + - "Process ID: 70000\n" + - "\n" + - "Process Name: test-conditionals-live-hover\n" + - "\n" + - "---\n" + - "\n" + - "Condition: OnBeanCondition\n" + "\n" - + "Message: @ConditionalOnMissingBean (types: example.Hello; SearchStrategy: all) did not find any beans\n" + - "\n" + - "Process ID: 80000\n" + - "\n" + - "Process Name: test-conditionals-live-hover\n" + - "\n" + - "---\n" + - "\n" + - "Condition: OnBeanCondition\n" + "\n" - + "Message: @ConditionalOnMissingBean (types: example.Hello; SearchStrategy: all) did not find any beans\n" + - "\n" + - "Process ID: 90000\n" + - "\n" + - "Process Name: test-conditionals-live-hover"); - - } +// +// @Test +// public void testLiveHoverConditionalOnBean() throws Exception { +// +// File directory = new File( +// ProjectsHarness.class.getResource("/test-projects/test-conditionals-live-hover/").toURI()); +// String docUri = "file://" + directory.getAbsolutePath() + "/src/main/java/example/ConditionalOnBeanConfig.java"; +// +// // Build a mock running boot app +// mockAppProvider.builder().isSpringBootApp(true).port("1111").processId("22022").host("cfapps.io") +// .processName("test-conditionals-live-hover") +// .getAutoConfigReport( +// "{\"positiveMatches\":{\"ConditionalOnBeanConfig#hi\":[{\"condition\":\"OnBeanCondition\",\"message\":\"@ConditionalOnBean (types: example.Hello; SearchStrategy: all) found bean 'missing'\"}]}}") +// .build(); +// +// harness.intialize(directory); +// +// Editor editor = harness.newEditorFromFileUri(docUri, LanguageId.JAVA); +// editor.assertHoverContains("@ConditionalOnBean", "Condition: OnBeanCondition\n" + "\n" +// + "Message: @ConditionalOnBean (types: example.Hello; SearchStrategy: all) found bean 'missing'"); +// +// } +// +// @Test +// public void testLiveHoverConditionalOnMissingBean() throws Exception { +// +// File directory = new File( +// ProjectsHarness.class.getResource("/test-projects/test-conditionals-live-hover/").toURI()); +// String docUri = "file://" + directory.getAbsolutePath() +// + "/src/main/java/example/ConditionalOnMissingBeanConfig.java"; +// +// // Build a mock running boot app +// mockAppProvider.builder().isSpringBootApp(true).port("1111").processId("22022").host("cfapps.io") +// .processName("test-conditionals-live-hover") +// .getAutoConfigReport( +// "{\"positiveMatches\":{\"ConditionalOnMissingBeanConfig#missing\":[{\"condition\":\"OnBeanCondition\",\"message\":\"@ConditionalOnMissingBean (types: example.Hello; SearchStrategy: all) did not find any beans\"}]}}") +// .build(); +// +// harness.intialize(directory); +// +// Editor editor = harness.newEditorFromFileUri(docUri, LanguageId.JAVA); +// editor.assertHoverContains("@ConditionalOnMissingBean", "Condition: OnBeanCondition\n" + "\n" +// + "Message: @ConditionalOnMissingBean (types: example.Hello; SearchStrategy: all) did not find any beans"); +// +// } +// +// @Test +// public void testMultipleLiveHoverContentRealProject() throws Exception { +// +// File directory = new File( +// ProjectsHarness.class.getResource("/test-projects/test-conditionals-live-hover/").toURI()); +// String docUri = "file://" + directory.getAbsolutePath() + "/src/main/java/example/MultipleConditionals.java"; +// +// // Build a mock running boot app +// mockAppProvider.builder().isSpringBootApp(true).port("1111").processId("22022").host("cfapps.io") +// .processName("test-conditionals-live-hover") +// .getAutoConfigReport( +// "{\"positiveMatches\":{\"HelloConfig#missing\":[{\"condition\":\"OnBeanCondition\",\"message\":\"@ConditionalOnMissingBean (types: example.Hello; SearchStrategy: all) did not find any beans\"}],\"HelloConfig2#hi\":[{\"condition\":\"OnBeanCondition\",\"message\":\"@ConditionalOnBean (types: example.Hello; SearchStrategy: all) found bean 'missing'\"}],\"MultipleConditionals#hi\":[{\"condition\":\"OnClassCondition\",\"message\":\"@ConditionalOnClass found required class; @ConditionalOnMissingClass did not find unwanted class\"},{\"condition\":\"OnWebApplicationCondition\",\"message\":\"@ConditionalOnWebApplication (required) found StandardServletEnvironment\"},{\"condition\":\"OnJavaCondition\",\"message\":\"@ConditionalOnJava (1.8 or newer) found 1.8\"},{\"condition\":\"OnExpressionCondition\",\"message\":\"@ConditionalOnExpression (#{true}) resulted in true\"},{\"condition\":\"OnBeanCondition\",\"message\":\"@ConditionalOnBean (types: example.Hello; SearchStrategy: all) found beans 'hi', 'missing'\"}]}}") +// .build(); +// +// harness.intialize(directory); +// +// Editor editor = harness.newEditorFromFileUri(docUri, LanguageId.JAVA); +// +// editor.assertHoverContains("@ConditionalOnBean", "Condition: OnBeanCondition\n" + "\n" +// + "Message: @ConditionalOnBean (types: example.Hello; SearchStrategy: all) found beans 'hi', 'missing'"); +// +// editor.assertHoverContains("@ConditionalOnWebApplication", "Condition: OnWebApplicationCondition\n" + "\n" +// + "Message: @ConditionalOnWebApplication (required) found StandardServletEnvironment"); +// +// editor.assertHoverContains("@ConditionalOnJava(value=ConditionalOnJava.JavaVersion.EIGHT)", +// "Condition: OnJavaCondition\n" + "\n" + "Message: @ConditionalOnJava (1.8 or newer) found 1.8"); +// +// editor.assertHoverContains("@ConditionalOnMissingClass", "Condition: OnClassCondition\n" + "\n" +// + "Message: @ConditionalOnClass found required class; @ConditionalOnMissingClass did not find unwanted class"); +// +// editor.assertHoverContains("@ConditionalOnExpression", "Condition: OnExpressionCondition\n" + "\n" +// + "Message: @ConditionalOnExpression (#{true}) resulted in true"); +// } +// +// +// @Test +// public void testMultipleAppsLiveHover() throws Exception { +// +// File directory = new File( +// ProjectsHarness.class.getResource("/test-projects/test-conditionals-live-hover/").toURI()); +// String docUri = "file://" + directory.getAbsolutePath() +// + "/src/main/java/example/ConditionalOnMissingBeanConfig.java"; +// +// // Build a mock running boot app +// mockAppProvider.builder().isSpringBootApp(true).port("1000").processId("70000").host("cfapps.io") +// .processName("test-conditionals-live-hover") +// .getAutoConfigReport( +// "{\"positiveMatches\":{\"ConditionalOnMissingBeanConfig#missing\":[{\"condition\":\"OnBeanCondition\",\"message\":\"@ConditionalOnMissingBean (types: example.Hello; SearchStrategy: all) did not find any beans\"}]}}") +// .build(); +// +// mockAppProvider.builder().isSpringBootApp(true).port("1001").processId("80000").host("cfapps.io") +// .processName("test-conditionals-live-hover") +// .getAutoConfigReport( +// "{\"positiveMatches\":{\"ConditionalOnMissingBeanConfig#missing\":[{\"condition\":\"OnBeanCondition\",\"message\":\"@ConditionalOnMissingBean (types: example.Hello; SearchStrategy: all) did not find any beans\"}]}}") +// .build(); +// +// mockAppProvider.builder().isSpringBootApp(true).port("1002").processId("90000").host("cfapps.io") +// .processName("test-conditionals-live-hover") +// .getAutoConfigReport( +// "{\"positiveMatches\":{\"ConditionalOnMissingBeanConfig#missing\":[{\"condition\":\"OnBeanCondition\",\"message\":\"@ConditionalOnMissingBean (types: example.Hello; SearchStrategy: all) did not find any beans\"}]}}") +// .build(); +// +// harness.intialize(directory); +// +// Editor editor = harness.newEditorFromFileUri(docUri, LanguageId.JAVA); +// +// +// editor.assertHoverContains("@ConditionalOnMissingBean", "Condition: OnBeanCondition\n" + "\n" +// + "Message: @ConditionalOnMissingBean (types: example.Hello; SearchStrategy: all) did not find any beans\n" + +// "\n" + +// "Process ID: 70000\n" + +// "\n" + +// "Process Name: test-conditionals-live-hover\n" + +// "\n" + +// "---\n" + +// "\n" + +// "Condition: OnBeanCondition\n" + "\n" +// + "Message: @ConditionalOnMissingBean (types: example.Hello; SearchStrategy: all) did not find any beans\n" + +// "\n" + +// "Process ID: 80000\n" + +// "\n" + +// "Process Name: test-conditionals-live-hover\n" + +// "\n" + +// "---\n" + +// "\n" + +// "Condition: OnBeanCondition\n" + "\n" +// + "Message: @ConditionalOnMissingBean (types: example.Hello; SearchStrategy: all) did not find any beans\n" + +// "\n" + +// "Process ID: 90000\n" + +// "\n" + +// "Process Name: test-conditionals-live-hover"); +// +// } // @Test // public void testMultipleLiveHoverHints() throws Exception { From 4dfca0cebe122d35b88fe9936df4230c3dbde0a0 Mon Sep 17 00:00:00 2001 From: BoykoAlex Date: Thu, 26 Oct 2017 21:25:09 -0400 Subject: [PATCH 05/10] Fix unit tests --- .../app/cli/requestmappings/RequestMappingImpl1.java | 12 +++++++++++- .../commons/boot/app/cli/RequestMappingImp1Test.java | 10 +++++----- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/requestmappings/RequestMappingImpl1.java b/headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/requestmappings/RequestMappingImpl1.java index d3d7b290d..6dd7a2004 100644 --- a/headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/requestmappings/RequestMappingImpl1.java +++ b/headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/requestmappings/RequestMappingImpl1.java @@ -159,7 +159,17 @@ There are two styles of entries: @Override public String[] getSplitPath() { String paths = requestPathSupplier.get(); - return Arrays.stream(paths.split("\\|\\|")).map(s -> s.trim()).toArray(String[]::new); + return Arrays.stream(paths.split("\\|\\|")) + .map(s -> s.trim()) + .filter(s -> !s.isEmpty()) + .map(s -> { + if (s.charAt(0) != '/') { + return '/' + s; + } else { + return s; + } + }) + .toArray(String[]::new); } @Override diff --git a/headless-services/commons/commons-boot-app-cli/src/test/java/org/springframework/ide/vscode/commons/boot/app/cli/RequestMappingImp1Test.java b/headless-services/commons/commons-boot-app-cli/src/test/java/org/springframework/ide/vscode/commons/boot/app/cli/RequestMappingImp1Test.java index 803cff043..0e845fd8a 100644 --- a/headless-services/commons/commons-boot-app-cli/src/test/java/org/springframework/ide/vscode/commons/boot/app/cli/RequestMappingImp1Test.java +++ b/headless-services/commons/commons-boot-app-cli/src/test/java/org/springframework/ide/vscode/commons/boot/app/cli/RequestMappingImp1Test.java @@ -38,21 +38,21 @@ public class RequestMappingImp1Test { @Test public void testSplitPathSimpleCase() { - RequestMappingImpl1 rm = new RequestMappingImpl1("/superpath/mypath || mypath.json", null); + RequestMappingImpl1 rm = new RequestMappingImpl1("{[/superpath/mypath || mypath.json]}", null); String[] splitPath = rm.getSplitPath(); assertEquals(2, splitPath.length); assertEquals("/superpath/mypath", splitPath[0]); - assertEquals("/superpath/mypath.json", splitPath[1]); + assertEquals("/mypath.json", splitPath[1]); } @Test public void testSplitPathMultipleCases() { - RequestMappingImpl1 rm = new RequestMappingImpl1("/superpath/mypath || mypath.json || somethingelse.what", null); + RequestMappingImpl1 rm = new RequestMappingImpl1("{[/superpath/mypath || mypath.json || somethingelse.what]}", null); String[] splitPath = rm.getSplitPath(); assertEquals(3, splitPath.length); assertEquals("/superpath/mypath", splitPath[0]); - assertEquals("/superpath/mypath.json", splitPath[1]); - assertEquals("/superpath/somethingelse.what", splitPath[2]); + assertEquals("/mypath.json", splitPath[1]); + assertEquals("/somethingelse.what", splitPath[2]); } } From a29b2d0a3bdc5b86f8fdf0e405ec331438d74c15 Mon Sep 17 00:00:00 2001 From: BoykoAlex Date: Fri, 27 Oct 2017 10:30:14 -0400 Subject: [PATCH 06/10] Format multiple paths urls on the hover better --- .../RequestMappingHoverProvider.java | 44 +++++++++---------- 1 file changed, 20 insertions(+), 24 deletions(-) diff --git a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/RequestMappingHoverProvider.java b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/RequestMappingHoverProvider.java index 87930d291..6aa65fc81 100644 --- a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/RequestMappingHoverProvider.java +++ b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/RequestMappingHoverProvider.java @@ -14,15 +14,14 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.List; -import java.util.Optional; import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; import org.eclipse.jdt.core.dom.ASTNode; import org.eclipse.jdt.core.dom.Annotation; import org.eclipse.jdt.core.dom.IMethodBinding; import org.eclipse.jdt.core.dom.ITypeBinding; import org.eclipse.jdt.core.dom.MethodDeclaration; -import org.eclipse.jdt.core.dom.TypeDeclaration; import org.eclipse.lsp4j.Hover; import org.eclipse.lsp4j.MarkedString; import org.eclipse.lsp4j.Range; @@ -32,6 +31,8 @@ import org.springframework.ide.vscode.commons.boot.app.cli.SpringBootApp; import org.springframework.ide.vscode.commons.boot.app.cli.requestmappings.RequestMapping; import org.springframework.ide.vscode.commons.util.BadLocationException; import org.springframework.ide.vscode.commons.util.Log; +import org.springframework.ide.vscode.commons.util.Renderable; +import org.springframework.ide.vscode.commons.util.Renderables; import org.springframework.ide.vscode.commons.util.text.TextDocument; import com.google.common.collect.ImmutableList; @@ -54,8 +55,8 @@ public class RequestMappingHoverProvider implements HoverProvider { public Collection getLiveHoverHints(Annotation annotation, TextDocument doc, SpringBootApp[] runningApps) { try { if (runningApps.length > 0) { - Optional>> val = getRequestMappingMethodFromRunningApp(annotation, runningApps); - if (val.isPresent()) { + List> val = getRequestMappingMethodFromRunningApp(annotation, runningApps); + if (!val.isEmpty()) { Range hoverRange = doc.toRange(annotation.getStartPosition(), annotation.getLength()); return ImmutableList.of(hoverRange); } @@ -73,10 +74,10 @@ public class RequestMappingHoverProvider implements HoverProvider { try { List> hoverContent = new ArrayList<>(); - Optional>> val = getRequestMappingMethodFromRunningApp(annotation, runningApps); + List> val = getRequestMappingMethodFromRunningApp(annotation, runningApps); - if (val.isPresent()) { - addHoverContent(val.get(), hoverContent); + if (!val.isEmpty()) { + addHoverContent(val, hoverContent); } Range hoverRange = doc.toRange(annotation.getStartPosition(), annotation.getLength()); @@ -93,11 +94,11 @@ public class RequestMappingHoverProvider implements HoverProvider { return null; } - private Optional>> getRequestMappingMethodFromRunningApp(Annotation annotation, + private List> getRequestMappingMethodFromRunningApp(Annotation annotation, SpringBootApp[] runningApps) { + List> results = new ArrayList<>(); try { - List> results = new ArrayList<>(); for (SpringBootApp app : runningApps) { Collection mappings = app.getRequestMappings(); if (mappings != null && !mappings.isEmpty()) { @@ -108,11 +109,10 @@ public class RequestMappingHoverProvider implements HoverProvider { .findFirst().ifPresent(t -> results.add(t)); } } - return Optional.of(results); } catch (Exception e) { Log.log(e); } - return Optional.empty(); + return results; } private boolean methodMatchesAnnotation(Annotation annotation, RequestMapping rm) { @@ -128,9 +128,9 @@ public class RequestMappingHoverProvider implements HoverProvider { .map(t -> t.getTypeDeclaration().getQualifiedName()) .toArray(String[]::new), rm.getMethodParameters()); - } else if (parent instanceof TypeDeclaration) { - TypeDeclaration typeDec = (TypeDeclaration) parent; - return typeDec.resolveBinding().getQualifiedName().equals(rqClassName); +// } else if (parent instanceof TypeDeclaration) { +// TypeDeclaration typeDec = (TypeDeclaration) parent; +// return typeDec.resolveBinding().getQualifiedName().equals(rqClassName); } return false; } @@ -143,25 +143,21 @@ public class RequestMappingHoverProvider implements HoverProvider { String processName = mappingMethod.getT2().getProcessName(); String port = mappingMethod.getT2().getPort(); String host = mappingMethod.getT2().getHost(); - StringBuilder builder = new StringBuilder(); - - Arrays.stream(mappingMethod.getT1().getSplitPath()).forEach(path -> { + List renderableUrls = Arrays.stream(mappingMethod.getT1().getSplitPath()).map(path -> { String url = UrlUtil.createUrl(host, port, path); - - if (builder.length() > 0) { - builder.append("\n"); - } + StringBuilder builder = new StringBuilder(); builder.append("["); builder.append(url); builder.append("]"); builder.append("("); builder.append(url); builder.append(")"); + return Renderables.concat(Renderables.text(builder.toString()), Renderables.lineBreak()); + }) + .collect(Collectors.toList()); - }); - - hoverContent.add(Either.forLeft(builder.toString())); + hoverContent.add(Either.forLeft(Renderables.concat(renderableUrls).toMarkdown())); hoverContent.add(Either.forLeft("Process ID: " + processId)); hoverContent.add(Either.forLeft("Process Name: " + processName)); if (i < mappingMethods.size() - 1) { From 06e86a33a499d157d869222a4da0fcc4fae0d5ea Mon Sep 17 00:00:00 2001 From: BoykoAlex Date: Fri, 27 Oct 2017 11:08:10 -0400 Subject: [PATCH 07/10] Adjust unit tests --- .../java/requestmapping/RequestMappingHoverProvider.java | 8 ++++++-- .../requestmapping/test/RequestMappingLiveHoverTest.java | 4 ++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/RequestMappingHoverProvider.java b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/RequestMappingHoverProvider.java index 6aa65fc81..c1cbb7ad1 100644 --- a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/RequestMappingHoverProvider.java +++ b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/RequestMappingHoverProvider.java @@ -16,6 +16,7 @@ import java.util.Collection; import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.stream.Collectors; +import java.util.stream.Stream; import org.eclipse.jdt.core.dom.ASTNode; import org.eclipse.jdt.core.dom.Annotation; @@ -144,7 +145,7 @@ public class RequestMappingHoverProvider implements HoverProvider { String port = mappingMethod.getT2().getPort(); String host = mappingMethod.getT2().getHost(); - List renderableUrls = Arrays.stream(mappingMethod.getT1().getSplitPath()).map(path -> { + List renderableUrls = Arrays.stream(mappingMethod.getT1().getSplitPath()).flatMap(path -> { String url = UrlUtil.createUrl(host, port, path); StringBuilder builder = new StringBuilder(); builder.append("["); @@ -153,10 +154,13 @@ public class RequestMappingHoverProvider implements HoverProvider { builder.append("("); builder.append(url); builder.append(")"); - return Renderables.concat(Renderables.text(builder.toString()), Renderables.lineBreak()); + return Stream.of(Renderables.text(builder.toString()), Renderables.lineBreak()); }) .collect(Collectors.toList()); + // Remove the last line break + renderableUrls.remove(renderableUrls.size() - 1); + hoverContent.add(Either.forLeft(Renderables.concat(renderableUrls).toMarkdown())); hoverContent.add(Either.forLeft("Process ID: " + processId)); hoverContent.add(Either.forLeft("Process Name: " + processName)); diff --git a/headless-services/boot-java-language-server/src/test/java/org/springframework/ide/vscode/boot/java/requestmapping/test/RequestMappingLiveHoverTest.java b/headless-services/boot-java-language-server/src/test/java/org/springframework/ide/vscode/boot/java/requestmapping/test/RequestMappingLiveHoverTest.java index dd3455556..050227d84 100644 --- a/headless-services/boot-java-language-server/src/test/java/org/springframework/ide/vscode/boot/java/requestmapping/test/RequestMappingLiveHoverTest.java +++ b/headless-services/boot-java-language-server/src/test/java/org/springframework/ide/vscode/boot/java/requestmapping/test/RequestMappingLiveHoverTest.java @@ -62,7 +62,7 @@ public class RequestMappingLiveHoverTest { harness.intialize(directory); Editor editor = harness.newEditorFromFileUri(docUri, LanguageId.JAVA); - editor.assertHoverContains("@RequestMapping(\"/hello-world\")", "[http://cfapps.io:1111/hello-world](http://cfapps.io:1111/hello-world)\n" + + editor.assertHoverContains("@RequestMapping(method=RequestMethod.GET)", "[http://cfapps.io:1111/hello-world](http://cfapps.io:1111/hello-world)\n" + "\n" + "Process ID: 22022\n" + "\n" + @@ -310,7 +310,7 @@ public class RequestMappingLiveHoverTest { "}", docUri); - editor.assertHoverContains("@RequestMapping(value={\"/greetings\", \"/hello\"}, method=GET)", "[http://cfapps.io:999/greetings](http://cfapps.io:999/greetings)\n" + + editor.assertHoverContains("@RequestMapping(value={\"/greetings\", \"/hello\"}, method=GET)", "[http://cfapps.io:999/greetings](http://cfapps.io:999/greetings) \n" + "[http://cfapps.io:999/hello](http://cfapps.io:999/hello)\n" + "\n" + "Process ID: 76543\n" + From c0e55cb7e288956d0e1261f70ff700b2e8592989 Mon Sep 17 00:00:00 2001 From: nsingh Date: Fri, 27 Oct 2017 11:04:41 -0700 Subject: [PATCH 08/10] Removed old way of matching annotations --- .../RequestMappingHoverProvider.java | 115 ------------------ 1 file changed, 115 deletions(-) diff --git a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/RequestMappingHoverProvider.java b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/RequestMappingHoverProvider.java index c1cbb7ad1..81b778110 100644 --- a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/RequestMappingHoverProvider.java +++ b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/requestmapping/RequestMappingHoverProvider.java @@ -104,7 +104,6 @@ public class RequestMappingHoverProvider implements HoverProvider { Collection mappings = app.getRequestMappings(); if (mappings != null && !mappings.isEmpty()) { mappings.stream() -// .filter(rm -> matchesAnnotation(annotation, rm)) .filter(rm -> methodMatchesAnnotation(annotation, rm)) .map(rm -> Tuples.of(rm, app)) .findFirst().ifPresent(t -> results.add(t)); @@ -171,118 +170,4 @@ public class RequestMappingHoverProvider implements HoverProvider { } } - -// protected String getRequestMethod(SingleMemberAnnotation annotation) { -// ITypeBinding type = annotation.resolveTypeBinding(); -// if (type != null) { -// switch (type.getQualifiedName()) { -// case Constants.SPRING_GET_MAPPING: -// return "GET"; -// case Constants.SPRING_POST_MAPPING: -// return "POST"; -// case Constants.SPRING_DELETE_MAPPING: -// return "DELETE"; -// case Constants.SPRING_PUT_MAPPING: -// return "PUT"; -// case Constants.SPRING_PATCH_MAPPING: -// return "PATCH"; -// } -// } -// return null; -// } -// -// private boolean matchesAnnotation(Annotation annotation, RequestMapping rm) { -// String[] mappingPath = null; -// Set methods = null; -// if (annotation instanceof SingleMemberAnnotation) { -// SingleMemberAnnotation singleAnnotation = (SingleMemberAnnotation) annotation; -// Expression valueContent = singleAnnotation.getValue(); -// if (valueContent instanceof StringLiteral) { -// mappingPath = new String[] { ((StringLiteral)valueContent).getLiteralValue() }; -// } -// String method = getRequestMethod(singleAnnotation); -// if (method != null) { -// methods = new HashSet<>(); -// methods.add(method); -// } -// } -// else if (annotation instanceof NormalAnnotation) { -// List values = ((NormalAnnotation) annotation).values(); -// for (Object value : values) { -// if (value instanceof MemberValuePair) { -// MemberValuePair pair = (MemberValuePair)value; -// String name = pair.getName().toString(); -// switch (name) { -// case "value": -// case "path": -// mappingPath = getPaths(pair.getValue()); -// break; -// case "method": -// methods = getRequestMethod(pair.getValue()); -// break; -// } -// } -// } -// -// } -// -// if (mappingPath != null) { -// if (Arrays.equals(mappingPath, rm.getSplitPath())) { -// if (methods == null || methods.isEmpty()) { -// return true; -// } else { -// return methods.equals(rm.getRequestMethods()); -// } -// } -// } -// return false; -// } -// -// private static String getExpressionValueAsString(Expression exp) { -// if (exp instanceof StringLiteral) { -// return ((StringLiteral)exp).getLiteralValue(); -// } else if (exp instanceof QualifiedName) { -// return getExpressionValueAsString(((QualifiedName)exp).getName()); -// } else if (exp instanceof SimpleName) { -// return ((SimpleName)exp).getIdentifier(); -// } else { -// return null; -// } -// } -// -// @SuppressWarnings("unchecked") -// private static String[] getPaths(Expression exp) { -// if (exp instanceof ArrayInitializer) { -// ArrayInitializer array = (ArrayInitializer) exp; -// return ((List)array.expressions()).stream() -// .map(e -> getExpressionValueAsString(e)) -// .filter(Objects::nonNull) -// .toArray(String[]::new); -// } else { -// String rm = getExpressionValueAsString(exp); -// if (rm != null) { -// return new String[] { rm }; -// } -// } -// return null; -// } -// -// @SuppressWarnings("unchecked") -// private static Set getRequestMethod(Expression exp) { -// if (exp instanceof ArrayInitializer) { -// ArrayInitializer array = (ArrayInitializer) exp; -// return ((List)array.expressions()).stream() -// .map(e -> getExpressionValueAsString(e)) -// .filter(Objects::nonNull) -// .collect(Collectors.toSet()); -// } else { -// String rm = getExpressionValueAsString(exp); -// if (rm != null) { -// HashSet methods = new HashSet<>(); -// methods.add(rm); -// } -// } -// return null; -// } - } From d1d884268c5fc07a4ad5b09ff8d8642a7d38cb82 Mon Sep 17 00:00:00 2001 From: nsingh Date: Fri, 27 Oct 2017 11:39:56 -0700 Subject: [PATCH 09/10] Created hover content utils with commonly used information --- .../ConditionalsLiveHoverProvider.java | 3 ++- .../boot/java/utils/HoverContentUtils.java | 21 +++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/HoverContentUtils.java diff --git a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/conditionals/ConditionalsLiveHoverProvider.java b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/conditionals/ConditionalsLiveHoverProvider.java index 0f5d951cf..bd09eea96 100644 --- a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/conditionals/ConditionalsLiveHoverProvider.java +++ b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/conditionals/ConditionalsLiveHoverProvider.java @@ -30,6 +30,7 @@ import org.eclipse.lsp4j.jsonrpc.messages.Either; import org.json.JSONArray; import org.json.JSONObject; import org.springframework.ide.vscode.boot.java.handlers.HoverProvider; +import org.springframework.ide.vscode.boot.java.utils.HoverContentUtils; import org.springframework.ide.vscode.commons.boot.app.cli.SpringBootApp; import org.springframework.ide.vscode.commons.util.BadLocationException; import org.springframework.ide.vscode.commons.util.Log; @@ -100,7 +101,7 @@ public class ConditionalsLiveHoverProvider implements HoverProvider { RunningAppConditional condition = conditions.get(i); hoverContent.add(Either.forLeft(condition.message)); hoverContent.add(Either - .forLeft("Process " + condition.app.getProcessID() + ": " + condition.app.getProcessName())); + .forLeft(HoverContentUtils.getProcessInformation(condition.app))); if (i < conditions.size() - 1) { hoverContent.add(Either.forLeft("---")); diff --git a/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/HoverContentUtils.java b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/HoverContentUtils.java new file mode 100644 index 000000000..5b34dea3c --- /dev/null +++ b/headless-services/boot-java-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/HoverContentUtils.java @@ -0,0 +1,21 @@ +/******************************************************************************* + * 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.utils; + +import org.springframework.ide.vscode.commons.boot.app.cli.SpringBootApp; + +public class HoverContentUtils { + + public static String getProcessInformation(SpringBootApp app) { + return "Process " + app.getProcessID() + ": " + app.getProcessName(); + } + +} From 5b2e074d2de441b568308a35c1381170d405690a Mon Sep 17 00:00:00 2001 From: nsingh Date: Fri, 27 Oct 2017 13:17:54 -0700 Subject: [PATCH 10/10] Fixed conditional test cases Re-enabled and fixed to test new one-line process information --- .../test/ConditionalsLiveHoverTest.java | 277 +++++++++--------- 1 file changed, 138 insertions(+), 139 deletions(-) diff --git a/headless-services/boot-java-language-server/src/test/java/org/springframework/ide/vscode/boot/java/conditionals/test/ConditionalsLiveHoverTest.java b/headless-services/boot-java-language-server/src/test/java/org/springframework/ide/vscode/boot/java/conditionals/test/ConditionalsLiveHoverTest.java index 291f30135..ce876ca07 100644 --- a/headless-services/boot-java-language-server/src/test/java/org/springframework/ide/vscode/boot/java/conditionals/test/ConditionalsLiveHoverTest.java +++ b/headless-services/boot-java-language-server/src/test/java/org/springframework/ide/vscode/boot/java/conditionals/test/ConditionalsLiveHoverTest.java @@ -56,145 +56,144 @@ public class ConditionalsLiveHoverTest { Editor editorWithMethodLiveHover = harness.newEditorFromFileUri(docUri, LanguageId.JAVA); editorWithMethodLiveHover.assertNoHover("@ConditionalOnMissingBean"); } -// -// @Test -// public void testLiveHoverConditionalOnBean() throws Exception { -// -// File directory = new File( -// ProjectsHarness.class.getResource("/test-projects/test-conditionals-live-hover/").toURI()); -// String docUri = "file://" + directory.getAbsolutePath() + "/src/main/java/example/ConditionalOnBeanConfig.java"; -// -// // Build a mock running boot app -// mockAppProvider.builder().isSpringBootApp(true).port("1111").processId("22022").host("cfapps.io") -// .processName("test-conditionals-live-hover") -// .getAutoConfigReport( -// "{\"positiveMatches\":{\"ConditionalOnBeanConfig#hi\":[{\"condition\":\"OnBeanCondition\",\"message\":\"@ConditionalOnBean (types: example.Hello; SearchStrategy: all) found bean 'missing'\"}]}}") -// .build(); -// -// harness.intialize(directory); -// -// Editor editor = harness.newEditorFromFileUri(docUri, LanguageId.JAVA); -// editor.assertHoverContains("@ConditionalOnBean", "Condition: OnBeanCondition\n" + "\n" -// + "Message: @ConditionalOnBean (types: example.Hello; SearchStrategy: all) found bean 'missing'"); -// -// } -// -// @Test -// public void testLiveHoverConditionalOnMissingBean() throws Exception { -// -// File directory = new File( -// ProjectsHarness.class.getResource("/test-projects/test-conditionals-live-hover/").toURI()); -// String docUri = "file://" + directory.getAbsolutePath() -// + "/src/main/java/example/ConditionalOnMissingBeanConfig.java"; -// -// // Build a mock running boot app -// mockAppProvider.builder().isSpringBootApp(true).port("1111").processId("22022").host("cfapps.io") -// .processName("test-conditionals-live-hover") -// .getAutoConfigReport( -// "{\"positiveMatches\":{\"ConditionalOnMissingBeanConfig#missing\":[{\"condition\":\"OnBeanCondition\",\"message\":\"@ConditionalOnMissingBean (types: example.Hello; SearchStrategy: all) did not find any beans\"}]}}") -// .build(); -// -// harness.intialize(directory); -// -// Editor editor = harness.newEditorFromFileUri(docUri, LanguageId.JAVA); -// editor.assertHoverContains("@ConditionalOnMissingBean", "Condition: OnBeanCondition\n" + "\n" -// + "Message: @ConditionalOnMissingBean (types: example.Hello; SearchStrategy: all) did not find any beans"); -// -// } -// -// @Test -// public void testMultipleLiveHoverContentRealProject() throws Exception { -// -// File directory = new File( -// ProjectsHarness.class.getResource("/test-projects/test-conditionals-live-hover/").toURI()); -// String docUri = "file://" + directory.getAbsolutePath() + "/src/main/java/example/MultipleConditionals.java"; -// -// // Build a mock running boot app -// mockAppProvider.builder().isSpringBootApp(true).port("1111").processId("22022").host("cfapps.io") -// .processName("test-conditionals-live-hover") -// .getAutoConfigReport( -// "{\"positiveMatches\":{\"HelloConfig#missing\":[{\"condition\":\"OnBeanCondition\",\"message\":\"@ConditionalOnMissingBean (types: example.Hello; SearchStrategy: all) did not find any beans\"}],\"HelloConfig2#hi\":[{\"condition\":\"OnBeanCondition\",\"message\":\"@ConditionalOnBean (types: example.Hello; SearchStrategy: all) found bean 'missing'\"}],\"MultipleConditionals#hi\":[{\"condition\":\"OnClassCondition\",\"message\":\"@ConditionalOnClass found required class; @ConditionalOnMissingClass did not find unwanted class\"},{\"condition\":\"OnWebApplicationCondition\",\"message\":\"@ConditionalOnWebApplication (required) found StandardServletEnvironment\"},{\"condition\":\"OnJavaCondition\",\"message\":\"@ConditionalOnJava (1.8 or newer) found 1.8\"},{\"condition\":\"OnExpressionCondition\",\"message\":\"@ConditionalOnExpression (#{true}) resulted in true\"},{\"condition\":\"OnBeanCondition\",\"message\":\"@ConditionalOnBean (types: example.Hello; SearchStrategy: all) found beans 'hi', 'missing'\"}]}}") -// .build(); -// -// harness.intialize(directory); -// -// Editor editor = harness.newEditorFromFileUri(docUri, LanguageId.JAVA); -// -// editor.assertHoverContains("@ConditionalOnBean", "Condition: OnBeanCondition\n" + "\n" -// + "Message: @ConditionalOnBean (types: example.Hello; SearchStrategy: all) found beans 'hi', 'missing'"); -// -// editor.assertHoverContains("@ConditionalOnWebApplication", "Condition: OnWebApplicationCondition\n" + "\n" -// + "Message: @ConditionalOnWebApplication (required) found StandardServletEnvironment"); -// -// editor.assertHoverContains("@ConditionalOnJava(value=ConditionalOnJava.JavaVersion.EIGHT)", -// "Condition: OnJavaCondition\n" + "\n" + "Message: @ConditionalOnJava (1.8 or newer) found 1.8"); -// -// editor.assertHoverContains("@ConditionalOnMissingClass", "Condition: OnClassCondition\n" + "\n" -// + "Message: @ConditionalOnClass found required class; @ConditionalOnMissingClass did not find unwanted class"); -// -// editor.assertHoverContains("@ConditionalOnExpression", "Condition: OnExpressionCondition\n" + "\n" -// + "Message: @ConditionalOnExpression (#{true}) resulted in true"); -// } -// -// -// @Test -// public void testMultipleAppsLiveHover() throws Exception { -// -// File directory = new File( -// ProjectsHarness.class.getResource("/test-projects/test-conditionals-live-hover/").toURI()); -// String docUri = "file://" + directory.getAbsolutePath() -// + "/src/main/java/example/ConditionalOnMissingBeanConfig.java"; -// -// // Build a mock running boot app -// mockAppProvider.builder().isSpringBootApp(true).port("1000").processId("70000").host("cfapps.io") -// .processName("test-conditionals-live-hover") -// .getAutoConfigReport( -// "{\"positiveMatches\":{\"ConditionalOnMissingBeanConfig#missing\":[{\"condition\":\"OnBeanCondition\",\"message\":\"@ConditionalOnMissingBean (types: example.Hello; SearchStrategy: all) did not find any beans\"}]}}") -// .build(); -// -// mockAppProvider.builder().isSpringBootApp(true).port("1001").processId("80000").host("cfapps.io") -// .processName("test-conditionals-live-hover") -// .getAutoConfigReport( -// "{\"positiveMatches\":{\"ConditionalOnMissingBeanConfig#missing\":[{\"condition\":\"OnBeanCondition\",\"message\":\"@ConditionalOnMissingBean (types: example.Hello; SearchStrategy: all) did not find any beans\"}]}}") -// .build(); -// -// mockAppProvider.builder().isSpringBootApp(true).port("1002").processId("90000").host("cfapps.io") -// .processName("test-conditionals-live-hover") -// .getAutoConfigReport( -// "{\"positiveMatches\":{\"ConditionalOnMissingBeanConfig#missing\":[{\"condition\":\"OnBeanCondition\",\"message\":\"@ConditionalOnMissingBean (types: example.Hello; SearchStrategy: all) did not find any beans\"}]}}") -// .build(); -// -// harness.intialize(directory); -// -// Editor editor = harness.newEditorFromFileUri(docUri, LanguageId.JAVA); -// -// -// editor.assertHoverContains("@ConditionalOnMissingBean", "Condition: OnBeanCondition\n" + "\n" -// + "Message: @ConditionalOnMissingBean (types: example.Hello; SearchStrategy: all) did not find any beans\n" + -// "\n" + -// "Process ID: 70000\n" + -// "\n" + -// "Process Name: test-conditionals-live-hover\n" + -// "\n" + -// "---\n" + -// "\n" + -// "Condition: OnBeanCondition\n" + "\n" -// + "Message: @ConditionalOnMissingBean (types: example.Hello; SearchStrategy: all) did not find any beans\n" + -// "\n" + -// "Process ID: 80000\n" + -// "\n" + -// "Process Name: test-conditionals-live-hover\n" + -// "\n" + -// "---\n" + -// "\n" + -// "Condition: OnBeanCondition\n" + "\n" -// + "Message: @ConditionalOnMissingBean (types: example.Hello; SearchStrategy: all) did not find any beans\n" + -// "\n" + -// "Process ID: 90000\n" + -// "\n" + -// "Process Name: test-conditionals-live-hover"); -// -// } + + @Test + public void testLiveHoverConditionalOnBean() throws Exception { + + File directory = new File( + ProjectsHarness.class.getResource("/test-projects/test-conditionals-live-hover/").toURI()); + String docUri = "file://" + directory.getAbsolutePath() + "/src/main/java/example/ConditionalOnBeanConfig.java"; + + // Build a mock running boot app + mockAppProvider.builder().isSpringBootApp(true).port("1111").processId("22022").host("cfapps.io") + .processName("test-conditionals-live-hover") + .getAutoConfigReport( + "{\"positiveMatches\":{\"ConditionalOnBeanConfig#hi\":[{\"condition\":\"OnBeanCondition\",\"message\":\"@ConditionalOnBean (types: example.Hello; SearchStrategy: all) found bean 'missing'\"}]}}") + .build(); + + harness.intialize(directory); + + Editor editor = harness.newEditorFromFileUri(docUri, LanguageId.JAVA); + editor.assertHoverContains("@ConditionalOnBean", + "@ConditionalOnBean (types: example.Hello; SearchStrategy: all) found bean 'missing'\n" + + "\n" + + "Process 22022: test-conditionals-live-hover"); + } + + @Test + public void testLiveHoverConditionalOnMissingBean() throws Exception { + + File directory = new File( + ProjectsHarness.class.getResource("/test-projects/test-conditionals-live-hover/").toURI()); + String docUri = "file://" + directory.getAbsolutePath() + + "/src/main/java/example/ConditionalOnMissingBeanConfig.java"; + + // Build a mock running boot app + mockAppProvider.builder().isSpringBootApp(true).port("1111").processId("22022").host("cfapps.io") + .processName("test-conditionals-live-hover") + .getAutoConfigReport( + "{\"positiveMatches\":{\"ConditionalOnMissingBeanConfig#missing\":[{\"condition\":\"OnBeanCondition\",\"message\":\"@ConditionalOnMissingBean (types: example.Hello; SearchStrategy: all) did not find any beans\"}]}}") + .build(); + + harness.intialize(directory); + + Editor editor = harness.newEditorFromFileUri(docUri, LanguageId.JAVA); + editor.assertHoverContains("@ConditionalOnMissingBean", "@ConditionalOnMissingBean (types: example.Hello; SearchStrategy: all) did not find any beans\n"+ + "\n" + + "Process 22022: test-conditionals-live-hover"); + + } + + @Test + public void testMultipleLiveHoverContentRealProject() throws Exception { + + File directory = new File( + ProjectsHarness.class.getResource("/test-projects/test-conditionals-live-hover/").toURI()); + String docUri = "file://" + directory.getAbsolutePath() + "/src/main/java/example/MultipleConditionals.java"; + + // Build a mock running boot app + mockAppProvider.builder().isSpringBootApp(true).port("1111").processId("22022").host("cfapps.io") + .processName("test-conditionals-live-hover") + .getAutoConfigReport( + "{\"positiveMatches\":{\"HelloConfig#missing\":[{\"condition\":\"OnBeanCondition\",\"message\":\"@ConditionalOnMissingBean (types: example.Hello; SearchStrategy: all) did not find any beans\"}],\"HelloConfig2#hi\":[{\"condition\":\"OnBeanCondition\",\"message\":\"@ConditionalOnBean (types: example.Hello; SearchStrategy: all) found bean 'missing'\"}],\"MultipleConditionals#hi\":[{\"condition\":\"OnClassCondition\",\"message\":\"@ConditionalOnClass found required class; @ConditionalOnMissingClass did not find unwanted class\"},{\"condition\":\"OnWebApplicationCondition\",\"message\":\"@ConditionalOnWebApplication (required) found StandardServletEnvironment\"},{\"condition\":\"OnJavaCondition\",\"message\":\"@ConditionalOnJava (1.8 or newer) found 1.8\"},{\"condition\":\"OnExpressionCondition\",\"message\":\"@ConditionalOnExpression (#{true}) resulted in true\"},{\"condition\":\"OnBeanCondition\",\"message\":\"@ConditionalOnBean (types: example.Hello; SearchStrategy: all) found beans 'hi', 'missing'\"}]}}") + .build(); + + harness.intialize(directory); + + Editor editor = harness.newEditorFromFileUri(docUri, LanguageId.JAVA); + + editor.assertHoverContains("@ConditionalOnBean", "@ConditionalOnBean (types: example.Hello; SearchStrategy: all) found beans 'hi', 'missing'\n" + + "\n" + + "Process 22022: test-conditionals-live-hover"); + + editor.assertHoverContains("@ConditionalOnWebApplication", "@ConditionalOnWebApplication (required) found StandardServletEnvironment\n"+ + "\n" + + "Process 22022: test-conditionals-live-hover"); + + editor.assertHoverContains("@ConditionalOnJava(value=ConditionalOnJava.JavaVersion.EIGHT)", + "@ConditionalOnJava (1.8 or newer) found 1.8\n" + + "\n" + + "Process 22022: test-conditionals-live-hover"); + + editor.assertHoverContains("@ConditionalOnMissingClass", "@ConditionalOnClass found required class; @ConditionalOnMissingClass did not find unwanted class\n" + + "\n" + + "Process 22022: test-conditionals-live-hover"); + + editor.assertHoverContains("@ConditionalOnExpression", "@ConditionalOnExpression (#{true}) resulted in true\n" + + "\n" + + "Process 22022: test-conditionals-live-hover"); + } + + + @Test + public void testMultipleAppsLiveHover() throws Exception { + + File directory = new File( + ProjectsHarness.class.getResource("/test-projects/test-conditionals-live-hover/").toURI()); + String docUri = "file://" + directory.getAbsolutePath() + + "/src/main/java/example/ConditionalOnMissingBeanConfig.java"; + + // Build a mock running boot app + mockAppProvider.builder().isSpringBootApp(true).port("1000").processId("70000").host("cfapps.io") + .processName("test-conditionals-live-hover") + .getAutoConfigReport( + "{\"positiveMatches\":{\"ConditionalOnMissingBeanConfig#missing\":[{\"condition\":\"OnBeanCondition\",\"message\":\"@ConditionalOnMissingBean (types: example.Hello; SearchStrategy: all) did not find any beans\"}]}}") + .build(); + + mockAppProvider.builder().isSpringBootApp(true).port("1001").processId("80000").host("cfapps.io") + .processName("test-conditionals-live-hover") + .getAutoConfigReport( + "{\"positiveMatches\":{\"ConditionalOnMissingBeanConfig#missing\":[{\"condition\":\"OnBeanCondition\",\"message\":\"@ConditionalOnMissingBean (types: example.Hello; SearchStrategy: all) did not find any beans\"}]}}") + .build(); + + mockAppProvider.builder().isSpringBootApp(true).port("1002").processId("90000").host("cfapps.io") + .processName("test-conditionals-live-hover") + .getAutoConfigReport( + "{\"positiveMatches\":{\"ConditionalOnMissingBeanConfig#missing\":[{\"condition\":\"OnBeanCondition\",\"message\":\"@ConditionalOnMissingBean (types: example.Hello; SearchStrategy: all) did not find any beans\"}]}}") + .build(); + + harness.intialize(directory); + + Editor editor = harness.newEditorFromFileUri(docUri, LanguageId.JAVA); + + + editor.assertHoverContains("@ConditionalOnMissingBean", "@ConditionalOnMissingBean (types: example.Hello; SearchStrategy: all) did not find any beans\n" + + "\n" + + "Process 70000: test-conditionals-live-hover\n" + + "\n" + + "---\n" + + "\n" + + "@ConditionalOnMissingBean (types: example.Hello; SearchStrategy: all) did not find any beans\n" + + "\n" + + "Process 80000: test-conditionals-live-hover\n" + + "\n" + + "---\n" + + "\n" + + "@ConditionalOnMissingBean (types: example.Hello; SearchStrategy: all) did not find any beans\n" + + "\n" + + "Process 90000: test-conditionals-live-hover"); + + } // @Test // public void testMultipleLiveHoverHints() throws Exception {